package tests

import (
	"fmt"
	"path/filepath"
	"strings"
	"testing"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	v1alpha1 "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
	actionsgithubcom "github.com/actions/actions-runner-controller/controllers/actions.github.com"
	"github.com/gruntwork-io/terratest/modules/helm"
	"github.com/gruntwork-io/terratest/modules/k8s"
	"github.com/gruntwork-io/terratest/modules/logger"
	"github.com/gruntwork-io/terratest/modules/random"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
)

func TestTemplateRenderedGitHubSecretWithGitHubToken(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"})

	var githubSecret corev1.Secret
	helm.UnmarshalK8SYaml(t, output, &githubSecret)

	assert.Equal(t, namespaceName, githubSecret.Namespace)
	assert.Equal(t, "test-runners-gha-rs-github-secret", githubSecret.Name)
	assert.Equal(t, "gh_token12345", string(githubSecret.Data["github_token"]))
	assert.Equal(t, "actions.github.com/cleanup-protection", githubSecret.Finalizers[0])
}

func TestTemplateRenderedGitHubSecretWithGitHubApp(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                               "https://github.com/actions",
			"githubConfigSecret.github_app_id":              "10",
			"githubConfigSecret.github_app_installation_id": "100",
			"githubConfigSecret.github_app_private_key":     "private_key",
			"controllerServiceAccount.name":                 "arc",
			"controllerServiceAccount.namespace":            "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"})

	var githubSecret corev1.Secret
	helm.UnmarshalK8SYaml(t, output, &githubSecret)

	assert.Equal(t, namespaceName, githubSecret.Namespace)
	assert.Equal(t, "10", string(githubSecret.Data["github_app_id"]))
	assert.Equal(t, "100", string(githubSecret.Data["github_app_installation_id"]))
	assert.Equal(t, "private_key", string(githubSecret.Data["github_app_private_key"]))
}

func TestTemplateRenderedGitHubSecretErrorWithMissingAuthInput(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_app_id":   "",
			"githubConfigSecret.github_token":    "",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	_, err = helm.RenderTemplateContextE(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"})
	require.Error(t, err)

	assert.ErrorContains(t, err, "provide .Values.githubConfigSecret.github_token or .Values.githubConfigSecret.github_app_id")
}

func TestTemplateRenderedGitHubSecretErrorWithMissingAppInput(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_app_id":   "10",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	_, err = helm.RenderTemplateContextE(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"})
	require.Error(t, err)

	assert.ErrorContains(t, err, "provide .Values.githubConfigSecret.github_app_installation_id and .Values.githubConfigSecret.github_app_private_key")
}

func TestTemplateNotRenderedGitHubSecretWithPredefinedSecret(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret":                 "pre-defined-secret",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	_, err = helm.RenderTemplateContextE(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"})
	assert.ErrorContains(t, err, "could not find template templates/githubsecret.yaml in chart", "secret should not be rendered since a pre-defined secret is provided")
}

func TestTemplateRenderedSetServiceAccountToNoPermission(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/no_permission_serviceaccount.yaml"})
	var serviceAccount corev1.ServiceAccount
	helm.UnmarshalK8SYaml(t, output, &serviceAccount)

	assert.Equal(t, namespaceName, serviceAccount.Namespace)
	assert.Equal(t, "test-runners-gha-rs-no-permission", serviceAccount.Name)

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, "test-runners-gha-rs-no-permission", ars.Spec.Template.Spec.ServiceAccountName)
	assert.Empty(t, ars.Annotations[actionsgithubcom.AnnotationKeyKubernetesModeServiceAccountName]) // no finalizer protections in place
}

func TestTemplateRenderedSetServiceAccountToKubeMode(t *testing.T) {
	t.Parallel()
	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"containerMode.type":                 "kubernetes",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_serviceaccount.yaml"})
	var serviceAccount corev1.ServiceAccount
	helm.UnmarshalK8SYaml(t, output, &serviceAccount)

	assert.Equal(t, namespaceName, serviceAccount.Namespace)
	assert.Equal(t, "test-runners-gha-rs-kube-mode", serviceAccount.Name)
	assert.Equal(t, "actions.github.com/cleanup-protection", serviceAccount.Finalizers[0])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_role.yaml"})
	var role rbacv1.Role
	helm.UnmarshalK8SYaml(t, output, &role)

	assert.Equal(t, namespaceName, role.Namespace)
	assert.Equal(t, "test-runners-gha-rs-kube-mode", role.Name)

	assert.Equal(t, "actions.github.com/cleanup-protection", role.Finalizers[0])

	assert.Len(t, role.Rules, 5, "kube mode role should have 5 rules")
	assert.Equal(t, "pods", role.Rules[0].Resources[0])
	assert.Equal(t, "pods/exec", role.Rules[1].Resources[0])
	assert.Equal(t, "pods/log", role.Rules[2].Resources[0])
	assert.Equal(t, "jobs", role.Rules[3].Resources[0])
	assert.Equal(t, "secrets", role.Rules[4].Resources[0])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_role_binding.yaml"})
	var roleBinding rbacv1.RoleBinding
	helm.UnmarshalK8SYaml(t, output, &roleBinding)

	assert.Equal(t, namespaceName, roleBinding.Namespace)
	assert.Equal(t, "test-runners-gha-rs-kube-mode", roleBinding.Name)
	assert.Len(t, roleBinding.Subjects, 1)
	assert.Equal(t, "test-runners-gha-rs-kube-mode", roleBinding.Subjects[0].Name)
	assert.Equal(t, namespaceName, roleBinding.Subjects[0].Namespace)
	assert.Equal(t, "test-runners-gha-rs-kube-mode", roleBinding.RoleRef.Name)
	assert.Equal(t, "Role", roleBinding.RoleRef.Kind)
	assert.Equal(t, "actions.github.com/cleanup-protection", serviceAccount.Finalizers[0])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	expectedServiceAccountName := "test-runners-gha-rs-kube-mode"
	assert.Equal(t, expectedServiceAccountName, ars.Spec.Template.Spec.ServiceAccountName)
	assert.Equal(t, expectedServiceAccountName, ars.Annotations[actionsgithubcom.AnnotationKeyKubernetesModeServiceAccountName])
}

func TestTemplateRenderedSetServiceAccountToKubeNoVolumeMode(t *testing.T) {
	t.Parallel()
	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"containerMode.type":                 "kubernetes-novolume",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_serviceaccount.yaml"})
	var serviceAccount corev1.ServiceAccount
	helm.UnmarshalK8SYaml(t, output, &serviceAccount)

	assert.Equal(t, namespaceName, serviceAccount.Namespace)
	assert.Equal(t, "test-runners-gha-rs-kube-mode", serviceAccount.Name)
	assert.Equal(t, "actions.github.com/cleanup-protection", serviceAccount.Finalizers[0])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_role.yaml"})
	var role rbacv1.Role
	helm.UnmarshalK8SYaml(t, output, &role)

	assert.Equal(t, namespaceName, role.Namespace)
	assert.Equal(t, "test-runners-gha-rs-kube-mode", role.Name)

	assert.Equal(t, "actions.github.com/cleanup-protection", role.Finalizers[0])

	assert.Len(t, role.Rules, 4, "kube mode role should have 4 rules")
	assert.Equal(t, "pods", role.Rules[0].Resources[0])
	assert.Equal(t, "pods/exec", role.Rules[1].Resources[0])
	assert.Equal(t, "pods/log", role.Rules[2].Resources[0])
	assert.Equal(t, "secrets", role.Rules[3].Resources[0])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_role_binding.yaml"})
	var roleBinding rbacv1.RoleBinding
	helm.UnmarshalK8SYaml(t, output, &roleBinding)

	assert.Equal(t, namespaceName, roleBinding.Namespace)
	assert.Equal(t, "test-runners-gha-rs-kube-mode", roleBinding.Name)
	assert.Len(t, roleBinding.Subjects, 1)
	assert.Equal(t, "test-runners-gha-rs-kube-mode", roleBinding.Subjects[0].Name)
	assert.Equal(t, namespaceName, roleBinding.Subjects[0].Namespace)
	assert.Equal(t, "test-runners-gha-rs-kube-mode", roleBinding.RoleRef.Name)
	assert.Equal(t, "Role", roleBinding.RoleRef.Kind)
	assert.Equal(t, "actions.github.com/cleanup-protection", serviceAccount.Finalizers[0])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	expectedServiceAccountName := "test-runners-gha-rs-kube-mode"
	assert.Equal(t, expectedServiceAccountName, ars.Spec.Template.Spec.ServiceAccountName)
	assert.Equal(t, expectedServiceAccountName, ars.Annotations[actionsgithubcom.AnnotationKeyKubernetesModeServiceAccountName])
}

func TestTemplateRenderedNoPermissionServiceAccountNotRenderedInKubernetesModes(t *testing.T) {
	t.Parallel()

	for _, mode := range []string{"kubernetes", "kubernetes-novolume"} {
		t.Run("containerMode "+mode, func(t *testing.T) {
			helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
			require.NoError(t, err)

			releaseName := "test-runners"
			namespaceName := "test-" + strings.ToLower(random.UniqueID())

			options := &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"githubConfigUrl":                    "https://github.com/actions",
					"githubConfigSecret.github_token":    "gh_token12345",
					"controllerServiceAccount.name":      "arc",
					"controllerServiceAccount.namespace": "arc-system",
					"containerMode.type":                 mode,
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
			}

			_, err = helm.RenderTemplateContextE(
				t,
				t.Context(),
				options,
				helmChartPath,
				releaseName,
				[]string{"templates/no_permission_serviceaccount.yaml"},
			)
			assert.ErrorContains(
				t,
				err,
				"could not find template templates/no_permission_serviceaccount.yaml in chart",
				"no permission service account should not be rendered in "+mode+" mode",
			)
		})
	}
}

func TestTemplateRenderedUserProvideSetServiceAccount(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"template.spec.serviceAccountName":   "test-service-account",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	_, err = helm.RenderTemplateContextE(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/no_permission_serviceaccount.yaml"})
	assert.ErrorContains(t, err, "could not find template templates/no_permission_serviceaccount.yaml in chart", "no permission service account should not be rendered")

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, "test-service-account", ars.Spec.Template.Spec.ServiceAccountName)
	assert.Empty(t, ars.Annotations[actionsgithubcom.AnnotationKeyKubernetesModeServiceAccountName])
}

func TestTemplateRenderedAutoScalingRunnerSet(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, namespaceName, ars.Namespace)
	assert.Equal(t, "test-runners", ars.Name)

	assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/name"])
	assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/instance"])
	assert.Equal(t, "gha-rs", ars.Labels["app.kubernetes.io/part-of"])
	assert.Equal(t, "autoscaling-runner-set", ars.Labels["app.kubernetes.io/component"])
	assert.NotEmpty(t, ars.Labels["app.kubernetes.io/version"])

	assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl)
	assert.Equal(t, "test-runners-gha-rs-github-secret", ars.Spec.GitHubConfigSecret)

	assert.Empty(t, ars.Spec.RunnerGroup, "RunnerGroup should be empty")

	assert.Nil(t, ars.Spec.MinRunners, "MinRunners should be nil")
	assert.Nil(t, ars.Spec.MaxRunners, "MaxRunners should be nil")
	assert.Nil(t, ars.Spec.Proxy, "Proxy should be nil")
	assert.Nil(t, ars.Spec.GitHubServerTLS, "GitHubServerTLS should be nil")

	assert.NotNil(t, ars.Spec.Template.Spec, "Template.Spec should not be nil")

	assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "Template.Spec should have 1 container")
	assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name)
	assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image)
}

func TestTemplateRenderedAutoScalingRunnerSet_RunnerScaleSetName(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	nameOverride := "test-runner-scale-set-name"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"runnerScaleSetName":                 nameOverride,
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, namespaceName, ars.Namespace)
	assert.Equal(t, nameOverride, ars.Name)

	assert.Equal(t, nameOverride, ars.Labels["app.kubernetes.io/name"])
	assert.Equal(t, nameOverride, ars.Labels["app.kubernetes.io/instance"])
	assert.Equal(t, nameOverride, ars.Labels["actions.github.com/scale-set-name"])
	assert.Equal(t, namespaceName, ars.Labels["actions.github.com/scale-set-namespace"])
	assert.Equal(t, "gha-rs", ars.Labels["app.kubernetes.io/part-of"])
	assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl)
	assert.Equal(t, nameOverride+"-gha-rs-github-secret", ars.Spec.GitHubConfigSecret)
	assert.Equal(t, "test-runner-scale-set-name", ars.Spec.RunnerScaleSetName)

	assert.Empty(t, ars.Spec.RunnerGroup, "RunnerGroup should be empty")

	assert.Nil(t, ars.Spec.MinRunners, "MinRunners should be nil")
	assert.Nil(t, ars.Spec.MaxRunners, "MaxRunners should be nil")
	assert.Nil(t, ars.Spec.Proxy, "Proxy should be nil")
	assert.Nil(t, ars.Spec.GitHubServerTLS, "GitHubServerTLS should be nil")

	assert.NotNil(t, ars.Spec.Template.Spec, "Template.Spec should not be nil")

	assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "Template.Spec should have 1 container")
	assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name)
	assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image)
}

func TestTemplateRenderedAutoScalingRunnerSet_ScaleSetLabels(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"scaleSetLabels[0]":                  "linux",
			"scaleSetLabels[1]":                  "x64",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, []string{"linux", "x64"}, ars.Spec.RunnerScaleSetLabels)
}

func TestTemplateRenderedAutoScalingRunnerSet_ProvideMetadata(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                     "https://github.com/actions",
			"githubConfigSecret.github_token":     "gh_token12345",
			"template.metadata.labels.test1":      "test1",
			"template.metadata.labels.test2":      "test2",
			"template.metadata.annotations.test3": "test3",
			"template.metadata.annotations.test4": "test4",
			"controllerServiceAccount.name":       "arc",
			"controllerServiceAccount.namespace":  "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, namespaceName, ars.Namespace)
	assert.Equal(t, "test-runners", ars.Name)

	assert.NotNil(t, ars.Spec.Template.Labels, "Template.Spec.Labels should not be nil")
	assert.Equal(t, "test1", ars.Spec.Template.Labels["test1"], "Template.Spec.Labels should have test1")
	assert.Equal(t, "test2", ars.Spec.Template.Labels["test2"], "Template.Spec.Labels should have test2")

	assert.NotNil(t, ars.Spec.Template.Annotations, "Template.Spec.Annotations should not be nil")
	assert.Equal(t, "test3", ars.Spec.Template.Annotations["test3"], "Template.Spec.Annotations should have test3")
	assert.Equal(t, "test4", ars.Spec.Template.Annotations["test4"], "Template.Spec.Annotations should have test4")

	assert.NotNil(t, ars.Spec.Template.Spec, "Template.Spec should not be nil")

	assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "Template.Spec should have 1 container")
	assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name)
	assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image)
}

func TestTemplateRenderedAutoScalingRunnerSet_MaxRunnersValidationError(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"maxRunners":                         "-1",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	_, err = helm.RenderTemplateContextE(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	require.Error(t, err)

	assert.ErrorContains(t, err, "maxRunners has to be greater or equal to 0")
}

func TestTemplateRenderedAutoScalingRunnerSet_MinRunnersValidationError(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"maxRunners":                         "1",
			"minRunners":                         "-1",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	_, err = helm.RenderTemplateContextE(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	require.Error(t, err)

	assert.ErrorContains(t, err, "minRunners has to be greater or equal to 0")
}

func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidationError(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"maxRunners":                         "0",
			"minRunners":                         "1",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	_, err = helm.RenderTemplateContextE(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	require.Error(t, err)

	assert.ErrorContains(t, err, "maxRunners has to be greater or equal to minRunners")
}

func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidationSameValue(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"maxRunners":                         "0",
			"minRunners":                         "0",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, 0, *ars.Spec.MinRunners, "MinRunners should be 0")
	assert.Equal(t, 0, *ars.Spec.MaxRunners, "MaxRunners should be 0")
}

func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidation_OnlyMin(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"minRunners":                         "5",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, 5, *ars.Spec.MinRunners, "MinRunners should be 5")
	assert.Nil(t, ars.Spec.MaxRunners, "MaxRunners should be nil")
}

func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidation_OnlyMax(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"maxRunners":                         "5",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, 5, *ars.Spec.MaxRunners, "MaxRunners should be 5")
	assert.Nil(t, ars.Spec.MinRunners, "MinRunners should be nil")
}

func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunners_FromValuesFile(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger:         logger.Discard,
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, 5, *ars.Spec.MinRunners, "MinRunners should be 5")
	assert.Equal(t, 10, *ars.Spec.MaxRunners, "MaxRunners should be 10")
}

func TestTemplateRenderedAutoScalingRunnerSet_ExtraVolumes(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_extra_volumes.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Len(t, ars.Spec.Template.Spec.Volumes, 3, "Volumes should be 3")
	assert.Equal(t, "foo", ars.Spec.Template.Spec.Volumes[0].Name, "Volume name should be foo")
	assert.Equal(t, "bar", ars.Spec.Template.Spec.Volumes[1].Name, "Volume name should be bar")
	assert.Equal(t, "work", ars.Spec.Template.Spec.Volumes[2].Name, "Volume name should be work")
	assert.Equal(t, "/data", ars.Spec.Template.Spec.Volumes[2].HostPath.Path, "Volume host path should be /data")
}

func TestTemplateRenderedAutoScalingRunnerSet_DinD_ExtraInitContainers(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_dind_extra_init_containers.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Len(t, ars.Spec.Template.Spec.InitContainers, 4, "InitContainers should be 4")
	assert.Equal(t, "kube-init", ars.Spec.Template.Spec.InitContainers[2].Name, "InitContainers[1] Name should be kube-init")
	assert.Equal(t, "runner-image:latest", ars.Spec.Template.Spec.InitContainers[2].Image, "InitContainers[1] Image should be runner-image:latest")
	assert.Equal(t, "sudo", ars.Spec.Template.Spec.InitContainers[2].Command[0], "InitContainers[1] Command[0] should be sudo")
	assert.Equal(t, "chown", ars.Spec.Template.Spec.InitContainers[2].Command[1], "InitContainers[1] Command[1] should be chown")
	assert.Equal(t, "-R", ars.Spec.Template.Spec.InitContainers[2].Command[2], "InitContainers[1] Command[2] should be -R")
	assert.Equal(t, "1001:123", ars.Spec.Template.Spec.InitContainers[2].Command[3], "InitContainers[1] Command[3] should be 1001:123")
	assert.Equal(t, "/home/runner/_work", ars.Spec.Template.Spec.InitContainers[2].Command[4], "InitContainers[1] Command[4] should be /home/runner/_work")
	assert.Equal(t, "work", ars.Spec.Template.Spec.InitContainers[2].VolumeMounts[0].Name, "InitContainers[1] VolumeMounts[0] Name should be work")
	assert.Equal(t, "/home/runner/_work", ars.Spec.Template.Spec.InitContainers[2].VolumeMounts[0].MountPath, "InitContainers[1] VolumeMounts[0] MountPath should be /home/runner/_work")

	assert.Equal(t, "ls", ars.Spec.Template.Spec.InitContainers[3].Name, "InitContainers[2] Name should be ls")
	assert.Equal(t, "ubuntu:latest", ars.Spec.Template.Spec.InitContainers[3].Image, "InitContainers[2] Image should be ubuntu:latest")
	assert.Equal(t, "ls", ars.Spec.Template.Spec.InitContainers[3].Command[0], "InitContainers[2] Command[0] should be ls")
}

func TestTemplateRenderedAutoScalingRunnerSet_DinD_ExtraVolumes(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_dind_extra_volumes.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Len(t, ars.Spec.Template.Spec.Volumes, 5, "Volumes should be 5")
	assert.Equal(t, "dind-sock", ars.Spec.Template.Spec.Volumes[0].Name, "Volume name should be dind-sock")
	assert.Equal(t, "dind-externals", ars.Spec.Template.Spec.Volumes[1].Name, "Volume name should be dind-externals")
	assert.Equal(t, "work", ars.Spec.Template.Spec.Volumes[2].Name, "Volume name should be work")
	assert.Equal(t, "/data", ars.Spec.Template.Spec.Volumes[2].HostPath.Path, "Volume host path should be /data")
	assert.Equal(t, "foo", ars.Spec.Template.Spec.Volumes[3].Name, "Volume name should be foo")
	assert.Equal(t, "bar", ars.Spec.Template.Spec.Volumes[4].Name, "Volume name should be bar")
}

func TestTemplateRenderedAutoScalingRunnerSet_K8S_ExtraVolumes(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_k8s_extra_volumes.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Len(t, ars.Spec.Template.Spec.Volumes, 3, "Volumes should be 3")
	assert.Equal(t, "work", ars.Spec.Template.Spec.Volumes[0].Name, "Volume name should be work")
	assert.Equal(t, "/data", ars.Spec.Template.Spec.Volumes[0].HostPath.Path, "Volume host path should be /data")
	assert.Equal(t, "foo", ars.Spec.Template.Spec.Volumes[1].Name, "Volume name should be foo")
	assert.Equal(t, "bar", ars.Spec.Template.Spec.Volumes[2].Name, "Volume name should be bar")
}

func TestTemplateRenderedAutoScalingRunnerSet_EnableDinD(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"containerMode.type":                 "dind",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, namespaceName, ars.Namespace)
	assert.Equal(t, "test-runners", ars.Name)

	assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/name"])
	assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/instance"])
	assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl)
	assert.Equal(t, "test-runners-gha-rs-github-secret", ars.Spec.GitHubConfigSecret)

	assert.Empty(t, ars.Spec.RunnerGroup, "RunnerGroup should be empty")

	assert.Nil(t, ars.Spec.MinRunners, "MinRunners should be nil")
	assert.Nil(t, ars.Spec.MaxRunners, "MaxRunners should be nil")
	assert.Nil(t, ars.Spec.Proxy, "Proxy should be nil")
	assert.Nil(t, ars.Spec.GitHubServerTLS, "GitHubServerTLS should be nil")

	assert.NotNil(t, ars.Spec.Template.Spec, "Template.Spec should not be nil")

	assert.Len(t, ars.Spec.Template.Spec.InitContainers, 2, "Template.Spec should have 2 init container")
	assert.Equal(t, "init-dind-externals", ars.Spec.Template.Spec.InitContainers[0].Name)
	assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.InitContainers[0].Image)
	assert.Equal(t, "cp", ars.Spec.Template.Spec.InitContainers[0].Command[0])
	assert.Equal(t, "-r /home/runner/externals/. /home/runner/tmpDir/", strings.Join(ars.Spec.Template.Spec.InitContainers[0].Args, " "))

	assert.Equal(t, "dind", ars.Spec.Template.Spec.InitContainers[1].Name)
	assert.Equal(t, "docker:dind", ars.Spec.Template.Spec.InitContainers[1].Image)
	assert.True(t, *ars.Spec.Template.Spec.InitContainers[1].SecurityContext.Privileged)
	assert.Len(t, ars.Spec.Template.Spec.InitContainers[1].VolumeMounts, 3, "The dind container should have 3 volume mounts, dind-sock, work and externals")
	assert.Equal(t, "work", ars.Spec.Template.Spec.InitContainers[1].VolumeMounts[0].Name)
	assert.Equal(t, "/home/runner/_work", ars.Spec.Template.Spec.InitContainers[1].VolumeMounts[0].MountPath)

	assert.Equal(t, "dind-sock", ars.Spec.Template.Spec.InitContainers[1].VolumeMounts[1].Name)
	assert.Equal(t, "/var/run", ars.Spec.Template.Spec.InitContainers[1].VolumeMounts[1].MountPath)

	assert.Equal(t, "dind-externals", ars.Spec.Template.Spec.InitContainers[1].VolumeMounts[2].Name)
	assert.Equal(t, "/home/runner/externals", ars.Spec.Template.Spec.InitContainers[1].VolumeMounts[2].MountPath)

	assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "Template.Spec should have 1 container")
	assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name)
	assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image)
	assert.Len(t, ars.Spec.Template.Spec.Containers[0].Env, 2, "The runner container should have 2 env vars, DOCKER_HOST and RUNNER_WAIT_FOR_DOCKER_IN_SECONDS")
	assert.Equal(t, "DOCKER_HOST", ars.Spec.Template.Spec.Containers[0].Env[0].Name)
	assert.Equal(t, "unix:///var/run/docker.sock", ars.Spec.Template.Spec.Containers[0].Env[0].Value)
	assert.Equal(t, "RUNNER_WAIT_FOR_DOCKER_IN_SECONDS", ars.Spec.Template.Spec.Containers[0].Env[1].Name)
	assert.Equal(t, "120", ars.Spec.Template.Spec.Containers[0].Env[1].Value)

	assert.Len(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts, 2, "The runner container should have 2 volume mounts, dind-sock and work")
	assert.Equal(t, "work", ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name)
	assert.Equal(t, "/home/runner/_work", ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath)
	assert.False(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].ReadOnly)

	assert.Equal(t, "dind-sock", ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].Name)
	assert.Equal(t, "/var/run", ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].MountPath)

	assert.Len(t, ars.Spec.Template.Spec.Volumes, 3, "Volumes should be 3")
	assert.Equal(t, "dind-sock", ars.Spec.Template.Spec.Volumes[0].Name, "Volume name should be dind-sock")
	assert.Equal(t, "dind-externals", ars.Spec.Template.Spec.Volumes[1].Name, "Volume name should be dind-externals")
	assert.Equal(t, "work", ars.Spec.Template.Spec.Volumes[2].Name, "Volume name should be work")
	assert.NotNil(t, ars.Spec.Template.Spec.Volumes[2].EmptyDir, "Volume work should be an emptyDir")
}

func TestTemplateRenderedAutoScalingRunnerSet_EnableKubernetesMode(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"containerMode.type":                 "kubernetes",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, namespaceName, ars.Namespace)
	assert.Equal(t, "test-runners", ars.Name)

	assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/name"])
	assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/instance"])
	assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl)
	assert.Equal(t, "test-runners-gha-rs-github-secret", ars.Spec.GitHubConfigSecret)

	assert.Empty(t, ars.Spec.RunnerGroup, "RunnerGroup should be empty")
	assert.Nil(t, ars.Spec.MinRunners, "MinRunners should be nil")
	assert.Nil(t, ars.Spec.MaxRunners, "MaxRunners should be nil")
	assert.Nil(t, ars.Spec.Proxy, "Proxy should be nil")
	assert.Nil(t, ars.Spec.GitHubServerTLS, "GitHubServerTLS should be nil")

	assert.NotNil(t, ars.Spec.Template.Spec, "Template.Spec should not be nil")

	assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "Template.Spec should have 1 container")
	assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name)
	assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image)

	assert.Equal(t, "ACTIONS_RUNNER_CONTAINER_HOOKS", ars.Spec.Template.Spec.Containers[0].Env[0].Name)
	assert.Equal(t, "/home/runner/k8s/index.js", ars.Spec.Template.Spec.Containers[0].Env[0].Value)
	assert.Equal(t, "ACTIONS_RUNNER_POD_NAME", ars.Spec.Template.Spec.Containers[0].Env[1].Name)
	assert.Equal(t, "ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER", ars.Spec.Template.Spec.Containers[0].Env[2].Name)
	assert.Equal(t, "true", ars.Spec.Template.Spec.Containers[0].Env[2].Value)

	assert.Len(t, ars.Spec.Template.Spec.Volumes, 1, "Template.Spec should have 1 volume")
	assert.Equal(t, "work", ars.Spec.Template.Spec.Volumes[0].Name)
	assert.NotNil(t, ars.Spec.Template.Spec.Volumes[0].Ephemeral, "Template.Spec should have 1 ephemeral volume")
}

func TestTemplateRenderedAutoScalingRunnerSet_EnableKubernetesModeNoVolume(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"containerMode.type":                 "kubernetes-novolume",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, namespaceName, ars.Namespace)
	assert.Equal(t, "test-runners", ars.Name)

	assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/name"])
	assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/instance"])
	assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl)
	assert.Equal(t, "test-runners-gha-rs-github-secret", ars.Spec.GitHubConfigSecret)

	assert.Empty(t, ars.Spec.RunnerGroup, "RunnerGroup should be empty")
	assert.Nil(t, ars.Spec.MinRunners, "MinRunners should be nil")
	assert.Nil(t, ars.Spec.MaxRunners, "MaxRunners should be nil")
	assert.Nil(t, ars.Spec.Proxy, "Proxy should be nil")
	assert.Nil(t, ars.Spec.GitHubServerTLS, "GitHubServerTLS should be nil")

	assert.NotNil(t, ars.Spec.Template.Spec, "Template.Spec should not be nil")

	assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "Template.Spec should have 1 container")
	assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name)
	assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image)

	require.Len(t, ars.Spec.Template.Spec.Containers[0].Env, 4, "The runner container should have 4 env vars")
	assert.Equal(t, "ACTIONS_RUNNER_CONTAINER_HOOKS", ars.Spec.Template.Spec.Containers[0].Env[0].Name)
	assert.Equal(t, "/home/runner/k8s-novolume/index.js", ars.Spec.Template.Spec.Containers[0].Env[0].Value)
	assert.Equal(t, "ACTIONS_RUNNER_POD_NAME", ars.Spec.Template.Spec.Containers[0].Env[1].Name)
	assert.Equal(t, "ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER", ars.Spec.Template.Spec.Containers[0].Env[2].Name)
	assert.Equal(t, "true", ars.Spec.Template.Spec.Containers[0].Env[2].Value)
	assert.Equal(t, "ACTIONS_RUNNER_IMAGE", ars.Spec.Template.Spec.Containers[0].Env[3].Name)
	assert.Equal(t, ars.Spec.Template.Spec.Containers[0].Image, ars.Spec.Template.Spec.Containers[0].Env[3].Value)

	assert.Len(t, ars.Spec.Template.Spec.Volumes, 0, "Template.Spec should have 0 volumes")
	assert.NotNil(t, ars.Spec.Template.Spec.Volumes, "Template.Spec.Volumes should be non-nil empty slice, not null")

	// Regression check: ensure volumeMounts is also non-nil empty slice for kubernetes-novolume
	runnerContainer := ars.Spec.Template.Spec.Containers[0]
	assert.NotNil(t, runnerContainer.VolumeMounts, "runner container VolumeMounts should be non-nil empty slice, not null")
	assert.Len(t, runnerContainer.VolumeMounts, 0, "runner container should have 0 volumeMounts in kubernetes-novolume mode")
}

func TestTemplateRenderedAutoScalingRunnerSet_EnableKubernetesModeNoVolume_WithCustomVolumes(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_k8s_novolume_custom_volumes.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	// Test that user-provided volumes are preserved even in kubernetes-novolume mode
	options := &helm.Options{
		Logger:         logger.Discard,
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	// Override preservation: user-provided volume should be present, not replaced by empty array
	assert.Len(t, ars.Spec.Template.Spec.Volumes, 1, "Template.Spec should have 1 volume (user-provided override)")
	assert.Equal(t, "custom-volume", ars.Spec.Template.Spec.Volumes[0].Name, "Volume name should be preserved as custom-volume")
	assert.NotNil(t, ars.Spec.Template.Spec.Volumes[0].EmptyDir, "Volume should have EmptyDir configured")
}

func TestTemplateRenderedAutoScalingRunnerSet_EnableKubernetesModeNoVolume_WithCustomVolumeMounts(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_k8s_novolume_custom_volume_mounts.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	// Test that user-provided volumeMounts are preserved in kubernetes-novolume runner container
	options := &helm.Options{
		Logger:         logger.Discard,
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	runnerContainer := ars.Spec.Template.Spec.Containers[0]
	// Override preservation: user-provided volumeMounts should be present, not replaced by empty array
	assert.Len(t, runnerContainer.VolumeMounts, 1, "runner container should have 1 volumeMount (user-provided override)")
	assert.Equal(t, "custom-volume", runnerContainer.VolumeMounts[0].Name, "VolumeMount name should be preserved as custom-volume")
	assert.Equal(t, "/mnt/custom", runnerContainer.VolumeMounts[0].MountPath, "VolumeMount path should be preserved as /mnt/custom")
}

func TestTemplateRenderedAutoscalingRunnerSet_ListenerPodTemplate(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_listener_template.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	require.NotNil(t, ars.Spec.ListenerTemplate, "ListenerPodTemplate should not be nil")

	assert.Equal(t, ars.Spec.ListenerTemplate.Spec.Hostname, "example")

	require.Len(t, ars.Spec.ListenerTemplate.Spec.Containers, 2, "ListenerPodTemplate should have 2 containers")
	assert.Equal(t, ars.Spec.ListenerTemplate.Spec.Containers[0].Name, "listener")
	assert.Equal(t, ars.Spec.ListenerTemplate.Spec.Containers[0].Image, "listener:latest")
	assert.ElementsMatch(t, ars.Spec.ListenerTemplate.Spec.Containers[0].Command, []string{"/path/to/entrypoint"})
	assert.Len(t, ars.Spec.ListenerTemplate.Spec.Containers[0].VolumeMounts, 1, "VolumeMounts should be 1")
	assert.Equal(t, ars.Spec.ListenerTemplate.Spec.Containers[0].VolumeMounts[0].Name, "work")
	assert.Equal(t, ars.Spec.ListenerTemplate.Spec.Containers[0].VolumeMounts[0].MountPath, "/home/example")

	assert.Equal(t, ars.Spec.ListenerTemplate.Spec.Containers[1].Name, "side-car")
	assert.Equal(t, ars.Spec.ListenerTemplate.Spec.Containers[1].Image, "nginx:latest")
}

func TestTemplateRenderedAutoScalingRunnerSet_UsePredefinedSecret(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret":                 "pre-defined-secrets",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, namespaceName, ars.Namespace)
	assert.Equal(t, "test-runners", ars.Name)

	assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/name"])
	assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/instance"])
	assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl)
	assert.Equal(t, "pre-defined-secrets", ars.Spec.GitHubConfigSecret)
}

func TestTemplateRenderedAutoScalingRunnerSet_ErrorOnEmptyPredefinedSecret(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret":                 "",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	_, err = helm.RenderTemplateContextE(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	require.Error(t, err)

	assert.ErrorContains(t, err, "Values.githubConfigSecret is required for setting auth with GitHub server")
}

func TestTemplateRenderedWithProxy(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret":                 "pre-defined-secrets",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
			"proxy.http.url":                     "http://proxy.example.com",
			"proxy.http.credentialSecretRef":     "http-secret",
			"proxy.https.url":                    "https://proxy.example.com",
			"proxy.https.credentialSecretRef":    "https-secret",
			"proxy.noProxy":                      "{example.com,example.org}",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	require.NotNil(t, ars.Spec.Proxy)
	require.NotNil(t, ars.Spec.Proxy.HTTP)
	assert.Equal(t, "http://proxy.example.com", ars.Spec.Proxy.HTTP.Url)
	assert.Equal(t, "http-secret", ars.Spec.Proxy.HTTP.CredentialSecretRef)

	require.NotNil(t, ars.Spec.Proxy.HTTPS)
	assert.Equal(t, "https://proxy.example.com", ars.Spec.Proxy.HTTPS.Url)
	assert.Equal(t, "https-secret", ars.Spec.Proxy.HTTPS.CredentialSecretRef)

	require.NotNil(t, ars.Spec.Proxy.NoProxy)
	require.Len(t, ars.Spec.Proxy.NoProxy, 2)
	assert.Contains(t, ars.Spec.Proxy.NoProxy, "example.com")
	assert.Contains(t, ars.Spec.Proxy.NoProxy, "example.org")
}

func TestTemplateRenderedWithTLS(t *testing.T) {
	t.Parallel()

	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	render := func(t *testing.T, options *helm.Options) v1alpha1.AutoscalingRunnerSet {
		// Path to the helm chart we will test
		helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
		require.NoError(t, err)

		releaseName := "test-runners"

		output := helm.RenderTemplateContext(
			t,
			t.Context(),
			options,
			helmChartPath,
			releaseName,
			[]string{"templates/autoscalingrunnerset.yaml"},
		)

		var ars v1alpha1.AutoscalingRunnerSet
		helm.UnmarshalK8SYaml(t, output, &ars)

		return ars
	}

	t.Run("providing githubServerTLS.runnerMountPath", func(t *testing.T) {
		t.Run("mode default", func(t *testing.T) {
			options := &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"githubConfigUrl":    "https://github.com/actions",
					"githubConfigSecret": "pre-defined-secrets",
					"githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap",
					"githubServerTLS.certificateFrom.configMapKeyRef.key":  "cert.pem",
					"githubServerTLS.runnerMountPath":                      "/runner/mount/path",
					"controllerServiceAccount.name":                        "arc",
					"controllerServiceAccount.namespace":                   "arc-system",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
			}

			ars := render(t, options)

			require.NotNil(t, ars.Spec.GitHubServerTLS)
			expected := &v1alpha1.TLSConfig{
				CertificateFrom: &v1alpha1.TLSCertificateSource{
					ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "certs-configmap",
						},
						Key: "cert.pem",
					},
				},
			}
			assert.Equal(t, expected, ars.Spec.GitHubServerTLS)

			var volume *corev1.Volume
			for _, v := range ars.Spec.Template.Spec.Volumes {
				if v.Name == "github-server-tls-cert" {
					volume = &v
					break
				}
			}
			require.NotNil(t, volume)
			assert.Equal(t, "certs-configmap", volume.ConfigMap.Name)
			assert.Equal(t, "cert.pem", volume.ConfigMap.Items[0].Key)
			assert.Equal(t, "cert.pem", volume.ConfigMap.Items[0].Path)

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
				Name:      "github-server-tls-cert",
				MountPath: "/runner/mount/path/cert.pem",
				SubPath:   "cert.pem",
			})

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "NODE_EXTRA_CA_CERTS",
				Value: "/runner/mount/path/cert.pem",
			})

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "RUNNER_UPDATE_CA_CERTS",
				Value: "1",
			})
		})

		t.Run("mode dind", func(t *testing.T) {
			options := &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"githubConfigUrl":    "https://github.com/actions",
					"githubConfigSecret": "pre-defined-secrets",
					"githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap",
					"githubServerTLS.certificateFrom.configMapKeyRef.key":  "cert.pem",
					"githubServerTLS.runnerMountPath":                      "/runner/mount/path/",
					"containerMode.type":                                   "dind",
					"controllerServiceAccount.name":                        "arc",
					"controllerServiceAccount.namespace":                   "arc-system",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
			}

			ars := render(t, options)

			require.NotNil(t, ars.Spec.GitHubServerTLS)
			expected := &v1alpha1.TLSConfig{
				CertificateFrom: &v1alpha1.TLSCertificateSource{
					ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "certs-configmap",
						},
						Key: "cert.pem",
					},
				},
			}
			assert.Equal(t, expected, ars.Spec.GitHubServerTLS)

			var volume *corev1.Volume
			for _, v := range ars.Spec.Template.Spec.Volumes {
				if v.Name == "github-server-tls-cert" {
					volume = &v
					break
				}
			}
			require.NotNil(t, volume)
			assert.Equal(t, "certs-configmap", volume.ConfigMap.Name)
			assert.Equal(t, "cert.pem", volume.ConfigMap.Items[0].Key)
			assert.Equal(t, "cert.pem", volume.ConfigMap.Items[0].Path)

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
				Name:      "github-server-tls-cert",
				MountPath: "/runner/mount/path/cert.pem",
				SubPath:   "cert.pem",
			})

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "NODE_EXTRA_CA_CERTS",
				Value: "/runner/mount/path/cert.pem",
			})

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "RUNNER_UPDATE_CA_CERTS",
				Value: "1",
			})
		})

		t.Run("mode kubernetes", func(t *testing.T) {
			options := &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"githubConfigUrl":    "https://github.com/actions",
					"githubConfigSecret": "pre-defined-secrets",
					"githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap",
					"githubServerTLS.certificateFrom.configMapKeyRef.key":  "cert.pem",
					"githubServerTLS.runnerMountPath":                      "/runner/mount/path",
					"containerMode.type":                                   "kubernetes",
					"controllerServiceAccount.name":                        "arc",
					"controllerServiceAccount.namespace":                   "arc-system",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
			}

			ars := render(t, options)

			require.NotNil(t, ars.Spec.GitHubServerTLS)
			expected := &v1alpha1.TLSConfig{
				CertificateFrom: &v1alpha1.TLSCertificateSource{
					ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "certs-configmap",
						},
						Key: "cert.pem",
					},
				},
			}
			assert.Equal(t, expected, ars.Spec.GitHubServerTLS)

			var volume *corev1.Volume
			for _, v := range ars.Spec.Template.Spec.Volumes {
				if v.Name == "github-server-tls-cert" {
					volume = &v
					break
				}
			}
			require.NotNil(t, volume)
			assert.Equal(t, "certs-configmap", volume.ConfigMap.Name)
			assert.Equal(t, "cert.pem", volume.ConfigMap.Items[0].Key)
			assert.Equal(t, "cert.pem", volume.ConfigMap.Items[0].Path)

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
				Name:      "github-server-tls-cert",
				MountPath: "/runner/mount/path/cert.pem",
				SubPath:   "cert.pem",
			})

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "NODE_EXTRA_CA_CERTS",
				Value: "/runner/mount/path/cert.pem",
			})

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "RUNNER_UPDATE_CA_CERTS",
				Value: "1",
			})
		})

		t.Run("mode kubernetes-novolume", func(t *testing.T) {
			options := &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"githubConfigUrl":    "https://github.com/actions",
					"githubConfigSecret": "pre-defined-secrets",
					"githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap",
					"githubServerTLS.certificateFrom.configMapKeyRef.key":  "cert.pem",
					"githubServerTLS.runnerMountPath":                      "/runner/mount/path",
					"containerMode.type":                                   "kubernetes-novolume",
					"controllerServiceAccount.name":                        "arc",
					"controllerServiceAccount.namespace":                   "arc-system",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
			}

			ars := render(t, options)

			require.NotNil(t, ars.Spec.GitHubServerTLS)
			expected := &v1alpha1.TLSConfig{
				CertificateFrom: &v1alpha1.TLSCertificateSource{
					ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "certs-configmap",
						},
						Key: "cert.pem",
					},
				},
			}
			assert.Equal(t, expected, ars.Spec.GitHubServerTLS)

			var volume *corev1.Volume
			for _, v := range ars.Spec.Template.Spec.Volumes {
				if v.Name == "github-server-tls-cert" {
					volume = &v
					break
				}
			}
			require.NotNil(t, volume)
			assert.Equal(t, "certs-configmap", volume.ConfigMap.Name)
			assert.Equal(t, "cert.pem", volume.ConfigMap.Items[0].Key)
			assert.Equal(t, "cert.pem", volume.ConfigMap.Items[0].Path)

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
				Name:      "github-server-tls-cert",
				MountPath: "/runner/mount/path/cert.pem",
				SubPath:   "cert.pem",
			})

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "NODE_EXTRA_CA_CERTS",
				Value: "/runner/mount/path/cert.pem",
			})

			assert.Contains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "RUNNER_UPDATE_CA_CERTS",
				Value: "1",
			})
		})
	})

	t.Run("without providing githubServerTLS.runnerMountPath", func(t *testing.T) {
		t.Run("mode default", func(t *testing.T) {
			options := &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"githubConfigUrl":    "https://github.com/actions",
					"githubConfigSecret": "pre-defined-secrets",
					"githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap",
					"githubServerTLS.certificateFrom.configMapKeyRef.key":  "cert.pem",
					"controllerServiceAccount.name":                        "arc",
					"controllerServiceAccount.namespace":                   "arc-system",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
			}

			ars := render(t, options)

			require.NotNil(t, ars.Spec.GitHubServerTLS)
			expected := &v1alpha1.TLSConfig{
				CertificateFrom: &v1alpha1.TLSCertificateSource{
					ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "certs-configmap",
						},
						Key: "cert.pem",
					},
				},
			}
			assert.Equal(t, expected, ars.Spec.GitHubServerTLS)

			var volume *corev1.Volume
			for _, v := range ars.Spec.Template.Spec.Volumes {
				if v.Name == "github-server-tls-cert" {
					volume = &v
					break
				}
			}
			assert.Nil(t, volume)

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
				Name:      "github-server-tls-cert",
				MountPath: "/runner/mount/path/cert.pem",
				SubPath:   "cert.pem",
			})

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "NODE_EXTRA_CA_CERTS",
				Value: "/runner/mount/path/cert.pem",
			})

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "RUNNER_UPDATE_CA_CERTS",
				Value: "1",
			})
		})

		t.Run("mode dind", func(t *testing.T) {
			options := &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"githubConfigUrl":    "https://github.com/actions",
					"githubConfigSecret": "pre-defined-secrets",
					"githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap",
					"githubServerTLS.certificateFrom.configMapKeyRef.key":  "cert.pem",
					"containerMode.type":                 "dind",
					"controllerServiceAccount.name":      "arc",
					"controllerServiceAccount.namespace": "arc-system",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
			}

			ars := render(t, options)

			require.NotNil(t, ars.Spec.GitHubServerTLS)
			expected := &v1alpha1.TLSConfig{
				CertificateFrom: &v1alpha1.TLSCertificateSource{
					ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "certs-configmap",
						},
						Key: "cert.pem",
					},
				},
			}
			assert.Equal(t, expected, ars.Spec.GitHubServerTLS)

			var volume *corev1.Volume
			for _, v := range ars.Spec.Template.Spec.Volumes {
				if v.Name == "github-server-tls-cert" {
					volume = &v
					break
				}
			}
			assert.Nil(t, volume)

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
				Name:      "github-server-tls-cert",
				MountPath: "/runner/mount/path/cert.pem",
				SubPath:   "cert.pem",
			})

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "NODE_EXTRA_CA_CERTS",
				Value: "/runner/mount/path/cert.pem",
			})

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "RUNNER_UPDATE_CA_CERTS",
				Value: "1",
			})
		})

		t.Run("mode kubernetes", func(t *testing.T) {
			options := &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"githubConfigUrl":    "https://github.com/actions",
					"githubConfigSecret": "pre-defined-secrets",
					"githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap",
					"githubServerTLS.certificateFrom.configMapKeyRef.key":  "cert.pem",
					"containerMode.type":                 "kubernetes",
					"controllerServiceAccount.name":      "arc",
					"controllerServiceAccount.namespace": "arc-system",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
			}

			ars := render(t, options)

			require.NotNil(t, ars.Spec.GitHubServerTLS)
			expected := &v1alpha1.TLSConfig{
				CertificateFrom: &v1alpha1.TLSCertificateSource{
					ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "certs-configmap",
						},
						Key: "cert.pem",
					},
				},
			}
			assert.Equal(t, expected, ars.Spec.GitHubServerTLS)

			var volume *corev1.Volume
			for _, v := range ars.Spec.Template.Spec.Volumes {
				if v.Name == "github-server-tls-cert" {
					volume = &v
					break
				}
			}
			assert.Nil(t, volume)

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
				Name:      "github-server-tls-cert",
				MountPath: "/runner/mount/path/cert.pem",
				SubPath:   "cert.pem",
			})

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "NODE_EXTRA_CA_CERTS",
				Value: "/runner/mount/path/cert.pem",
			})

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "RUNNER_UPDATE_CA_CERTS",
				Value: "1",
			})
		})

		t.Run("mode kubernetes-novolume", func(t *testing.T) {
			options := &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"githubConfigUrl":    "https://github.com/actions",
					"githubConfigSecret": "pre-defined-secrets",
					"githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap",
					"githubServerTLS.certificateFrom.configMapKeyRef.key":  "cert.pem",
					"containerMode.type":                 "kubernetes-novolume",
					"controllerServiceAccount.name":      "arc",
					"controllerServiceAccount.namespace": "arc-system",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
			}

			ars := render(t, options)

			require.NotNil(t, ars.Spec.GitHubServerTLS)
			expected := &v1alpha1.TLSConfig{
				CertificateFrom: &v1alpha1.TLSCertificateSource{
					ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "certs-configmap",
						},
						Key: "cert.pem",
					},
				},
			}
			assert.Equal(t, expected, ars.Spec.GitHubServerTLS)

			var volume *corev1.Volume
			for _, v := range ars.Spec.Template.Spec.Volumes {
				if v.Name == "github-server-tls-cert" {
					volume = &v
					break
				}
			}
			assert.Nil(t, volume)

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
				Name:      "github-server-tls-cert",
				MountPath: "/runner/mount/path/cert.pem",
				SubPath:   "cert.pem",
			})

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "NODE_EXTRA_CA_CERTS",
				Value: "/runner/mount/path/cert.pem",
			})

			assert.NotContains(t, ars.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
				Name:  "RUNNER_UPDATE_CA_CERTS",
				Value: "1",
			})
		})
	})
}

func TestTemplateNamingConstraints(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	setValues := map[string]string{
		"githubConfigUrl":                    "https://github.com/actions",
		"githubConfigSecret":                 "",
		"controllerServiceAccount.name":      "arc",
		"controllerServiceAccount.namespace": "arc-system",
	}

	tt := map[string]struct {
		releaseName   string
		namespaceName string
		expectedError string
	}{
		"Name too long": {
			releaseName:   strings.Repeat("a", 46),
			namespaceName: "test-" + strings.ToLower(random.UniqueID()),
			expectedError: "Name must have up to 45 characters",
		},
		"Namespace too long": {
			releaseName:   "test-" + strings.ToLower(random.UniqueID()),
			namespaceName: strings.Repeat("a", 64),
			expectedError: "Namespace must have up to 63 characters",
		},
	}

	for name, tc := range tt {
		t.Run(name, func(t *testing.T) {
			options := &helm.Options{
				Logger:         logger.Discard,
				SetValues:      setValues,
				KubectlOptions: k8s.NewKubectlOptions("", "", tc.namespaceName),
			}
			_, err = helm.RenderTemplateContextE(t, t.Context(), options, helmChartPath, tc.releaseName, []string{"templates/autoscalingrunnerset.yaml"})
			require.Error(t, err)
			assert.ErrorContains(t, err, tc.expectedError)
		})
	}
}

func TestTemplateRenderedGitHubConfigUrlEndsWIthSlash(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions/",
			"githubConfigSecret.github_token":    "gh_token12345",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, namespaceName, ars.Namespace)
	assert.Equal(t, "test-runners", ars.Name)
	assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl)
}

func TestTemplate_CreateManagerRole(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/manager_role.yaml"})

	var managerRole rbacv1.Role
	helm.UnmarshalK8SYaml(t, output, &managerRole)

	assert.Equal(t, namespaceName, managerRole.Namespace, "namespace should match the namespace of the Helm release")
	assert.Equal(t, "test-runners-gha-rs-manager", managerRole.Name)
	assert.Equal(t, "actions.github.com/cleanup-protection", managerRole.Finalizers[0])
	assert.Equal(t, 6, len(managerRole.Rules))

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)
}

func TestTemplate_CreateManagerRole_UseConfigMaps(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                                      "https://github.com/actions",
			"githubConfigSecret.github_token":                      "gh_token12345",
			"controllerServiceAccount.name":                        "arc",
			"controllerServiceAccount.namespace":                   "arc-system",
			"githubServerTLS.certificateFrom.configMapKeyRef.name": "test",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/manager_role.yaml"})

	var managerRole rbacv1.Role
	helm.UnmarshalK8SYaml(t, output, &managerRole)

	assert.Equal(t, namespaceName, managerRole.Namespace, "namespace should match the namespace of the Helm release")
	assert.Equal(t, "test-runners-gha-rs-manager", managerRole.Name)
	assert.Equal(t, "actions.github.com/cleanup-protection", managerRole.Finalizers[0])
	assert.Equal(t, 7, len(managerRole.Rules))
	assert.Equal(t, "configmaps", managerRole.Rules[6].Resources[0])
}

func TestTemplate_CreateManagerRoleBinding(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/manager_role_binding.yaml"})

	var managerRoleBinding rbacv1.RoleBinding
	helm.UnmarshalK8SYaml(t, output, &managerRoleBinding)

	assert.Equal(t, namespaceName, managerRoleBinding.Namespace, "namespace should match the namespace of the Helm release")
	assert.Equal(t, "test-runners-gha-rs-manager", managerRoleBinding.Name)
	assert.Equal(t, "test-runners-gha-rs-manager", managerRoleBinding.RoleRef.Name)
	assert.Equal(t, "actions.github.com/cleanup-protection", managerRoleBinding.Finalizers[0])
	assert.Equal(t, "arc", managerRoleBinding.Subjects[0].Name)
	assert.Equal(t, "arc-system", managerRoleBinding.Subjects[0].Namespace)
}

func TestTemplateRenderedAutoScalingRunnerSet_ExtraContainers(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_extra_containers.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}, "--debug")

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Len(t, ars.Spec.Template.Spec.Containers, 2, "There should be 2 containers")
	assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name, "Container name should be runner")
	assert.Equal(t, "other", ars.Spec.Template.Spec.Containers[1].Name, "Container name should be other")
	assert.Equal(t, "250m", ars.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().String(), "CPU Limit should be set")
	assert.Equal(t, "64Mi", ars.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().String(), "Memory Limit should be set")
	assert.Equal(t, "250m", ars.Spec.Template.Spec.Containers[1].Resources.Limits.Cpu().String(), "CPU Limit should be set")
	assert.Equal(t, "64Mi", ars.Spec.Template.Spec.Containers[1].Resources.Limits.Memory().String(), "Memory Limit should be set")
	assert.Equal(t, "SOME_ENV", ars.Spec.Template.Spec.Containers[0].Env[0].Name, "SOME_ENV should be set")
	assert.Equal(t, "SOME_VALUE", ars.Spec.Template.Spec.Containers[0].Env[0].Value, "SOME_ENV should be set to `SOME_VALUE`")
	assert.Equal(t, "MY_NODE_NAME", ars.Spec.Template.Spec.Containers[0].Env[1].Name, "MY_NODE_NAME should be set")
	assert.Equal(t, "spec.nodeName", ars.Spec.Template.Spec.Containers[0].Env[1].ValueFrom.FieldRef.FieldPath, "MY_NODE_NAME should be set to `spec.nodeName`")
	assert.Equal(t, "work", ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name, "VolumeMount name should be work")
	assert.Equal(t, "/work", ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath, "VolumeMount mountPath should be /work")
	assert.Equal(t, "others", ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].Name, "VolumeMount name should be others")
	assert.Equal(t, "/others", ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].MountPath, "VolumeMount mountPath should be /others")
	assert.Equal(t, "work", ars.Spec.Template.Spec.Volumes[0].Name, "Volume name should be work")
	assert.Equal(t, corev1.DNSNone, ars.Spec.Template.Spec.DNSPolicy, "DNS Policy should be None")
	assert.Equal(t, "192.0.2.1", ars.Spec.Template.Spec.DNSConfig.Nameservers[0], "DNS Nameserver should be set")
}

func TestTemplateRenderedAutoScalingRunnerSet_RestartPolicy(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, corev1.RestartPolicyNever, ars.Spec.Template.Spec.RestartPolicy, "RestartPolicy should be Never")

	options = &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
			"template.spec.restartPolicy":        "Always",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}, "--debug")

	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Equal(t, corev1.RestartPolicyAlways, ars.Spec.Template.Spec.RestartPolicy, "RestartPolicy should be Always")
}

func TestTemplateRenderedAutoScalingRunnerSet_ExtraPodSpec(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_extra_pod_spec.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "There should be 1 containers")
	assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name, "Container name should be runner")
	assert.Equal(t, corev1.DNSNone, ars.Spec.Template.Spec.DNSPolicy, "DNS Policy should be None")
	assert.Equal(t, "192.0.2.1", ars.Spec.Template.Spec.DNSConfig.Nameservers[0], "DNS Nameserver should be set")
}

func TestTemplateRenderedAutoScalingRunnerSet_DinDMergePodSpec(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_dind_merge_spec.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}, "--debug")

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "There should be 1 containers")
	assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name, "Container name should be runner")
	assert.Equal(t, "250m", ars.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().String(), "CPU Limit should be set")
	assert.Equal(t, "64Mi", ars.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().String(), "Memory Limit should be set")
	assert.Equal(t, "DOCKER_HOST", ars.Spec.Template.Spec.Containers[0].Env[0].Name, "DOCKER_HOST should be set")
	assert.Equal(t, "tcp://localhost:9999", ars.Spec.Template.Spec.Containers[0].Env[0].Value, "DOCKER_HOST should be set to `tcp://localhost:9999`")
	assert.Equal(t, "MY_NODE_NAME", ars.Spec.Template.Spec.Containers[0].Env[1].Name, "MY_NODE_NAME should be set")
	assert.Equal(t, "spec.nodeName", ars.Spec.Template.Spec.Containers[0].Env[1].ValueFrom.FieldRef.FieldPath, "MY_NODE_NAME should be set to `spec.nodeName`")
	assert.Equal(t, "work", ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name, "VolumeMount name should be work")
	assert.Equal(t, "/work", ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath, "VolumeMount mountPath should be /work")
	assert.Equal(t, "others", ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].Name, "VolumeMount name should be others")
	assert.Equal(t, "/others", ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].MountPath, "VolumeMount mountPath should be /others")
}

func TestTemplateRenderedAutoScalingRunnerSet_KubeModeMergePodSpec(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values_k8s_merge_spec.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}, "--debug")

	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)

	assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "There should be 1 containers")
	assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name, "Container name should be runner")
	assert.Equal(t, "250m", ars.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().String(), "CPU Limit should be set")
	assert.Equal(t, "64Mi", ars.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().String(), "Memory Limit should be set")
	assert.Equal(t, "ACTIONS_RUNNER_CONTAINER_HOOKS", ars.Spec.Template.Spec.Containers[0].Env[0].Name, "ACTIONS_RUNNER_CONTAINER_HOOKS should be set")
	assert.Equal(t, "/k8s/index.js", ars.Spec.Template.Spec.Containers[0].Env[0].Value, "ACTIONS_RUNNER_CONTAINER_HOOKS should be set to `/k8s/index.js`")
	assert.Equal(t, "MY_NODE_NAME", ars.Spec.Template.Spec.Containers[0].Env[1].Name, "MY_NODE_NAME should be set")
	assert.Equal(t, "spec.nodeName", ars.Spec.Template.Spec.Containers[0].Env[1].ValueFrom.FieldRef.FieldPath, "MY_NODE_NAME should be set to `spec.nodeName`")
	assert.Equal(t, "ACTIONS_RUNNER_POD_NAME", ars.Spec.Template.Spec.Containers[0].Env[2].Name, "ACTIONS_RUNNER_POD_NAME should be set")
	assert.Equal(t, "ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER", ars.Spec.Template.Spec.Containers[0].Env[3].Name, "ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER should be set")
	assert.Equal(t, "work", ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name, "VolumeMount name should be work")
	assert.Equal(t, "/work", ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath, "VolumeMount mountPath should be /work")
	assert.Equal(t, "others", ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].Name, "VolumeMount name should be others")
	assert.Equal(t, "/others", ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].MountPath, "VolumeMount mountPath should be /others")
}

func TestTemplateRenderedAutoscalingRunnerSetAnnotation_GitHubSecret(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	annotationExpectedTests := map[string]*helm.Options{
		"GitHub token": {
			Logger: logger.Discard,
			SetValues: map[string]string{
				"githubConfigUrl":                    "https://github.com/actions",
				"githubConfigSecret.github_token":    "gh_token12345",
				"controllerServiceAccount.name":      "arc",
				"controllerServiceAccount.namespace": "arc-system",
			},
			KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
		},
		"GitHub app": {
			Logger: logger.Discard,
			SetValues: map[string]string{
				"githubConfigUrl":                               "https://github.com/actions",
				"githubConfigSecret.github_app_id":              "10",
				"githubConfigSecret.github_app_installation_id": "100",
				"githubConfigSecret.github_app_private_key":     "private_key",
				"controllerServiceAccount.name":                 "arc",
				"controllerServiceAccount.namespace":            "arc-system",
			},
			KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
		},
	}

	for name, options := range annotationExpectedTests {
		t.Run("Annotation set: "+name, func(t *testing.T) {
			output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
			var autoscalingRunnerSet v1alpha1.AutoscalingRunnerSet
			helm.UnmarshalK8SYaml(t, output, &autoscalingRunnerSet)

			assert.NotEmpty(t, autoscalingRunnerSet.Annotations[actionsgithubcom.AnnotationKeyGitHubSecretName])
		})
	}

	t.Run("Annotation should not be set", func(t *testing.T) {
		options := &helm.Options{
			Logger: logger.Discard,
			SetValues: map[string]string{
				"githubConfigUrl":                    "https://github.com/actions",
				"githubConfigSecret":                 "pre-defined-secret",
				"controllerServiceAccount.name":      "arc",
				"controllerServiceAccount.namespace": "arc-system",
			},
			KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
		}
		output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
		var autoscalingRunnerSet v1alpha1.AutoscalingRunnerSet
		helm.UnmarshalK8SYaml(t, output, &autoscalingRunnerSet)

		assert.Empty(t, autoscalingRunnerSet.Annotations[actionsgithubcom.AnnotationKeyGitHubSecretName])
	})
}

func TestTemplateRenderedAutoscalingRunnerSetAnnotation_KubernetesModeCleanup(t *testing.T) {
	t.Parallel()

	for _, mode := range []string{"kubernetes", "kubernetes-novolume"} {
		t.Run("containerMode "+mode, func(t *testing.T) {
			// Path to the helm chart we will test
			helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
			require.NoError(t, err)

			releaseName := "test-runners"
			namespaceName := "test-" + strings.ToLower(random.UniqueID())

			options := &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"githubConfigUrl":                    "https://github.com/actions",
					"githubConfigSecret.github_token":    "gh_token12345",
					"controllerServiceAccount.name":      "arc",
					"controllerServiceAccount.namespace": "arc-system",
					"containerMode.type":                 mode,
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
			}

			output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
			var autoscalingRunnerSet v1alpha1.AutoscalingRunnerSet
			helm.UnmarshalK8SYaml(t, output, &autoscalingRunnerSet)

			annotationValues := map[string]string{
				actionsgithubcom.AnnotationKeyGitHubSecretName:                 "test-runners-gha-rs-github-secret",
				actionsgithubcom.AnnotationKeyManagerRoleName:                  "test-runners-gha-rs-manager",
				actionsgithubcom.AnnotationKeyManagerRoleBindingName:           "test-runners-gha-rs-manager",
				actionsgithubcom.AnnotationKeyKubernetesModeServiceAccountName: "test-runners-gha-rs-kube-mode",
				actionsgithubcom.AnnotationKeyKubernetesModeRoleName:           "test-runners-gha-rs-kube-mode",
				actionsgithubcom.AnnotationKeyKubernetesModeRoleBindingName:    "test-runners-gha-rs-kube-mode",
			}

			for annotation, value := range annotationValues {
				assert.Equal(t, value, autoscalingRunnerSet.Annotations[annotation], fmt.Sprintf("Annotation %q does not match the expected value", annotation))
			}
		})
	}
}

func TestRunnerContainerEnvNotEmptyMap(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger:         logger.Discard,
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	type testModel struct {
		Spec struct {
			Template struct {
				Spec struct {
					Containers []map[string]any `yaml:"containers"`
				} `yaml:"spec"`
			} `yaml:"template"`
		} `yaml:"spec"`
	}

	var m testModel
	helm.UnmarshalK8SYaml(t, output, &m)
	_, ok := m.Spec.Template.Spec.Containers[0]["env"]
	assert.False(t, ok, "env should not be set")
}

func TestRunnerContainerVolumeNotEmptyMap(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	testValuesPath, err := filepath.Abs("../tests/values.yaml")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger:         logger.Discard,
		ValuesFiles:    []string{testValuesPath},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	type testModel struct {
		Spec struct {
			Template struct {
				Spec struct {
					Containers []map[string]any `yaml:"containers"`
				} `yaml:"spec"`
			} `yaml:"template"`
		} `yaml:"spec"`
	}

	var m testModel
	helm.UnmarshalK8SYaml(t, output, &m)
	_, ok := m.Spec.Template.Spec.Containers[0]["volumeMounts"]
	assert.False(t, ok, "volumeMounts should not be set")
}

func TestAutoscalingRunnerSetAnnotationValuesHash(t *testing.T) {
	t.Parallel()

	const valuesHash = "actions.github.com/values-hash"

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token12345",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var autoscalingRunnerSet v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &autoscalingRunnerSet)

	firstHash := autoscalingRunnerSet.Annotations["actions.github.com/values-hash"]
	assert.NotEmpty(t, firstHash)
	assert.LessOrEqual(t, len(firstHash), 63)

	helmChartPath, err = filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	options = &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                    "https://github.com/actions",
			"githubConfigSecret.github_token":    "gh_token1234567890",
			"controllerServiceAccount.name":      "arc",
			"controllerServiceAccount.namespace": "arc-system",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	helm.UnmarshalK8SYaml(t, output, &autoscalingRunnerSet)
	secondHash := autoscalingRunnerSet.Annotations[valuesHash]
	assert.NotEmpty(t, secondHash)
	assert.NotEqual(t, firstHash, secondHash)
	assert.LessOrEqual(t, len(secondHash), 63)
}

func TestCustomLabels(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                                              "https://github.com/actions",
			"githubConfigSecret.github_token":                              "gh_token12345",
			"controllerServiceAccount.name":                                "arc",
			"containerMode.type":                                           "kubernetes",
			"controllerServiceAccount.namespace":                           "arc-system",
			`labels.argocd\.argoproj\.io/sync-wave`:                        `"1"`,
			`labels.app\.kubernetes\.io/part-of`:                           "no-override", // this shouldn't be overwritten
			"resourceMeta.autoscalingRunnerSet.labels.ars-custom":          "ars-custom-value",
			"resourceMeta.githubConfigSecret.labels.gh-custom":             "gh-custom-value",
			"resourceMeta.kubernetesModeRole.labels.kmr-custom":            "kmr-custom-value",
			"resourceMeta.kubernetesModeRoleBinding.labels.kmrb-custom":    "kmrb-custom-value",
			"resourceMeta.kubernetesModeServiceAccount.labels.kmsa-custom": "kmsa-custom-value",
			"resourceMeta.managerRole.labels.mr-custom":                    "mr-custom-value",
			"resourceMeta.managerRoleBinding.labels.mrb-custom":            "mrb-custom-value",
			"resourceMeta.autoscalingListener.labels.al-custom":            "al-custom-value",
			"resourceMeta.listenerServiceAccount.labels.lsa-custom":        "lsa-custom-value",
			"resourceMeta.listenerRole.labels.lr-custom":                   "lr-custom-value",
			"resourceMeta.listenerRoleBinding.labels.lrb-custom":           "lrb-custom-value",
			"resourceMeta.listenerConfigSecret.labels.lcs-custom":          "lcs-custom-value",
			"resourceMeta.ephemeralRunnerSet.labels.ers-custom":            "ers-custom-value",
			"resourceMeta.ephemeralRunner.labels.er-custom":                "er-custom-value",
			"resourceMeta.ephemeralRunnerConfigSecret.labels.ercs-custom":  "ercs-custom-value",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"})

	const targetLabel = "argocd.argoproj.io/sync-wave"
	const wantCustomValue = `"1"`
	const reservedLabel = "app.kubernetes.io/part-of"
	const wantReservedValue = "gha-rs"

	var githubSecret corev1.Secret
	helm.UnmarshalK8SYaml(t, output, &githubSecret)
	assert.Equal(t, wantCustomValue, githubSecret.Labels[targetLabel])
	assert.Equal(t, wantReservedValue, githubSecret.Labels[reservedLabel])
	assert.Equal(t, "gh-custom-value", githubSecret.Labels["gh-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_role.yaml"})
	var role rbacv1.Role
	helm.UnmarshalK8SYaml(t, output, &role)
	assert.Equal(t, wantCustomValue, role.Labels[targetLabel])
	assert.Equal(t, wantReservedValue, role.Labels[reservedLabel])
	assert.Equal(t, "kmr-custom-value", role.Labels["kmr-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_role_binding.yaml"})
	var roleBinding rbacv1.RoleBinding
	helm.UnmarshalK8SYaml(t, output, &roleBinding)
	assert.Equal(t, wantCustomValue, roleBinding.Labels[targetLabel])
	assert.Equal(t, wantReservedValue, roleBinding.Labels[reservedLabel])
	assert.Equal(t, "kmrb-custom-value", roleBinding.Labels["kmrb-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)
	assert.Equal(t, wantCustomValue, ars.Labels[targetLabel])
	assert.Equal(t, wantReservedValue, ars.Labels[reservedLabel])
	assert.Equal(t, "ars-custom-value", ars.Labels["ars-custom"])
	require.NotNil(t, ars.Spec.AutoscalingListenerMetadata)
	assert.Equal(t, "al-custom-value", ars.Spec.AutoscalingListenerMetadata.Labels["al-custom"])
	require.NotNil(t, ars.Spec.ListenerServiceAccountMetadata)
	assert.Equal(t, "lsa-custom-value", ars.Spec.ListenerServiceAccountMetadata.Labels["lsa-custom"])
	require.NotNil(t, ars.Spec.ListenerRoleMetadata)
	assert.Equal(t, "lr-custom-value", ars.Spec.ListenerRoleMetadata.Labels["lr-custom"])
	require.NotNil(t, ars.Spec.ListenerRoleBindingMetadata)
	assert.Equal(t, "lrb-custom-value", ars.Spec.ListenerRoleBindingMetadata.Labels["lrb-custom"])
	require.NotNil(t, ars.Spec.ListenerConfigSecretMetadata)
	assert.Equal(t, "lcs-custom-value", ars.Spec.ListenerConfigSecretMetadata.Labels["lcs-custom"])
	require.NotNil(t, ars.Spec.EphemeralRunnerSetMetadata)
	assert.Equal(t, "ers-custom-value", ars.Spec.EphemeralRunnerSetMetadata.Labels["ers-custom"])
	require.NotNil(t, ars.Spec.EphemeralRunnerMetadata)
	assert.Equal(t, "er-custom-value", ars.Spec.EphemeralRunnerMetadata.Labels["er-custom"])
	require.NotNil(t, ars.Spec.EphemeralRunnerConfigSecretMetadata)
	assert.Equal(t, "ercs-custom-value", ars.Spec.EphemeralRunnerConfigSecretMetadata.Labels["ercs-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_serviceaccount.yaml"})
	var serviceAccount corev1.ServiceAccount
	helm.UnmarshalK8SYaml(t, output, &serviceAccount)
	assert.Equal(t, wantCustomValue, serviceAccount.Labels[targetLabel])
	assert.Equal(t, wantReservedValue, serviceAccount.Labels[reservedLabel])
	assert.Equal(t, "kmsa-custom-value", serviceAccount.Labels["kmsa-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/manager_role.yaml"})
	var managerRole rbacv1.Role
	helm.UnmarshalK8SYaml(t, output, &managerRole)
	assert.Equal(t, wantCustomValue, managerRole.Labels[targetLabel])
	assert.Equal(t, wantReservedValue, managerRole.Labels[reservedLabel])
	assert.Equal(t, "mr-custom-value", managerRole.Labels["mr-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/manager_role_binding.yaml"})
	var managerRoleBinding rbacv1.RoleBinding
	helm.UnmarshalK8SYaml(t, output, &managerRoleBinding)
	assert.Equal(t, wantCustomValue, managerRoleBinding.Labels[targetLabel])
	assert.Equal(t, wantReservedValue, managerRoleBinding.Labels[reservedLabel])
	assert.Equal(t, "mrb-custom-value", managerRoleBinding.Labels["mrb-custom"])

	options = &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                                            "https://github.com/actions",
			"githubConfigSecret.github_token":                            "gh_token12345",
			"controllerServiceAccount.name":                              "arc",
			"controllerServiceAccount.namespace":                         "arc-system",
			`labels.argocd\.argoproj\.io/sync-wave`:                      `"1"`,
			"resourceMeta.noPermissionServiceAccount.labels.npsa-custom": "npsa-custom-value",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/no_permission_serviceaccount.yaml"})
	var noPermissionServiceAccount corev1.ServiceAccount
	helm.UnmarshalK8SYaml(t, output, &noPermissionServiceAccount)
	assert.Equal(t, wantCustomValue, noPermissionServiceAccount.Labels[targetLabel])
	assert.Equal(t, wantReservedValue, noPermissionServiceAccount.Labels[reservedLabel])
	assert.Equal(t, "npsa-custom-value", noPermissionServiceAccount.Labels["npsa-custom"])
}

func TestCustomAnnotations(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                                                   "https://github.com/actions",
			"githubConfigSecret.github_token":                                   "gh_token12345",
			"containerMode.type":                                                "kubernetes",
			"controllerServiceAccount.name":                                     "arc",
			"controllerServiceAccount.namespace":                                "arc-system",
			`annotations.argocd\.argoproj\.io/sync-wave`:                        `"1"`,
			"resourceMeta.autoscalingRunnerSet.annotations.ars-custom":          "ars-custom-value",
			"resourceMeta.githubConfigSecret.annotations.gh-custom":             "gh-custom-value",
			"resourceMeta.kubernetesModeRole.annotations.kmr-custom":            "kmr-custom-value",
			"resourceMeta.kubernetesModeRoleBinding.annotations.kmrb-custom":    "kmrb-custom-value",
			"resourceMeta.kubernetesModeServiceAccount.annotations.kmsa-custom": "kmsa-custom-value",
			"resourceMeta.managerRole.annotations.mr-custom":                    "mr-custom-value",
			"resourceMeta.managerRoleBinding.annotations.mrb-custom":            "mrb-custom-value",
			"resourceMeta.autoscalingListener.annotations.al-custom":            "al-custom-value",
			"resourceMeta.listenerServiceAccount.annotations.lsa-custom":        "lsa-custom-value",
			"resourceMeta.listenerRole.annotations.lr-custom":                   "lr-custom-value",
			"resourceMeta.listenerRoleBinding.annotations.lrb-custom":           "lrb-custom-value",
			"resourceMeta.listenerConfigSecret.annotations.lcs-custom":          "lcs-custom-value",
			"resourceMeta.ephemeralRunnerSet.annotations.ers-custom":            "ers-custom-value",
			"resourceMeta.ephemeralRunner.annotations.er-custom":                "er-custom-value",
			"resourceMeta.ephemeralRunnerConfigSecret.annotations.ercs-custom":  "ercs-custom-value",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	const targetAnnotations = "argocd.argoproj.io/sync-wave"
	const wantCustomValue = `"1"`

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"})

	var githubSecret corev1.Secret
	helm.UnmarshalK8SYaml(t, output, &githubSecret)
	assert.Equal(t, wantCustomValue, githubSecret.Annotations[targetAnnotations])
	assert.Equal(t, "gh-custom-value", githubSecret.Annotations["gh-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_role.yaml"})
	var role rbacv1.Role
	helm.UnmarshalK8SYaml(t, output, &role)
	assert.Equal(t, wantCustomValue, role.Annotations[targetAnnotations])
	assert.Equal(t, "kmr-custom-value", role.Annotations["kmr-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_role_binding.yaml"})
	var roleBinding rbacv1.RoleBinding
	helm.UnmarshalK8SYaml(t, output, &roleBinding)
	assert.Equal(t, wantCustomValue, roleBinding.Annotations[targetAnnotations])
	assert.Equal(t, "kmrb-custom-value", roleBinding.Annotations["kmrb-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})
	var ars v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &ars)
	assert.Equal(t, wantCustomValue, ars.Annotations[targetAnnotations])
	assert.Equal(t, "ars-custom-value", ars.Annotations["ars-custom"])
	require.NotNil(t, ars.Spec.AutoscalingListenerMetadata)
	assert.Equal(t, "al-custom-value", ars.Spec.AutoscalingListenerMetadata.Annotations["al-custom"])
	require.NotNil(t, ars.Spec.ListenerServiceAccountMetadata)
	assert.Equal(t, "lsa-custom-value", ars.Spec.ListenerServiceAccountMetadata.Annotations["lsa-custom"])
	require.NotNil(t, ars.Spec.ListenerRoleMetadata)
	assert.Equal(t, "lr-custom-value", ars.Spec.ListenerRoleMetadata.Annotations["lr-custom"])
	require.NotNil(t, ars.Spec.ListenerRoleBindingMetadata)
	assert.Equal(t, "lrb-custom-value", ars.Spec.ListenerRoleBindingMetadata.Annotations["lrb-custom"])
	require.NotNil(t, ars.Spec.ListenerConfigSecretMetadata)
	assert.Equal(t, "lcs-custom-value", ars.Spec.ListenerConfigSecretMetadata.Annotations["lcs-custom"])
	require.NotNil(t, ars.Spec.EphemeralRunnerSetMetadata)
	assert.Equal(t, "ers-custom-value", ars.Spec.EphemeralRunnerSetMetadata.Annotations["ers-custom"])
	require.NotNil(t, ars.Spec.EphemeralRunnerMetadata)
	assert.Equal(t, "er-custom-value", ars.Spec.EphemeralRunnerMetadata.Annotations["er-custom"])
	require.NotNil(t, ars.Spec.EphemeralRunnerConfigSecretMetadata)
	assert.Equal(t, "ercs-custom-value", ars.Spec.EphemeralRunnerConfigSecretMetadata.Annotations["ercs-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/kube_mode_serviceaccount.yaml"})
	var serviceAccount corev1.ServiceAccount
	helm.UnmarshalK8SYaml(t, output, &serviceAccount)
	assert.Equal(t, wantCustomValue, serviceAccount.Annotations[targetAnnotations])
	assert.Equal(t, "kmsa-custom-value", serviceAccount.Annotations["kmsa-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/manager_role.yaml"})
	var managerRole rbacv1.Role
	helm.UnmarshalK8SYaml(t, output, &managerRole)
	assert.Equal(t, wantCustomValue, managerRole.Annotations[targetAnnotations])
	assert.Equal(t, "mr-custom-value", managerRole.Annotations["mr-custom"])

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/manager_role_binding.yaml"})
	var managerRoleBinding rbacv1.RoleBinding
	helm.UnmarshalK8SYaml(t, output, &managerRoleBinding)
	assert.Equal(t, wantCustomValue, managerRoleBinding.Annotations[targetAnnotations])
	assert.Equal(t, "mrb-custom-value", managerRoleBinding.Annotations["mrb-custom"])

	options = &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                                                 "https://github.com/actions",
			"githubConfigSecret.github_token":                                 "gh_token12345",
			"controllerServiceAccount.name":                                   "arc",
			"controllerServiceAccount.namespace":                              "arc-system",
			`annotations.argocd\.argoproj\.io/sync-wave`:                      `"1"`,
			"resourceMeta.noPermissionServiceAccount.annotations.npsa-custom": "npsa-custom-value",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/no_permission_serviceaccount.yaml"})
	var noPermissionServiceAccount corev1.ServiceAccount
	helm.UnmarshalK8SYaml(t, output, &noPermissionServiceAccount)
	assert.Equal(t, wantCustomValue, noPermissionServiceAccount.Annotations[targetAnnotations])
	assert.Equal(t, "npsa-custom-value", noPermissionServiceAccount.Annotations["npsa-custom"])
}

func TestNamespaceOverride(t *testing.T) {
	t.Parallel()

	chartPath := "../../gha-runner-scale-set"

	releaseName := "test"
	releaseNamespace := "test-" + strings.ToLower(random.UniqueID())
	namespaceOverride := "test-" + strings.ToLower(random.UniqueID())

	tt := map[string]struct {
		file    string
		options *helm.Options
	}{
		"manager_role": {
			file: "manager_role.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
		"manager_role_binding": {
			file: "manager_role_binding.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
		"no_permission_serviceaccount": {
			file: "no_permission_serviceaccount.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
		"autoscalingrunnerset": {
			file: "autoscalingrunnerset.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
		"githubsecret": {
			file: "githubsecret.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
		"kube_mode_role": {
			file: "kube_mode_role.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"containerMode.type":                 "kubernetes",
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
		"kube_novolume_mode_role": {
			file: "kube_mode_role.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"containerMode.type":                 "kubernetes-novolume",
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
		"kube_mode_role_binding": {
			file: "kube_mode_role_binding.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"containerMode.type":                 "kubernetes",
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
		"kube_novolume_mode_role_binding": {
			file: "kube_mode_role_binding.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"containerMode.type":                 "kubernetes-novolume",
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
		"kube_mode_serviceaccount": {
			file: "kube_mode_serviceaccount.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"containerMode.type":                 "kubernetes",
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
		"kube_novolume_mode_serviceaccount": {
			file: "kube_mode_serviceaccount.yaml",
			options: &helm.Options{
				Logger: logger.Discard,
				SetValues: map[string]string{
					"namespaceOverride":                  namespaceOverride,
					"containerMode.type":                 "kubernetes-novolume",
					"controllerServiceAccount.name":      "foo",
					"controllerServiceAccount.namespace": "bar",
					"githubConfigSecret.github_token":    "gh_token12345",
					"githubConfigUrl":                    "https://github.com",
				},
				KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace),
			},
		},
	}

	for name, tc := range tt {
		c := tc
		t.Run(name, func(t *testing.T) {
			t.Parallel()
			templateFile := filepath.Join("./templates", c.file)

			output, err := helm.RenderTemplateContextE(t, t.Context(), c.options, chartPath, releaseName, []string{templateFile})
			if err != nil {
				t.Errorf("Error rendering template %s from chart %s: %s", c.file, chartPath, err)
			}

			type object struct {
				Metadata metav1.ObjectMeta
			}
			var renderedObject object
			helm.UnmarshalK8SYaml(t, output, &renderedObject)
			assert.Equal(t, namespaceOverride, renderedObject.Metadata.Namespace)
		})
	}
}

func TestAutoscalingRunnerSetCustomAnnotationsAndLabelsApplied(t *testing.T) {
	t.Parallel()

	// Path to the helm chart we will test
	helmChartPath, err := filepath.Abs("../../gha-runner-scale-set")
	require.NoError(t, err)

	releaseName := "test-runners"
	namespaceName := "test-" + strings.ToLower(random.UniqueID())

	options := &helm.Options{
		Logger: logger.Discard,
		SetValues: map[string]string{
			"githubConfigUrl":                                              "https://github.com/actions",
			"githubConfigSecret.github_token":                              "gh_token12345",
			"controllerServiceAccount.name":                                "arc",
			"controllerServiceAccount.namespace":                           "arc-system",
			"annotations.actions\\.github\\.com/vault":                     "azure_key_vault",
			"annotations.actions\\.github\\.com/cleanup-manager-role-name": "not-propagated",
			"labels.custom":                                                "custom",
			"labels.app\\.kubernetes\\.io/component":                       "not-propagated",
		},
		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
	}

	output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"})

	var autoscalingRunnerSet v1alpha1.AutoscalingRunnerSet
	helm.UnmarshalK8SYaml(t, output, &autoscalingRunnerSet)

	vault := autoscalingRunnerSet.Annotations["actions.github.com/vault"]
	assert.Equal(t, "azure_key_vault", vault)

	custom := autoscalingRunnerSet.Labels["custom"]
	assert.Equal(t, "custom", custom)

	assert.NotEqual(t, "not-propagated", autoscalingRunnerSet.Annotations["actions.github.com/cleanup-manager-role-name"])
	assert.NotEqual(t, "not-propagated", autoscalingRunnerSet.Labels["app.kubernetes.io/component"])
}
