diff --git a/.github/workflows/test-helm-chart.yml b/.github/workflows/test-helm-chart.yml new file mode 100644 index 0000000..2c62c82 --- /dev/null +++ b/.github/workflows/test-helm-chart.yml @@ -0,0 +1,75 @@ +name: Test Helm Chart + +on: + pull_request: + push: + branches: + - main + +jobs: + unittest: + name: Unit Tests + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: 'latest' + + - name: Install helm-unittest + run: | + helm plugin install https://github.com/quintush/helm-unittest --verify=false + + - name: Run unit tests + run: | + helm unittest braintrust + + lint-test: + name: Lint and Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: 'latest' + + - name: Lint chart + run: helm lint braintrust --strict + + test-values: + name: Test with Values Files + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: 'latest' + + - name: Test chart rendering with Azure values + run: | + helm template test-release braintrust --values braintrust/ci/values-azure.yaml > /dev/null + echo "✅ Chart rendered successfully with braintrust/ci/values-azure.yaml" + + - name: Test chart rendering with Google values + run: | + helm template test-release braintrust --values braintrust/ci/values-google.yaml > /dev/null + echo "✅ Chart rendered successfully with braintrust/ci/values-google.yaml" + + - name: Test chart rendering with AWS values + run: | + helm template test-release braintrust --values braintrust/ci/values-aws.yaml > /dev/null + echo "✅ Chart rendered successfully with braintrust/ci/values-aws.yaml" + + - name: Test chart rendering with Minimal values + run: | + helm template test-release braintrust --values braintrust/ci/values-minimal.yaml > /dev/null + echo "✅ Chart rendered successfully with braintrust/ci/values-minimal.yaml" diff --git a/.helmignore b/.helmignore new file mode 100644 index 0000000..0b279cd --- /dev/null +++ b/.helmignore @@ -0,0 +1,9 @@ +# Patterns to ignore when building packages. +.git +.github +.gitignore +README.md +LICENSE +docs/ +*.md +!braintrust/README.md diff --git a/braintrust/README.md b/braintrust/README.md index da4bbb6..8c37a5f 100644 --- a/braintrust/README.md +++ b/braintrust/README.md @@ -147,6 +147,14 @@ For Standard mode clusters, create node pools with local SSDs, then deploy: - Local SSDs are automatically available via emptyDir volumes - Pod anti-affinity ensures readers and writers don't share nodes (each pod gets dedicated node access) +## Testing + +This Helm chart includes comprehensive automated unit tests. + +```bash +# Run all tests +./test.sh +``` ## Breaking Changes diff --git a/braintrust/ci/values-aws.yaml b/braintrust/ci/values-aws.yaml new file mode 100644 index 0000000..6dc4d7b --- /dev/null +++ b/braintrust/ci/values-aws.yaml @@ -0,0 +1,103 @@ +# Test values for AWS deployment +global: + orgName: "test-org" + createNamespace: false + namespace: "braintrust" + labels: {} + namespaceAnnotations: {} + controlPlaneTelemetry: "status,metrics,usage" + +cloud: "aws" + +objectStorage: + aws: + brainstoreBucket: "test-brainstore-bucket" + responseBucket: "test-response-bucket" + codeBundleBucket: "test-code-bundle-bucket" + +api: + name: "braintrust-api" + replicas: 1 + image: + repository: "public.ecr.aws/braintrust/standalone-api" + tag: "v1.1.28" + pullPolicy: "IfNotPresent" + service: + type: ClusterIP + port: 8000 + serviceAccount: + name: "braintrust-api" + awsRoleArn: "arn:aws:iam::123456789012:role/test-role" + resources: + requests: + cpu: "2" + memory: "4Gi" + limits: + cpu: "4" + memory: "8Gi" + allowCodeFunctionExecution: true + backfillDisableHistorical: false + backfillDisableNonhistorical: false + allowInvalidBase64: false + nodeMemoryPercent: "80" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + +brainstore: + serviceAccount: + name: "brainstore" + awsRoleArn: "arn:aws:iam::123456789012:role/test-role" + annotations: {} + image: + repository: "public.ecr.aws/braintrust/brainstore" + tag: "v1.1.28" + pullPolicy: "IfNotPresent" + locksBackend: "redis" + reader: + name: "brainstore-reader" + replicas: 1 + service: + type: ClusterIP + port: 4000 + resources: + requests: + cpu: "4" + memory: "8Gi" + limits: + cpu: "8" + memory: "16Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + volume: + size: "100Gi" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + writer: + name: "brainstore-writer" + replicas: 1 + service: + type: ClusterIP + port: 4000 + resources: + requests: + cpu: "8" + memory: "16Gi" + limits: + cpu: "16" + memory: "32Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + volume: + size: "200Gi" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} diff --git a/braintrust/ci/values-azure.yaml b/braintrust/ci/values-azure.yaml new file mode 100644 index 0000000..3a0d622 --- /dev/null +++ b/braintrust/ci/values-azure.yaml @@ -0,0 +1,127 @@ +# Test values for Azure deployment +global: + orgName: "test-org" + createNamespace: false + namespace: "braintrust" + labels: {} + namespaceAnnotations: {} + controlPlaneTelemetry: "status,metrics,usage" + +cloud: "azure" + +azure: + tenantId: "test-tenant-id" + enableAzureContainerStorageDriver: true + enableAzureKeyVaultDriver: true + keyVaultCSIclientID: "test-client-id" + keyVaultName: "test-keyvault" + +objectStorage: + azure: + storageAccountName: "teststorageaccount" + brainstoreContainer: "brainstore" + responseContainer: "responses" + codeBundleContainer: "code-bundles" + +api: + name: "braintrust-api" + replicas: 1 + image: + repository: "public.ecr.aws/braintrust/standalone-api" + tag: "v1.1.28" + pullPolicy: "IfNotPresent" + service: + type: ClusterIP + port: 8000 + serviceAccount: + name: "braintrust-api" + resources: + requests: + cpu: "2" + memory: "4Gi" + limits: + cpu: "4" + memory: "8Gi" + allowCodeFunctionExecution: true + backfillDisableHistorical: false + backfillDisableNonhistorical: false + allowInvalidBase64: false + nodeMemoryPercent: "80" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + +brainstore: + serviceAccount: + name: "brainstore" + annotations: {} + image: + repository: "public.ecr.aws/braintrust/brainstore" + tag: "v1.1.28" + pullPolicy: "IfNotPresent" + locksBackend: "redis" + reader: + name: "brainstore-reader" + replicas: 1 + service: + type: ClusterIP + port: 4000 + resources: + requests: + cpu: "4" + memory: "8Gi" + limits: + cpu: "8" + memory: "16Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + volume: + size: "100Gi" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + writer: + name: "brainstore-writer" + replicas: 1 + service: + type: ClusterIP + port: 4000 + resources: + requests: + cpu: "8" + memory: "16Gi" + limits: + cpu: "16" + memory: "32Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + volume: + size: "200Gi" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + +azureKeyVaultDriver: + secrets: + - keyVaultSecretName: "redis-connection-string" + keyVaultSecretType: "secret" + kubernetesSecretKey: "REDIS_URL" + - keyVaultSecretName: "postgres-connection-string" + kubernetesSecretKey: "PG_URL" + keyVaultSecretType: "secret" + - keyVaultSecretName: "brainstore-license-key" + keyVaultSecretType: "secret" + kubernetesSecretKey: "BRAINSTORE_LICENSE_KEY" + - keyVaultSecretName: "function-secret-key" + keyVaultSecretType: "secret" + kubernetesSecretKey: "FUNCTION_SECRET_KEY" + - keyVaultSecretName: "azure-storage-connection-string" + keyVaultSecretType: "secret" + kubernetesSecretKey: "AZURE_STORAGE_CONNECTION_STRING" diff --git a/braintrust/ci/values-google.yaml b/braintrust/ci/values-google.yaml new file mode 100644 index 0000000..23f5b83 --- /dev/null +++ b/braintrust/ci/values-google.yaml @@ -0,0 +1,105 @@ +# Test values for Google Cloud deployment +global: + orgName: "test-org" + createNamespace: false + namespace: "braintrust" + labels: {} + namespaceAnnotations: {} + controlPlaneTelemetry: "status,metrics,usage" + +cloud: "google" + +google: + mode: "autopilot" + autopilotMachineFamily: "c4" + +objectStorage: + google: + brainstoreBucket: "test-brainstore-bucket" + apiBucket: "test-api-bucket" + +api: + name: "braintrust-api" + replicas: 1 + image: + repository: "public.ecr.aws/braintrust/standalone-api" + tag: "v1.1.28" + pullPolicy: "IfNotPresent" + service: + type: ClusterIP + port: 8000 + serviceAccount: + name: "braintrust-api" + resources: + requests: + cpu: "2" + memory: "4Gi" + limits: + cpu: "4" + memory: "8Gi" + allowCodeFunctionExecution: true + backfillDisableHistorical: false + backfillDisableNonhistorical: false + allowInvalidBase64: false + nodeMemoryPercent: "80" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + +brainstore: + serviceAccount: + name: "brainstore" + googleServiceAccount: "test@project.iam.gserviceaccount.com" + annotations: {} + image: + repository: "public.ecr.aws/braintrust/brainstore" + tag: "v1.1.28" + pullPolicy: "IfNotPresent" + locksBackend: "redis" + reader: + name: "brainstore-reader" + replicas: 1 + service: + type: ClusterIP + port: 4000 + resources: + requests: + cpu: "4" + memory: "8Gi" + limits: + cpu: "8" + memory: "16Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + volume: + size: "100Gi" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + writer: + name: "brainstore-writer" + replicas: 1 + service: + type: ClusterIP + port: 4000 + resources: + requests: + cpu: "8" + memory: "16Gi" + limits: + cpu: "16" + memory: "32Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + volume: + size: "200Gi" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} diff --git a/braintrust/ci/values-minimal.yaml b/braintrust/ci/values-minimal.yaml new file mode 100644 index 0000000..f765e52 --- /dev/null +++ b/braintrust/ci/values-minimal.yaml @@ -0,0 +1,91 @@ +# Minimal test values - just enough to render the chart +global: + orgName: "test-org" + createNamespace: false + namespace: "braintrust" + labels: {} + namespaceAnnotations: {} + controlPlaneTelemetry: "status,metrics" + +cloud: "" + +api: + name: "braintrust-api" + replicas: 1 + image: + repository: "public.ecr.aws/braintrust/standalone-api" + tag: "v1.1.28" + pullPolicy: "IfNotPresent" + service: + type: ClusterIP + port: 8000 + serviceAccount: + name: "braintrust-api" + resources: + requests: + cpu: "2" + memory: "4Gi" + limits: + cpu: "4" + memory: "8Gi" + allowCodeFunctionExecution: true + backfillDisableHistorical: false + backfillDisableNonhistorical: false + allowInvalidBase64: false + nodeMemoryPercent: "80" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + +brainstore: + serviceAccount: + name: "brainstore" + annotations: {} + image: + repository: "public.ecr.aws/braintrust/brainstore" + tag: "v1.1.28" + pullPolicy: "IfNotPresent" + locksBackend: "redis" + reader: + name: "brainstore-reader" + replicas: 1 + service: + type: ClusterIP + port: 4000 + resources: + requests: + cpu: "4" + memory: "8Gi" + limits: + cpu: "8" + memory: "16Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + writer: + name: "brainstore-writer" + replicas: 1 + service: + type: ClusterIP + port: 4000 + resources: + requests: + cpu: "8" + memory: "16Gi" + limits: + cpu: "16" + memory: "32Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} diff --git a/braintrust/tests/__fixtures__/aws-values.yaml b/braintrust/tests/__fixtures__/aws-values.yaml new file mode 100644 index 0000000..5853e4a --- /dev/null +++ b/braintrust/tests/__fixtures__/aws-values.yaml @@ -0,0 +1,17 @@ +# AWS-specific test values +# Extends base-values.yaml with AWS configuration +cloud: "aws" + +objectStorage: + aws: + brainstoreBucket: "test-brainstore-bucket" + responseBucket: "test-response-bucket" + codeBundleBucket: "test-code-bundle-bucket" + +api: + serviceAccount: + awsRoleArn: "arn:aws:iam::123456789012:role/test-api-role" + +brainstore: + serviceAccount: + awsRoleArn: "arn:aws:iam::123456789012:role/test-brainstore-role" diff --git a/braintrust/tests/__fixtures__/azure-values.yaml b/braintrust/tests/__fixtures__/azure-values.yaml new file mode 100644 index 0000000..3f94dc2 --- /dev/null +++ b/braintrust/tests/__fixtures__/azure-values.yaml @@ -0,0 +1,43 @@ +# Azure-specific test values +# Extends base-values.yaml with Azure configuration +cloud: "azure" + +azure: + tenantId: "test-tenant-id" + enableAzureContainerStorageDriver: true + enableAzureKeyVaultDriver: true + keyVaultCSIclientID: "test-client-id" + keyVaultName: "test-keyvault" + +objectStorage: + azure: + storageAccountName: "teststorageaccount" + brainstoreContainer: "brainstore" + responseContainer: "responses" + codeBundleContainer: "code-bundles" + +api: + serviceAccount: + azureClientId: "test-api-client-id" + +brainstore: + serviceAccount: + azureClientId: "test-brainstore-client-id" + +azureKeyVaultDriver: + secrets: + - keyVaultSecretName: "redis-connection-string" + keyVaultSecretType: "secret" + kubernetesSecretKey: "REDIS_URL" + - keyVaultSecretName: "postgres-connection-string" + kubernetesSecretKey: "PG_URL" + keyVaultSecretType: "secret" + - keyVaultSecretName: "brainstore-license-key" + keyVaultSecretType: "secret" + kubernetesSecretKey: "BRAINSTORE_LICENSE_KEY" + - keyVaultSecretName: "function-secret-key" + keyVaultSecretType: "secret" + kubernetesSecretKey: "FUNCTION_SECRET_KEY" + - keyVaultSecretName: "azure-storage-connection-string" + keyVaultSecretType: "secret" + kubernetesSecretKey: "AZURE_STORAGE_CONNECTION_STRING" diff --git a/braintrust/tests/__fixtures__/base-values.yaml b/braintrust/tests/__fixtures__/base-values.yaml new file mode 100644 index 0000000..b485e11 --- /dev/null +++ b/braintrust/tests/__fixtures__/base-values.yaml @@ -0,0 +1,122 @@ +# Base test values - minimal required values (cloud-agnostic) +# Use this as a base for all tests and override specific values as needed +global: + orgName: "test-org" + createNamespace: false + namespace: "braintrust" + labels: {} + namespaceAnnotations: {} + controlPlaneTelemetry: "status,metrics" + +cloud: "" + +api: + name: "braintrust-api" + labels: {} + annotations: + configmap: {} + deployment: {} + service: {} + pod: {} + serviceaccount: {} + replicas: 1 + image: + repository: "test/api" + tag: "v1.0.0" + pullPolicy: "IfNotPresent" + service: + name: "" + type: ClusterIP + port: 8000 + portName: http + serviceAccount: + name: "braintrust-api" + resources: + requests: + cpu: "2" + memory: "4Gi" + limits: + cpu: "4" + memory: "8Gi" + allowCodeFunctionExecution: true + backfillDisableHistorical: false + backfillDisableNonhistorical: false + allowInvalidBase64: false + nodeMemoryPercent: "80" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + +brainstore: + labels: {} + serviceAccount: + name: "brainstore" + annotations: {} + image: + repository: "test/brainstore" + tag: "v1.0.0" + pullPolicy: "IfNotPresent" + locksBackend: "redis" + reader: + name: "brainstore-reader" + labels: {} + annotations: + configmap: {} + deployment: {} + service: {} + pod: {} + replicas: 1 + service: + name: "" + type: ClusterIP + port: 4000 + portName: http + resources: + requests: + cpu: "4" + memory: "8Gi" + limits: + cpu: "8" + memory: "16Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + volume: + size: "100Gi" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} + writer: + name: "brainstore-writer" + labels: {} + annotations: + configmap: {} + deployment: {} + service: {} + pod: {} + replicas: 1 + service: + name: "" + type: ClusterIP + port: 4000 + portName: http + resources: + requests: + cpu: "8" + memory: "16Gi" + limits: + cpu: "16" + memory: "32Gi" + cacheDir: "/mnt/tmp/brainstore" + objectStoreCacheMemoryLimit: "1Gi" + objectStoreCacheFileSize: "50Gi" + verbose: true + volume: + size: "200Gi" + extraEnvVars: [] + nodeSelector: {} + tolerations: [] + affinity: {} diff --git a/braintrust/tests/__fixtures__/google-values.yaml b/braintrust/tests/__fixtures__/google-values.yaml new file mode 100644 index 0000000..8655dd0 --- /dev/null +++ b/braintrust/tests/__fixtures__/google-values.yaml @@ -0,0 +1,16 @@ +# Google Cloud-specific test values +# Extends base-values.yaml with GCP configuration +cloud: "google" + +google: + mode: "autopilot" + autopilotMachineFamily: "c4" + +objectStorage: + google: + brainstoreBucket: "test-brainstore-bucket" + apiBucket: "test-api-bucket" + +brainstore: + serviceAccount: + googleServiceAccount: "test@project.iam.gserviceaccount.com" diff --git a/braintrust/tests/api-configmap_test.yaml b/braintrust/tests/api-configmap_test.yaml new file mode 100644 index 0000000..5d206bd --- /dev/null +++ b/braintrust/tests/api-configmap_test.yaml @@ -0,0 +1,184 @@ +suite: test API configmap template +templates: + - api-configmap.yaml +tests: + - it: should render API configmap with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: braintrust-api + - equal: + path: data.ORG_NAME + value: "test-org" + - equal: + path: data.BRAINSTORE_ENABLED + value: "true" + - equal: + path: data.BRAINSTORE_DEFAULT + value: "force" + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should use global namespace when createNamespace is true + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: true + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: custom-ns + + - it: should configure AWS buckets when cloud is aws + values: + - __fixtures__/base-values.yaml + - __fixtures__/aws-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.RESPONSE_BUCKET + value: "test-response-bucket" + - equal: + path: data.CODE_BUNDLE_BUCKET + value: "test-code-bundle-bucket" + - equal: + path: data.BRAINSTORE_REALTIME_WAL_BUCKET + value: "test-brainstore-bucket" + + - it: should configure Azure storage when cloud is azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.AZURE_STORAGE_ACCOUNT_NAME + value: "teststorageaccount" + - equal: + path: data.RESPONSE_BUCKET + value: "responses" + - equal: + path: data.CODE_BUNDLE_BUCKET + value: "code-bundles" + - equal: + path: data.BRAINSTORE_REALTIME_WAL_BUCKET + value: "brainstore" + - equal: + path: data.BRAINSTORE_REALTIME_WAL_BUCKET_PREFIX + value: "brainstore/wal" + + - it: should configure Google Cloud buckets when cloud is google + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.RESPONSE_BUCKET + value: "test-api-bucket" + - equal: + path: data.RESPONSE_BUCKET_PREFIX + value: "response/" + - equal: + path: data.CODE_BUNDLE_BUCKET + value: "test-api-bucket" + - equal: + path: data.CODE_BUNDLE_BUCKET_PREFIX + value: "code-bundle/" + - equal: + path: data.BRAINSTORE_REALTIME_WAL_BUCKET + value: "test-brainstore-bucket" + - equal: + path: data.AWS_ENDPOINT_URL + value: "https://storage.googleapis.com" + + - it: should configure brainstore URLs correctly + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_URL + value: "http://brainstore-reader.braintrust:4000" + - equal: + path: data.BRAINSTORE_WRITER_URL + value: "http://brainstore-writer.braintrust:4000" + + - it: should include control plane telemetry + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.CONTROL_PLANE_TELEMETRY + value: "status,metrics" + + - it: should merge global and api labels + values: + - __fixtures__/base-values.yaml + set: + api.labels.component: "api" + global.labels.environment: "production" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.component + value: api + + - it: should include configmap annotations when provided + values: + - __fixtures__/base-values.yaml + set: + api.annotations.configmap: + custom-annotation: "value" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value + + - it: should configure backfill settings + values: + - __fixtures__/base-values.yaml + set: + api.backfillDisableHistorical: true + api.backfillDisableNonhistorical: true + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_BACKFILL_DISABLE_HISTORICAL + value: "true" + - equal: + path: data.BRAINSTORE_BACKFILL_DISABLE_NONHISTORICAL + value: "true" diff --git a/braintrust/tests/api-deployment_test.yaml b/braintrust/tests/api-deployment_test.yaml new file mode 100644 index 0000000..f495cd0 --- /dev/null +++ b/braintrust/tests/api-deployment_test.yaml @@ -0,0 +1,241 @@ +suite: test API deployment template +templates: + - api-deployment.yaml +tests: + - it: should render API deployment with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: Deployment + - equal: + path: metadata.name + value: braintrust-api + - equal: + path: spec.replicas + value: 1 + - equal: + path: spec.template.spec.containers[0].image + value: test/api:v1.0.0 + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: IfNotPresent + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should use global namespace when createNamespace is true + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: true + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: custom-ns + + - it: should include azure workload identity label when cloud is azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.metadata.labels["azure.workload.identity/use"] + value: "true" + + - it: should include Azure storage connection string env var when cloud is azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AZURE_STORAGE_CONNECTION_STRING + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: AZURE_STORAGE_CONNECTION_STRING + + - it: should include GCS env vars when cloud is google + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AWS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: GCS_ACCESS_KEY_ID + - contains: + path: spec.template.spec.containers[0].env + content: + name: AWS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: GCS_SECRET_ACCESS_KEY + + - it: should include Azure Key Vault volume when enabled + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: secrets-store-inline + mountPath: "/mnt/secrets-store" + readOnly: true + - contains: + path: spec.template.spec.volumes + content: + name: secrets-store-inline + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: test-keyvault + + - it: should not include Azure Key Vault volume when disabled + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + set: + azure.enableAzureKeyVaultDriver: false + release: + namespace: "braintrust" + asserts: + - isNull: + path: spec.template.spec.volumes + + - it: should include extraEnvVars when provided + values: + - __fixtures__/base-values.yaml + set: + api.extraEnvVars: + - name: CUSTOM_VAR + value: "custom-value" + - name: ANOTHER_VAR + value: "another-value" + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: CUSTOM_VAR + value: "custom-value" + - contains: + path: spec.template.spec.containers[0].env + content: + name: ANOTHER_VAR + value: "another-value" + + - it: should merge global and api labels + values: + - __fixtures__/base-values.yaml + set: + api.labels.component: "api" + global.labels.environment: "production" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.component + value: api + + - it: should include livenessProbe when configured + values: + - __fixtures__/base-values.yaml + set: + api.livenessProbe: + httpGet: + path: / + port: 8000 + initialDelaySeconds: 30 + periodSeconds: 10 + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.spec.containers[0].livenessProbe.httpGet.path + value: / + - equal: + path: spec.template.spec.containers[0].livenessProbe.httpGet.port + value: 8000 + - equal: + path: spec.template.spec.containers[0].livenessProbe.initialDelaySeconds + value: 30 + - equal: + path: spec.template.spec.containers[0].livenessProbe.periodSeconds + value: 10 + + - it: should include readinessProbe when configured + values: + - __fixtures__/base-values.yaml + set: + api.readinessProbe: + httpGet: + path: /status + port: 8001 + initialDelaySeconds: 10 + periodSeconds: 5 + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.path + value: /status + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.port + value: 8001 + - equal: + path: spec.template.spec.containers[0].readinessProbe.initialDelaySeconds + value: 10 + - equal: + path: spec.template.spec.containers[0].readinessProbe.periodSeconds + value: 5 + + - it: should not include probes when explicitly disabled + values: + - __fixtures__/base-values.yaml + set: + api.livenessProbe: null + api.readinessProbe: null + release: + namespace: "braintrust" + asserts: + - isNull: + path: spec.template.spec.containers[0].livenessProbe + - isNull: + path: spec.template.spec.containers[0].readinessProbe diff --git a/braintrust/tests/api-service_test.yaml b/braintrust/tests/api-service_test.yaml new file mode 100644 index 0000000..1a76bb7 --- /dev/null +++ b/braintrust/tests/api-service_test.yaml @@ -0,0 +1,119 @@ +suite: test API service template +templates: + - api-service.yaml +tests: + - it: should render API service with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: Service + - equal: + path: metadata.name + value: braintrust-api + - equal: + path: spec.type + value: ClusterIP + - equal: + path: spec.selector.app + value: braintrust-api + + - it: should use custom service name when provided + values: + - __fixtures__/base-values.yaml + set: + api.service.name: "custom-api-service" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.name + value: custom-api-service + + - it: should configure port correctly + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.ports[0].name + value: http + - equal: + path: spec.ports[0].port + value: 8000 + - equal: + path: spec.ports[0].targetPort + value: 8000 + - equal: + path: spec.ports[0].protocol + value: TCP + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should use global namespace when createNamespace is true + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: true + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: custom-ns + + - it: should merge global and api labels + values: + - __fixtures__/base-values.yaml + set: + api.labels.component: "api" + global.labels.environment: "production" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.component + value: api + + - it: should include service annotations when provided + values: + - __fixtures__/base-values.yaml + set: + api.annotations.service: + custom-annotation: "value" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value + + - it: should support LoadBalancer type + values: + - __fixtures__/base-values.yaml + set: + api.service.type: "LoadBalancer" + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.type + value: LoadBalancer diff --git a/braintrust/tests/api-serviceaccount_test.yaml b/braintrust/tests/api-serviceaccount_test.yaml new file mode 100644 index 0000000..296b2a9 --- /dev/null +++ b/braintrust/tests/api-serviceaccount_test.yaml @@ -0,0 +1,103 @@ +suite: test API serviceaccount template +templates: + - api-serviceaccount.yaml +tests: + - it: should render API serviceaccount with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: ServiceAccount + - equal: + path: metadata.name + value: braintrust-api + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should use global namespace when createNamespace is true + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: true + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: custom-ns + + - it: should include AWS IAM role annotation when cloud is aws + values: + - __fixtures__/base-values.yaml + - __fixtures__/aws-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["eks.amazonaws.com/role-arn"] + value: arn:aws:iam::123456789012:role/test-api-role + + - it: should include Azure workload identity annotation when cloud is azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["azure.workload.identity/client-id"] + value: test-api-client-id + + - it: should not include cloud-specific annotations when cloud is empty + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isNull: + path: metadata.annotations["eks.amazonaws.com/role-arn"] + - isNull: + path: metadata.annotations["azure.workload.identity/client-id"] + + - it: should merge global and api labels + values: + - __fixtures__/base-values.yaml + set: + api.labels.component: "api" + global.labels.environment: "production" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.component + value: api + + - it: should include custom serviceaccount annotations when provided + values: + - __fixtures__/base-values.yaml + set: + api.annotations.serviceaccount: + custom-annotation: "value" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value diff --git a/braintrust/tests/brainstore-reader-configmap_test.yaml b/braintrust/tests/brainstore-reader-configmap_test.yaml new file mode 100644 index 0000000..7626c76 --- /dev/null +++ b/braintrust/tests/brainstore-reader-configmap_test.yaml @@ -0,0 +1,184 @@ +suite: test Brainstore Reader configmap template +templates: + - brainstore-reader-configmap.yaml +tests: + - it: should render Brainstore Reader configmap with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: brainstore-reader + - equal: + path: data.BRAINSTORE_READER_ONLY_MODE + value: "true" + - equal: + path: data.BRAINSTORE_PORT + value: "4000" + - equal: + path: data.BRAINSTORE_CACHE_DIR + value: "/mnt/tmp/brainstore" + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should configure verbose mode + values: + - __fixtures__/base-values.yaml + set: + brainstore.reader.verbose: true + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_VERBOSE + value: "1" + + - it: should configure AWS URIs when cloud is aws + values: + - __fixtures__/base-values.yaml + - __fixtures__/aws-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_INDEX_URI + value: "s3://test-brainstore-bucket/brainstore/index" + - equal: + path: data.BRAINSTORE_REALTIME_WAL_URI + value: "s3://test-brainstore-bucket/brainstore/wal" + + - it: should configure Azure URIs when cloud is azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.AZURE_STORAGE_ACCOUNT_NAME + value: "teststorageaccount" + - equal: + path: data.BRAINSTORE_INDEX_URI + value: "az://brainstore/brainstore/index" + - equal: + path: data.BRAINSTORE_REALTIME_WAL_URI + value: "az://brainstore/brainstore/wal" + + - it: should configure Google Cloud URIs when cloud is google + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_INDEX_URI + value: "gs://test-brainstore-bucket/brainstore/index" + - equal: + path: data.BRAINSTORE_REALTIME_WAL_URI + value: "gs://test-brainstore-bucket/brainstore/wal" + + - it: should include locks URI when locksBackend is objectStorage for AWS + values: + - __fixtures__/base-values.yaml + - __fixtures__/aws-values.yaml + set: + brainstore.locksBackend: "objectStorage" + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_LOCKS_URI + value: "s3://test-brainstore-bucket/brainstore/locks" + + - it: should include locks URI when locksBackend is objectStorage for Azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + set: + brainstore.locksBackend: "objectStorage" + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_LOCKS_URI + value: "az://brainstore/brainstore/locks" + + - it: should include locks URI when locksBackend is objectStorage for Google + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + set: + brainstore.locksBackend: "objectStorage" + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_LOCKS_URI + value: "gs://test-brainstore-bucket/brainstore/locks" + + - it: should not include locks URI when locksBackend is redis + values: + - __fixtures__/base-values.yaml + - __fixtures__/aws-values.yaml + set: + brainstore.locksBackend: "redis" + release: + namespace: "braintrust" + asserts: + - isNull: + path: data.BRAINSTORE_LOCKS_URI + + - it: should include control plane telemetry + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_CONTROL_PLANE_TELEMETRY + value: "status,metrics" + + - it: should merge global and reader labels + values: + - __fixtures__/base-values.yaml + set: + brainstore.reader.labels.component: "brainstore-reader" + global.labels.environment: "production" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.component + value: brainstore-reader + + - it: should include configmap annotations when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.reader.annotations.configmap: + custom-annotation: "value" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value diff --git a/braintrust/tests/brainstore-reader-service_test.yaml b/braintrust/tests/brainstore-reader-service_test.yaml new file mode 100644 index 0000000..0b3e683 --- /dev/null +++ b/braintrust/tests/brainstore-reader-service_test.yaml @@ -0,0 +1,107 @@ +suite: test Brainstore Reader service template +templates: + - brainstore-reader-service.yaml +tests: + - it: should render Brainstore Reader service with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: Service + - equal: + path: metadata.name + value: brainstore-reader + - equal: + path: spec.type + value: ClusterIP + - equal: + path: spec.selector.app + value: brainstore-reader + + - it: should use custom service name when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.reader.service.name: "custom-reader-service" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.name + value: custom-reader-service + + - it: should configure port correctly + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.ports[0].name + value: http + - equal: + path: spec.ports[0].port + value: 4000 + - equal: + path: spec.ports[0].targetPort + value: 4000 + - equal: + path: spec.ports[0].protocol + value: TCP + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should use global namespace when createNamespace is true + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: true + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: custom-ns + + - it: should merge global and reader labels + values: + - __fixtures__/base-values.yaml + set: + brainstore.reader.labels.component: "brainstore-reader" + global.labels.environment: "production" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.component + value: brainstore-reader + + - it: should include service annotations when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.reader.annotations.service: + custom-annotation: "value" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value diff --git a/braintrust/tests/brainstore-reader_test.yaml b/braintrust/tests/brainstore-reader_test.yaml new file mode 100644 index 0000000..4beacd1 --- /dev/null +++ b/braintrust/tests/brainstore-reader_test.yaml @@ -0,0 +1,205 @@ +suite: test Brainstore Reader deployment template +templates: + - brainstore-reader-deployment.yaml +tests: + - it: should render Brainstore Reader deployment with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: Deployment + - equal: + path: metadata.name + value: brainstore-reader + - equal: + path: spec.replicas + value: 1 + - equal: + path: spec.template.spec.containers[0].name + value: brainstore-reader + - equal: + path: spec.template.spec.containers[0].image + value: test/brainstore:v1.0.0 + + - it: should include azure workload identity label when cloud is azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.metadata.labels["azure.workload.identity/use"] + value: "true" + + - it: should include gke workload identity label when cloud is google + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.metadata.labels["gke-workload-identity/use"] + value: "true" + - equal: + path: spec.template.metadata.annotations["iam.gke.io/gcp-service-account"] + value: test@project.iam.gserviceaccount.com + + - it: should use Azure Container Storage volume when enabled + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - isNotNull: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate + - equal: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate.spec.storageClassName + value: local + - equal: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate.spec.resources.requests.storage + value: "100Gi" + + - it: should use emptyDir volume when Azure Container Storage is disabled + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - isNotNull: + path: spec.template.spec.volumes[0].emptyDir + + - it: should include Azure Key Vault volume mount when enabled + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: secrets-store-inline + mountPath: "/mnt/secrets-store" + readOnly: true + + - it: should include nodeSelector for GKE Autopilot when enabled + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.spec.nodeSelector["cloud.google.com/machine-family"] + value: c4 + - equal: + path: spec.template.spec.nodeSelector["cloud.google.com/gke-ephemeral-storage-local-ssd"] + value: "true" + - equal: + path: spec.template.spec.nodeSelector["cloud.google.com/compute-class"] + value: "Performance" + + - it: should include BRAINSTORE_LOCKS_URI when locksBackend is redis + values: + - __fixtures__/base-values.yaml + set: + brainstore.locksBackend: "redis" + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: BRAINSTORE_LOCKS_URI + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: REDIS_URL + + - it: should include extraEnvVars when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.reader.extraEnvVars: + - name: CUSTOM_VAR + value: "custom-value" + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: CUSTOM_VAR + value: "custom-value" + + - it: should include livenessProbe when configured + values: + - __fixtures__/base-values.yaml + set: + brainstore.livenessProbe: + httpGet: + path: / + port: 4000 + initialDelaySeconds: 60 + periodSeconds: 10 + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.spec.containers[0].livenessProbe.httpGet.path + value: / + - equal: + path: spec.template.spec.containers[0].livenessProbe.httpGet.port + value: 4000 + - equal: + path: spec.template.spec.containers[0].livenessProbe.initialDelaySeconds + value: 60 + - equal: + path: spec.template.spec.containers[0].livenessProbe.periodSeconds + value: 10 + + - it: should include readinessProbe when configured + values: + - __fixtures__/base-values.yaml + set: + brainstore.readinessProbe: + httpGet: + path: /status + port: 4000 + initialDelaySeconds: 30 + periodSeconds: 5 + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.path + value: /status + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.port + value: 4000 + - equal: + path: spec.template.spec.containers[0].readinessProbe.initialDelaySeconds + value: 30 + - equal: + path: spec.template.spec.containers[0].readinessProbe.periodSeconds + value: 5 + + - it: should not include probes when explicitly disabled + values: + - __fixtures__/base-values.yaml + set: + brainstore.livenessProbe: null + brainstore.readinessProbe: null + release: + namespace: "braintrust" + asserts: + - isNull: + path: spec.template.spec.containers[0].livenessProbe + - isNull: + path: spec.template.spec.containers[0].readinessProbe diff --git a/braintrust/tests/brainstore-serviceaccount_test.yaml b/braintrust/tests/brainstore-serviceaccount_test.yaml new file mode 100644 index 0000000..af26bba --- /dev/null +++ b/braintrust/tests/brainstore-serviceaccount_test.yaml @@ -0,0 +1,116 @@ +suite: test Brainstore serviceaccount template +templates: + - brainstore-serviceaccount.yaml +tests: + - it: should render Brainstore serviceaccount with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: ServiceAccount + - equal: + path: metadata.name + value: brainstore + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should use global namespace when createNamespace is true + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: true + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: custom-ns + + - it: should include AWS IAM role annotation when cloud is aws + values: + - __fixtures__/base-values.yaml + - __fixtures__/aws-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["eks.amazonaws.com/role-arn"] + value: arn:aws:iam::123456789012:role/test-brainstore-role + + - it: should include Azure workload identity annotation when cloud is azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["azure.workload.identity/client-id"] + value: test-brainstore-client-id + + - it: should include GCP service account annotation when cloud is google + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["iam.gke.io/gcp-service-account"] + value: test@project.iam.gserviceaccount.com + + - it: should not include cloud-specific annotations when cloud is empty + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isNull: + path: metadata.annotations["eks.amazonaws.com/role-arn"] + - isNull: + path: metadata.annotations["azure.workload.identity/client-id"] + - isNull: + path: metadata.annotations["iam.gke.io/gcp-service-account"] + + - it: should merge global and brainstore labels + values: + - __fixtures__/base-values.yaml + set: + brainstore.labels.component: "brainstore" + global.labels.environment: "production" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.component + value: brainstore + + - it: should include custom serviceaccount annotations when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.serviceAccount.annotations: + custom-annotation: "value" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value diff --git a/braintrust/tests/brainstore-writer-configmap_test.yaml b/braintrust/tests/brainstore-writer-configmap_test.yaml new file mode 100644 index 0000000..0de94d4 --- /dev/null +++ b/braintrust/tests/brainstore-writer-configmap_test.yaml @@ -0,0 +1,184 @@ +suite: test Brainstore Writer configmap template +templates: + - brainstore-writer-configmap.yaml +tests: + - it: should render Brainstore Writer configmap with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: brainstore-writer + - equal: + path: data.BRAINSTORE_READER_ONLY_MODE + value: "false" + - equal: + path: data.BRAINSTORE_PORT + value: "4000" + - equal: + path: data.BRAINSTORE_CACHE_DIR + value: "/mnt/tmp/brainstore" + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should configure verbose mode + values: + - __fixtures__/base-values.yaml + set: + brainstore.writer.verbose: true + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_VERBOSE + value: "1" + + - it: should configure AWS URIs when cloud is aws + values: + - __fixtures__/base-values.yaml + - __fixtures__/aws-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_INDEX_URI + value: "s3://test-brainstore-bucket/brainstore/index" + - equal: + path: data.BRAINSTORE_REALTIME_WAL_URI + value: "s3://test-brainstore-bucket/brainstore/wal" + + - it: should configure Azure URIs when cloud is azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.AZURE_STORAGE_ACCOUNT_NAME + value: "teststorageaccount" + - equal: + path: data.BRAINSTORE_INDEX_URI + value: "az://brainstore/brainstore/index" + - equal: + path: data.BRAINSTORE_REALTIME_WAL_URI + value: "az://brainstore/brainstore/wal" + + - it: should configure Google Cloud URIs when cloud is google + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_INDEX_URI + value: "gs://test-brainstore-bucket/brainstore/index" + - equal: + path: data.BRAINSTORE_REALTIME_WAL_URI + value: "gs://test-brainstore-bucket/brainstore/wal" + + - it: should include locks URI when locksBackend is objectStorage for AWS + values: + - __fixtures__/base-values.yaml + - __fixtures__/aws-values.yaml + set: + brainstore.locksBackend: "objectStorage" + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_LOCKS_URI + value: "s3://test-brainstore-bucket/brainstore/locks" + + - it: should include locks URI when locksBackend is objectStorage for Azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + set: + brainstore.locksBackend: "objectStorage" + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_LOCKS_URI + value: "az://brainstore/brainstore/locks" + + - it: should include locks URI when locksBackend is objectStorage for Google + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + set: + brainstore.locksBackend: "objectStorage" + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_LOCKS_URI + value: "gs://test-brainstore-bucket/brainstore/locks" + + - it: should not include locks URI when locksBackend is redis + values: + - __fixtures__/base-values.yaml + - __fixtures__/aws-values.yaml + set: + brainstore.locksBackend: "redis" + release: + namespace: "braintrust" + asserts: + - isNull: + path: data.BRAINSTORE_LOCKS_URI + + - it: should include control plane telemetry + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: data.BRAINSTORE_CONTROL_PLANE_TELEMETRY + value: "status,metrics" + + - it: should merge global and writer labels + values: + - __fixtures__/base-values.yaml + set: + brainstore.writer.labels.component: "brainstore-writer" + global.labels.environment: "production" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.component + value: brainstore-writer + + - it: should include configmap annotations when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.writer.annotations.configmap: + custom-annotation: "value" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value diff --git a/braintrust/tests/brainstore-writer-service_test.yaml b/braintrust/tests/brainstore-writer-service_test.yaml new file mode 100644 index 0000000..66575e8 --- /dev/null +++ b/braintrust/tests/brainstore-writer-service_test.yaml @@ -0,0 +1,107 @@ +suite: test Brainstore Writer service template +templates: + - brainstore-writer-service.yaml +tests: + - it: should render Brainstore Writer service with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: Service + - equal: + path: metadata.name + value: brainstore-writer + - equal: + path: spec.type + value: ClusterIP + - equal: + path: spec.selector.app + value: brainstore-writer + + - it: should use custom service name when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.writer.service.name: "custom-writer-service" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.name + value: custom-writer-service + + - it: should configure port correctly + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.ports[0].name + value: http + - equal: + path: spec.ports[0].port + value: 4000 + - equal: + path: spec.ports[0].targetPort + value: 4000 + - equal: + path: spec.ports[0].protocol + value: TCP + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should use global namespace when createNamespace is true + values: + - __fixtures__/base-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: true + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: custom-ns + + - it: should merge global and writer labels + values: + - __fixtures__/base-values.yaml + set: + brainstore.writer.labels.component: "brainstore-writer" + global.labels.environment: "production" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.component + value: brainstore-writer + + - it: should include service annotations when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.writer.annotations.service: + custom-annotation: "value" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value diff --git a/braintrust/tests/brainstore-writer_test.yaml b/braintrust/tests/brainstore-writer_test.yaml new file mode 100644 index 0000000..d6af6d7 --- /dev/null +++ b/braintrust/tests/brainstore-writer_test.yaml @@ -0,0 +1,205 @@ +suite: test Brainstore Writer deployment template +templates: + - brainstore-writer-deployment.yaml +tests: + - it: should render Brainstore Writer deployment with default values + values: + - __fixtures__/base-values.yaml + release: + namespace: "braintrust" + asserts: + - isKind: + of: Deployment + - equal: + path: metadata.name + value: brainstore-writer + - equal: + path: spec.replicas + value: 1 + - equal: + path: spec.template.spec.containers[0].name + value: brainstore-writer + - equal: + path: spec.template.spec.containers[0].image + value: test/brainstore:v1.0.0 + + - it: should include azure workload identity label when cloud is azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.metadata.labels["azure.workload.identity/use"] + value: "true" + + - it: should include gke workload identity label when cloud is google + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.metadata.labels["gke-workload-identity/use"] + value: "true" + - equal: + path: spec.template.metadata.annotations["iam.gke.io/gcp-service-account"] + value: test@project.iam.gserviceaccount.com + + - it: should use Azure Container Storage volume when enabled + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - isNotNull: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate + - equal: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate.spec.storageClassName + value: local + - equal: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate.spec.resources.requests.storage + value: "200Gi" + + - it: should use emptyDir volume when Azure Container Storage is disabled + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - isNotNull: + path: spec.template.spec.volumes[0].emptyDir + + - it: should include Azure Key Vault volume mount when enabled + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: secrets-store-inline + mountPath: "/mnt/secrets-store" + readOnly: true + + - it: should include nodeSelector for GKE Autopilot when enabled + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.spec.nodeSelector["cloud.google.com/machine-family"] + value: c4 + - equal: + path: spec.template.spec.nodeSelector["cloud.google.com/gke-ephemeral-storage-local-ssd"] + value: "true" + - equal: + path: spec.template.spec.nodeSelector["cloud.google.com/compute-class"] + value: "Performance" + + - it: should include BRAINSTORE_LOCKS_URI when locksBackend is redis + values: + - __fixtures__/base-values.yaml + set: + brainstore.locksBackend: "redis" + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: BRAINSTORE_LOCKS_URI + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: REDIS_URL + + - it: should include extraEnvVars when provided + values: + - __fixtures__/base-values.yaml + set: + brainstore.writer.extraEnvVars: + - name: CUSTOM_VAR + value: "custom-value" + release: + namespace: "braintrust" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: CUSTOM_VAR + value: "custom-value" + + - it: should include livenessProbe when configured + values: + - __fixtures__/base-values.yaml + set: + brainstore.livenessProbe: + httpGet: + path: / + port: 4000 + initialDelaySeconds: 60 + periodSeconds: 10 + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.spec.containers[0].livenessProbe.httpGet.path + value: / + - equal: + path: spec.template.spec.containers[0].livenessProbe.httpGet.port + value: 4000 + - equal: + path: spec.template.spec.containers[0].livenessProbe.initialDelaySeconds + value: 60 + - equal: + path: spec.template.spec.containers[0].livenessProbe.periodSeconds + value: 10 + + - it: should include readinessProbe when configured + values: + - __fixtures__/base-values.yaml + set: + brainstore.readinessProbe: + httpGet: + path: /status + port: 4000 + initialDelaySeconds: 30 + periodSeconds: 5 + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.path + value: /status + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.port + value: 4000 + - equal: + path: spec.template.spec.containers[0].readinessProbe.initialDelaySeconds + value: 30 + - equal: + path: spec.template.spec.containers[0].readinessProbe.periodSeconds + value: 5 + + - it: should not include probes when explicitly disabled + values: + - __fixtures__/base-values.yaml + set: + brainstore.livenessProbe: null + brainstore.readinessProbe: null + release: + namespace: "braintrust" + asserts: + - isNull: + path: spec.template.spec.containers[0].livenessProbe + - isNull: + path: spec.template.spec.containers[0].readinessProbe diff --git a/braintrust/tests/namespace_test.yaml b/braintrust/tests/namespace_test.yaml new file mode 100644 index 0000000..9cbb7dd --- /dev/null +++ b/braintrust/tests/namespace_test.yaml @@ -0,0 +1,70 @@ +suite: test namespace template and helper +templates: + - namespace.yaml +tests: + - it: should not render namespace when createNamespace is false + set: + global.createNamespace: false + global.namespace: "braintrust" + asserts: + - hasDocuments: + count: 0 + + - it: should render namespace when createNamespace is true + set: + global.createNamespace: true + global.namespace: "my-namespace" + asserts: + - hasDocuments: + count: 1 + - isKind: + of: Namespace + - equal: + path: metadata.name + value: my-namespace + - equal: + path: metadata.labels.name + value: my-namespace + + - it: should include global labels in namespace + set: + global.createNamespace: true + global.namespace: "test-ns" + global.labels.environment: "production" + global.labels.team: "platform" + asserts: + - equal: + path: metadata.labels.environment + value: production + - equal: + path: metadata.labels.team + value: platform + + - it: should include namespace annotations + set: + global.createNamespace: true + global.namespace: "test-ns" + global.namespaceAnnotations.custom-annotation: "value" + asserts: + - equal: + path: metadata.annotations["custom-annotation"] + value: value + + - it: should include azure workload identity annotation when cloud is azure + set: + cloud: "azure" + global.createNamespace: true + global.namespace: "test-ns" + asserts: + - equal: + path: metadata.annotations["azure.workload.identity/use"] + value: "true" + + - it: should not include azure workload identity annotation when cloud is not azure + set: + cloud: "google" + global.createNamespace: true + global.namespace: "test-ns" + asserts: + - isNull: + path: metadata.annotations["azure.workload.identity/use"] diff --git a/braintrust/tests/secretproviderclass_test.yaml b/braintrust/tests/secretproviderclass_test.yaml new file mode 100644 index 0000000..1118d34 --- /dev/null +++ b/braintrust/tests/secretproviderclass_test.yaml @@ -0,0 +1,150 @@ +suite: test SecretProviderClass template +templates: + - secretproviderclass.yaml +tests: + - it: should not render when cloud is not azure + values: + - __fixtures__/base-values.yaml + - __fixtures__/google-values.yaml + asserts: + - hasDocuments: + count: 0 + + - it: should not render when cloud is aws + values: + - __fixtures__/base-values.yaml + - __fixtures__/aws-values.yaml + asserts: + - hasDocuments: + count: 0 + + - it: should not render when cloud is azure but enableAzureKeyVaultDriver is false + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + set: + azure.enableAzureKeyVaultDriver: false + asserts: + - hasDocuments: + count: 0 + + - it: should render SecretProviderClass when cloud is azure and enableAzureKeyVaultDriver is true + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - hasDocuments: + count: 1 + - isKind: + of: SecretProviderClass + - equal: + path: metadata.name + value: test-keyvault + + - it: should use correct namespace from helper when createNamespace is false + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: false + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: release-namespace + + - it: should use global namespace when createNamespace is true + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + set: + global.namespace: "custom-ns" + global.createNamespace: true + release: + namespace: "release-namespace" + asserts: + - equal: + path: metadata.namespace + value: custom-ns + + - it: should configure Azure provider correctly + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.provider + value: azure + - equal: + path: spec.parameters.usePodIdentity + value: "false" + - equal: + path: spec.parameters.useVMManagedIdentity + value: "false" + - equal: + path: spec.parameters.keyvaultName + value: test-keyvault + - equal: + path: spec.parameters.clientID + value: test-client-id + - equal: + path: spec.parameters.tenantId + value: test-tenant-id + + - it: should configure secret objects for kubernetes secret sync + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + release: + namespace: "braintrust" + asserts: + - equal: + path: spec.secretObjects[0].secretName + value: braintrust-secrets + - equal: + path: spec.secretObjects[0].type + value: Opaque + - contains: + path: spec.secretObjects[0].data + content: + key: REDIS_URL + objectName: redis-connection-string + - contains: + path: spec.secretObjects[0].data + content: + key: PG_URL + objectName: postgres-connection-string + - contains: + path: spec.secretObjects[0].data + content: + key: BRAINSTORE_LICENSE_KEY + objectName: brainstore-license-key + - contains: + path: spec.secretObjects[0].data + content: + key: FUNCTION_SECRET_KEY + objectName: function-secret-key + - contains: + path: spec.secretObjects[0].data + content: + key: AZURE_STORAGE_CONNECTION_STRING + objectName: azure-storage-connection-string + + - it: should include global labels + values: + - __fixtures__/base-values.yaml + - __fixtures__/azure-values.yaml + set: + global.labels.environment: "production" + release: + namespace: "braintrust" + asserts: + - equal: + path: metadata.labels.environment + value: production diff --git a/braintrust/tests/storageclass_test.yaml b/braintrust/tests/storageclass_test.yaml new file mode 100644 index 0000000..2b1fb7c --- /dev/null +++ b/braintrust/tests/storageclass_test.yaml @@ -0,0 +1,52 @@ +suite: test StorageClass template +templates: + - storageclass.yaml +tests: + - it: should not render when cloud is not azure + set: + cloud: "google" + azure.enableAzureContainerStorageDriver: true + asserts: + - hasDocuments: + count: 0 + + - it: should not render when cloud is azure but enableAzureContainerStorageDriver is false + set: + cloud: "azure" + azure.enableAzureContainerStorageDriver: false + asserts: + - hasDocuments: + count: 0 + + - it: should render StorageClass when cloud is azure and enableAzureContainerStorageDriver is true + set: + cloud: "azure" + azure.enableAzureContainerStorageDriver: true + asserts: + - hasDocuments: + count: 1 + - isKind: + of: StorageClass + - equal: + path: metadata.name + value: local + - equal: + path: provisioner + value: localdisk.csi.acstor.io + - equal: + path: reclaimPolicy + value: Delete + - equal: + path: volumeBindingMode + value: WaitForFirstConsumer + - equal: + path: allowVolumeExpansion + value: true + + - it: should not render when cloud is aws + set: + cloud: "aws" + azure.enableAzureContainerStorageDriver: true + asserts: + - hasDocuments: + count: 0 diff --git a/braintrust/values.yaml b/braintrust/values.yaml index ee1875c..c253e09 100644 --- a/braintrust/values.yaml +++ b/braintrust/values.yaml @@ -105,7 +105,7 @@ api: # Brainstore backfill configuration. These defaults are fine for most cases. backfillDisableHistorical: false backfillDisableNonhistorical: false - allowInvalidBase64: false # By default, we will error on invalid base64 strings. Setting this to true will allow invalid base64 strings to be processed. + allowInvalidBase64: false # By default, we will error on invalid base64 strings. Setting this to true will allow invalid base64 strings to be processed. nodeMemoryPercent: "80" extraEnvVars: # Example: diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..fbce312 --- /dev/null +++ b/test.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +# Test script for Braintrust Helm chart +# Runs both unit tests and integration tests + +set -e + +# Get the directory where the script is located +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + +# Change to the script's directory +cd "$SCRIPT_DIR" || exit 1 + +CHART_DIR="braintrust" + +# Check if helm is installed +if ! command -v helm &> /dev/null; then + echo "❌ Helm is not installed. Please install Helm first." + exit 1 +fi + +# Check if helm-unittest is installed using helm plugin list +# Suppress stderr to avoid plugin loading errors from other plugins +HELM_UNITTEST_INSTALLED=false +if helm plugin list 2>/dev/null | grep -q "unittest"; then + HELM_UNITTEST_INSTALLED=true +fi + +# If not found, try to install it +if [ "$HELM_UNITTEST_INSTALLED" = false ]; then + echo "⚠️ helm-unittest plugin not found. Installing..." + # Suppress plugin loading errors by redirecting stderr + helm plugin install https://github.com/helm-unittest/helm-unittest.git --verify=false 2>/dev/null || { + echo "❌ Failed to install helm-unittest plugin" + exit 1 + } + HELM_UNITTEST_INSTALLED=true +fi + +echo "" +echo "==========================================" +echo "1. Running Unit Tests (helm-unittest)" +echo "==========================================" + +# Run helm unittest, suppressing plugin loading errors from stderr +if helm unittest "$CHART_DIR" 2>/dev/null; then + echo "" + echo "✅ Unit tests passed" +else + echo "" + echo "❌ Unit tests failed" + exit 1 +fi + +echo "" +echo "==========================================" +echo "2. Testing Chart Rendering" +echo "==========================================" +echo "" + +# Test rendering with different values files +VALUES_FILES=( + "$CHART_DIR/ci/values-azure.yaml" + "$CHART_DIR/ci/values-google.yaml" + "$CHART_DIR/ci/values-aws.yaml" + "$CHART_DIR/ci/values-minimal.yaml" +) + +RENDER_FAILED=false + +for values_file in "${VALUES_FILES[@]}"; do + if [ -f "$values_file" ]; then + echo "Testing with $(basename "$values_file")..." + if helm template test-release "$CHART_DIR" --values "$values_file" > /dev/null 2>&1; then + echo " ✅ Rendered successfully" + else + echo " ❌ Failed to render" + RENDER_FAILED=true + fi + else + echo " ⚠️ Values file not found: $values_file" + fi +done + +if [ "$RENDER_FAILED" = true ]; then + echo "" + echo "❌ Chart rendering tests failed" + exit 1 +fi + +echo "" +echo "✅ Chart rendering tests passed" + +echo "" +echo "==========================================" +echo "3. Running Helm Lint" +echo "==========================================" +echo "" + +if helm lint "$CHART_DIR" --strict; then + echo "" + echo "✅ Chart linting passed" +else + echo "" + echo "❌ Chart linting failed" + exit 1 +fi + +echo "" +echo "==========================================" +echo "✅ All tests passed!" +echo "=========================================="