Skip to content

Commit 2fa86bc

Browse files
feat: add Helm chart unit tests and CI validation (#8) (#19)
* feat: add Helm chart unit tests and CI validation. Add 8 helm-unittest tests covering deployment rendering, securityContext enforcement, disabled sampleDeployment producing no resources, multiple initContainers, extraVolumes/extraVolumeMounts, image configuration, workdir mount, and labels. Add helm-unittest plugin install and run step to CI helm-lint job. Closes #8 * fix: add --verify=false to helm plugin install for CI compatibility and added --verion
1 parent 5c544f1 commit 2fa86bc

3 files changed

Lines changed: 269 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,7 @@ jobs:
4141
steps:
4242
- uses: actions/checkout@v4
4343
- uses: azure/setup-helm@v4
44+
- run: helm plugin install --verify=false https://github.com/helm-unittest/helm-unittest.git --version v0.5.1
4445
- run: helm lint charts/initium
4546
- run: helm template test-release charts/initium --set sampleDeployment.enabled=true --set 'initContainers[0].name=wait' --set 'initContainers[0].command[0]=wait-for' --set 'initContainers[0].args[0]=--target' --set 'initContainers[0].args[1]=tcp://localhost:5432'
47+
- run: helm unittest charts/initium

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Added
11+
- Helm chart unit tests using helm-unittest plugin (`charts/initium/tests/deployment_test.yaml`) covering deployment rendering, securityContext enforcement, disabled sampleDeployment, multiple initContainers, extraVolumes/extraVolumeMounts, image configuration, workdir mount, and labels
12+
- `helm unittest` step added to CI helm-lint job with automatic plugin installation
1113
- Duration unit support for all time parameters (`--timeout`, `--initial-delay`, `--max-delay`, seed phase `timeout`, seed wait-for `timeout`): accepts `ms`, `s`, `m`, `h` suffixes with decimal values (e.g. `1.5m`, `2.7s`) and combined units (e.g. `1m30s`, `2s700ms`, `18h36m4s200ms`); bare numbers default to seconds
1214
- `src/duration.rs` module with `parse_duration` and `format_duration` utilities
1315
- Environment variable support for all CLI flags via `INITIUM_*` prefix (e.g., `--json``INITIUM_JSON`, `--timeout``INITIUM_TIMEOUT`); flag values take precedence over env vars
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
suite: deployment template tests
2+
templates:
3+
- templates/deployment.yaml
4+
tests:
5+
- it: should not render any resources when sampleDeployment is disabled
6+
set:
7+
sampleDeployment:
8+
enabled: false
9+
asserts:
10+
- hasDocuments:
11+
count: 0
12+
13+
- it: should render a Deployment when sampleDeployment is enabled
14+
set:
15+
sampleDeployment:
16+
enabled: true
17+
name: test-app
18+
replicas: 2
19+
mainContainer:
20+
image: nginx:1.27-alpine
21+
port: 80
22+
initContainers:
23+
- name: wait-for-db
24+
command:
25+
- wait-for
26+
args:
27+
- --target
28+
- tcp://postgres:5432
29+
asserts:
30+
- isKind:
31+
of: Deployment
32+
- equal:
33+
path: metadata.name
34+
value: test-app
35+
- equal:
36+
path: spec.replicas
37+
value: 2
38+
- equal:
39+
path: spec.template.spec.initContainers[0].name
40+
value: wait-for-db
41+
- equal:
42+
path: spec.template.spec.initContainers[0].command
43+
value:
44+
- wait-for
45+
- equal:
46+
path: spec.template.spec.initContainers[0].args
47+
value:
48+
- --target
49+
- tcp://postgres:5432
50+
- equal:
51+
path: spec.template.spec.containers[0].image
52+
value: nginx:1.27-alpine
53+
54+
- it: should always apply securityContext to initContainers
55+
set:
56+
sampleDeployment:
57+
enabled: true
58+
securityContext:
59+
runAsNonRoot: true
60+
runAsUser: 65534
61+
runAsGroup: 65534
62+
readOnlyRootFilesystem: true
63+
allowPrivilegeEscalation: false
64+
capabilities:
65+
drop:
66+
- ALL
67+
initContainers:
68+
- name: init-one
69+
command:
70+
- wait-for
71+
args:
72+
- --target
73+
- tcp://db:5432
74+
asserts:
75+
- equal:
76+
path: spec.template.spec.initContainers[0].securityContext.runAsNonRoot
77+
value: true
78+
- equal:
79+
path: spec.template.spec.initContainers[0].securityContext.runAsUser
80+
value: 65534
81+
- equal:
82+
path: spec.template.spec.initContainers[0].securityContext.readOnlyRootFilesystem
83+
value: true
84+
- equal:
85+
path: spec.template.spec.initContainers[0].securityContext.allowPrivilegeEscalation
86+
value: false
87+
- equal:
88+
path: spec.template.spec.initContainers[0].securityContext.capabilities.drop
89+
value:
90+
- ALL
91+
92+
- it: should render multiple initContainers correctly
93+
set:
94+
sampleDeployment:
95+
enabled: true
96+
initContainers:
97+
- name: wait-for-db
98+
command:
99+
- wait-for
100+
args:
101+
- --target
102+
- tcp://postgres:5432
103+
- name: run-migration
104+
command:
105+
- migrate
106+
args:
107+
- --
108+
- flyway
109+
- migrate
110+
env:
111+
- name: FLYWAY_URL
112+
value: "jdbc:postgresql://postgres:5432/mydb"
113+
- name: seed-data
114+
command:
115+
- seed
116+
args:
117+
- --spec
118+
- /seeds/seed.yaml
119+
asserts:
120+
- equal:
121+
path: spec.template.spec.initContainers[0].name
122+
value: wait-for-db
123+
- equal:
124+
path: spec.template.spec.initContainers[1].name
125+
value: run-migration
126+
- equal:
127+
path: spec.template.spec.initContainers[1].env[0].name
128+
value: FLYWAY_URL
129+
- equal:
130+
path: spec.template.spec.initContainers[2].name
131+
value: seed-data
132+
- equal:
133+
path: spec.template.spec.initContainers[2].args
134+
value:
135+
- --spec
136+
- /seeds/seed.yaml
137+
138+
- it: should include extraVolumes and extraVolumeMounts
139+
set:
140+
sampleDeployment:
141+
enabled: true
142+
initContainers:
143+
- name: render-config
144+
command:
145+
- render
146+
args:
147+
- --template
148+
- /templates/app.conf.tmpl
149+
- --output
150+
- app.conf
151+
extraVolumes:
152+
- name: templates
153+
configMap:
154+
name: app-templates
155+
- name: secrets
156+
secret:
157+
secretName: app-secrets
158+
extraVolumeMounts:
159+
- name: templates
160+
mountPath: /templates
161+
readOnly: true
162+
- name: secrets
163+
mountPath: /secrets
164+
readOnly: true
165+
asserts:
166+
- contains:
167+
path: spec.template.spec.volumes
168+
content:
169+
name: templates
170+
configMap:
171+
name: app-templates
172+
- contains:
173+
path: spec.template.spec.volumes
174+
content:
175+
name: secrets
176+
secret:
177+
secretName: app-secrets
178+
- contains:
179+
path: spec.template.spec.initContainers[0].volumeMounts
180+
content:
181+
name: templates
182+
mountPath: /templates
183+
readOnly: true
184+
- contains:
185+
path: spec.template.spec.initContainers[0].volumeMounts
186+
content:
187+
name: secrets
188+
mountPath: /secrets
189+
readOnly: true
190+
191+
- it: should use the correct image repository and tag
192+
set:
193+
sampleDeployment:
194+
enabled: true
195+
image:
196+
repository: ghcr.io/kitstream/initium
197+
tag: "1.2.3"
198+
pullPolicy: Always
199+
initContainers:
200+
- name: wait
201+
command:
202+
- wait-for
203+
args:
204+
- --target
205+
- tcp://db:5432
206+
asserts:
207+
- equal:
208+
path: spec.template.spec.initContainers[0].image
209+
value: "ghcr.io/kitstream/initium:1.2.3"
210+
- equal:
211+
path: spec.template.spec.initContainers[0].imagePullPolicy
212+
value: Always
213+
214+
- it: should include workdir volume mount on all initContainers
215+
set:
216+
sampleDeployment:
217+
enabled: true
218+
workdir: /custom-work
219+
initContainers:
220+
- name: init-one
221+
command:
222+
- wait-for
223+
args:
224+
- --target
225+
- tcp://db:5432
226+
- name: init-two
227+
command:
228+
- exec
229+
args:
230+
- --
231+
- echo
232+
- hello
233+
asserts:
234+
- contains:
235+
path: spec.template.spec.initContainers[0].volumeMounts
236+
content:
237+
name: workdir
238+
mountPath: /custom-work
239+
- contains:
240+
path: spec.template.spec.initContainers[1].volumeMounts
241+
content:
242+
name: workdir
243+
mountPath: /custom-work
244+
245+
- it: should include standard labels on the deployment
246+
set:
247+
sampleDeployment:
248+
enabled: true
249+
name: labeled-app
250+
initContainers:
251+
- name: wait
252+
command:
253+
- wait-for
254+
args:
255+
- --target
256+
- tcp://db:5432
257+
asserts:
258+
- equal:
259+
path: metadata.labels["app.kubernetes.io/name"]
260+
value: labeled-app
261+
- equal:
262+
path: metadata.labels["app.kubernetes.io/managed-by"]
263+
value: Helm
264+
- isNotEmpty:
265+
path: metadata.labels["helm.sh/chart"]

0 commit comments

Comments
 (0)