From e95adcc75795c783de36e2b6c5bd9b5d18ca13db Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Sun, 22 Mar 2026 21:45:31 +0100 Subject: [PATCH] fix(helm): add unit tests, extraEnv schema validation, and minor fixes (#18836) ## Summary Follow-up to #18157. Cherry-picks the useful parts from #18481 (by @dnplkndll), adapted to align with the current state of `main`: - **Helm unit tests** for Redis external authentication (secret-based + plaintext password, both server and worker) - **Helm unit tests** for `extraEnv` injection (plain values and `valueFrom` on both server and worker) - **JSON schema validation** for `server.extraEnv` and `worker.extraEnv` in `values.schema.json` - **Fix** `server_url_test.yaml` to use JSONPath filters (`@.name=="SERVER_URL"`) instead of brittle `env[0]` index selectors - **Fix** worker `storageEnv` whitespace (missing `-` in `{{- $storageEnv | nindent 12 }}`) Stale tests from #18481 (for `disableDbMigrations`, `server.env` pass-through, and `run-migrations` init container) were dropped since those features were removed during the #18157 cleanup. ## Test plan - [ ] `helm lint` passes - [ ] `helm template` renders cleanly - [ ] Unit tests pass with `helm unittest` (requires the plugin) Made with [Cursor](https://cursor.com) --- .../twenty/templates/deployment-worker.yaml | 2 +- .../helm/twenty/tests/env_mapping_test.yaml | 58 ++++++++++++ .../helm/twenty/tests/redis_auth_test.yaml | 89 +++++++++++++++++++ .../twenty/tests/schema_permissions_test.yaml | 15 ---- .../helm/twenty/tests/server_url_test.yaml | 11 +-- .../helm/twenty/values.schema.json | 32 +++++++ 6 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 packages/twenty-docker/helm/twenty/tests/env_mapping_test.yaml create mode 100644 packages/twenty-docker/helm/twenty/tests/redis_auth_test.yaml diff --git a/packages/twenty-docker/helm/twenty/templates/deployment-worker.yaml b/packages/twenty-docker/helm/twenty/templates/deployment-worker.yaml index d2e506d053..50fe02e7c9 100644 --- a/packages/twenty-docker/helm/twenty/templates/deployment-worker.yaml +++ b/packages/twenty-docker/helm/twenty/templates/deployment-worker.yaml @@ -91,7 +91,7 @@ spec: {{- end }} {{- $storageEnv := (include "twenty.storageEnv" .) }} {{- if $storageEnv }} - {{ $storageEnv | nindent 12 }} + {{- $storageEnv | nindent 12 }} {{- end }} resources: {{- toYaml .Values.worker.resources | nindent 12 }} diff --git a/packages/twenty-docker/helm/twenty/tests/env_mapping_test.yaml b/packages/twenty-docker/helm/twenty/tests/env_mapping_test.yaml new file mode 100644 index 0000000000..9c3fe1c33a --- /dev/null +++ b/packages/twenty-docker/helm/twenty/tests/env_mapping_test.yaml @@ -0,0 +1,58 @@ +suite: env mapping +templates: + - templates/deployment-server.yaml + - templates/deployment-worker.yaml +release: + name: my-twenty + namespace: default +tests: + - it: renders server extraEnv with plain value + template: templates/deployment-server.yaml + set: + server.extraEnv: + - name: FEATURE_X_ENABLED + value: "true" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: FEATURE_X_ENABLED + value: "true" + + - it: renders server extraEnv with valueFrom + template: templates/deployment-server.yaml + set: + server.extraEnv: + - name: SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: smtp-creds + key: password + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: smtp-creds + key: password + + - it: renders worker extraEnv with valueFrom + template: templates/deployment-worker.yaml + set: + worker.extraEnv: + - name: CUSTOM_SECRET + valueFrom: + secretKeyRef: + name: my-secret + key: custom + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: CUSTOM_SECRET + valueFrom: + secretKeyRef: + name: my-secret + key: custom diff --git a/packages/twenty-docker/helm/twenty/tests/redis_auth_test.yaml b/packages/twenty-docker/helm/twenty/tests/redis_auth_test.yaml new file mode 100644 index 0000000000..610c83b404 --- /dev/null +++ b/packages/twenty-docker/helm/twenty/tests/redis_auth_test.yaml @@ -0,0 +1,89 @@ +suite: redis external authentication +templates: + - templates/deployment-server.yaml + - templates/deployment-worker.yaml +release: + name: my-twenty + namespace: default +tests: + - it: injects REDIS_PASSWORD from external secret into server + template: templates/deployment-server.yaml + set: + redisInternal.enabled: false + redis.external.host: redis.example.com + redis.external.secretName: redis-creds + redis.external.passwordKey: password + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: redis-creds + key: password + + - it: injects REDIS_PASSWORD from external secret into worker + template: templates/deployment-worker.yaml + set: + redisInternal.enabled: false + redis.external.host: redis.example.com + redis.external.secretName: redis-creds + redis.external.passwordKey: password + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: redis-creds + key: password + + - it: injects plaintext REDIS_PASSWORD when password set directly in server + template: templates/deployment-server.yaml + set: + redisInternal.enabled: false + redis.external.host: redis.example.com + redis.external.password: "s3cr3t" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PASSWORD + value: "s3cr3t" + + - it: injects plaintext REDIS_PASSWORD when password set directly in worker + template: templates/deployment-worker.yaml + set: + redisInternal.enabled: false + redis.external.host: redis.example.com + redis.external.password: "s3cr3t" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PASSWORD + value: "s3cr3t" + + - it: does not inject REDIS_PASSWORD into server when using internal redis + template: templates/deployment-server.yaml + set: + redisInternal.enabled: true + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PASSWORD + any: true + + - it: does not inject REDIS_PASSWORD into worker when using internal redis + template: templates/deployment-worker.yaml + set: + redisInternal.enabled: true + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PASSWORD + any: true diff --git a/packages/twenty-docker/helm/twenty/tests/schema_permissions_test.yaml b/packages/twenty-docker/helm/twenty/tests/schema_permissions_test.yaml index 435dae8fcf..f553fa89ee 100644 --- a/packages/twenty-docker/helm/twenty/tests/schema_permissions_test.yaml +++ b/packages/twenty-docker/helm/twenty/tests/schema_permissions_test.yaml @@ -105,18 +105,3 @@ tests: path: spec.template.spec.initContainers[?(@.name=="ensure-database-exists")].command[2] pattern: ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES - # TypeORM Migration Tests - # ====================== - # TypeORM migrations are configured to use the core.datasource which targets the - # 'core' schema. This ensures the _typeorm_migrations table and all application - # tables use the dedicated core schema. - - - it: migrations run against core datasource - template: templates/deployment-server.yaml - set: - db.enabled: true - asserts: - - matchRegex: - path: spec.template.spec.initContainers[?(@.name=="run-migrations")].command[2] - pattern: core\.datasource - diff --git a/packages/twenty-docker/helm/twenty/tests/server_url_test.yaml b/packages/twenty-docker/helm/twenty/tests/server_url_test.yaml index e4a3109799..c7ce0d43ca 100644 --- a/packages/twenty-docker/helm/twenty/tests/server_url_test.yaml +++ b/packages/twenty-docker/helm/twenty/tests/server_url_test.yaml @@ -19,7 +19,7 @@ tests: - crm.example.com asserts: - equal: - path: spec.template.spec.containers[0].env[0].value + path: spec.template.spec.containers[0].env[?(@.name=="SERVER_URL")].value value: "https://crm.example.com:443" - it: falls back to service when ingress disabled set: @@ -28,7 +28,7 @@ tests: server.env.SERVER_URL: "" asserts: - matchRegex: - path: spec.template.spec.containers[0].env[0].value + path: spec.template.spec.containers[0].env[?(@.name=="SERVER_URL")].value pattern: ^http://my-twenty-twenty-server\.default\.svc\.cluster\.local:3000$ --- suite: ingress configuration @@ -66,6 +66,7 @@ tests: set: server.ingress.acme: true asserts: - - equal: - path: metadata.annotations[cert-manager.io/cluster-issuer] - value: letsencrypt-prod + - isSubset: + path: metadata.annotations + content: + cert-manager.io/cluster-issuer: letsencrypt-prod diff --git a/packages/twenty-docker/helm/twenty/values.schema.json b/packages/twenty-docker/helm/twenty/values.schema.json index ef75efc6d6..48db9817d1 100644 --- a/packages/twenty-docker/helm/twenty/values.schema.json +++ b/packages/twenty-docker/helm/twenty/values.schema.json @@ -66,6 +66,22 @@ "PASSWORD_RESET_TOKEN_EXPIRES_IN": { "type": "string" } } }, + "extraEnv": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "value": { "type": "string" }, + "valueFrom": { "type": "object" } + }, + "required": ["name"], + "oneOf": [ + { "required": ["value"] }, + { "required": ["valueFrom"] } + ] + } + }, "service": { "type": "object", "properties": { @@ -141,6 +157,22 @@ "STORAGE_TYPE": { "type": "string" }, "DISABLE_DB_MIGRATIONS": { "type": "string" } } + }, + "extraEnv": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "value": { "type": "string" }, + "valueFrom": { "type": "object" } + }, + "required": ["name"], + "oneOf": [ + { "required": ["value"] }, + { "required": ["valueFrom"] } + ] + } } } },