diff --git a/.github/workflows/chart-test.yaml b/.github/workflows/chart-test.yaml index f20fb91af..4fc4bdc44 100644 --- a/.github/workflows/chart-test.yaml +++ b/.github/workflows/chart-test.yaml @@ -92,9 +92,19 @@ jobs: - name: Run chart unit tests id: unittest run: | - for dir in $(ls -d charts/*/); do - pushd $dir; helm dependencies update; popd - helm unittest $dir + # Process charts directory + for dir in charts/*/; do + [ -d "$dir" ] || continue + (cd "$dir" && helm dependencies update) + helm unittest "$dir" + done + # Run tests for charts in other-charts/ directory + for dir in other-charts/*/; do + [ -d "$dir" ] || continue + if [ -d "$dir/tests" ]; then + (cd "$dir" && helm dependencies update) + helm unittest "$dir" + fi done continue-on-error: true @@ -164,16 +174,15 @@ jobs: run: kubectl create namespace posit-test - name: Create License File Secrets + env: + PWB_LICENSE: ${{ secrets.PWB_LICENSE_FILE }} + PCT_LICENSE: ${{ secrets.PCT_LICENSE_FILE }} + PPM_LICENSE: ${{ secrets.PPM_LICENSE_FILE }} run: | - echo "${{ secrets.PWB_LICENSE_FILE }}" > pwb.lic - kubectl create secret generic pwb-license --from-file=pwb.lic --namespace posit-test - rm pwb.lic - echo "${{ secrets.PCT_LICENSE_FILE }}" > pct.lic - kubectl create secret generic pct-license --from-file=pct.lic --namespace posit-test - rm pct.lic - echo "${{ secrets.PPM_LICENSE_FILE }}" > ppm.lic - kubectl create secret generic ppm-license --from-file=ppm.lic --namespace posit-test - rm ppm.lic + # Use env vars and stdin to avoid secrets in temp files and command line args + printf '%s' "$PWB_LICENSE" | kubectl create secret generic pwb-license --from-file=pwb.lic=/dev/stdin --namespace posit-test + printf '%s' "$PCT_LICENSE" | kubectl create secret generic pct-license --from-file=pct.lic=/dev/stdin --namespace posit-test + printf '%s' "$PPM_LICENSE" | kubectl create secret generic ppm-license --from-file=ppm.lic=/dev/stdin --namespace posit-test - name: Create Workbench Secrets to test existingSecrets run: | @@ -189,13 +198,13 @@ jobs: - name: Run chart-testing (install changed) id: ct-install if: ${{ github.ref != 'refs/heads/main' && steps.list-changed.outputs.changed == 'true' }} - run: ct install --target-branch main --chart-dirs charts --chart-dirs other-charts --excluded-charts rstudio-library --namespace posit-test + run: ct install --target-branch main --chart-dirs charts --chart-dirs other-charts --excluded-charts rstudio-library,rstudio-library-test --namespace posit-test continue-on-error: true # no allow-failure until https://github.com/actions/toolkit/issues/399 - name: Run chart-testing (install all) id: ct-install-all - run: ct install --target-branch main --all --chart-dirs charts --chart-dirs other-charts --excluded-charts rstudio-library --namespace posit-test + run: ct install --target-branch main --all --chart-dirs charts --chart-dirs other-charts --excluded-charts rstudio-library,rstudio-library-test --namespace posit-test continue-on-error: true - name: Notify Slack of chart install failure if on main diff --git a/other-charts/rstudio-library-test/Chart.lock b/other-charts/rstudio-library-test/Chart.lock new file mode 100644 index 000000000..24722d556 --- /dev/null +++ b/other-charts/rstudio-library-test/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: rstudio-library + repository: file://../../charts/rstudio-library + version: 0.1.35 +digest: sha256:706c9e8c17f03c54f61ac9492f735082b6e771f3d87320f384821ffb4c197f51 +generated: "2026-01-28T12:47:20.245416746-06:00" diff --git a/other-charts/rstudio-library-test/Chart.yaml b/other-charts/rstudio-library-test/Chart.yaml new file mode 100644 index 000000000..ae20e9431 --- /dev/null +++ b/other-charts/rstudio-library-test/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: rstudio-library-test +description: Test harness for rstudio-library templates +type: application +version: 0.1.0 +appVersion: "0.1.0" + +dependencies: + - name: rstudio-library + version: "0.1.35" + repository: "file://../../charts/rstudio-library" diff --git a/other-charts/rstudio-library-test/README.md b/other-charts/rstudio-library-test/README.md new file mode 100644 index 000000000..a5d68d8fc --- /dev/null +++ b/other-charts/rstudio-library-test/README.md @@ -0,0 +1,91 @@ + + +# rstudio-library-test + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.0](https://img.shields.io/badge/AppVersion-0.1.0-informational?style=flat-square) + +Test harness for rstudio-library templates + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| file://../../charts/rstudio-library | rstudio-library | 0.1.35 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| testChronicle.enabled | bool | `true` | | +| testChronicle.image.registry | string | `"ghcr.io"` | | +| testChronicle.image.repository | string | `"rstudio/chronicle-agent"` | | +| testChronicle.image.tag | string | `"1.0.0"` | | +| testChronicle.serverAddress | string | `"http://chronicle-server.default:8080"` | | +| testChronicle.serverNamespace | string | `""` | | +| testConfig.dcf.config.key1 | string | `"value1"` | | +| testConfig.dcf.config.nested.subkey | string | `"subvalue"` | | +| testConfig.dcf.filename | string | `"test.dcf"` | | +| testConfig.gcfg.config.section1.key1 | string | `"value1"` | | +| testConfig.gcfg.config.section1.key2 | string | `"value2"` | | +| testConfig.gcfg.config.section2.arrayKey[0] | string | `"item1"` | | +| testConfig.gcfg.config.section2.arrayKey[1] | string | `"item2"` | | +| testConfig.gcfg.filename | string | `"test.gcfg"` | | +| testConfig.ini.config.section1.key1 | string | `"value1"` | | +| testConfig.ini.config.section1.key2 | int | `123` | | +| testConfig.ini.filename | string | `"test.ini"` | | +| testConfig.json.config.arrayKey[0] | string | `"item1"` | | +| testConfig.json.config.arrayKey[1] | string | `"item2"` | | +| testConfig.json.config.boolKey | bool | `true` | | +| testConfig.json.config.numberKey | int | `42` | | +| testConfig.json.config.stringKey | string | `"stringValue"` | | +| testConfig.json.filename | string | `"test.json"` | | +| testConfig.txt.config.key1 | string | `"value1"` | | +| testConfig.txt.config.key2 | string | `"value2"` | | +| testConfig.txt.filename | string | `"test.txt"` | | +| testDebug.boolValue | bool | `true` | | +| testDebug.mapValue.key | string | `"value"` | | +| testDebug.sliceValue[0] | string | `"item1"` | | +| testDebug.sliceValue[1] | string | `"item2"` | | +| testDebug.stringValue | string | `"test string"` | | +| testIngress.path | string | `"/test"` | | +| testIngress.pathType | string | `"Prefix"` | | +| testIngress.serviceName | string | `"test-service"` | | +| testIngress.servicePort | int | `8080` | | +| testLauncherTemplates.content.key1 | string | `"value1"` | | +| testLauncherTemplates.content.key2 | string | `"value2"` | | +| testLauncherTemplates.content.nested.subkey | string | `"subvalue"` | | +| testLauncherTemplates.templateName | string | `"test-template"` | | +| testLicense.licenseFile | string | `"LICENSE CONTENT HERE\n"` | | +| testLicense.licenseKey | string | `"test-license-key"` | | +| testLicense.licenseServer | string | `"license.example.com"` | | +| testProfiles.advanced.data."launcher.kubernetes.profiles.conf".*.default-cpus | int | `1` | | +| testProfiles.advanced.data."launcher.kubernetes.profiles.conf".testuser.default-cpus | int | `4` | | +| testProfiles.advanced.filePath | string | `"/etc/rstudio/"` | | +| testProfiles.advanced.jobJsonDefaults | list | `[]` | | +| testProfiles.basicIni."launcher.kubernetes.profiles.conf".*.default-cpus | int | `1` | | +| testProfiles.basicIni."launcher.kubernetes.profiles.conf".*.default-mem-mb | int | `512` | | +| testProfiles.basicIni."launcher.kubernetes.profiles.conf".testuser.default-cpus | int | `2` | | +| testProfiles.collapseArray.simple[0] | string | `"one"` | | +| testProfiles.collapseArray.simple[1] | string | `"two"` | | +| testProfiles.collapseArray.simple[2] | string | `"three"` | | +| testProfiles.collapseArray.targetFile[0].file | string | `"/etc/config/pods.json"` | | +| testProfiles.collapseArray.targetFile[0].target | string | `"pods"` | | +| testProfiles.collapseArray.targetFile[1].file | string | `"/etc/config/services.json"` | | +| testProfiles.collapseArray.targetFile[1].target | string | `"services"` | | +| testProfiles.singleFile.*.job-name | string | `"default-job"` | | +| testProfiles.singleFile.testuser.job-name | string | `"user-job"` | | +| testRbac.annotations | object | `{}` | | +| testRbac.clusterRoleCreate | bool | `false` | | +| testRbac.labels | object | `{}` | | +| testRbac.namespace | string | `"test-namespace"` | | +| testRbac.removeNamespaceReferences | bool | `false` | | +| testRbac.serviceAccountCreate | bool | `true` | | +| testRbac.serviceAccountName | string | `"test-sa"` | | +| testRbac.targetNamespace | string | `"test-target"` | | +| testTplvalues.objectValue.name | string | `"{{ .Release.Name }}"` | | +| testTplvalues.objectValue.namespace | string | `"{{ .Release.Namespace }}"` | | +| testTplvalues.staticValue | string | `"static"` | | +| testTplvalues.templateValue | string | `"{{ .Release.Name }}-suffix"` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.13.1](https://github.com/norwoodj/helm-docs/releases/v1.13.1) diff --git a/other-charts/rstudio-library-test/templates/_helpers.tpl b/other-charts/rstudio-library-test/templates/_helpers.tpl new file mode 100644 index 000000000..4db152ea8 --- /dev/null +++ b/other-charts/rstudio-library-test/templates/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Standard Helm chart helpers for the test harness. +*/}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "rstudio-library-test.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "rstudio-library-test.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "rstudio-library-test.labels" -}} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +{{ include "rstudio-library-test.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "rstudio-library-test.selectorLabels" -}} +app.kubernetes.io/name: {{ include "rstudio-library-test.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/other-charts/rstudio-library-test/templates/test-chronicle-agent.yaml b/other-charts/rstudio-library-test/templates/test-chronicle-agent.yaml new file mode 100644 index 000000000..8e765ef0a --- /dev/null +++ b/other-charts/rstudio-library-test/templates/test-chronicle-agent.yaml @@ -0,0 +1,26 @@ +{{/* +Test templates for rstudio-library chronicle-agent helpers. +Note: These templates use `lookup` which requires cluster access. +Tests with kubernetesProvider mock can only test static paths. +*/}} + +{{- if .Values.testChronicle }} +{{- if .Values.testChronicle.enabled }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-chronicle-agent + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + {{- /* Test image template - only when tag is specified to avoid lookup */}} + {{- if .Values.testChronicle.image.tag }} + image: {{ include "rstudio-library.chronicle-agent.image" (dict "chronicleAgent" .Values.testChronicle "Release" .Release) | quote }} + {{- end }} + {{- /* Test serverAddress template - only when serverAddress is specified to avoid lookup */}} + {{- if .Values.testChronicle.serverAddress }} + serverAddress: {{ include "rstudio-library.chronicle-agent.serverAddress" (dict "chronicleAgent" .Values.testChronicle "Release" .Release) | quote }} + {{- end }} +{{- end }} +{{- end }} diff --git a/other-charts/rstudio-library-test/templates/test-config.yaml b/other-charts/rstudio-library-test/templates/test-config.yaml new file mode 100644 index 000000000..4a18f7bfb --- /dev/null +++ b/other-charts/rstudio-library-test/templates/test-config.yaml @@ -0,0 +1,67 @@ +{{/* +Test templates for rstudio-library config formatters. +Each ConfigMap exercises a different config format template. +*/}} + +{{- if .Values.testConfig }} +{{- if .Values.testConfig.gcfg }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-config-gcfg + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + {{ .Values.testConfig.gcfg.filename }}: | + {{- include "rstudio-library.config.gcfg" .Values.testConfig.gcfg.config | nindent 4 }} +{{- end }} + +{{- if .Values.testConfig.ini }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-config-ini + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + {{- include "rstudio-library.config.ini" (dict .Values.testConfig.ini.filename .Values.testConfig.ini.config) | nindent 2 }} +{{- end }} + +{{- if .Values.testConfig.dcf }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-config-dcf + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + {{- include "rstudio-library.config.dcf" (dict .Values.testConfig.dcf.filename .Values.testConfig.dcf.config) | nindent 2 }} +{{- end }} + +{{- if .Values.testConfig.json }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-config-json + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + {{- include "rstudio-library.config.json" (dict .Values.testConfig.json.filename .Values.testConfig.json.config) | nindent 2 }} +{{- end }} + +{{- if .Values.testConfig.txt }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-config-txt + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + {{- include "rstudio-library.config.txt" (dict "data" (dict .Values.testConfig.txt.filename .Values.testConfig.txt.config)) | nindent 2 }} +{{- end }} +{{- end }} diff --git a/other-charts/rstudio-library-test/templates/test-debug.yaml b/other-charts/rstudio-library-test/templates/test-debug.yaml new file mode 100644 index 000000000..8e2fa3840 --- /dev/null +++ b/other-charts/rstudio-library-test/templates/test-debug.yaml @@ -0,0 +1,40 @@ +{{/* +Test templates for rstudio-library debug helpers. +The type-check template either succeeds silently or fails with an error. +We test this by passing various types and checking for success/failure. +*/}} + +{{- if .Values.testDebug }} + +{{- /* Test passing type checks - these should succeed silently */}} +{{- if .Values.testDebug.mapValue }} +{{- include "rstudio-library.debug.type-check" (dict "name" "testMap" "object" .Values.testDebug.mapValue "expected" "map" "description" "test map value") }} +{{- end }} + +{{- if .Values.testDebug.sliceValue }} +{{- include "rstudio-library.debug.type-check" (dict "name" "testSlice" "object" .Values.testDebug.sliceValue "expected" "slice" "description" "test slice value") }} +{{- end }} + +{{- if .Values.testDebug.stringValue }} +{{- include "rstudio-library.debug.type-check" (dict "name" "testString" "object" .Values.testDebug.stringValue "expected" "string" "description" "test string value") }} +{{- end }} + +{{- if .Values.testDebug.boolValue }} +{{- include "rstudio-library.debug.type-check" (dict "name" "testBool" "object" .Values.testDebug.boolValue "expected" "bool" "description" "test bool value") }} +{{- end }} + +{{- /* Test failing type check - this should fail */}} +{{- if .Values.testDebug.failingCheck }} +{{- include "rstudio-library.debug.type-check" (dict "name" .Values.testDebug.failingCheck.name "object" .Values.testDebug.failingCheck.object "expected" .Values.testDebug.failingCheck.expected "description" .Values.testDebug.failingCheck.description) }} +{{- end }} + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-debug-success + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + status: "type-checks-passed" +{{- end }} diff --git a/other-charts/rstudio-library-test/templates/test-ingress.yaml b/other-charts/rstudio-library-test/templates/test-ingress.yaml new file mode 100644 index 000000000..b3d5549fb --- /dev/null +++ b/other-charts/rstudio-library-test/templates/test-ingress.yaml @@ -0,0 +1,48 @@ +{{/* +Test templates for rstudio-library ingress helpers. +Outputs computed ingress values to ConfigMap for testing. +*/}} + +{{- if .Values.testIngress }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-ingress-values + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + apiVersion: {{ include "rstudio-library.ingress.apiVersion" . }} + supportsIngressClassName: {{ include "rstudio-library.ingress.supportsIngressClassName" (include "rstudio-library.ingress.apiVersion" .) }} + supportsPathType: {{ include "rstudio-library.ingress.supportsPathType" (include "rstudio-library.ingress.apiVersion" .) }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-ingress-path-string + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + path.yaml: | + {{- include "rstudio-library.ingress.path" (dict "apiVersion" (include "rstudio-library.ingress.apiVersion" .) "pathData" .Values.testIngress.path) | nindent 4 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-ingress-path-object + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + path.yaml: | + {{- include "rstudio-library.ingress.path" (dict "apiVersion" (include "rstudio-library.ingress.apiVersion" .) "pathData" (dict "path" .Values.testIngress.path "pathType" .Values.testIngress.pathType)) | nindent 4 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-ingress-backend + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + backend.yaml: | + {{- include "rstudio-library.ingress.backend" (dict "apiVersion" (include "rstudio-library.ingress.apiVersion" .) "svcName" .Values.testIngress.serviceName "svcPort" .Values.testIngress.servicePort) | nindent 4 }} +{{- end }} diff --git a/other-charts/rstudio-library-test/templates/test-launcher-templates.yaml b/other-charts/rstudio-library-test/templates/test-launcher-templates.yaml new file mode 100644 index 000000000..b1f9671bb --- /dev/null +++ b/other-charts/rstudio-library-test/templates/test-launcher-templates.yaml @@ -0,0 +1,48 @@ +{{/* +Test templates for rstudio-library launcher template helpers. +These generate Helm template definitions that can be used in launcher configurations. +*/}} + +{{- if .Values.testLauncherTemplates }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-launcher-skeleton + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + template.tpl: | + {{- include "rstudio-library.templates.skeleton" (dict "name" .Values.testLauncherTemplates.templateName "value" .Values.testLauncherTemplates.content) | nindent 4 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-launcher-data-output + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + template.tpl: | + {{- include "rstudio-library.templates.dataOutput" (dict "name" .Values.testLauncherTemplates.templateName "value" .Values.testLauncherTemplates.content) | nindent 4 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-launcher-data-output-pretty + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + template.tpl: | + {{- include "rstudio-library.templates.dataOutputPretty" (dict "name" .Values.testLauncherTemplates.templateName "value" .Values.testLauncherTemplates.content) | nindent 4 }} +{{- /* Test with trailingDash=false */}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-launcher-no-trailing-dash + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + template.tpl: | + {{- include "rstudio-library.templates.skeleton" (dict "name" .Values.testLauncherTemplates.templateName "value" .Values.testLauncherTemplates.content "trailingDash" false) | nindent 4 }} +{{- end }} diff --git a/other-charts/rstudio-library-test/templates/test-license.yaml b/other-charts/rstudio-library-test/templates/test-license.yaml new file mode 100644 index 000000000..e8b3c0fbb --- /dev/null +++ b/other-charts/rstudio-library-test/templates/test-license.yaml @@ -0,0 +1,56 @@ +{{/* +Test templates for rstudio-library license helpers. +Outputs license configuration to ConfigMaps/Secrets for testing. +*/}} + +{{- if .Values.testLicense }} +{{- $licenseContext := dict + "product" "test-product" + "envVarPrefix" "TEST" + "license" (dict + "key" .Values.testLicense.licenseKey + "server" .Values.testLicense.licenseServer + "file" (dict + "contents" .Values.testLicense.licenseFile + "secret" "" + "mountPath" "/etc/license" + "secretKey" "license.lic" + "mountSubPath" false + ) + ) + "fullName" (printf "%s-license" .Release.Name) + "namespace" .Release.Namespace +}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-license-env + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + env.yaml: | + {{- include "rstudio-library.license-env" $licenseContext | nindent 4 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-license-mount + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + mount.yaml: | + {{- include "rstudio-library.license-mount" $licenseContext | nindent 4 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-license-volume + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + volume.yaml: | + {{- include "rstudio-library.license-volume" $licenseContext | nindent 4 }} +{{- /* License secret outputs its own --- separators, so no additional separator needed */}} +{{ include "rstudio-library.license-secret" $licenseContext }} +{{- end }} diff --git a/other-charts/rstudio-library-test/templates/test-profiles.yaml b/other-charts/rstudio-library-test/templates/test-profiles.yaml new file mode 100644 index 000000000..3356482bc --- /dev/null +++ b/other-charts/rstudio-library-test/templates/test-profiles.yaml @@ -0,0 +1,102 @@ +{{/* +Test templates for rstudio-library profiles helpers. +Outputs profile configurations to ConfigMaps for testing. +*/}} + +{{- if .Values.testProfiles }} + +{{- /* Test basic profiles.ini */}} +{{- if and .Values.testProfiles.basicIni (kindIs "map" .Values.testProfiles.basicIni) }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-profiles-ini + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + {{- include "rstudio-library.profiles.ini" .Values.testProfiles.basicIni | nindent 2 }} +{{- end }} + +{{- /* Test profiles.ini.singleFile */}} +{{- if and .Values.testProfiles.singleFile (kindIs "map" .Values.testProfiles.singleFile) }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-profiles-single-file + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + profiles.conf: | + {{- include "rstudio-library.profiles.ini.singleFile" .Values.testProfiles.singleFile | nindent 4 }} +{{- end }} + +{{- /* Test profiles.ini.collapse-array */}} +{{- if and .Values.testProfiles.collapseArray (kindIs "map" .Values.testProfiles.collapseArray) }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-profiles-collapse-array + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + simple: {{ include "rstudio-library.profiles.ini.collapse-array" .Values.testProfiles.collapseArray.simple | quote }} + {{- if and (hasKey .Values.testProfiles.collapseArray "targetFile") .Values.testProfiles.collapseArray.targetFile }} + targetFile: {{ include "rstudio-library.profiles.ini.collapse-array" .Values.testProfiles.collapseArray.targetFile | quote }} + {{- end }} +{{- end }} + +{{- /* Test profiles.ini.advanced */}} +{{- if and .Values.testProfiles.advanced (kindIs "map" .Values.testProfiles.advanced) }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-profiles-advanced + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + {{- include "rstudio-library.profiles.ini.advanced" (dict + "data" .Values.testProfiles.advanced.data + "jobJsonDefaults" (default (list) .Values.testProfiles.advanced.jobJsonDefaults) + "filePath" (default "" .Values.testProfiles.advanced.filePath) + ) | nindent 2 }} +{{- end }} + +{{- /* Test profiles.json-from-overrides-config */}} +{{- if and .Values.testProfiles.jsonOverrides (kindIs "map" .Values.testProfiles.jsonOverrides) }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-profiles-json-overrides + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + {{- include "rstudio-library.profiles.json-from-overrides-config" (dict + "data" .Values.testProfiles.jsonOverrides.data + "default" (default (list) .Values.testProfiles.jsonOverrides.default) + ) | nindent 2 }} +{{- end }} + +{{- /* Test profiles.apply-everyone-and-default-to-others */}} +{{- if and .Values.testProfiles.applyEveryone (kindIs "map" .Values.testProfiles.applyEveryone) }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-profiles-apply-everyone + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + profiles.conf: | + {{- include "rstudio-library.profiles.apply-everyone-and-default-to-others" (dict + "data" .Values.testProfiles.applyEveryone.data + "default" (default (list) .Values.testProfiles.applyEveryone.default) + "filePath" (default "" .Values.testProfiles.applyEveryone.filePath) + ) | nindent 4 }} +{{- end }} + +{{- end }} diff --git a/other-charts/rstudio-library-test/templates/test-rbac.yaml b/other-charts/rstudio-library-test/templates/test-rbac.yaml new file mode 100644 index 000000000..9371b8997 --- /dev/null +++ b/other-charts/rstudio-library-test/templates/test-rbac.yaml @@ -0,0 +1,17 @@ +{{/* +Test templates for rstudio-library RBAC helper. +Outputs RBAC resources directly. +*/}} + +{{- if .Values.testRbac }} +{{- include "rstudio-library.rbac" (dict + "namespace" .Values.testRbac.namespace + "targetNamespace" .Values.testRbac.targetNamespace + "serviceAccountName" .Values.testRbac.serviceAccountName + "serviceAccountCreate" .Values.testRbac.serviceAccountCreate + "serviceAccountAnnotations" .Values.testRbac.annotations + "serviceAccountLabels" .Values.testRbac.labels + "clusterRoleCreate" .Values.testRbac.clusterRoleCreate + "removeNamespaceReferences" (default false .Values.testRbac.removeNamespaceReferences) +) }} +{{- end }} diff --git a/other-charts/rstudio-library-test/templates/test-tplvalues.yaml b/other-charts/rstudio-library-test/templates/test-tplvalues.yaml new file mode 100644 index 000000000..24424b179 --- /dev/null +++ b/other-charts/rstudio-library-test/templates/test-tplvalues.yaml @@ -0,0 +1,24 @@ +{{/* +Test templates for rstudio-library tplvalues helper. +Tests template rendering within values. +*/}} + +{{- if .Values.testTplvalues }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-tplvalues + labels: + {{- include "rstudio-library-test.labels" . | nindent 4 }} +data: + {{- /* Test string value with template expression */}} + renderedString: {{ include "rstudio-library.tplvalues.render" (dict "value" .Values.testTplvalues.templateValue "context" $) | quote }} + {{- /* Test static value passthrough */}} + staticString: {{ include "rstudio-library.tplvalues.render" (dict "value" .Values.testTplvalues.staticValue "context" $) | quote }} + {{- /* Test object value with template expression */}} + {{- if .Values.testTplvalues.objectValue }} + renderedObject: | + {{- include "rstudio-library.tplvalues.render" (dict "value" .Values.testTplvalues.objectValue "context" $) | nindent 4 }} + {{- end }} +{{- end }} diff --git a/other-charts/rstudio-library-test/tests/_base_values.yaml b/other-charts/rstudio-library-test/tests/_base_values.yaml new file mode 100644 index 000000000..c1641c998 --- /dev/null +++ b/other-charts/rstudio-library-test/tests/_base_values.yaml @@ -0,0 +1,33 @@ +# Base values that null out all test categories +# Each test suite should include this file and then override only what it needs +# This reduces boilerplate and makes adding new test categories easier + +testConfig: + gcfg: null + ini: null + dcf: null + json: null + txt: null + +testIngress: null + +testLicense: null + +testRbac: null + +testProfiles: + basicIni: null + singleFile: null + collapseArray: null + advanced: null + jsonOverrides: null + applyEveryone: null + +testDebug: null + +testLauncherTemplates: null + +testTplvalues: null + +testChronicle: + enabled: false diff --git a/other-charts/rstudio-library-test/tests/chronicle_agent_test.yaml b/other-charts/rstudio-library-test/tests/chronicle_agent_test.yaml new file mode 100644 index 000000000..45dbec439 --- /dev/null +++ b/other-charts/rstudio-library-test/tests/chronicle_agent_test.yaml @@ -0,0 +1,88 @@ +suite: Chronicle Agent Helpers +templates: + - templates/test-chronicle-agent.yaml +values: + - _base_values.yaml + +# Note: Chronicle agent templates use `lookup` which requires cluster access. +# These tests only cover static paths where values are explicitly provided. +# Full testing of cluster lookup functionality requires integration tests. + +tests: + # ======================================== + # Image Template Tests (Static Path) + # ======================================== + - it: should return image with specified tag + set: + testChronicle: + enabled: true + serverAddress: "http://server:8080" + image: + registry: ghcr.io + repository: rstudio/chronicle-agent + tag: "1.2.3" + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-chronicle-agent + - equal: + path: data.image + value: "ghcr.io/rstudio/chronicle-agent:1.2.3" + + - it: should use custom registry in image + set: + testChronicle: + enabled: true + serverAddress: "http://server:8080" + image: + registry: my-registry.example.com + repository: custom/agent + tag: "2.0.0" + asserts: + - equal: + path: data.image + value: "my-registry.example.com/custom/agent:2.0.0" + + # ======================================== + # Server Address Template Tests (Static Path) + # ======================================== + - it: should return provided server address + set: + testChronicle: + enabled: true + serverAddress: "http://chronicle-server.monitoring:9090" + image: + registry: ghcr.io + repository: rstudio/chronicle-agent + tag: "1.0.0" + asserts: + - equal: + path: data.serverAddress + value: "http://chronicle-server.monitoring:9090" + + - it: should handle https server address + set: + testChronicle: + enabled: true + serverAddress: "https://secure-chronicle.example.com:443" + image: + registry: ghcr.io + repository: rstudio/chronicle-agent + tag: "1.0.0" + asserts: + - equal: + path: data.serverAddress + value: "https://secure-chronicle.example.com:443" + + # ======================================== + # Disabled Chronicle Tests + # ======================================== + - it: should not render when chronicle is disabled + set: + testChronicle: + enabled: false + asserts: + - hasDocuments: + count: 0 diff --git a/other-charts/rstudio-library-test/tests/config_test.yaml b/other-charts/rstudio-library-test/tests/config_test.yaml new file mode 100644 index 000000000..ecd27bbf4 --- /dev/null +++ b/other-charts/rstudio-library-test/tests/config_test.yaml @@ -0,0 +1,297 @@ +suite: Config Formatters +templates: + - templates/test-config.yaml +values: + - _base_values.yaml + +tests: + # ======================================== + # GCFG Format Tests + # ======================================== + - it: should render gcfg with basic section and key-value pairs + set: + testConfig: + gcfg: + filename: test.gcfg + config: + Server: + Host: localhost + Port: 8080 + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-config-gcfg + - matchRegex: + path: data["test.gcfg"] + pattern: "\\[Server\\]" + - matchRegex: + path: data["test.gcfg"] + pattern: "Host = localhost" + - matchRegex: + path: data["test.gcfg"] + pattern: "Port = 8080" + + - it: should render gcfg with array values as repeated keys + set: + testConfig: + gcfg: + filename: test.gcfg + config: + Server: + Listen: + - ":8080" + - ":8443" + asserts: + - matchRegex: + path: data["test.gcfg"] + pattern: "Listen = :8080" + - matchRegex: + path: data["test.gcfg"] + pattern: "Listen = :8443" + + - it: should render gcfg with multiple sections + set: + testConfig: + gcfg: + filename: config.gcfg + config: + Server: + Host: server1 + Database: + Host: db1 + asserts: + - matchRegex: + path: data["config.gcfg"] + pattern: "\\[Server\\]" + - matchRegex: + path: data["config.gcfg"] + pattern: "\\[Database\\]" + + - it: should handle boolean and numeric values in gcfg + set: + testConfig: + gcfg: + filename: test.gcfg + config: + Settings: + Enabled: true + MaxConnections: 100 + asserts: + - matchRegex: + path: data["test.gcfg"] + pattern: "Enabled = true" + - matchRegex: + path: data["test.gcfg"] + pattern: "MaxConnections = 100" + + - it: should handle empty config section in gcfg + set: + testConfig: + gcfg: + filename: empty.gcfg + config: + EmptySection: {} + asserts: + - matchRegex: + path: data["empty.gcfg"] + pattern: "\\[EmptySection\\]" + + # ======================================== + # INI Format Tests + # ======================================== + - it: should render ini with basic section and key-value pairs + set: + testConfig: + ini: + filename: test.ini + config: + section1: + key1: value1 + key2: value2 + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-config-ini + - matchRegex: + path: data["test.ini"] + pattern: "\\[section1\\]" + - matchRegex: + path: data["test.ini"] + pattern: "key1=value1" + - matchRegex: + path: data["test.ini"] + pattern: "key2=value2" + + - it: should render ini with boolean and numeric values + set: + testConfig: + ini: + filename: settings.ini + config: + options: + enabled: true + count: 42 + asserts: + - matchRegex: + path: data["settings.ini"] + pattern: "enabled=true" + - matchRegex: + path: data["settings.ini"] + pattern: "count=42" + + # ======================================== + # DCF Format Tests + # ======================================== + - it: should render dcf with basic key-value pairs + set: + testConfig: + dcf: + filename: test.dcf + config: + Package: mypackage + Version: 1.0.0 + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-config-dcf + - matchRegex: + path: data["test.dcf"] + pattern: "Package: mypackage" + - matchRegex: + path: data["test.dcf"] + pattern: "Version: 1.0.0" + + # ======================================== + # JSON Format Tests + # ======================================== + - it: should render json with simple object + set: + testConfig: + json: + filename: config.json + config: + name: test + enabled: true + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-config-json + - matchRegex: + path: data["config.json"] + pattern: '"name": "test"' + - matchRegex: + path: data["config.json"] + pattern: '"enabled": true' + + - it: should render json with nested object + set: + testConfig: + json: + filename: nested.json + config: + server: + host: localhost + port: 8080 + asserts: + - matchRegex: + path: data["nested.json"] + pattern: '"server":' + - matchRegex: + path: data["nested.json"] + pattern: '"host": "localhost"' + + - it: should render json with array values + set: + testConfig: + json: + filename: array.json + config: + items: + - first + - second + asserts: + - matchRegex: + path: data["array.json"] + pattern: '"items":' + - matchRegex: + path: data["array.json"] + pattern: '"first"' + - matchRegex: + path: data["array.json"] + pattern: '"second"' + + - it: should render json with empty object + set: + testConfig: + json: + filename: empty.json + config: {} + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-config-json + + # ======================================== + # TXT Format Tests + # ======================================== + - it: should render txt with default comment delimiter + set: + testConfig: + txt: + filename: config.txt + config: + key1: value1 + key2: value2 + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-config-txt + - matchRegex: + path: data["config.txt"] + pattern: "# key1" + - matchRegex: + path: data["config.txt"] + pattern: "value1" + + # ======================================== + # Empty/Null Value Tests + # ======================================== + - it: should render gcfg with empty config section + set: + testConfig: + gcfg: + filename: empty.gcfg + config: + EmptySection: {} + asserts: + - isKind: + of: ConfigMap + - matchRegex: + path: data["empty.gcfg"] + pattern: "\\[EmptySection\\]" + + - it: should render ini with empty section + set: + testConfig: + ini: + filename: empty.ini + config: + empty_section: {} + asserts: + - isKind: + of: ConfigMap + - matchRegex: + path: data["empty.ini"] + pattern: "\\[empty_section\\]" diff --git a/other-charts/rstudio-library-test/tests/debug_test.yaml b/other-charts/rstudio-library-test/tests/debug_test.yaml new file mode 100644 index 000000000..307381ee1 --- /dev/null +++ b/other-charts/rstudio-library-test/tests/debug_test.yaml @@ -0,0 +1,88 @@ +suite: Debug Helpers +templates: + - templates/test-debug.yaml +values: + - _base_values.yaml + +tests: + # ======================================== + # Passing Type Check Tests + # ======================================== + - it: should pass type check for map type + set: + testDebug: + mapValue: + key1: value1 + key2: value2 + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-debug-success + - equal: + path: data.status + value: "type-checks-passed" + + - it: should pass type check for slice type + set: + testDebug: + sliceValue: + - item1 + - item2 + - item3 + asserts: + - isKind: + of: ConfigMap + - equal: + path: data.status + value: "type-checks-passed" + + - it: should pass type check for string type + set: + testDebug: + stringValue: "test string value" + asserts: + - isKind: + of: ConfigMap + - equal: + path: data.status + value: "type-checks-passed" + + - it: should pass type check for bool type + set: + testDebug: + boolValue: true + asserts: + - isKind: + of: ConfigMap + - equal: + path: data.status + value: "type-checks-passed" + + # ======================================== + # Failing Type Check Tests + # ======================================== + - it: should fail type check with descriptive error for wrong type (string instead of map) + set: + testDebug: + failingCheck: + name: "testConfig" + object: "this is a string" + expected: "map" + description: "configuration object" + asserts: + - failedTemplate: + errorPattern: "testConfig must be a 'map'" + + - it: should fail type check for string instead of slice + set: + testDebug: + failingCheck: + name: "items" + object: "not-an-array" + expected: "slice" + description: "list of items" + asserts: + - failedTemplate: + errorPattern: "items must be a 'slice'" diff --git a/other-charts/rstudio-library-test/tests/ingress_test.yaml b/other-charts/rstudio-library-test/tests/ingress_test.yaml new file mode 100644 index 000000000..47c66cfe3 --- /dev/null +++ b/other-charts/rstudio-library-test/tests/ingress_test.yaml @@ -0,0 +1,128 @@ +suite: Ingress Helpers +templates: + - templates/test-ingress.yaml +values: + - _base_values.yaml + +tests: + # ======================================== + # API Version Detection Tests (default K8s version) + # ======================================== + - it: should return networking.k8s.io/v1 for default K8s version + set: + testIngress: + serviceName: test-svc + servicePort: 80 + path: / + pathType: Prefix + asserts: + - equal: + path: data.apiVersion + value: networking.k8s.io/v1 + documentIndex: 0 + + # ======================================== + # supportsIngressClassName Tests + # ======================================== + - it: should return true for supportsIngressClassName on default K8s version + set: + testIngress: + serviceName: test-svc + servicePort: 80 + path: / + pathType: Prefix + asserts: + - equal: + path: data.supportsIngressClassName + value: true + documentIndex: 0 + + # ======================================== + # supportsPathType Tests + # ======================================== + - it: should return true for supportsPathType on default K8s version + set: + testIngress: + serviceName: test-svc + servicePort: 80 + path: / + pathType: Prefix + asserts: + - equal: + path: data.supportsPathType + value: true + documentIndex: 0 + + # ======================================== + # Path Rendering Tests + # ======================================== + - it: should render path with string input + set: + testIngress: + serviceName: test-svc + servicePort: 80 + path: /api + pathType: Prefix + asserts: + - matchRegex: + path: data["path.yaml"] + pattern: "path: /api" + documentIndex: 1 + - matchRegex: + path: data["path.yaml"] + pattern: "pathType: Prefix" + documentIndex: 1 + + - it: should render path with object input + set: + testIngress: + serviceName: test-svc + servicePort: 80 + path: /custom + pathType: Exact + asserts: + - matchRegex: + path: data["path.yaml"] + pattern: "path: /custom" + documentIndex: 2 + - matchRegex: + path: data["path.yaml"] + pattern: "pathType: Exact" + documentIndex: 2 + + # ======================================== + # Backend Rendering Tests (v1 API) + # ======================================== + - it: should render backend for v1 API with numeric port + set: + testIngress: + serviceName: my-service + servicePort: 8080 + path: / + pathType: Prefix + asserts: + - matchRegex: + path: data["backend.yaml"] + pattern: "service:" + documentIndex: 3 + - matchRegex: + path: data["backend.yaml"] + pattern: "name: my-service" + documentIndex: 3 + - matchRegex: + path: data["backend.yaml"] + pattern: "number: 8080" + documentIndex: 3 + + - it: should render backend for v1 API with named port + set: + testIngress: + serviceName: my-service + servicePort: http + path: / + pathType: Prefix + asserts: + - matchRegex: + path: data["backend.yaml"] + pattern: "name: http" + documentIndex: 3 diff --git a/other-charts/rstudio-library-test/tests/launcher_templates_test.yaml b/other-charts/rstudio-library-test/tests/launcher_templates_test.yaml new file mode 100644 index 000000000..1b0a0015a --- /dev/null +++ b/other-charts/rstudio-library-test/tests/launcher_templates_test.yaml @@ -0,0 +1,135 @@ +suite: Launcher Templates Helpers +templates: + - templates/test-launcher-templates.yaml +values: + - _base_values.yaml + +tests: + # ======================================== + # templates.skeleton Tests + # ======================================== + - it: should output valid YAML wrapped in define block + set: + testLauncherTemplates: + templateName: my-template + content: + key1: value1 + key2: value2 + asserts: + - isKind: + of: ConfigMap + documentIndex: 0 + - equal: + path: metadata.name + value: test-launcher-skeleton + documentIndex: 0 + - matchRegex: + path: data["template.tpl"] + pattern: 'define "my-template"' + documentIndex: 0 + - matchRegex: + path: data["template.tpl"] + pattern: "key1: value1" + documentIndex: 0 + - matchRegex: + path: data["template.tpl"] + pattern: "key2: value2" + documentIndex: 0 + + - it: should render nested values correctly in skeleton + set: + testLauncherTemplates: + templateName: nested-template + content: + parent: + child1: value1 + child2: + grandchild: deep-value + asserts: + - matchRegex: + path: data["template.tpl"] + pattern: "parent:" + documentIndex: 0 + - matchRegex: + path: data["template.tpl"] + pattern: "child1: value1" + documentIndex: 0 + + # ======================================== + # templates.dataOutput Tests + # ======================================== + - it: should output JSON wrapped in define block + set: + testLauncherTemplates: + templateName: json-template + content: + key1: value1 + key2: value2 + asserts: + - isKind: + of: ConfigMap + documentIndex: 1 + - equal: + path: metadata.name + value: test-launcher-data-output + documentIndex: 1 + - matchRegex: + path: data["template.tpl"] + pattern: 'define "json-template"' + documentIndex: 1 + + # ======================================== + # templates.dataOutputPretty Tests + # ======================================== + - it: should output pretty JSON wrapped in define block + set: + testLauncherTemplates: + templateName: pretty-json-template + content: + key1: value1 + key2: value2 + asserts: + - isKind: + of: ConfigMap + documentIndex: 2 + - equal: + path: metadata.name + value: test-launcher-data-output-pretty + documentIndex: 2 + - matchRegex: + path: data["template.tpl"] + pattern: 'define "pretty-json-template"' + documentIndex: 2 + + # ======================================== + # trailingDash Parameter Tests + # ======================================== + - it: should not include trailing dash when trailingDash is false + set: + testLauncherTemplates: + templateName: no-dash-template + content: + key: value + asserts: + - isKind: + of: ConfigMap + documentIndex: 3 + - equal: + path: metadata.name + value: test-launcher-no-trailing-dash + documentIndex: 3 + + # ======================================== + # Template Name Tests + # ======================================== + - it: should handle template names with dots + set: + testLauncherTemplates: + templateName: my.namespaced.template + content: + data: value + asserts: + - matchRegex: + path: data["template.tpl"] + pattern: 'define "my.namespaced.template"' + documentIndex: 0 diff --git a/other-charts/rstudio-library-test/tests/license_test.yaml b/other-charts/rstudio-library-test/tests/license_test.yaml new file mode 100644 index 000000000..f58dc6edf --- /dev/null +++ b/other-charts/rstudio-library-test/tests/license_test.yaml @@ -0,0 +1,93 @@ +suite: License Helpers +templates: + - templates/test-license.yaml +values: + - _base_values.yaml + +# Note: License templates output multiple document types. +# Document order: 0=ConfigMap (env), 1=ConfigMap (mount), 2=ConfigMap (volume), 3+=Secrets + +tests: + # ======================================== + # License Key Configuration Tests + # ======================================== + - it: should create license secret with key configured + set: + testLicense: + licenseKey: "SECRET-KEY-12345" + licenseServer: "" + licenseFile: "" + asserts: + - hasDocuments: + count: 4 + - isKind: + of: Secret + documentIndex: 3 + - equal: + path: stringData["test-product"] + value: "SECRET-KEY-12345" + documentIndex: 3 + + - it: should create env ConfigMap with license environment variable + set: + testLicense: + licenseKey: "SECRET-KEY-12345" + licenseServer: "" + licenseFile: "" + asserts: + - isKind: + of: ConfigMap + documentIndex: 0 + - equal: + path: metadata.name + value: test-license-env + documentIndex: 0 + - matchRegex: + path: data["env.yaml"] + pattern: "TEST_LICENSE" + documentIndex: 0 + + # ======================================== + # License File Configuration Tests + # ======================================== + - it: should render license template with file configured + set: + testLicense: + licenseKey: "" + licenseServer: "" + licenseFile: | + LICENSE CONTENT + asserts: + - hasDocuments: + count: 4 + + # ======================================== + # Full Configuration Tests + # ======================================== + - it: should render all documents with all options configured + set: + testLicense: + licenseKey: "KEY" + licenseServer: "server.example.com" + licenseFile: | + LICENSE + asserts: + - hasDocuments: + count: 5 + + # ======================================== + # Release Context Tests + # ======================================== + - it: should include release name in secret name + set: + testLicense: + licenseKey: "TEST-KEY" + licenseServer: "" + licenseFile: "" + release: + name: my-app + asserts: + - matchRegex: + path: metadata.name + pattern: "my-app-license" + documentIndex: 3 diff --git a/other-charts/rstudio-library-test/tests/profiles_test.yaml b/other-charts/rstudio-library-test/tests/profiles_test.yaml new file mode 100644 index 000000000..1149624d5 --- /dev/null +++ b/other-charts/rstudio-library-test/tests/profiles_test.yaml @@ -0,0 +1,237 @@ +suite: Profiles Helpers +templates: + - templates/test-profiles.yaml +values: + - _base_values.yaml + +tests: + # ======================================== + # Basic profiles.ini Tests + # ======================================== + - it: should render basic profiles.ini with sections + set: + testProfiles: + basicIni: + launcher.kubernetes.profiles.conf: + "*": + default-cpus: 1 + default-mem-mb: 512 + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-profiles-ini + - matchRegex: + path: data["launcher.kubernetes.profiles.conf"] + pattern: "\\[\\*\\]" + - matchRegex: + path: data["launcher.kubernetes.profiles.conf"] + pattern: "default-cpus=1" + + # ======================================== + # profiles.ini.singleFile Tests + # ======================================== + - it: should render single INI file content + set: + testProfiles: + singleFile: + "*": + setting: value1 + testuser: + setting: value2 + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-profiles-single-file + - matchRegex: + path: data["profiles.conf"] + pattern: "\\[\\*\\]" + - matchRegex: + path: data["profiles.conf"] + pattern: "\\[testuser\\]" + + # ======================================== + # profiles.ini.collapse-array Tests + # ======================================== + - it: should collapse simple array with commas + set: + testProfiles: + collapseArray: + simple: + - one + - two + - three + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-profiles-collapse-array + - equal: + path: data.simple + value: "one,two,three" + + - it: should collapse target/file array with quotes and colons + set: + testProfiles: + collapseArray: + simple: + - dummy + targetFile: + - target: pods + file: /etc/pods.json + - target: services + file: /etc/svc.json + asserts: + - matchRegex: + path: data.targetFile + pattern: '"pods":"/etc/pods.json"' + - matchRegex: + path: data.targetFile + pattern: '"services":"/etc/svc.json"' + + # ======================================== + # profiles.ini.advanced Tests + # ======================================== + - it: should render advanced profiles with everyone section + set: + testProfiles: + advanced: + data: + launcher.kubernetes.profiles.conf: + "*": + default-cpus: 2 + jobJsonDefaults: [] + filePath: /etc/rstudio/ + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-profiles-advanced + - matchRegex: + path: data["launcher.kubernetes.profiles.conf"] + pattern: "\\[\\*\\]" + - matchRegex: + path: data["launcher.kubernetes.profiles.conf"] + pattern: "default-cpus=2" + + # ======================================== + # profiles.json-from-overrides-config Tests + # ======================================== + - it: should generate JSON files from overrides config + set: + testProfiles: + jsonOverrides: + data: + "*": + job-json-overrides: + - name: default-limits + target: pods + json: + resources: + limits: + cpu: "2" + default: [] + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-profiles-json-overrides + - matchRegex: + path: data["default-limits.json"] + pattern: '"resources"' + + - it: should fail when override missing required keys + set: + testProfiles: + jsonOverrides: + data: + "*": + job-json-overrides: + - name: incomplete + json: + data: value + default: [] + asserts: + - failedTemplate: + errorPattern: "must have keys 'name', 'json', and 'target'" + + # ======================================== + # profiles.apply-everyone-and-default-to-others Tests + # ======================================== + - it: should apply everyone config to all sections + set: + testProfiles: + applyEveryone: + data: + "*": + shared-setting: everyone-value + user1: + user-setting: user1-value + default: [] + filePath: "" + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-profiles-apply-everyone + - matchRegex: + path: data["profiles.conf"] + pattern: "shared-setting=everyone-value" + + # ======================================== + # Type Safety Tests + # ======================================== + - it: should not render basicIni when value is a string instead of map + set: + testProfiles: + basicIni: "not-a-map" + asserts: + - hasDocuments: + count: 0 + + - it: should not render singleFile when value is a string instead of map + set: + testProfiles: + singleFile: "not-a-map" + asserts: + - hasDocuments: + count: 0 + + - it: should not render collapseArray when value is a string instead of map + set: + testProfiles: + collapseArray: "not-a-map" + asserts: + - hasDocuments: + count: 0 + + - it: should not render advanced when value is a string instead of map + set: + testProfiles: + advanced: "not-a-map" + asserts: + - hasDocuments: + count: 0 + + - it: should not render jsonOverrides when value is a string instead of map + set: + testProfiles: + jsonOverrides: "not-a-map" + asserts: + - hasDocuments: + count: 0 + + - it: should not render applyEveryone when value is a string instead of map + set: + testProfiles: + applyEveryone: "not-a-map" + asserts: + - hasDocuments: + count: 0 diff --git a/other-charts/rstudio-library-test/tests/rbac_test.yaml b/other-charts/rstudio-library-test/tests/rbac_test.yaml new file mode 100644 index 000000000..b0c6b1f80 --- /dev/null +++ b/other-charts/rstudio-library-test/tests/rbac_test.yaml @@ -0,0 +1,221 @@ +suite: RBAC Helper +templates: + - templates/test-rbac.yaml +values: + - _base_values.yaml + +tests: + # ======================================== + # ServiceAccount Creation Tests + # ======================================== + - it: should create ServiceAccount when serviceAccountCreate is true + set: + testRbac: + serviceAccountCreate: true + serviceAccountName: my-sa + namespace: my-namespace + targetNamespace: my-namespace + clusterRoleCreate: false + removeNamespaceReferences: false + labels: {} + annotations: {} + asserts: + - isKind: + of: ServiceAccount + documentIndex: 0 + - equal: + path: metadata.name + value: my-sa + documentIndex: 0 + - equal: + path: metadata.namespace + value: my-namespace + documentIndex: 0 + + - it: should not create ServiceAccount when serviceAccountCreate is false + set: + testRbac: + serviceAccountCreate: false + serviceAccountName: my-sa + namespace: my-namespace + targetNamespace: my-namespace + clusterRoleCreate: false + removeNamespaceReferences: false + labels: {} + annotations: {} + asserts: + - isKind: + of: Role + documentIndex: 0 + - isKind: + of: RoleBinding + documentIndex: 1 + + # ======================================== + # Role and RoleBinding Tests + # ======================================== + - it: should create Role with correct permissions + set: + testRbac: + serviceAccountCreate: true + serviceAccountName: launcher-sa + namespace: default + targetNamespace: default + clusterRoleCreate: false + removeNamespaceReferences: false + labels: {} + annotations: {} + asserts: + - isKind: + of: Role + documentIndex: 1 + - matchRegex: + path: rules[0].resources[0] + pattern: "serviceaccounts" + documentIndex: 1 + + - it: should create RoleBinding linking SA to Role + set: + testRbac: + serviceAccountCreate: true + serviceAccountName: launcher-sa + namespace: default + targetNamespace: default + clusterRoleCreate: false + removeNamespaceReferences: false + labels: {} + annotations: {} + asserts: + - isKind: + of: RoleBinding + documentIndex: 2 + - equal: + path: roleRef.kind + value: Role + documentIndex: 2 + - equal: + path: roleRef.name + value: launcher-sa + documentIndex: 2 + + # ======================================== + # ClusterRole Tests + # ======================================== + - it: should create ClusterRole when clusterRoleCreate is true + set: + testRbac: + serviceAccountCreate: true + serviceAccountName: cluster-sa + namespace: default + targetNamespace: default + clusterRoleCreate: true + removeNamespaceReferences: false + labels: {} + annotations: {} + asserts: + - isKind: + of: ClusterRole + documentIndex: 0 + - matchRegex: + path: rules[0].resources[0] + pattern: "nodes" + documentIndex: 0 + + - it: should create ClusterRoleBinding when clusterRoleCreate is true + set: + testRbac: + serviceAccountCreate: true + serviceAccountName: cluster-sa + namespace: default + targetNamespace: default + clusterRoleCreate: true + removeNamespaceReferences: false + labels: {} + annotations: {} + asserts: + - isKind: + of: ClusterRoleBinding + documentIndex: 1 + - equal: + path: roleRef.kind + value: ClusterRole + documentIndex: 1 + + # ======================================== + # Namespace Handling Tests + # ======================================== + - it: should remove namespace references when removeNamespaceReferences is true + set: + testRbac: + serviceAccountCreate: true + serviceAccountName: no-ns-sa + namespace: default + targetNamespace: default + clusterRoleCreate: false + removeNamespaceReferences: true + labels: {} + annotations: {} + asserts: + - notExists: + path: metadata.namespace + documentIndex: 0 + - notExists: + path: metadata.namespace + documentIndex: 1 + + # ======================================== + # Custom Annotations and Labels Tests + # ======================================== + - it: should add custom annotations to ServiceAccount + set: + testRbac: + serviceAccountCreate: true + serviceAccountName: annotated-sa + namespace: default + targetNamespace: default + clusterRoleCreate: false + removeNamespaceReferences: false + labels: {} + annotations: + eks.amazonaws.com/role-arn: "arn:aws:iam::123456789:role/my-role" + asserts: + - equal: + path: metadata.annotations["eks.amazonaws.com/role-arn"] + value: "arn:aws:iam::123456789:role/my-role" + documentIndex: 0 + + - it: should add custom labels to ServiceAccount + set: + testRbac: + serviceAccountCreate: true + serviceAccountName: labeled-sa + namespace: default + targetNamespace: default + clusterRoleCreate: false + removeNamespaceReferences: false + labels: + app.kubernetes.io/component: launcher + annotations: {} + asserts: + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: launcher + documentIndex: 0 + + # ======================================== + # Type Validation Tests + # ======================================== + - it: should fail when serviceAccountCreate is not boolean (string) + set: + testRbac: + serviceAccountCreate: "yes" + serviceAccountName: bad-sa + namespace: default + targetNamespace: default + clusterRoleCreate: false + removeNamespaceReferences: false + labels: {} + annotations: {} + asserts: + - failedTemplate: + errorPattern: "serviceAccountCreate must be a 'bool'" diff --git a/other-charts/rstudio-library-test/tests/tplvalues_test.yaml b/other-charts/rstudio-library-test/tests/tplvalues_test.yaml new file mode 100644 index 000000000..de18db0ab --- /dev/null +++ b/other-charts/rstudio-library-test/tests/tplvalues_test.yaml @@ -0,0 +1,115 @@ +suite: tplvalues Helpers +templates: + - templates/test-tplvalues.yaml +values: + - _base_values.yaml + +tests: + # ======================================== + # String Value Rendering Tests + # ======================================== + - it: should render string value with template expression + set: + testTplvalues: + staticValue: "static" + templateValue: "{{ .Release.Name }}-suffix" + release: + name: my-release + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: test-tplvalues + - equal: + path: data.renderedString + value: "my-release-suffix" + + - it: should pass through non-template strings unchanged + set: + testTplvalues: + staticValue: "plain-static-value" + templateValue: "no-template-here" + asserts: + - equal: + path: data.staticString + value: "plain-static-value" + - equal: + path: data.renderedString + value: "no-template-here" + + # ======================================== + # Release Context Tests + # ======================================== + - it: should access Release.Name in template + set: + testTplvalues: + staticValue: "x" + templateValue: "prefix-{{ .Release.Name }}" + release: + name: test-release + asserts: + - equal: + path: data.renderedString + value: "prefix-test-release" + + - it: should access Release.Namespace in template + set: + testTplvalues: + staticValue: "x" + templateValue: "ns-{{ .Release.Namespace }}" + release: + name: test + namespace: production + asserts: + - equal: + path: data.renderedString + value: "ns-production" + + # ======================================== + # Object Value Rendering Tests + # ======================================== + - it: should render object value with template expressions + set: + testTplvalues: + staticValue: "x" + templateValue: "x" + objectValue: + name: "{{ .Release.Name }}" + namespace: "{{ .Release.Namespace }}" + release: + name: obj-release + namespace: obj-namespace + asserts: + - matchRegex: + path: data.renderedObject + pattern: "obj-release" + - matchRegex: + path: data.renderedObject + pattern: "obj-namespace" + + # ======================================== + # Edge Cases + # ======================================== + - it: should handle empty string template value + set: + testTplvalues: + staticValue: "x" + templateValue: "" + asserts: + - equal: + path: data.renderedString + value: "" + + - it: should handle multiple template expressions in one value + set: + testTplvalues: + staticValue: "x" + templateValue: "{{ .Release.Name }}-{{ .Release.Namespace }}-app" + release: + name: multi + namespace: test + asserts: + - equal: + path: data.renderedString + value: "multi-test-app" diff --git a/other-charts/rstudio-library-test/values.yaml b/other-charts/rstudio-library-test/values.yaml new file mode 100644 index 000000000..54a96552f --- /dev/null +++ b/other-charts/rstudio-library-test/values.yaml @@ -0,0 +1,161 @@ +# Test values for rstudio-library-test harness +# These values are used by test templates to exercise library functions +# +# Each testXxx block corresponds to a test template in templates/test-xxx.yaml +# Tests use _base_values.yaml to null out all categories, then override +# only the specific category being tested + +# Config formatter test values +# Exercises: config.gcfg, config.ini, config.dcf, config.json, config.txt +testConfig: + gcfg: + filename: test.gcfg + config: + section1: + key1: value1 + key2: value2 + section2: + arrayKey: + - item1 + - item2 + ini: + filename: test.ini + config: + section1: + key1: value1 + key2: 123 + dcf: + filename: test.dcf + config: + key1: value1 + nested: + subkey: subvalue + json: + filename: test.json + config: + stringKey: stringValue + numberKey: 42 + boolKey: true + arrayKey: + - item1 + - item2 + txt: + filename: test.txt + config: + key1: value1 + key2: value2 + +# Ingress helper test values +# Exercises: ingress.apiVersion, ingress.path, ingress.backend, +# ingress.supportsIngressClassName, ingress.supportsPathType +testIngress: + serviceName: test-service + servicePort: 8080 + path: /test + pathType: Prefix + +# License helper test values +# Exercises: license-env, license-mount, license-volume, license-secret +testLicense: + licenseKey: "test-license-key" + licenseServer: "license.example.com" + licenseFile: | + LICENSE CONTENT HERE + +# RBAC test values +# Exercises: rbac (ServiceAccount, Role, RoleBinding, ClusterRole) +testRbac: + serviceAccountCreate: true + serviceAccountName: test-sa + clusterRoleCreate: false + namespace: test-namespace + targetNamespace: test-target + removeNamespaceReferences: false + labels: {} + annotations: {} + +# Profiles test values +# Exercises: profiles.ini, profiles.ini.singleFile, profiles.ini.collapse-array, +# profiles.ini.advanced, profiles.json-from-overrides-config, +# profiles.apply-everyone-and-default-to-others +testProfiles: + # Basic INI test + basicIni: + launcher.kubernetes.profiles.conf: + "*": + default-cpus: 1 + default-mem-mb: 512 + testuser: + default-cpus: 2 + # Single file test + singleFile: + "*": + job-name: default-job + testuser: + job-name: user-job + # Collapse array test + collapseArray: + simple: + - one + - two + - three + targetFile: + - target: pods + file: /etc/config/pods.json + - target: services + file: /etc/config/services.json + # Advanced profiles test + advanced: + data: + launcher.kubernetes.profiles.conf: + "*": + default-cpus: 1 + testuser: + default-cpus: 4 + jobJsonDefaults: [] + filePath: /etc/rstudio/ + # JSON overrides test (optional - tests set this explicitly) + # jsonOverrides: ... + # Apply everyone test (optional - tests set this explicitly) + # applyEveryone: ... + +# Debug helper test values +# Exercises: debug.type-check +testDebug: + mapValue: + key: value + sliceValue: + - item1 + - item2 + stringValue: "test string" + boolValue: true + +# Launcher template test values +# Exercises: templates.skeleton, templates.dataOutput, templates.dataOutputPretty +testLauncherTemplates: + templateName: test-template + content: + key1: value1 + key2: value2 + nested: + subkey: subvalue + +# Tplvalues test values +# Exercises: tplvalues.render +testTplvalues: + staticValue: "static" + templateValue: "{{ .Release.Name }}-suffix" + objectValue: + name: "{{ .Release.Name }}" + namespace: "{{ .Release.Namespace }}" + +# Chronicle agent test values +# Exercises: chronicle-agent.image, chronicle-agent.serverAddress +testChronicle: + enabled: true + serverAddress: "http://chronicle-server.default:8080" + serverNamespace: "" + image: + registry: ghcr.io + repository: rstudio/chronicle-agent + tag: "1.0.0"