diff --git a/DOCUMENT.md b/DOCUMENT.md
index 328c4eb82..75f700779 100644
--- a/DOCUMENT.md
+++ b/DOCUMENT.md
@@ -114,7 +114,7 @@ Available assertion types are listed below:
| `notEqual` | **path**: *string*. The `set` path to assert.
**value**: *any*. The value expected not to be. | Assert the value of specified **path** NOT equal to the **value**. |
notEqual:
path: metadata.name
value: my-deploy
|
| `matchRegex` | **path**: *string*. The `set` path to assert, the value must be a *string*.
**pattern**: *string*. The regex pattern to match (without quoting `/`). | Assert the value of specified **path** match **pattern**. | matchRegex:
path: metadata.name
pattern: -my-chart$
|
| `notMatchRegex` | **path**: *string*. The `set` path to assert, the value must be a *string*.
**pattern**: *string*. The regex pattern NOT to match (without quoting `/`). | Assert the value of specified **path** NOT match **pattern**. | notMatchRegex:
path: metadata.name
pattern: -my-chat$
|
-| `contains` | **path**: *string*. The `set` path to assert, the value must be an *array*.
**content**: *any*. The content to be contained. | Assert the array as the value of specified **path** contains the **content**. |contains:
path: spec.ports
content:
name: web
port: 80
targetPort: 80
protocle:TCP
|
+| `contains` | **path**: *string*. The `set` path to assert, the value must be an *array*.
**content**: *any*. The content to be contained.
**count**: *int, optional*. The count of content to be contained.
**any**: *bool, optional*. ignores any other values within the found content. | Assert the array as the value of specified **path** contains the **content**. |contains:
path: spec.ports
content:
name: web
port: 80
targetPort: 80
protocle:TCP
|
| `notContains` | **path**: *string*. The `set` path to assert, the value must be an *array*.
**content**: *any*. The content NOT to be contained. | Assert the array as the value of specified **path** NOT contains the **content**. |notContains:
path: spec.ports
content:
name: server
port: 80
targetPort: 80
protocle: TCP
|
| `isNull` | **path**: *string*. The `set` path to assert. | Assert the value of specified **path** is `null`. |isNull:
path: spec.strategy
|
| `isNotNull` | **path**: *string*. The `set` path to assert. | Assert the value of specified **path** is NOT `null`. |isNotNull:
path: spec.replicas
|
diff --git a/Makefile b/Makefile
index a420c7595..b1504e7eb 100644
--- a/Makefile
+++ b/Makefile
@@ -16,8 +16,12 @@ install: bootstrap build
.PHONY: hookInstall
hookInstall: bootstrap build
+.PHONY: unittest
+unittest:
+ go test ./unittest/... -v -cover
+
.PHONY: build
-build:
+build: unittest
go build -o untt -ldflags $(LDFLAGS) ./main.go
.PHONY: dist
diff --git a/__fixtures__/v2/basic/tests/__snapshot__/notes_test.yaml.snap b/__fixtures__/v2/basic/tests/__snapshot__/notes_test.yaml.snap
new file mode 100644
index 000000000..2ca380109
--- /dev/null
+++ b/__fixtures__/v2/basic/tests/__snapshot__/notes_test.yaml.snap
@@ -0,0 +1,20 @@
+should pass the notes file with ingress enabled:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+should pass the notes file with service type LoadBalancer:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w RELEASE-NAME-basic'
+ export SERVICE_IP=$(kubectl get svc --namespace NAMESPACE RELEASE-NAME-basic -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:9999
+should pass the notes file with service type NodePort:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-basic)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
diff --git a/__fixtures__/v2/basic/tests/notes_test.yaml b/__fixtures__/v2/basic/tests/notes_test.yaml
new file mode 100644
index 000000000..3e950e9d6
--- /dev/null
+++ b/__fixtures__/v2/basic/tests/notes_test.yaml
@@ -0,0 +1,35 @@
+suite: test notes
+templates:
+ - NOTES.txt
+tests:
+ - it: should pass the notes file with ingress enabled
+ set:
+ ingress.enabled: true
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type NodePort
+ set:
+ service.type: NodePort
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-basic)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type LoadBalancer
+ set:
+ service.type: LoadBalancer
+ service.externalPort: 9999
+ asserts:
+ - matchRegexRaw:
+ pattern: http://\$SERVICE_IP:9999
+ - matchSnapshotRaw: {}
+
\ No newline at end of file
diff --git a/__fixtures__/v2/basic/tests_failed/notes_test.yaml b/__fixtures__/v2/basic/tests_failed/notes_test.yaml
new file mode 100644
index 000000000..2c6cbccf5
--- /dev/null
+++ b/__fixtures__/v2/basic/tests_failed/notes_test.yaml
@@ -0,0 +1,31 @@
+suite: test notes
+templates:
+ - NOTES.txt
+tests:
+ - it: should pass the notes file with ingress enabled
+ set:
+ ingress.enabled: true
+ asserts:
+ - notEqualRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+
+ - it: should pass the notes file with service type NodePort
+ set:
+ service.type: NodePort
+ asserts:
+ - notEqualRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services MY-RELEASE)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+
+ - it: should pass the notes file with service type LoadBalancer
+ set:
+ service.type: LoadBalancer
+ service.externalPort: 9999
+ asserts:
+ - matchRegexRaw:
+ pattern: http://\$SERVICE_IP:80
diff --git a/__fixtures__/v2/with-subchart/charts/child-chart/tests/__snapshot__/notes_test.yaml.snap b/__fixtures__/v2/with-subchart/charts/child-chart/tests/__snapshot__/notes_test.yaml.snap
new file mode 100644
index 000000000..95055e673
--- /dev/null
+++ b/__fixtures__/v2/with-subchart/charts/child-chart/tests/__snapshot__/notes_test.yaml.snap
@@ -0,0 +1,20 @@
+should pass the notes file with ingress enabled:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+should pass the notes file with service type LoadBalancer:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w RELEASE-NAME-child-chart'
+ export SERVICE_IP=$(kubectl get svc --namespace NAMESPACE RELEASE-NAME-child-chart -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:9999
+should pass the notes file with service type NodePort:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-child-chart)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
diff --git a/__fixtures__/v2/with-subchart/charts/child-chart/tests/notes_test.yaml b/__fixtures__/v2/with-subchart/charts/child-chart/tests/notes_test.yaml
new file mode 100644
index 000000000..dafb3ca0d
--- /dev/null
+++ b/__fixtures__/v2/with-subchart/charts/child-chart/tests/notes_test.yaml
@@ -0,0 +1,35 @@
+suite: test notes
+templates:
+ - NOTES.txt
+tests:
+ - it: should pass the notes file with ingress enabled
+ set:
+ ingress.enabled: true
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type NodePort
+ set:
+ service.type: NodePort
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-child-chart)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type LoadBalancer
+ set:
+ service.type: LoadBalancer
+ service.externalPort: 9999
+ asserts:
+ - matchRegexRaw:
+ pattern: http://\$SERVICE_IP:9999
+ - matchSnapshotRaw: {}
+
\ No newline at end of file
diff --git a/__fixtures__/v2/with-subchart/templates/NOTES.txt b/__fixtures__/v2/with-subchart/templates/NOTES.txt
index 30e62529b..f0cd1009c 100644
--- a/__fixtures__/v2/with-subchart/templates/NOTES.txt
+++ b/__fixtures__/v2/with-subchart/templates/NOTES.txt
@@ -4,16 +4,16 @@
http://{{ . }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
- export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "parant-chart.fullname" . }})
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "parent-chart.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
- You can watch the status of by running 'kubectl get svc -w {{ template "parant-chart.fullname" . }}'
- export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "parant-chart.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ You can watch the status of by running 'kubectl get svc -w {{ template "parent-chart.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "parent-chart.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.externalPort }}
{{- else if contains "ClusterIP" .Values.service.type }}
- export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "parant-chart.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "parent-chart.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:{{ .Values.service.internalPort }}
{{- end }}
diff --git a/__fixtures__/v2/with-subchart/tests/__snapshot__/notes_test.yaml.snap b/__fixtures__/v2/with-subchart/tests/__snapshot__/notes_test.yaml.snap
new file mode 100644
index 000000000..1a2110db0
--- /dev/null
+++ b/__fixtures__/v2/with-subchart/tests/__snapshot__/notes_test.yaml.snap
@@ -0,0 +1,20 @@
+should pass the notes file with ingress enabled:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+should pass the notes file with service type LoadBalancer:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w RELEASE-NAME-parent-chart'
+ export SERVICE_IP=$(kubectl get svc --namespace NAMESPACE RELEASE-NAME-parent-chart -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:9999
+should pass the notes file with service type NodePort:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-parent-chart)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
diff --git a/__fixtures__/v2/with-subchart/tests/notes_test.yaml b/__fixtures__/v2/with-subchart/tests/notes_test.yaml
new file mode 100644
index 000000000..8a34e865c
--- /dev/null
+++ b/__fixtures__/v2/with-subchart/tests/notes_test.yaml
@@ -0,0 +1,35 @@
+suite: test notes
+templates:
+ - NOTES.txt
+tests:
+ - it: should pass the notes file with ingress enabled
+ set:
+ ingress.enabled: true
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type NodePort
+ set:
+ service.type: NodePort
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-parent-chart)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type LoadBalancer
+ set:
+ service.type: LoadBalancer
+ service.externalPort: 9999
+ asserts:
+ - matchRegexRaw:
+ pattern: http://\$SERVICE_IP:9999
+ - matchSnapshotRaw: {}
+
\ No newline at end of file
diff --git a/__fixtures__/v2/with-subfolder/.helmignore b/__fixtures__/v2/with-subfolder/.helmignore
new file mode 100644
index 000000000..dadf20295
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+
+tests
diff --git a/__fixtures__/v2/with-subfolder/Chart.yaml b/__fixtures__/v2/with-subfolder/Chart.yaml
new file mode 100644
index 000000000..0d1f15e99
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/Chart.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+description: A advanced example chart to demonstrate unittest plugin
+name: with-subfolder
+version: 0.1.0
diff --git a/__fixtures__/v2/with-subfolder/templates/NOTES.txt b/__fixtures__/v2/with-subfolder/templates/NOTES.txt
new file mode 100644
index 000000000..f3c81c022
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/templates/NOTES.txt
@@ -0,0 +1,19 @@
+1. Get the application URL by running these commands:
+{{- if .Values.ingress.enabled }}
+{{- range .Values.ingress.hosts }}
+ http://{{ . }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "with-subfolder.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w {{ template "with-subfolder.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "with-subfolder.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:{{ .Values.service.externalPort }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "with-subfolder.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl port-forward $POD_NAME 8080:{{ .Values.service.internalPort }}
+{{- end }}
diff --git a/__fixtures__/v2/with-subfolder/templates/_helpers.tpl b/__fixtures__/v2/with-subfolder/templates/_helpers.tpl
new file mode 100644
index 000000000..21c49b484
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/templates/_helpers.tpl
@@ -0,0 +1,16 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "with-subfolder.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "with-subfolder.fullname" -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
diff --git a/__fixtures__/v2/with-subfolder/templates/db/deployment.yaml b/__fixtures__/v2/with-subfolder/templates/db/deployment.yaml
new file mode 100644
index 000000000..054e55003
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/templates/db/deployment.yaml
@@ -0,0 +1,42 @@
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: {{ template "with-subfolder.fullname" . }}-db
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ release: {{ .Release.Name }}
+ annotations:
+ some_template: |
+ ---
+ apiVersion: ...
+ this: is test for old separator workaround bug
+ spec:
+ containers:
+ - name: {{ .Chart.Name }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - containerPort: {{ .Values.service.dbPort }}
+ livenessProbe:
+ httpGet:
+ path: /
+ port: {{ .Values.service.internalPort }}
+ readinessProbe:
+ httpGet:
+ path: /
+ port: {{ .Values.service.internalPort }}
+ resources:
+{{ toYaml .Values.resources | indent 12 }}
+ {{- if .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.nodeSelector | indent 8 }}
+ {{- end }}
diff --git a/__fixtures__/v2/with-subfolder/templates/webserver/deployment.yaml b/__fixtures__/v2/with-subfolder/templates/webserver/deployment.yaml
new file mode 100644
index 000000000..bf8a0d155
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/templates/webserver/deployment.yaml
@@ -0,0 +1,38 @@
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: {{ template "with-subfolder.fullname" . }}
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ replicas: {{ .Values.replicaCount }}
+ template:
+ metadata:
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ release: {{ .Release.Name }}
+ spec:
+ containers:
+ - name: {{ .Chart.Name }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - containerPort: {{ .Values.service.internalPort }}
+ livenessProbe:
+ httpGet:
+ path: /
+ port: {{ .Values.service.internalPort }}
+ readinessProbe:
+ httpGet:
+ path: /
+ port: {{ .Values.service.internalPort }}
+ resources:
+{{ toYaml .Values.resources | indent 12 }}
+ {{- if .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.nodeSelector | indent 8 }}
+ {{- end }}
diff --git a/__fixtures__/v2/with-subfolder/templates/webserver/ingress.yaml b/__fixtures__/v2/with-subfolder/templates/webserver/ingress.yaml
new file mode 100644
index 000000000..5f48d8e1c
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/templates/webserver/ingress.yaml
@@ -0,0 +1,32 @@
+{{- if .Values.ingress.enabled -}}
+{{- $serviceName := include "with-subfolder.fullname" . -}}
+{{- $servicePort := .Values.service.externalPort -}}
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: {{ template "with-subfolder.fullname" . }}
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+ annotations:
+ {{- range $key, $value := .Values.ingress.annotations }}
+ {{ $key }}: {{ $value | quote }}
+ {{- end }}
+spec:
+ rules:
+ {{- range $host := .Values.ingress.hosts }}
+ - host: {{ $host }}
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: {{ $serviceName }}
+ servicePort: {{ $servicePort }}
+ {{- end -}}
+ {{- if .Values.ingress.tls }}
+ tls:
+{{ toYaml .Values.ingress.tls | indent 4 }}
+ {{- end -}}
+{{- end -}}
diff --git a/__fixtures__/v2/with-subfolder/templates/webserver/service.yaml b/__fixtures__/v2/with-subfolder/templates/webserver/service.yaml
new file mode 100644
index 000000000..db0b551db
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/templates/webserver/service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ template "with-subfolder.fullname" . }}
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.externalPort }}
+ targetPort: {{ .Values.service.internalPort }}
+ protocol: TCP
+ name: {{ .Values.service.name }}
+ selector:
+ app: {{ template "with-subfolder.name" . }}
+ release: {{ .Release.Name }}
diff --git a/__fixtures__/v2/with-subfolder/tests/__snapshot__/deployment_test.yaml.snap b/__fixtures__/v2/with-subfolder/tests/__snapshot__/deployment_test.yaml.snap
new file mode 100644
index 000000000..d90e2b624
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/tests/__snapshot__/deployment_test.yaml.snap
@@ -0,0 +1,52 @@
+should pass all kinds of assertion for both deployments:
+ 1: |
+ replicas: 1
+ template:
+ metadata:
+ annotations:
+ some_template: |
+ ---
+ apiVersion: ...
+ this: is test for old separator workaround bug
+ labels:
+ app: with-subfolder
+ release: RELEASE-NAME
+ spec:
+ containers:
+ - image: apache:latest
+ imagePullPolicy: Always
+ livenessProbe:
+ httpGet:
+ path: /
+ port: 80
+ name: with-subfolder
+ ports:
+ - containerPort: null
+ readinessProbe:
+ httpGet:
+ path: /
+ port: 80
+ resources: {}
+ 2: |
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ app: with-subfolder
+ release: RELEASE-NAME
+ spec:
+ containers:
+ - image: apache:latest
+ imagePullPolicy: Always
+ livenessProbe:
+ httpGet:
+ path: /
+ port: 80
+ name: with-subfolder
+ ports:
+ - containerPort: 80
+ readinessProbe:
+ httpGet:
+ path: /
+ port: 80
+ resources: {}
diff --git a/__fixtures__/v2/with-subfolder/tests/__snapshot__/notes_test.yaml.snap b/__fixtures__/v2/with-subfolder/tests/__snapshot__/notes_test.yaml.snap
new file mode 100644
index 000000000..01d006a12
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/tests/__snapshot__/notes_test.yaml.snap
@@ -0,0 +1,20 @@
+should pass the notes file with ingress enabled:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+should pass the notes file with service type LoadBalancer:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w RELEASE-NAME-with-subfolder'
+ export SERVICE_IP=$(kubectl get svc --namespace NAMESPACE RELEASE-NAME-with-subfolder -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:9999
+should pass the notes file with service type NodePort:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-with-subfolder)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
diff --git a/__fixtures__/v2/with-subfolder/tests/deployment_test.yaml b/__fixtures__/v2/with-subfolder/tests/deployment_test.yaml
new file mode 100644
index 000000000..1bc9027d2
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/tests/deployment_test.yaml
@@ -0,0 +1,67 @@
+suite: test deployment
+templates:
+ - db/deployment.yaml
+ - webserver/deployment.yaml
+tests:
+ - it: should pass all kinds of assertion for both deployments
+ values:
+ - ./values/image.yaml
+ asserts:
+ - equal:
+ path: spec.template.spec.containers[0].image
+ value: apache:latest
+ - notEqual:
+ path: spec.template.spec.containers[0].image
+ value: nginx:stable
+ - matchRegex:
+ path: metadata.name
+ pattern: ^.*-subfolder.*$
+ - notMatchRegex:
+ path: metadata.name
+ pattern: ^.*-foobar$
+ - isNull:
+ path: spec.template.nodeSelector
+ - isNotNull:
+ path: spec.template
+ - isEmpty:
+ path: spec.template.spec.containers[0].resources
+ - isNotEmpty:
+ path: spec.template.spec.containers[0]
+ - isKind:
+ of: Deployment
+ - isAPIVersion:
+ of: extensions/v1beta1
+ - hasDocuments:
+ count: 1
+ - matchSnapshot:
+ path: spec
+ - it: should pass all kinds of assertion for webserver deployment
+ values:
+ - ./values/image.yaml
+ template: webserver/deployment.yaml
+ set:
+ service.internalPort: 8080
+ asserts:
+ - contains:
+ path: spec.template.spec.containers[0].ports
+ content:
+ containerPort: 8080
+ - notContains:
+ path: spec.template.spec.containers[0].ports
+ content:
+ containerPort: 80
+ - it: should pass all kinds of assertion for db deployment
+ values:
+ - ./values/image.yaml
+ template: db/deployment.yaml
+ set:
+ service.dbPort: 8080
+ asserts:
+ - contains:
+ path: spec.template.spec.containers[0].ports
+ content:
+ containerPort: 8080
+ - notContains:
+ path: spec.template.spec.containers[0].ports
+ content:
+ containerPort: 80
diff --git a/__fixtures__/v2/with-subfolder/tests/ingress_test.yaml b/__fixtures__/v2/with-subfolder/tests/ingress_test.yaml
new file mode 100644
index 000000000..a83dddb85
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/tests/ingress_test.yaml
@@ -0,0 +1,73 @@
+suite: test ingress
+templates:
+ - webserver/ingress.yaml
+tests:
+ - it: should render nothing if not enabled
+ asserts:
+ - hasDocuments:
+ count: 0
+
+ - it: should render Ingress right if enabled
+ set:
+ ingress.enabled: true
+ service.externalPort: 12345
+ release:
+ name: my-release
+ asserts:
+ - hasDocuments:
+ count: 1
+ - isKind:
+ of: Ingress
+ - contains:
+ path: spec.rules[0].http.paths
+ content:
+ path: /
+ backend:
+ serviceName: my-release-with-subfolder
+ servicePort: 12345
+ - isNull:
+ path: spec.tls
+
+ - it: should set annotations if given
+ set:
+ ingress.enabled: true
+ ingress.annotations:
+ kubernetes.io/ingress.class: nginx
+ kubernetes.io/tls-acme: "true"
+ ingress.kubernetes.io/rewrite-target: /
+ asserts:
+ - equal:
+ path: metadata.annotations
+ value:
+ kubernetes.io/ingress.class: nginx
+ kubernetes.io/tls-acme: "true"
+ ingress.kubernetes.io/rewrite-target: /
+
+ - it: should set annotations if given and verify the specific values.
+ set:
+ ingress.enabled: true
+ ingress.annotations:
+ kubernetes.io/ingress.class: nginx
+ kubernetes.io/tls-acme: "true"
+ ingress.kubernetes.io/rewrite-target: /
+ asserts:
+ - equal:
+ path: metadata.annotations.[kubernetes.io/ingress.class]
+ value: nginx
+ - equal:
+ path: metadata.annotations.[kubernetes.io/tls-acme]
+ value: "true"
+ - equal:
+ path: metadata.annotations.[ingress.kubernetes.io/rewrite-target]
+ value: /
+
+ - it: should set tls if given
+ set:
+ ingress.enabled: true
+ ingress.tls:
+ - secretName: my-tls-secret
+ asserts:
+ - equal:
+ path: spec.tls
+ value:
+ - secretName: my-tls-secret
diff --git a/__fixtures__/v2/with-subfolder/tests/notes_test.yaml b/__fixtures__/v2/with-subfolder/tests/notes_test.yaml
new file mode 100644
index 000000000..76e642863
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/tests/notes_test.yaml
@@ -0,0 +1,34 @@
+suite: test notes
+templates:
+ - NOTES.txt
+tests:
+ - it: should pass the notes file with ingress enabled
+ set:
+ ingress.enabled: true
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type NodePort
+ set:
+ service.type: NodePort
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-with-subfolder)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type LoadBalancer
+ set:
+ service.type: LoadBalancer
+ service.externalPort: 9999
+ asserts:
+ - matchRegexRaw:
+ pattern: http://\$SERVICE_IP:9999
+ - matchSnapshotRaw: {}
diff --git a/__fixtures__/v2/with-subfolder/tests/service_test.yaml b/__fixtures__/v2/with-subfolder/tests/service_test.yaml
new file mode 100644
index 000000000..bd39d27af
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/tests/service_test.yaml
@@ -0,0 +1,42 @@
+suite: test service
+templates:
+ - webserver/service.yaml
+tests:
+ - it: should pass
+ release:
+ name: my-release
+ asserts:
+ - contains:
+ path: spec.ports
+ content:
+ port: 80
+ targetPort: 80
+ protocol: TCP
+ name: nginx
+ - equal:
+ path: spec.type
+ value: ClusterIP
+ - equal:
+ path: spec.selector
+ value:
+ app: with-subfolder
+ release: my-release
+
+ - it: should render right if values given
+ set:
+ service:
+ type: NodePort
+ internalPort: 1234
+ externalPort: 4321
+ name: cool-service
+ asserts:
+ - contains:
+ path: spec.ports
+ content:
+ port: 4321
+ targetPort: 1234
+ protocol: TCP
+ name: cool-service
+ - equal:
+ path: spec.type
+ value: NodePort
diff --git a/__fixtures__/v2/with-subfolder/tests/values/image.yaml b/__fixtures__/v2/with-subfolder/tests/values/image.yaml
new file mode 100644
index 000000000..2514e3940
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/tests/values/image.yaml
@@ -0,0 +1,4 @@
+image:
+ repository: apache
+ tag: latest
+ pullPolicy: Always
diff --git a/__fixtures__/v2/with-subfolder/values.yaml b/__fixtures__/v2/with-subfolder/values.yaml
new file mode 100644
index 000000000..45d1817af
--- /dev/null
+++ b/__fixtures__/v2/with-subfolder/values.yaml
@@ -0,0 +1,38 @@
+# Default values for basic.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+replicaCount: 1
+image:
+ repository: nginx
+ tag: stable
+ pullPolicy: IfNotPresent
+service:
+ name: nginx
+ type: ClusterIP
+ externalPort: 80
+ internalPort: 80
+ingress:
+ class: foo
+ enabled: false
+ # Used to create an Ingress record.
+ hosts:
+ - chart-example.local
+ annotations:
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ tls:
+ # Secrets must be manually created in the namespace.
+ # - secretName: chart-example-tls
+ # hosts:
+ # - chart-example.local
+resources: {}
+ # We usually recommend not to specify default resources and to leave this as a conscious
+ # choice for the user. This also increases chances charts run on environments with little
+ # resources, such as Minikube. If you do want to specify resources, uncomment the following
+ # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
diff --git a/__fixtures__/v3/basic/tests/__snapshot__/notes_test.yaml.snap b/__fixtures__/v3/basic/tests/__snapshot__/notes_test.yaml.snap
new file mode 100644
index 000000000..2ca380109
--- /dev/null
+++ b/__fixtures__/v3/basic/tests/__snapshot__/notes_test.yaml.snap
@@ -0,0 +1,20 @@
+should pass the notes file with ingress enabled:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+should pass the notes file with service type LoadBalancer:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w RELEASE-NAME-basic'
+ export SERVICE_IP=$(kubectl get svc --namespace NAMESPACE RELEASE-NAME-basic -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:9999
+should pass the notes file with service type NodePort:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-basic)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
diff --git a/__fixtures__/v3/basic/tests/notes_test.yaml b/__fixtures__/v3/basic/tests/notes_test.yaml
new file mode 100644
index 000000000..3e950e9d6
--- /dev/null
+++ b/__fixtures__/v3/basic/tests/notes_test.yaml
@@ -0,0 +1,35 @@
+suite: test notes
+templates:
+ - NOTES.txt
+tests:
+ - it: should pass the notes file with ingress enabled
+ set:
+ ingress.enabled: true
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type NodePort
+ set:
+ service.type: NodePort
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-basic)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type LoadBalancer
+ set:
+ service.type: LoadBalancer
+ service.externalPort: 9999
+ asserts:
+ - matchRegexRaw:
+ pattern: http://\$SERVICE_IP:9999
+ - matchSnapshotRaw: {}
+
\ No newline at end of file
diff --git a/__fixtures__/v3/basic/tests_failed/notes_test.yaml b/__fixtures__/v3/basic/tests_failed/notes_test.yaml
new file mode 100644
index 000000000..2c6cbccf5
--- /dev/null
+++ b/__fixtures__/v3/basic/tests_failed/notes_test.yaml
@@ -0,0 +1,31 @@
+suite: test notes
+templates:
+ - NOTES.txt
+tests:
+ - it: should pass the notes file with ingress enabled
+ set:
+ ingress.enabled: true
+ asserts:
+ - notEqualRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+
+ - it: should pass the notes file with service type NodePort
+ set:
+ service.type: NodePort
+ asserts:
+ - notEqualRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services MY-RELEASE)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+
+ - it: should pass the notes file with service type LoadBalancer
+ set:
+ service.type: LoadBalancer
+ service.externalPort: 9999
+ asserts:
+ - matchRegexRaw:
+ pattern: http://\$SERVICE_IP:80
diff --git a/__fixtures__/v3/with-subchart/charts/child-chart/tests/__snapshot__/notes_test.yaml.snap b/__fixtures__/v3/with-subchart/charts/child-chart/tests/__snapshot__/notes_test.yaml.snap
new file mode 100644
index 000000000..95055e673
--- /dev/null
+++ b/__fixtures__/v3/with-subchart/charts/child-chart/tests/__snapshot__/notes_test.yaml.snap
@@ -0,0 +1,20 @@
+should pass the notes file with ingress enabled:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+should pass the notes file with service type LoadBalancer:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w RELEASE-NAME-child-chart'
+ export SERVICE_IP=$(kubectl get svc --namespace NAMESPACE RELEASE-NAME-child-chart -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:9999
+should pass the notes file with service type NodePort:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-child-chart)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
diff --git a/__fixtures__/v3/with-subchart/charts/child-chart/tests/notes_test.yaml b/__fixtures__/v3/with-subchart/charts/child-chart/tests/notes_test.yaml
new file mode 100644
index 000000000..dafb3ca0d
--- /dev/null
+++ b/__fixtures__/v3/with-subchart/charts/child-chart/tests/notes_test.yaml
@@ -0,0 +1,35 @@
+suite: test notes
+templates:
+ - NOTES.txt
+tests:
+ - it: should pass the notes file with ingress enabled
+ set:
+ ingress.enabled: true
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type NodePort
+ set:
+ service.type: NodePort
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-child-chart)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type LoadBalancer
+ set:
+ service.type: LoadBalancer
+ service.externalPort: 9999
+ asserts:
+ - matchRegexRaw:
+ pattern: http://\$SERVICE_IP:9999
+ - matchSnapshotRaw: {}
+
\ No newline at end of file
diff --git a/__fixtures__/v3/with-subchart/templates/NOTES.txt b/__fixtures__/v3/with-subchart/templates/NOTES.txt
index 30e62529b..f0cd1009c 100644
--- a/__fixtures__/v3/with-subchart/templates/NOTES.txt
+++ b/__fixtures__/v3/with-subchart/templates/NOTES.txt
@@ -4,16 +4,16 @@
http://{{ . }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
- export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "parant-chart.fullname" . }})
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "parent-chart.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
- You can watch the status of by running 'kubectl get svc -w {{ template "parant-chart.fullname" . }}'
- export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "parant-chart.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ You can watch the status of by running 'kubectl get svc -w {{ template "parent-chart.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "parent-chart.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.externalPort }}
{{- else if contains "ClusterIP" .Values.service.type }}
- export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "parant-chart.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "parent-chart.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:{{ .Values.service.internalPort }}
{{- end }}
diff --git a/__fixtures__/v3/with-subchart/tests/__snapshot__/notes_test.yaml.snap b/__fixtures__/v3/with-subchart/tests/__snapshot__/notes_test.yaml.snap
new file mode 100644
index 000000000..1a2110db0
--- /dev/null
+++ b/__fixtures__/v3/with-subchart/tests/__snapshot__/notes_test.yaml.snap
@@ -0,0 +1,20 @@
+should pass the notes file with ingress enabled:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+should pass the notes file with service type LoadBalancer:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w RELEASE-NAME-parent-chart'
+ export SERVICE_IP=$(kubectl get svc --namespace NAMESPACE RELEASE-NAME-parent-chart -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:9999
+should pass the notes file with service type NodePort:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-parent-chart)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
diff --git a/__fixtures__/v3/with-subchart/tests/notes_test.yaml b/__fixtures__/v3/with-subchart/tests/notes_test.yaml
new file mode 100644
index 000000000..8a34e865c
--- /dev/null
+++ b/__fixtures__/v3/with-subchart/tests/notes_test.yaml
@@ -0,0 +1,35 @@
+suite: test notes
+templates:
+ - NOTES.txt
+tests:
+ - it: should pass the notes file with ingress enabled
+ set:
+ ingress.enabled: true
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type NodePort
+ set:
+ service.type: NodePort
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-parent-chart)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+ - matchSnapshotRaw: {}
+
+ - it: should pass the notes file with service type LoadBalancer
+ set:
+ service.type: LoadBalancer
+ service.externalPort: 9999
+ asserts:
+ - matchRegexRaw:
+ pattern: http://\$SERVICE_IP:9999
+ - matchSnapshotRaw: {}
+
\ No newline at end of file
diff --git a/__fixtures__/v3/with-subfolder/.helmignore b/__fixtures__/v3/with-subfolder/.helmignore
new file mode 100644
index 000000000..dadf20295
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+
+tests
diff --git a/__fixtures__/v3/with-subfolder/Chart.yaml b/__fixtures__/v3/with-subfolder/Chart.yaml
new file mode 100644
index 000000000..49d878e22
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/Chart.yaml
@@ -0,0 +1,4 @@
+apiVersion: v2
+description: A advanced example chart to demonstrate unittest plugin
+name: with-subfolder
+version: 0.1.0
diff --git a/__fixtures__/v3/with-subfolder/templates/NOTES.txt b/__fixtures__/v3/with-subfolder/templates/NOTES.txt
new file mode 100644
index 000000000..f3c81c022
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/templates/NOTES.txt
@@ -0,0 +1,19 @@
+1. Get the application URL by running these commands:
+{{- if .Values.ingress.enabled }}
+{{- range .Values.ingress.hosts }}
+ http://{{ . }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "with-subfolder.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w {{ template "with-subfolder.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "with-subfolder.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:{{ .Values.service.externalPort }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "with-subfolder.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl port-forward $POD_NAME 8080:{{ .Values.service.internalPort }}
+{{- end }}
diff --git a/__fixtures__/v3/with-subfolder/templates/_helpers.tpl b/__fixtures__/v3/with-subfolder/templates/_helpers.tpl
new file mode 100644
index 000000000..21c49b484
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/templates/_helpers.tpl
@@ -0,0 +1,16 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "with-subfolder.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "with-subfolder.fullname" -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
diff --git a/__fixtures__/v3/with-subfolder/templates/db/deployment.yaml b/__fixtures__/v3/with-subfolder/templates/db/deployment.yaml
new file mode 100644
index 000000000..054e55003
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/templates/db/deployment.yaml
@@ -0,0 +1,42 @@
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: {{ template "with-subfolder.fullname" . }}-db
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ release: {{ .Release.Name }}
+ annotations:
+ some_template: |
+ ---
+ apiVersion: ...
+ this: is test for old separator workaround bug
+ spec:
+ containers:
+ - name: {{ .Chart.Name }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - containerPort: {{ .Values.service.dbPort }}
+ livenessProbe:
+ httpGet:
+ path: /
+ port: {{ .Values.service.internalPort }}
+ readinessProbe:
+ httpGet:
+ path: /
+ port: {{ .Values.service.internalPort }}
+ resources:
+{{ toYaml .Values.resources | indent 12 }}
+ {{- if .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.nodeSelector | indent 8 }}
+ {{- end }}
diff --git a/__fixtures__/v3/with-subfolder/templates/webserver/deployment.yaml b/__fixtures__/v3/with-subfolder/templates/webserver/deployment.yaml
new file mode 100644
index 000000000..bf8a0d155
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/templates/webserver/deployment.yaml
@@ -0,0 +1,38 @@
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: {{ template "with-subfolder.fullname" . }}
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ replicas: {{ .Values.replicaCount }}
+ template:
+ metadata:
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ release: {{ .Release.Name }}
+ spec:
+ containers:
+ - name: {{ .Chart.Name }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - containerPort: {{ .Values.service.internalPort }}
+ livenessProbe:
+ httpGet:
+ path: /
+ port: {{ .Values.service.internalPort }}
+ readinessProbe:
+ httpGet:
+ path: /
+ port: {{ .Values.service.internalPort }}
+ resources:
+{{ toYaml .Values.resources | indent 12 }}
+ {{- if .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.nodeSelector | indent 8 }}
+ {{- end }}
diff --git a/__fixtures__/v3/with-subfolder/templates/webserver/ingress.yaml b/__fixtures__/v3/with-subfolder/templates/webserver/ingress.yaml
new file mode 100644
index 000000000..5f48d8e1c
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/templates/webserver/ingress.yaml
@@ -0,0 +1,32 @@
+{{- if .Values.ingress.enabled -}}
+{{- $serviceName := include "with-subfolder.fullname" . -}}
+{{- $servicePort := .Values.service.externalPort -}}
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: {{ template "with-subfolder.fullname" . }}
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+ annotations:
+ {{- range $key, $value := .Values.ingress.annotations }}
+ {{ $key }}: {{ $value | quote }}
+ {{- end }}
+spec:
+ rules:
+ {{- range $host := .Values.ingress.hosts }}
+ - host: {{ $host }}
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: {{ $serviceName }}
+ servicePort: {{ $servicePort }}
+ {{- end -}}
+ {{- if .Values.ingress.tls }}
+ tls:
+{{ toYaml .Values.ingress.tls | indent 4 }}
+ {{- end -}}
+{{- end -}}
diff --git a/__fixtures__/v3/with-subfolder/templates/webserver/service.yaml b/__fixtures__/v3/with-subfolder/templates/webserver/service.yaml
new file mode 100644
index 000000000..db0b551db
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/templates/webserver/service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ template "with-subfolder.fullname" . }}
+ labels:
+ app: {{ template "with-subfolder.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.externalPort }}
+ targetPort: {{ .Values.service.internalPort }}
+ protocol: TCP
+ name: {{ .Values.service.name }}
+ selector:
+ app: {{ template "with-subfolder.name" . }}
+ release: {{ .Release.Name }}
diff --git a/__fixtures__/v3/with-subfolder/tests/__snapshot__/deployment_test.yaml.snap b/__fixtures__/v3/with-subfolder/tests/__snapshot__/deployment_test.yaml.snap
new file mode 100644
index 000000000..d90e2b624
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/tests/__snapshot__/deployment_test.yaml.snap
@@ -0,0 +1,52 @@
+should pass all kinds of assertion for both deployments:
+ 1: |
+ replicas: 1
+ template:
+ metadata:
+ annotations:
+ some_template: |
+ ---
+ apiVersion: ...
+ this: is test for old separator workaround bug
+ labels:
+ app: with-subfolder
+ release: RELEASE-NAME
+ spec:
+ containers:
+ - image: apache:latest
+ imagePullPolicy: Always
+ livenessProbe:
+ httpGet:
+ path: /
+ port: 80
+ name: with-subfolder
+ ports:
+ - containerPort: null
+ readinessProbe:
+ httpGet:
+ path: /
+ port: 80
+ resources: {}
+ 2: |
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ app: with-subfolder
+ release: RELEASE-NAME
+ spec:
+ containers:
+ - image: apache:latest
+ imagePullPolicy: Always
+ livenessProbe:
+ httpGet:
+ path: /
+ port: 80
+ name: with-subfolder
+ ports:
+ - containerPort: 80
+ readinessProbe:
+ httpGet:
+ path: /
+ port: 80
+ resources: {}
diff --git a/__fixtures__/v3/with-subfolder/tests/__snapshot__/notes_test.yaml.snap b/__fixtures__/v3/with-subfolder/tests/__snapshot__/notes_test.yaml.snap
new file mode 100644
index 000000000..01d006a12
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/tests/__snapshot__/notes_test.yaml.snap
@@ -0,0 +1,20 @@
+should pass the notes file with ingress enabled:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+should pass the notes file with service type LoadBalancer:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w RELEASE-NAME-with-subfolder'
+ export SERVICE_IP=$(kubectl get svc --namespace NAMESPACE RELEASE-NAME-with-subfolder -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:9999
+should pass the notes file with service type NodePort:
+ 1: |
+ |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-with-subfolder)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
diff --git a/__fixtures__/v3/with-subfolder/tests/deployment_test.yaml b/__fixtures__/v3/with-subfolder/tests/deployment_test.yaml
new file mode 100644
index 000000000..1bc9027d2
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/tests/deployment_test.yaml
@@ -0,0 +1,67 @@
+suite: test deployment
+templates:
+ - db/deployment.yaml
+ - webserver/deployment.yaml
+tests:
+ - it: should pass all kinds of assertion for both deployments
+ values:
+ - ./values/image.yaml
+ asserts:
+ - equal:
+ path: spec.template.spec.containers[0].image
+ value: apache:latest
+ - notEqual:
+ path: spec.template.spec.containers[0].image
+ value: nginx:stable
+ - matchRegex:
+ path: metadata.name
+ pattern: ^.*-subfolder.*$
+ - notMatchRegex:
+ path: metadata.name
+ pattern: ^.*-foobar$
+ - isNull:
+ path: spec.template.nodeSelector
+ - isNotNull:
+ path: spec.template
+ - isEmpty:
+ path: spec.template.spec.containers[0].resources
+ - isNotEmpty:
+ path: spec.template.spec.containers[0]
+ - isKind:
+ of: Deployment
+ - isAPIVersion:
+ of: extensions/v1beta1
+ - hasDocuments:
+ count: 1
+ - matchSnapshot:
+ path: spec
+ - it: should pass all kinds of assertion for webserver deployment
+ values:
+ - ./values/image.yaml
+ template: webserver/deployment.yaml
+ set:
+ service.internalPort: 8080
+ asserts:
+ - contains:
+ path: spec.template.spec.containers[0].ports
+ content:
+ containerPort: 8080
+ - notContains:
+ path: spec.template.spec.containers[0].ports
+ content:
+ containerPort: 80
+ - it: should pass all kinds of assertion for db deployment
+ values:
+ - ./values/image.yaml
+ template: db/deployment.yaml
+ set:
+ service.dbPort: 8080
+ asserts:
+ - contains:
+ path: spec.template.spec.containers[0].ports
+ content:
+ containerPort: 8080
+ - notContains:
+ path: spec.template.spec.containers[0].ports
+ content:
+ containerPort: 80
diff --git a/__fixtures__/v3/with-subfolder/tests/ingress_test.yaml b/__fixtures__/v3/with-subfolder/tests/ingress_test.yaml
new file mode 100644
index 000000000..a83dddb85
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/tests/ingress_test.yaml
@@ -0,0 +1,73 @@
+suite: test ingress
+templates:
+ - webserver/ingress.yaml
+tests:
+ - it: should render nothing if not enabled
+ asserts:
+ - hasDocuments:
+ count: 0
+
+ - it: should render Ingress right if enabled
+ set:
+ ingress.enabled: true
+ service.externalPort: 12345
+ release:
+ name: my-release
+ asserts:
+ - hasDocuments:
+ count: 1
+ - isKind:
+ of: Ingress
+ - contains:
+ path: spec.rules[0].http.paths
+ content:
+ path: /
+ backend:
+ serviceName: my-release-with-subfolder
+ servicePort: 12345
+ - isNull:
+ path: spec.tls
+
+ - it: should set annotations if given
+ set:
+ ingress.enabled: true
+ ingress.annotations:
+ kubernetes.io/ingress.class: nginx
+ kubernetes.io/tls-acme: "true"
+ ingress.kubernetes.io/rewrite-target: /
+ asserts:
+ - equal:
+ path: metadata.annotations
+ value:
+ kubernetes.io/ingress.class: nginx
+ kubernetes.io/tls-acme: "true"
+ ingress.kubernetes.io/rewrite-target: /
+
+ - it: should set annotations if given and verify the specific values.
+ set:
+ ingress.enabled: true
+ ingress.annotations:
+ kubernetes.io/ingress.class: nginx
+ kubernetes.io/tls-acme: "true"
+ ingress.kubernetes.io/rewrite-target: /
+ asserts:
+ - equal:
+ path: metadata.annotations.[kubernetes.io/ingress.class]
+ value: nginx
+ - equal:
+ path: metadata.annotations.[kubernetes.io/tls-acme]
+ value: "true"
+ - equal:
+ path: metadata.annotations.[ingress.kubernetes.io/rewrite-target]
+ value: /
+
+ - it: should set tls if given
+ set:
+ ingress.enabled: true
+ ingress.tls:
+ - secretName: my-tls-secret
+ asserts:
+ - equal:
+ path: spec.tls
+ value:
+ - secretName: my-tls-secret
diff --git a/__fixtures__/v3/with-subfolder/tests/notes_test.yaml b/__fixtures__/v3/with-subfolder/tests/notes_test.yaml
new file mode 100644
index 000000000..2fc6e2808
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/tests/notes_test.yaml
@@ -0,0 +1,32 @@
+suite: test notes
+templates:
+ - NOTES.txt
+tests:
+ - it: should pass the notes file with ingress enabled
+ set:
+ ingress.enabled: true
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+ - matchSnapshotRaw: {}
+ - it: should pass the notes file with service type NodePort
+ set:
+ service.type: NodePort
+ asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export NODE_PORT=$(kubectl get --namespace NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services RELEASE-NAME-with-subfolder)
+ export NODE_IP=$(kubectl get nodes --namespace NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+ - matchSnapshotRaw: {}
+ - it: should pass the notes file with service type LoadBalancer
+ set:
+ service.type: LoadBalancer
+ service.externalPort: 9999
+ asserts:
+ - matchRegexRaw:
+ pattern: http://\$SERVICE_IP:9999
+ - matchSnapshotRaw: {}
diff --git a/__fixtures__/v3/with-subfolder/tests/service_test.yaml b/__fixtures__/v3/with-subfolder/tests/service_test.yaml
new file mode 100644
index 000000000..bd39d27af
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/tests/service_test.yaml
@@ -0,0 +1,42 @@
+suite: test service
+templates:
+ - webserver/service.yaml
+tests:
+ - it: should pass
+ release:
+ name: my-release
+ asserts:
+ - contains:
+ path: spec.ports
+ content:
+ port: 80
+ targetPort: 80
+ protocol: TCP
+ name: nginx
+ - equal:
+ path: spec.type
+ value: ClusterIP
+ - equal:
+ path: spec.selector
+ value:
+ app: with-subfolder
+ release: my-release
+
+ - it: should render right if values given
+ set:
+ service:
+ type: NodePort
+ internalPort: 1234
+ externalPort: 4321
+ name: cool-service
+ asserts:
+ - contains:
+ path: spec.ports
+ content:
+ port: 4321
+ targetPort: 1234
+ protocol: TCP
+ name: cool-service
+ - equal:
+ path: spec.type
+ value: NodePort
diff --git a/__fixtures__/v3/with-subfolder/tests/values/image.yaml b/__fixtures__/v3/with-subfolder/tests/values/image.yaml
new file mode 100644
index 000000000..2514e3940
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/tests/values/image.yaml
@@ -0,0 +1,4 @@
+image:
+ repository: apache
+ tag: latest
+ pullPolicy: Always
diff --git a/__fixtures__/v3/with-subfolder/values.yaml b/__fixtures__/v3/with-subfolder/values.yaml
new file mode 100644
index 000000000..45d1817af
--- /dev/null
+++ b/__fixtures__/v3/with-subfolder/values.yaml
@@ -0,0 +1,38 @@
+# Default values for basic.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+replicaCount: 1
+image:
+ repository: nginx
+ tag: stable
+ pullPolicy: IfNotPresent
+service:
+ name: nginx
+ type: ClusterIP
+ externalPort: 80
+ internalPort: 80
+ingress:
+ class: foo
+ enabled: false
+ # Used to create an Ingress record.
+ hosts:
+ - chart-example.local
+ annotations:
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ tls:
+ # Secrets must be manually created in the namespace.
+ # - secretName: chart-example-tls
+ # hosts:
+ # - chart-example.local
+resources: {}
+ # We usually recommend not to specify default resources and to leave this as a conscious
+ # choice for the user. This also increases chances charts run on environments with little
+ # resources, such as Minikube. If you do want to specify resources, uncomment the following
+ # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
diff --git a/unittest/.snapshots/TestV2RunJobWithInvalidValuesFile b/unittest/.snapshots/TestV2RunJobWithInvalidValuesFile
deleted file mode 100644
index 2f40ecf1f..000000000
--- a/unittest/.snapshots/TestV2RunJobWithInvalidValuesFile
+++ /dev/null
@@ -1,30 +0,0 @@
-(*unittest.TestJobResult)({
- DisplayName: (string) (len=11) "should work",
- Index: (int) 0,
- Passed: (bool) false,
- ExecError: (error) ,
- AssertsResult: ([]*unittest.AssertionResult) (len=1) {
- (*unittest.AssertionResult)({
- Index: (int) 0,
- FailInfo: ([]string) (len=12) {
- (string) (len=41) "Template:\tbasic/templates/deployment.yaml",
- (string) (len=19) "Path:\tmetadata.name",
- (string) (len=9) "Expected:",
- (string) (len=23) "\tRELEASE-NAME-mary-jane",
- (string) (len=7) "Actual:",
- (string) (len=19) "\tRELEASE-NAME-basic",
- (string) (len=5) "Diff:",
- (string) (len=13) "\t--- Expected",
- (string) (len=11) "\t+++ Actual",
- (string) (len=16) "\t@@ -1,2 +1,2 @@",
- (string) (len=24) "\t-RELEASE-NAME-mary-jane",
- (string) (len=20) "\t+RELEASE-NAME-basic"
- },
- Passed: (bool) false,
- AssertType: (string) (len=5) "equal",
- Not: (bool) false,
- CustomInfo: (string) ""
- })
- },
- Duration: (time.Duration) 0s
-})
diff --git a/unittest/.snapshots/TestV2RunJobWithNOTESTemplateOk b/unittest/.snapshots/TestV2RunJobWithNOTESTemplateOk
new file mode 100644
index 000000000..6339785e5
--- /dev/null
+++ b/unittest/.snapshots/TestV2RunJobWithNOTESTemplateOk
@@ -0,0 +1,27 @@
+(*unittest.TestJobResult)({
+ DisplayName: (string) (len=11) "should work",
+ Index: (int) 0,
+ Passed: (bool) true,
+ ExecError: (error) ,
+ AssertsResult: ([]*unittest.AssertionResult) (len=2) {
+ (*unittest.AssertionResult)({
+ Index: (int) 0,
+ FailInfo: ([]string) {
+ },
+ Passed: (bool) true,
+ AssertType: (string) (len=8) "equalRaw",
+ Not: (bool) false,
+ CustomInfo: (string) ""
+ }),
+ (*unittest.AssertionResult)({
+ Index: (int) 1,
+ FailInfo: ([]string) {
+ },
+ Passed: (bool) true,
+ AssertType: (string) (len=13) "matchRegexRaw",
+ Not: (bool) false,
+ CustomInfo: (string) ""
+ })
+ },
+ Duration: (time.Duration) 0s
+})
diff --git a/unittest/.snapshots/TestV2RunSuiteWithSubfolderWhenPass b/unittest/.snapshots/TestV2RunSuiteWithSubfolderWhenPass
new file mode 100644
index 000000000..50470ac56
--- /dev/null
+++ b/unittest/.snapshots/TestV2RunSuiteWithSubfolderWhenPass
@@ -0,0 +1,41 @@
+(*unittest.TestSuiteResult)({
+ DisplayName: (string) (len=15) "test suite name",
+ FilePath: (string) "",
+ Passed: (bool) true,
+ ExecError: (error) ,
+ TestsResult: ([]*unittest.TestJobResult) (len=1) {
+ (*unittest.TestJobResult)({
+ DisplayName: (string) (len=11) "should pass",
+ Index: (int) 0,
+ Passed: (bool) true,
+ ExecError: (error) ,
+ AssertsResult: ([]*unittest.AssertionResult) (len=2) {
+ (*unittest.AssertionResult)({
+ Index: (int) 0,
+ FailInfo: ([]string) {
+ },
+ Passed: (bool) true,
+ AssertType: (string) (len=5) "equal",
+ Not: (bool) false,
+ CustomInfo: (string) ""
+ }),
+ (*unittest.AssertionResult)({
+ Index: (int) 1,
+ FailInfo: ([]string) {
+ },
+ Passed: (bool) true,
+ AssertType: (string) (len=13) "matchSnapshot",
+ Not: (bool) false,
+ CustomInfo: (string) ""
+ })
+ },
+ Duration: (time.Duration) 0s
+ })
+ },
+ SnapshotCounting: (struct { Total uint; Failed uint; Created uint; Vanished uint }) {
+ Total: (uint) 2,
+ Failed: (uint) 0,
+ Created: (uint) 2,
+ Vanished: (uint) 0
+ }
+})
diff --git a/unittest/.snapshots/TestV2RunnerOkWithFailedTests b/unittest/.snapshots/TestV2RunnerOkWithFailedTests
index a0c095428..076c8eef1 100644
--- a/unittest/.snapshots/TestV2RunnerOkWithFailedTests
+++ b/unittest/.snapshots/TestV2RunnerOkWithFailedTests
@@ -279,6 +279,28 @@
@@ -1,2 +1,2 @@
-- secretName: my-tls-secret
+null
+ FAIL test notes ../__fixtures__/v2/basic/tests_failed/notes_test.yaml
+ - should pass the notes file with ingress enabled
+
+ - asserts[0] `notEqualRaw` fail
+
+ Template: basic/templates/NOTES.txt
+ Expected NOT to equal:
+ |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+
+ - should pass the notes file with service type LoadBalancer
+
+ - asserts[0] `matchRegexRaw` fail
+
+ Template: basic/templates/NOTES.txt
+ Expected to match: http:///$SERVICE_IP:80
+ Actual: 1. Get the application URL by running these commands:
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w RELEASE-NAME-basic'
+ export SERVICE_IP=$(kubectl get svc --namespace NAMESPACE RELEASE-NAME-basic -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:9999
FAIL test service ../__fixtures__/v2/basic/tests_failed/service_test.yaml
- should failed
@@ -344,8 +366,8 @@
Charts: 1 failed, 0 passed, 1 total
-Test Suites: 3 failed, 0 passed, 3 total
-Tests: 7 failed, 0 passed, 7 total
+Test Suites: 4 failed, 0 passed, 4 total
+Tests: 9 failed, 1 passed, 10 total
Snapshot: 2 passed, 2 total
Time: XX.XXXms
diff --git a/unittest/.snapshots/TestV2RunnerOkWithPassedTests b/unittest/.snapshots/TestV2RunnerOkWithPassedTests
index 8ec871a67..03e1af958 100644
--- a/unittest/.snapshots/TestV2RunnerOkWithPassedTests
+++ b/unittest/.snapshots/TestV2RunnerOkWithPassedTests
@@ -4,13 +4,14 @@
PASS test deployment ../__fixtures__/v2/basic/tests/deployment_test.yaml
PASS test ingress ../__fixtures__/v2/basic/tests/ingress_test.yaml
+ PASS test notes ../__fixtures__/v2/basic/tests/notes_test.yaml
PASS test service ../__fixtures__/v2/basic/tests/service_test.yaml
Charts: 1 passed, 1 total
-Test Suites: 3 passed, 3 total
-Tests: 8 passed, 8 total
-Snapshot: 1 passed, 1 total
+Test Suites: 4 passed, 4 total
+Tests: 11 passed, 11 total
+Snapshot: 4 passed, 4 total
Time: XX.XXXms
diff --git a/unittest/.snapshots/TestV2RunnerOkWithSubSubfolder b/unittest/.snapshots/TestV2RunnerOkWithSubSubfolder
new file mode 100644
index 000000000..b61c06743
--- /dev/null
+++ b/unittest/.snapshots/TestV2RunnerOkWithSubSubfolder
@@ -0,0 +1,17 @@
+
+### Chart [ with-subfolder ] ../__fixtures__/v2/with-subfolder
+
+
+ PASS test deployment ../__fixtures__/v2/with-subfolder/tests/deployment_test.yaml
+ PASS test ingress ../__fixtures__/v2/with-subfolder/tests/ingress_test.yaml
+ PASS test notes ../__fixtures__/v2/with-subfolder/tests/notes_test.yaml
+ PASS test service ../__fixtures__/v2/with-subfolder/tests/service_test.yaml
+
+
+Charts: 1 passed, 1 total
+Test Suites: 4 passed, 4 total
+Tests: 13 passed, 13 total
+Snapshot: 5 passed, 5 total
+Time: XX.XXXms
+
+
diff --git a/unittest/.snapshots/TestV2RunnerWithTestsInSubchart b/unittest/.snapshots/TestV2RunnerWithTestsInSubchart
index bdaee8026..4427d9403 100644
--- a/unittest/.snapshots/TestV2RunnerWithTestsInSubchart
+++ b/unittest/.snapshots/TestV2RunnerWithTestsInSubchart
@@ -6,14 +6,16 @@
PASS test deployment ../__fixtures__/v2/with-subchart/tests/deployment_test.yaml
PASS test ingress ../__fixtures__/v2/with-subchart/charts/child-chart/tests/ingress_test.yaml
PASS test ingress ../__fixtures__/v2/with-subchart/tests/ingress_test.yaml
+ PASS test notes ../__fixtures__/v2/with-subchart/charts/child-chart/tests/notes_test.yaml
+ PASS test notes ../__fixtures__/v2/with-subchart/tests/notes_test.yaml
PASS test service ../__fixtures__/v2/with-subchart/charts/child-chart/tests/service_test.yaml
PASS test service ../__fixtures__/v2/with-subchart/tests/service_test.yaml
Charts: 1 passed, 1 total
-Test Suites: 6 passed, 6 total
-Tests: 14 passed, 14 total
-Snapshot: 2 passed, 2 total
+Test Suites: 8 passed, 8 total
+Tests: 20 passed, 20 total
+Snapshot: 8 passed, 8 total
Time: XX.XXXms
diff --git a/unittest/.snapshots/TestV2RunnerWithTestsInSubchartButFlagFalse b/unittest/.snapshots/TestV2RunnerWithTestsInSubchartButFlagFalse
index d04474fdc..33a4d6b5a 100644
--- a/unittest/.snapshots/TestV2RunnerWithTestsInSubchartButFlagFalse
+++ b/unittest/.snapshots/TestV2RunnerWithTestsInSubchartButFlagFalse
@@ -4,13 +4,14 @@
PASS test deployment ../__fixtures__/v2/with-subchart/tests/deployment_test.yaml
PASS test ingress ../__fixtures__/v2/with-subchart/tests/ingress_test.yaml
+ PASS test notes ../__fixtures__/v2/with-subchart/tests/notes_test.yaml
PASS test service ../__fixtures__/v2/with-subchart/tests/service_test.yaml
Charts: 1 passed, 1 total
-Test Suites: 3 passed, 3 total
-Tests: 7 passed, 7 total
-Snapshot: 1 passed, 1 total
+Test Suites: 4 passed, 4 total
+Tests: 10 passed, 10 total
+Snapshot: 4 passed, 4 total
Time: XX.XXXms
diff --git a/unittest/.snapshots/TestV3RunSuiteWithSubfolderWhenPass b/unittest/.snapshots/TestV3RunSuiteWithSubfolderWhenPass
new file mode 100644
index 000000000..50470ac56
--- /dev/null
+++ b/unittest/.snapshots/TestV3RunSuiteWithSubfolderWhenPass
@@ -0,0 +1,41 @@
+(*unittest.TestSuiteResult)({
+ DisplayName: (string) (len=15) "test suite name",
+ FilePath: (string) "",
+ Passed: (bool) true,
+ ExecError: (error) ,
+ TestsResult: ([]*unittest.TestJobResult) (len=1) {
+ (*unittest.TestJobResult)({
+ DisplayName: (string) (len=11) "should pass",
+ Index: (int) 0,
+ Passed: (bool) true,
+ ExecError: (error) ,
+ AssertsResult: ([]*unittest.AssertionResult) (len=2) {
+ (*unittest.AssertionResult)({
+ Index: (int) 0,
+ FailInfo: ([]string) {
+ },
+ Passed: (bool) true,
+ AssertType: (string) (len=5) "equal",
+ Not: (bool) false,
+ CustomInfo: (string) ""
+ }),
+ (*unittest.AssertionResult)({
+ Index: (int) 1,
+ FailInfo: ([]string) {
+ },
+ Passed: (bool) true,
+ AssertType: (string) (len=13) "matchSnapshot",
+ Not: (bool) false,
+ CustomInfo: (string) ""
+ })
+ },
+ Duration: (time.Duration) 0s
+ })
+ },
+ SnapshotCounting: (struct { Total uint; Failed uint; Created uint; Vanished uint }) {
+ Total: (uint) 2,
+ Failed: (uint) 0,
+ Created: (uint) 2,
+ Vanished: (uint) 0
+ }
+})
diff --git a/unittest/.snapshots/TestV3RunnerOkWithFailedTests b/unittest/.snapshots/TestV3RunnerOkWithFailedTests
index 811480e3f..e02df3399 100644
--- a/unittest/.snapshots/TestV3RunnerOkWithFailedTests
+++ b/unittest/.snapshots/TestV3RunnerOkWithFailedTests
@@ -279,6 +279,28 @@
@@ -1,2 +1,2 @@
-- secretName: my-tls-secret
+null
+ FAIL test notes ../__fixtures__/v3/basic/tests_failed/notes_test.yaml
+ - should pass the notes file with ingress enabled
+
+ - asserts[0] `notEqualRaw` fail
+
+ Template: basic/templates/NOTES.txt
+ Expected NOT to equal:
+ |
+ 1. Get the application URL by running these commands:
+ http://chart-example.local
+
+ - should pass the notes file with service type LoadBalancer
+
+ - asserts[0] `matchRegexRaw` fail
+
+ Template: basic/templates/NOTES.txt
+ Expected to match: http:///$SERVICE_IP:80
+ Actual: 1. Get the application URL by running these commands:
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc -w RELEASE-NAME-basic'
+ export SERVICE_IP=$(kubectl get svc --namespace NAMESPACE RELEASE-NAME-basic -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:9999
FAIL test service ../__fixtures__/v3/basic/tests_failed/service_test.yaml
- should failed
@@ -344,8 +366,8 @@
Charts: 1 failed, 0 passed, 1 total
-Test Suites: 3 failed, 0 passed, 3 total
-Tests: 7 failed, 0 passed, 7 total
+Test Suites: 4 failed, 0 passed, 4 total
+Tests: 9 failed, 1 passed, 10 total
Snapshot: 2 passed, 2 total
Time: XX.XXXms
diff --git a/unittest/.snapshots/TestV3RunnerOkWithPassedTests b/unittest/.snapshots/TestV3RunnerOkWithPassedTests
index 6044f5d5d..e3ef6599f 100644
--- a/unittest/.snapshots/TestV3RunnerOkWithPassedTests
+++ b/unittest/.snapshots/TestV3RunnerOkWithPassedTests
@@ -4,13 +4,14 @@
PASS test deployment ../__fixtures__/v3/basic/tests/deployment_test.yaml
PASS test ingress ../__fixtures__/v3/basic/tests/ingress_test.yaml
+ PASS test notes ../__fixtures__/v3/basic/tests/notes_test.yaml
PASS test service ../__fixtures__/v3/basic/tests/service_test.yaml
Charts: 1 passed, 1 total
-Test Suites: 3 passed, 3 total
-Tests: 8 passed, 8 total
-Snapshot: 1 passed, 1 total
+Test Suites: 4 passed, 4 total
+Tests: 11 passed, 11 total
+Snapshot: 4 passed, 4 total
Time: XX.XXXms
diff --git a/unittest/.snapshots/TestV3RunnerOkWithSubSubfolder b/unittest/.snapshots/TestV3RunnerOkWithSubSubfolder
new file mode 100644
index 000000000..7f00e9e26
--- /dev/null
+++ b/unittest/.snapshots/TestV3RunnerOkWithSubSubfolder
@@ -0,0 +1,17 @@
+
+### Chart [ with-subfolder ] ../__fixtures__/v3/with-subfolder
+
+
+ PASS test deployment ../__fixtures__/v3/with-subfolder/tests/deployment_test.yaml
+ PASS test ingress ../__fixtures__/v3/with-subfolder/tests/ingress_test.yaml
+ PASS test notes ../__fixtures__/v3/with-subfolder/tests/notes_test.yaml
+ PASS test service ../__fixtures__/v3/with-subfolder/tests/service_test.yaml
+
+
+Charts: 1 passed, 1 total
+Test Suites: 4 passed, 4 total
+Tests: 13 passed, 13 total
+Snapshot: 5 passed, 5 total
+Time: XX.XXXms
+
+
diff --git a/unittest/.snapshots/TestV3RunnerWithTestsInSubchart b/unittest/.snapshots/TestV3RunnerWithTestsInSubchart
index f331e7212..a2e4172a0 100644
--- a/unittest/.snapshots/TestV3RunnerWithTestsInSubchart
+++ b/unittest/.snapshots/TestV3RunnerWithTestsInSubchart
@@ -6,14 +6,16 @@
PASS test deployment ../__fixtures__/v3/with-subchart/tests/deployment_test.yaml
PASS test ingress ../__fixtures__/v3/with-subchart/charts/child-chart/tests/ingress_test.yaml
PASS test ingress ../__fixtures__/v3/with-subchart/tests/ingress_test.yaml
+ PASS test notes ../__fixtures__/v3/with-subchart/charts/child-chart/tests/notes_test.yaml
+ PASS test notes ../__fixtures__/v3/with-subchart/tests/notes_test.yaml
PASS test service ../__fixtures__/v3/with-subchart/charts/child-chart/tests/service_test.yaml
PASS test service ../__fixtures__/v3/with-subchart/tests/service_test.yaml
Charts: 1 passed, 1 total
-Test Suites: 6 passed, 6 total
-Tests: 14 passed, 14 total
-Snapshot: 2 passed, 2 total
+Test Suites: 8 passed, 8 total
+Tests: 20 passed, 20 total
+Snapshot: 8 passed, 8 total
Time: XX.XXXms
diff --git a/unittest/.snapshots/TestV3RunnerWithTestsInSubchartButFlagFalse b/unittest/.snapshots/TestV3RunnerWithTestsInSubchartButFlagFalse
index 603d4ff3b..76f4fcb73 100644
--- a/unittest/.snapshots/TestV3RunnerWithTestsInSubchartButFlagFalse
+++ b/unittest/.snapshots/TestV3RunnerWithTestsInSubchartButFlagFalse
@@ -4,13 +4,14 @@
PASS test deployment ../__fixtures__/v3/with-subchart/tests/deployment_test.yaml
PASS test ingress ../__fixtures__/v3/with-subchart/tests/ingress_test.yaml
+ PASS test notes ../__fixtures__/v3/with-subchart/tests/notes_test.yaml
PASS test service ../__fixtures__/v3/with-subchart/tests/service_test.yaml
Charts: 1 passed, 1 total
-Test Suites: 3 passed, 3 total
-Tests: 7 passed, 7 total
-Snapshot: 1 passed, 1 total
+Test Suites: 4 passed, 4 total
+Tests: 10 passed, 10 total
+Snapshot: 4 passed, 4 total
Time: XX.XXXms
diff --git a/unittest/assertion.go b/unittest/assertion.go
index f880db044..36267aafa 100644
--- a/unittest/assertion.go
+++ b/unittest/assertion.go
@@ -144,18 +144,25 @@ type assertTypeDef struct {
}
var assertTypeMapping = map[string]assertTypeDef{
- "matchSnapshot": {reflect.TypeOf(validators.MatchSnapshotValidator{}), false},
- "equal": {reflect.TypeOf(validators.EqualValidator{}), false},
- "notEqual": {reflect.TypeOf(validators.EqualValidator{}), true},
- "matchRegex": {reflect.TypeOf(validators.MatchRegexValidator{}), false},
- "notMatchRegex": {reflect.TypeOf(validators.MatchRegexValidator{}), true},
- "contains": {reflect.TypeOf(validators.ContainsValidator{}), false},
- "notContains": {reflect.TypeOf(validators.ContainsValidator{}), true},
- "isNull": {reflect.TypeOf(validators.IsNullValidator{}), false},
- "isNotNull": {reflect.TypeOf(validators.IsNullValidator{}), true},
- "isEmpty": {reflect.TypeOf(validators.IsEmptyValidator{}), false},
- "isNotEmpty": {reflect.TypeOf(validators.IsEmptyValidator{}), true},
- "isKind": {reflect.TypeOf(validators.IsKindValidator{}), false},
- "isAPIVersion": {reflect.TypeOf(validators.IsAPIVersionValidator{}), false},
- "hasDocuments": {reflect.TypeOf(validators.HasDocumentsValidator{}), false},
+ "matchSnapshot": {reflect.TypeOf(validators.MatchSnapshotValidator{}), false},
+ "matchSnapshotRaw": {reflect.TypeOf(validators.MatchSnapshotRawValidator{}), false},
+ "equal": {reflect.TypeOf(validators.EqualValidator{}), false},
+ "notEqual": {reflect.TypeOf(validators.EqualValidator{}), true},
+ "equalRaw": {reflect.TypeOf(validators.EqualRawValidator{}), false},
+ "notEqualRaw": {reflect.TypeOf(validators.EqualRawValidator{}), true},
+ "matchRegex": {reflect.TypeOf(validators.MatchRegexValidator{}), false},
+ "notMatchRegex": {reflect.TypeOf(validators.MatchRegexValidator{}), true},
+ "matchRegexRaw": {reflect.TypeOf(validators.MatchRegexRawValidator{}), false},
+ "notMatchRegexRaw": {reflect.TypeOf(validators.MatchRegexRawValidator{}), true},
+ "contains": {reflect.TypeOf(validators.ContainsValidator{}), false},
+ "notContains": {reflect.TypeOf(validators.ContainsValidator{}), true},
+ "isNull": {reflect.TypeOf(validators.IsNullValidator{}), false},
+ "isNotNull": {reflect.TypeOf(validators.IsNullValidator{}), true},
+ "isEmpty": {reflect.TypeOf(validators.IsEmptyValidator{}), false},
+ "isNotEmpty": {reflect.TypeOf(validators.IsEmptyValidator{}), true},
+ "isKind": {reflect.TypeOf(validators.IsKindValidator{}), false},
+ "isAPIVersion": {reflect.TypeOf(validators.IsAPIVersionValidator{}), false},
+ "hasDocuments": {reflect.TypeOf(validators.HasDocumentsValidator{}), false},
+ "isSubset": {reflect.TypeOf(validators.IsSubsetValidator{}), false},
+ "isNotSubset": {reflect.TypeOf(validators.IsSubsetValidator{}), true},
}
diff --git a/unittest/assertion_test.go b/unittest/assertion_test.go
index 2eb5aac87..ecdcee64c 100644
--- a/unittest/assertion_test.go
+++ b/unittest/assertion_test.go
@@ -10,12 +10,42 @@ import (
"gopkg.in/yaml.v2"
)
+func validateSucceededTestAssertions(
+ t *testing.T,
+ assertionsYAML string,
+ assertionCount int,
+ renderedMap map[string][]common.K8sManifest) {
+
+ assertions := make([]Assertion, assertionCount)
+ err := yaml.Unmarshal([]byte(assertionsYAML), &assertions)
+
+ a := assert.New(t)
+ a.Nil(err)
+
+ for idx, assertion := range assertions {
+ result := assertion.Assert(renderedMap, fakeSnapshotComparer(true), &AssertionResult{Index: idx})
+ a.Equal(&AssertionResult{
+ Index: idx,
+ FailInfo: []string{},
+ Passed: true,
+ AssertType: assertion.AssertType,
+ Not: false,
+ CustomInfo: "",
+ }, result)
+ }
+
+}
+
func TestAssertionUnmarshaledFromYAML(t *testing.T) {
assertionsYAML := `
- equal:
- notEqual:
+- equalRaw:
+- notEqualRaw:
- matchRegex:
- notMatchRegex:
+- matchRegexRaw:
+- notMatchRegexRaw:
- contains:
- notContains:
- isNull:
@@ -25,10 +55,12 @@ func TestAssertionUnmarshaledFromYAML(t *testing.T) {
- isKind:
- isAPIVersion:
- hasDocuments:
+- isSubset:
`
- assertionsAsMap := make([]map[string]interface{}, 13)
+
+ assertionsAsMap := make([]map[string]interface{}, 18)
yaml.Unmarshal([]byte(assertionsYAML), &assertionsAsMap)
- assertions := make([]Assertion, 13)
+ assertions := make([]Assertion, 18)
yaml.Unmarshal([]byte(assertionsYAML), &assertions)
a := assert.New(t)
@@ -45,10 +77,18 @@ func TestAssertionUnmarshaledFromYAMLWithNotTrue(t *testing.T) {
not: true
- notEqual:
not: true
+- equalRaw:
+ not: true
+- notEqualRaw:
+ not: true
- matchRegex:
not: true
- notMatchRegex:
not: true
+- matchRegexRaw:
+ not: true
+- notMatchRegexRaw:
+ not: true
- contains:
not: true
- notContains:
@@ -67,8 +107,10 @@ func TestAssertionUnmarshaledFromYAMLWithNotTrue(t *testing.T) {
not: true
- hasDocuments:
not: true
+- isSubset:
+ not: true
`
- assertions := make([]Assertion, 13)
+ assertions := make([]Assertion, 18)
yaml.Unmarshal([]byte(assertionsYAML), &assertions)
a := assert.New(t)
@@ -80,27 +122,36 @@ func TestAssertionUnmarshaledFromYAMLWithNotTrue(t *testing.T) {
func TestReverseAssertionTheSameAsOriginalOneWithNotTrue(t *testing.T) {
assertionsYAML := `
- equal:
- not: true
+ not: true
- notEqual:
+- equalRaw:
+ not: true
+- notEqualRaw:
- matchRegex:
- not: true
+ not: true
- notMatchRegex:
+- matchRegexRaw:
+ not: true
+- notMatchRegexRaw:
- contains:
- not: true
+ not: true
- notContains:
- isNull:
- not: true
+ not: true
- isNotNull:
- isEmpty:
- not: true
+ not: true
- isNotEmpty:
+- isSubset:
+ not: true
+- isNotSubset:
`
- assertions := make([]Assertion, 10)
+ assertions := make([]Assertion, 15)
yaml.Unmarshal([]byte(assertionsYAML), &assertions)
a := assert.New(t)
for idx := 0; idx < len(assertions); idx += 2 {
- a.Equal(assertions[idx], assertions[idx+1])
+ a.Equal(assertions[idx].Not, !assertions[idx+1].Not)
}
}
@@ -118,6 +169,8 @@ kind: Fake
apiVersion: v123
a: b
c: [d]
+e:
+ f: g
`
manifest := common.K8sManifest{}
yaml.Unmarshal([]byte(manifestDoc), &manifest)
@@ -173,24 +226,41 @@ c: [d]
count: 1
- template: t.yaml
matchSnapshot: {}
+- template: t.yaml
+ isSubset:
+ path: e
+ content:
+ f: g
`
- assertions := make([]Assertion, 13)
- err := yaml.Unmarshal([]byte(assertionsYAML), &assertions)
-
- a := assert.New(t)
- a.Nil(err)
+ validateSucceededTestAssertions(t, assertionsYAML, 14, renderedMap)
+}
- for idx, assertion := range assertions {
- result := assertion.Assert(renderedMap, fakeSnapshotComparer(true), &AssertionResult{Index: idx})
- a.Equal(&AssertionResult{
- Index: idx,
- FailInfo: []string{},
- Passed: true,
- AssertType: assertion.AssertType,
- Not: false,
- CustomInfo: "",
- }, result)
+func TestAssertionRawAssertWhenOk(t *testing.T) {
+ manifest := common.K8sManifest{common.RAW: "NOTES.txt"}
+ renderedMap := map[string][]common.K8sManifest{
+ "t.yaml": {manifest},
}
+
+ assertionsYAML := `
+- template: t.yaml
+ equalRaw:
+ value: NOTES.txt
+- template: t.yaml
+ notEqualRaw:
+ value: UNNOTES.txt
+- template: t.yaml
+ matchRegexRaw:
+ pattern: NOTES.txt
+- template: t.yaml
+ notMatchRegexRaw:
+ pattern: UNNOTES.txt
+- template: t.yaml
+ hasDocuments:
+ count: 1
+- template: t.yaml
+ matchSnapshot: {}
+`
+ validateSucceededTestAssertions(t, assertionsYAML, 5, renderedMap)
}
func TestAssertionAssertWhenTemplateNotExisted(t *testing.T) {
diff --git a/unittest/common/types.go b/unittest/common/types.go
index 2acd701a7..768a32cc6 100644
--- a/unittest/common/types.go
+++ b/unittest/common/types.go
@@ -2,3 +2,5 @@ package common
// K8sManifest type for rendered manifest unmarshaled to
type K8sManifest map[string]interface{}
+
+const RAW string = "raw"
diff --git a/unittest/test_job.go b/unittest/test_job.go
index a6e660f87..8bc0a5ec2 100644
--- a/unittest/test_job.go
+++ b/unittest/test_job.go
@@ -261,9 +261,9 @@ func (t *TestJob) parseManifestsFromOutputOfFiles(outputOfFiles map[string]strin
manifestsOfFiles := make(map[string][]common.K8sManifest)
for file, rendered := range outputOfFiles {
- decoder := yaml.NewDecoder(strings.NewReader(rendered))
if filepath.Ext(file) == ".yaml" {
+ decoder := yaml.NewDecoder(strings.NewReader(rendered))
manifests := make([]common.K8sManifest, 0)
for {
@@ -283,6 +283,17 @@ func (t *TestJob) parseManifestsFromOutputOfFiles(outputOfFiles map[string]strin
manifestsOfFiles[file] = manifests
}
+
+ if filepath.Ext(file) == ".txt" {
+ manifests := make([]common.K8sManifest, 0)
+ manifest := make(common.K8sManifest)
+ manifest[common.RAW] = rendered
+
+ if len(manifest) > 0 {
+ manifests = append(manifests, manifest)
+ }
+ manifestsOfFiles[file] = manifests
+ }
}
return manifestsOfFiles, nil
diff --git a/unittest/test_job_test.go b/unittest/test_job_test.go
index 0a9e4f2e4..9c9e8e4c5 100644
--- a/unittest/test_job_test.go
+++ b/unittest/test_job_test.go
@@ -89,6 +89,34 @@ asserts:
a.Equal(2, len(testResult.AssertsResult))
}
+func TestV2RunJobWithNOTESTemplateOk(t *testing.T) {
+ c, _ := v2util.Load("../__fixtures__/v2/basic")
+ manifest := `
+it: should work
+template: NOTES.txt
+asserts:
+ - equalRaw:
+ value: |
+ 1. Get the application URL by running these commands:
+ export POD_NAME=$(kubectl get pods --namespace NAMESPACE -l "app=basic,release=RELEASE-NAME" -o jsonpath="{.items[0].metadata.name}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl port-forward $POD_NAME 8080:80
+ - matchRegexRaw:
+ pattern: ^1. Get the application URL by running these commands
+`
+ var tj TestJob
+ yaml.Unmarshal([]byte(manifest), &tj)
+
+ testResult := tj.RunV2(c, &snapshot.Cache{}, &TestJobResult{})
+
+ a := assert.New(t)
+ cupaloy.SnapshotT(t, makeTestJobResultSnapshotable(testResult))
+
+ a.Nil(testResult.ExecError)
+ a.True(testResult.Passed)
+ a.Equal(2, len(testResult.AssertsResult))
+}
+
func TestV2RunJobWithTestJobTemplateOk(t *testing.T) {
c, _ := v2util.Load("../__fixtures__/v2/basic")
manifest := `
diff --git a/unittest/test_runner.go b/unittest/test_runner.go
index 61520d8d8..b1a4aeb96 100644
--- a/unittest/test_runner.go
+++ b/unittest/test_runner.go
@@ -155,7 +155,6 @@ func (tr *TestRunner) getTestSuites(chartPath, chartRoute string) ([]*TestSuite,
FilePath: file,
ExecError: err,
})
- continue
}
resultSuites = append(resultSuites, suite)
}
@@ -221,6 +220,7 @@ func (tr *TestRunner) runV2SuitesOfChart(suites []*TestSuite, chart *v2chart.Cha
FilePath: suite.definitionFile,
ExecError: err,
})
+ chartPassed = false
continue
}
@@ -245,6 +245,7 @@ func (tr *TestRunner) runV3SuitesOfChart(suites []*TestSuite, chart *v3chart.Cha
FilePath: suite.definitionFile,
ExecError: err,
})
+ chartPassed = false
continue
}
diff --git a/unittest/test_runner_test.go b/unittest/test_runner_test.go
index 43a9e0ca9..f58c62f4d 100644
--- a/unittest/test_runner_test.go
+++ b/unittest/test_runner_test.go
@@ -65,7 +65,7 @@ func TestV2RunnerOkWithPassedTests(t *testing.T) {
},
}
passed := runner.RunV2([]string{"../__fixtures__/v2/basic"})
- assert.True(t, passed)
+ assert.True(t, passed, buffer.String())
cupaloy.SnapshotT(t, makeOutputSnapshotable(buffer.String())...)
}
@@ -78,7 +78,20 @@ func TestV2RunnerOkWithFailedTests(t *testing.T) {
},
}
passed := runner.RunV2([]string{"../__fixtures__/v2/basic"})
- assert.False(t, passed)
+ assert.False(t, passed, buffer.String())
+ cupaloy.SnapshotT(t, makeOutputSnapshotable(buffer.String())...)
+}
+
+func TestV2RunnerOkWithSubSubfolder(t *testing.T) {
+ buffer := new(bytes.Buffer)
+ runner := TestRunner{
+ Printer: NewPrinter(buffer, nil),
+ Config: TestConfig{
+ TestFiles: []string{"tests/*_test.yaml"},
+ },
+ }
+ passed := runner.RunV2([]string{"../__fixtures__/v2/with-subfolder"})
+ assert.True(t, passed, buffer.String())
cupaloy.SnapshotT(t, makeOutputSnapshotable(buffer.String())...)
}
@@ -92,7 +105,7 @@ func TestV2RunnerWithTestsInSubchart(t *testing.T) {
},
}
passed := runner.RunV2([]string{"../__fixtures__/v2/with-subchart"})
- assert.True(t, passed)
+ assert.True(t, passed, buffer.String())
cupaloy.SnapshotT(t, makeOutputSnapshotable(buffer.String())...)
}
@@ -106,7 +119,7 @@ func TestV2RunnerWithTestsInSubchartButFlagFalse(t *testing.T) {
},
}
passed := runner.RunV2([]string{"../__fixtures__/v2/with-subchart"})
- assert.True(t, passed)
+ assert.True(t, passed, buffer.String())
cupaloy.SnapshotT(t, makeOutputSnapshotable(buffer.String())...)
}
@@ -119,7 +132,7 @@ func TestV3RunnerOkWithPassedTests(t *testing.T) {
},
}
passed := runner.RunV3([]string{"../__fixtures__/v3/basic"})
- assert.True(t, passed)
+ assert.True(t, passed, buffer.String())
cupaloy.SnapshotT(t, makeOutputSnapshotable(buffer.String())...)
}
@@ -132,7 +145,20 @@ func TestV3RunnerOkWithFailedTests(t *testing.T) {
},
}
passed := runner.RunV3([]string{"../__fixtures__/v3/basic"})
- assert.False(t, passed)
+ assert.False(t, passed, buffer.String())
+ cupaloy.SnapshotT(t, makeOutputSnapshotable(buffer.String())...)
+}
+
+func TestV3RunnerOkWithSubSubfolder(t *testing.T) {
+ buffer := new(bytes.Buffer)
+ runner := TestRunner{
+ Printer: NewPrinter(buffer, nil),
+ Config: TestConfig{
+ TestFiles: []string{"tests/*_test.yaml"},
+ },
+ }
+ passed := runner.RunV3([]string{"../__fixtures__/v3/with-subfolder"})
+ assert.True(t, passed, buffer.String())
cupaloy.SnapshotT(t, makeOutputSnapshotable(buffer.String())...)
}
@@ -146,7 +172,7 @@ func TestV3RunnerWithTestsInSubchart(t *testing.T) {
},
}
passed := runner.RunV3([]string{"../__fixtures__/v3/with-subchart"})
- assert.True(t, passed)
+ assert.True(t, passed, buffer.String())
cupaloy.SnapshotT(t, makeOutputSnapshotable(buffer.String())...)
}
@@ -160,6 +186,6 @@ func TestV3RunnerWithTestsInSubchartButFlagFalse(t *testing.T) {
},
}
passed := runner.RunV3([]string{"../__fixtures__/v3/with-subchart"})
- assert.True(t, passed)
+ assert.True(t, passed, buffer.String())
cupaloy.SnapshotT(t, makeOutputSnapshotable(buffer.String())...)
}
diff --git a/unittest/test_suite.go b/unittest/test_suite.go
index 70390f75a..abe1478d5 100644
--- a/unittest/test_suite.go
+++ b/unittest/test_suite.go
@@ -127,7 +127,9 @@ func (s *TestSuite) prepareV2Chart(targetChart *v2chart.Chart) (*v2chart.Chart,
for _, fileName := range s.Templates {
found := false
for _, template := range targetChart.Templates {
- if filepath.Base(template.Name) == fileName {
+ // Within templates unix separators are always used.
+ relativeFilePath := strings.Join([]string{"templates", fileName}, "/")
+ if template.Name == relativeFilePath {
filteredTemplate = append(filteredTemplate, template)
found = true
break
@@ -169,7 +171,9 @@ func (s *TestSuite) prepareV3Chart(targetChart *v3chart.Chart) (*v3chart.Chart,
for _, fileName := range s.Templates {
found := false
for _, template := range targetChart.Templates {
- if filepath.Base(template.Name) == fileName {
+ // Within templates unix separators are always used.
+ relativeFilePath := strings.Join([]string{"templates", fileName}, "/")
+ if template.Name == relativeFilePath {
filteredTemplate = append(filteredTemplate, template)
found = true
break
diff --git a/unittest/test_suite_test.go b/unittest/test_suite_test.go
index 256ec3669..0138b60f4 100644
--- a/unittest/test_suite_test.go
+++ b/unittest/test_suite_test.go
@@ -53,9 +53,20 @@ func TestV2ParseTestSuiteFileOk(t *testing.T) {
suite, err := ParseTestSuiteFile("../__fixtures__/v2/basic/tests/deployment_test.yaml", "basic")
a.Nil(err)
- a.Equal(suite.Name, "test deployment")
- a.Equal(suite.Templates, []string{"deployment.yaml"})
- a.Equal(suite.Tests[0].Name, "should pass all kinds of assertion")
+ a.Equal("test deployment", suite.Name)
+ a.Equal([]string{"deployment.yaml"}, suite.Templates)
+ a.Equal("should pass all kinds of assertion", suite.Tests[0].Name)
+}
+
+func TestV2ParseTestSuiteFileInSubfolderOk(t *testing.T) {
+ a := assert.New(t)
+ suite, err := ParseTestSuiteFile("../__fixtures__/v2/with-subfolder/tests/service_test.yaml", "with-subfolder")
+
+ a.Nil(err)
+ a.Equal("test service", suite.Name)
+ a.Equal([]string{"webserver/service.yaml"}, suite.Templates)
+ a.Equal("should pass", suite.Tests[0].Name)
+ a.Equal("should render right if values given", suite.Tests[1].Name)
}
func TestV2RunSuiteWithMultipleTemplatesWhenPass(t *testing.T) {
@@ -142,6 +153,30 @@ tests:
validateTestResultAndSnapshots(t, suiteResult, false, "test suite name", 1, 0, 0, 0, 0)
}
+func TestV2RunSuiteWithSubfolderWhenPass(t *testing.T) {
+ c, _ := v2util.Load("../__fixtures__/v2/with-subfolder")
+ suiteDoc := `
+suite: test suite name
+templates:
+ - db/deployment.yaml
+ - webserver/deployment.yaml
+tests:
+ - it: should pass
+ asserts:
+ - equal:
+ path: kind
+ value: Deployment
+ - matchSnapshot: {}
+`
+ testSuite := TestSuite{}
+ yaml.Unmarshal([]byte(suiteDoc), &testSuite)
+
+ cache, _ := snapshot.CreateSnapshotOfSuite(path.Join(tmpdir, "my_test.yaml"), false)
+ suiteResult := testSuite.RunV2(c, cache, &TestSuiteResult{})
+
+ validateTestResultAndSnapshots(t, suiteResult, true, "test suite name", 1, 2, 2, 0, 0)
+}
+
func TestV3ParseTestSuiteFileOk(t *testing.T) {
a := assert.New(t)
suite, err := ParseTestSuiteFile("../__fixtures__/v3/basic/tests/deployment_test.yaml", "basic")
@@ -235,3 +270,27 @@ tests:
validateTestResultAndSnapshots(t, suiteResult, false, "test suite name", 1, 0, 0, 0, 0)
}
+
+func TestV3RunSuiteWithSubfolderWhenPass(t *testing.T) {
+ c, _ := loader.Load("../__fixtures__/v3/with-subfolder")
+ suiteDoc := `
+suite: test suite name
+templates:
+ - db/deployment.yaml
+ - webserver/deployment.yaml
+tests:
+ - it: should pass
+ asserts:
+ - equal:
+ path: kind
+ value: Deployment
+ - matchSnapshot: {}
+`
+ testSuite := TestSuite{}
+ yaml.Unmarshal([]byte(suiteDoc), &testSuite)
+
+ cache, _ := snapshot.CreateSnapshotOfSuite(path.Join(tmpdir, "my_test.yaml"), false)
+ suiteResult := testSuite.RunV3(c, cache, &TestSuiteResult{})
+
+ validateTestResultAndSnapshots(t, suiteResult, true, "test suite name", 1, 2, 2, 0, 0)
+}
diff --git a/unittest/validators/common.go b/unittest/validators/common.go
index 65dffa4db..4cb397954 100644
--- a/unittest/validators/common.go
+++ b/unittest/validators/common.go
@@ -79,6 +79,13 @@ func diff(expected string, actual string) string {
return diff
}
+// uniform the content with correct line-endings
+func uniformContent(content interface{}) string {
+ // All decoded content uses LF
+ actual := fmt.Sprintf("%v", content)
+ return strings.ReplaceAll(actual, "\r\n", "\n")
+}
+
const errorFormat = `
Error:
%s
diff --git a/unittest/validators/common_test.go b/unittest/validators/common_test.go
index 3806a6dac..a98102898 100644
--- a/unittest/validators/common_test.go
+++ b/unittest/validators/common_test.go
@@ -2,6 +2,8 @@ package validators_test
import (
"github.com/lrills/helm-unittest/unittest/common"
+ "github.com/lrills/helm-unittest/unittest/snapshot"
+ "github.com/stretchr/testify/mock"
yaml "gopkg.in/yaml.v2"
)
@@ -10,3 +12,12 @@ func makeManifest(doc string) common.K8sManifest {
yaml.Unmarshal([]byte(doc), &manifest)
return manifest
}
+
+type mockSnapshotComparer struct {
+ mock.Mock
+}
+
+func (m *mockSnapshotComparer) CompareToSnapshot(content interface{}) *snapshot.CompareResult {
+ args := m.Called(content)
+ return args.Get(0).(*snapshot.CompareResult)
+}
diff --git a/unittest/validators/contains_validator.go b/unittest/validators/contains_validator.go
index c25c036b2..81590826e 100644
--- a/unittest/validators/contains_validator.go
+++ b/unittest/validators/contains_validator.go
@@ -13,6 +13,8 @@ import (
type ContainsValidator struct {
Path string
Content interface{}
+ Count *int
+ Any bool
}
func (v ContainsValidator) failInfo(actual interface{}, index int, not bool) []string {
@@ -36,6 +38,33 @@ Actual:
)
}
+func (v ContainsValidator) validateContent(actual []interface{}) (bool, int) {
+ found := false
+ validateFoundCount := 0
+
+ for _, ele := range actual {
+ // When any enabled, only the key is validated
+ if v.Any {
+ if subset, ok := ele.(map[interface{}]interface{}); ok {
+ for key, value := range subset {
+ ele := map[interface{}]interface{}{key: value}
+ if reflect.DeepEqual(ele, v.Content) {
+ found = true
+ validateFoundCount++
+ }
+ }
+ }
+ }
+
+ if !v.Any && reflect.DeepEqual(ele, v.Content) {
+ found = true
+ validateFoundCount++
+ }
+ }
+
+ return found, validateFoundCount
+}
+
// Validate implement Validatable
func (v ContainsValidator) Validate(context *ValidateContext) (bool, []string) {
manifests, err := context.getManifests()
@@ -56,20 +85,30 @@ func (v ContainsValidator) Validate(context *ValidateContext) (bool, []string) {
}
if actual, ok := actual.([]interface{}); ok {
- found := false
- for _, ele := range actual {
- if reflect.DeepEqual(ele, v.Content) {
- found = true
- }
- }
- if found == context.Negative {
+ found, validateFoundCount := v.validateContent(actual)
+
+ if v.Count == nil && found == context.Negative {
validateSuccess = validateSuccess && false
errorMessage := v.failInfo(actual, idx, context.Negative)
validateErrors = append(validateErrors, errorMessage...)
continue
}
+ if v.Count != nil && *v.Count != validateFoundCount && found == !context.Negative {
+ actualYAML, _ := yaml.Marshal(actual)
+ validateSuccess = validateSuccess && false
+ errorMessage := splitInfof(errorFormat, idx, fmt.Sprintf(
+ "expect count %d in '%s' to be in array, got %d:\n%s",
+ *v.Count,
+ v.Path,
+ validateFoundCount,
+ string(actualYAML),
+ ))
+ validateErrors = append(validateErrors, errorMessage...)
+ continue
+ }
+
validateSuccess = validateSuccess && true
continue
}
diff --git a/unittest/validators/contains_validator_test.go b/unittest/validators/contains_validator_test.go
index a415994e4..c59ba61de 100644
--- a/unittest/validators/contains_validator_test.go
+++ b/unittest/validators/contains_validator_test.go
@@ -14,6 +14,8 @@ a:
b:
- c: hello world
- d: foo bar
+ - e: bar
+ - e: bar
`
func TestContainsValidatorWhenOk(t *testing.T) {
@@ -22,6 +24,8 @@ func TestContainsValidatorWhenOk(t *testing.T) {
validator := ContainsValidator{
"a.b",
map[interface{}]interface{}{"d": "foo bar"},
+ nil,
+ false,
}
pass, diff := validator.Validate(&ValidateContext{
Docs: []common.K8sManifest{manifest},
@@ -31,10 +35,75 @@ func TestContainsValidatorWhenOk(t *testing.T) {
assert.Equal(t, []string{}, diff)
}
+func TestContainsValidatorWithAnyWhenOk(t *testing.T) {
+ docToTestContainsAny := `
+a:
+ b:
+ - name: VALUE1
+ value: bla
+ - name: VALUE2
+ value: bla2
+`
+ manifest := makeManifest(docToTestContainsAny)
+
+ validator := ContainsValidator{
+ "a.b",
+ map[interface{}]interface{}{"name": "VALUE1"},
+ nil,
+ true,
+ }
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+}
+
+func TestContainsValidatorWithAnyWhenNotFoundOk(t *testing.T) {
+ docToTestContainsAny := `
+a:
+ b:
+ - name: VALUE1
+ value: bla
+ - name: VALUE2
+ value: bla2
+`
+ manifest := makeManifest(docToTestContainsAny)
+
+ validator := ContainsValidator{
+ "a.b",
+ map[interface{}]interface{}{"name": "VALUE3"},
+ nil,
+ true,
+ }
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "DocumentIndex: 0",
+ "Path: a.b",
+ "Expected to contain:",
+ " - name: VALUE3",
+ "Actual:",
+ " - name: VALUE1",
+ " value: bla",
+ " - name: VALUE2",
+ " value: bla2",
+ }, diff)
+}
+
func TestContainsValidatorWhenNegativeAndOk(t *testing.T) {
manifest := makeManifest(docToTestContains)
- validator := ContainsValidator{"a.b", map[interface{}]interface{}{"d": "hello bar"}}
+ validator := ContainsValidator{
+ "a.b",
+ map[interface{}]interface{}{"d": "hello bar"},
+ nil,
+ false,
+ }
pass, diff := validator.Validate(&ValidateContext{
Docs: []common.K8sManifest{manifest},
Negative: true,
@@ -50,6 +119,8 @@ func TestContainsValidatorWhenFail(t *testing.T) {
validator := ContainsValidator{
"a.b",
map[interface{}]interface{}{"e": "bar bar"},
+ nil,
+ false,
}
pass, diff := validator.Validate(&ValidateContext{
Docs: []common.K8sManifest{manifest},
@@ -64,22 +135,26 @@ func TestContainsValidatorWhenFail(t *testing.T) {
"Actual:",
" - c: hello world",
" - d: foo bar",
+ " - e: bar",
+ " - e: bar",
}, diff)
}
func TestContainsValidatorMultiManifestWhenFail(t *testing.T) {
manifest1 := makeManifest(docToTestContains)
- var docToTestContains = `
+ extraDoc := `
a:
b:
- c: hello world
`
- manifest2 := makeManifest(docToTestContains)
+ manifest2 := makeManifest(extraDoc)
manifests := []common.K8sManifest{manifest1, manifest2}
validator := ContainsValidator{
"a.b",
map[interface{}]interface{}{"d": "foo bar"},
+ nil,
+ false,
}
pass, diff := validator.Validate(&ValidateContext{
Docs: manifests,
@@ -104,6 +179,8 @@ func TestContainsValidatorMultiManifestWhenBothFail(t *testing.T) {
validator := ContainsValidator{
"a.b",
map[interface{}]interface{}{"e": "foo bar"},
+ nil,
+ false,
}
pass, diff := validator.Validate(&ValidateContext{
Docs: manifests,
@@ -119,6 +196,8 @@ func TestContainsValidatorMultiManifestWhenBothFail(t *testing.T) {
"Actual:",
" - c: hello world",
" - d: foo bar",
+ " - e: bar",
+ " - e: bar",
"DocumentIndex: 1",
"Path: a.b",
"Expected to contain:",
@@ -126,6 +205,8 @@ func TestContainsValidatorMultiManifestWhenBothFail(t *testing.T) {
"Actual:",
" - c: hello world",
" - d: foo bar",
+ " - e: bar",
+ " - e: bar",
}, diff)
}
@@ -135,6 +216,8 @@ func TestContainsValidatorWhenNegativeAndFail(t *testing.T) {
validator := ContainsValidator{
"a.b",
map[interface{}]interface{}{"d": "foo bar"},
+ nil,
+ false,
}
pass, diff := validator.Validate(&ValidateContext{
Docs: []common.K8sManifest{manifest},
@@ -150,6 +233,8 @@ func TestContainsValidatorWhenNegativeAndFail(t *testing.T) {
"Actual:",
" - c: hello world",
" - d: foo bar",
+ " - e: bar",
+ " - e: bar",
}, diff)
}
@@ -162,7 +247,12 @@ a:
`
manifest := makeManifest(manifestDocNotArray)
- validator := ContainsValidator{"a.b", common.K8sManifest{"d": "foo bar"}}
+ validator := ContainsValidator{
+ "a.b",
+ common.K8sManifest{"d": "foo bar"},
+ nil,
+ false,
+ }
pass, diff := validator.Validate(&ValidateContext{
Docs: []common.K8sManifest{manifest},
})
@@ -180,7 +270,12 @@ a:
func TestContainsValidatorWhenInvalidIndex(t *testing.T) {
manifest := makeManifest(docToTestContains)
- validator := ContainsValidator{"a.b", common.K8sManifest{"d": "foo bar"}}
+ validator := ContainsValidator{
+ "a.b",
+ common.K8sManifest{"d": "foo bar"},
+ nil,
+ false,
+ }
pass, diff := validator.Validate(&ValidateContext{
Docs: []common.K8sManifest{manifest},
Index: 2,
@@ -196,7 +291,12 @@ func TestContainsValidatorWhenInvalidIndex(t *testing.T) {
func TestContainsValidatorWhenInvalidPath(t *testing.T) {
manifest := makeManifest(docToTestContains)
- validator := ContainsValidator{"a.b.e", common.K8sManifest{"d": "foo bar"}}
+ validator := ContainsValidator{
+ "a.b.e",
+ common.K8sManifest{"e": "bar"},
+ nil,
+ false,
+ }
pass, diff := validator.Validate(&ValidateContext{
Docs: []common.K8sManifest{manifest},
})
@@ -208,5 +308,73 @@ func TestContainsValidatorWhenInvalidPath(t *testing.T) {
" can't get [\"e\"] from a non map type:",
" - c: hello world",
" - d: foo bar",
+ " - e: bar",
+ " - e: bar",
+ }, diff)
+}
+
+func TestContainsValidatorWhenMultipleTimesInArray(t *testing.T) {
+ manifest := makeManifest(docToTestContains)
+
+ counter := new(int)
+ *counter = 2
+ validator := ContainsValidator{
+ "a.b",
+ map[interface{}]interface{}{"e": "bar"},
+ counter,
+ false,
+ }
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+}
+
+func TestContainsValidatorInverseWhenNotMultipleTimesInArray(t *testing.T) {
+ manifest := makeManifest(docToTestContains)
+
+ counter := new(int)
+ *counter = 1
+ validator := ContainsValidator{
+ "a.b",
+ map[interface{}]interface{}{"e": "bar"},
+ counter,
+ false,
+ }
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ Negative: true,
+ })
+
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+}
+
+func TestContainsValidatorWhenNotMultipleTimesInArray(t *testing.T) {
+ manifest := makeManifest(docToTestContains)
+
+ counter := new(int)
+ *counter = 1
+ validator := ContainsValidator{
+ "a.b",
+ map[interface{}]interface{}{"e": "bar"},
+ counter,
+ false,
+ }
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "DocumentIndex: 0",
+ "Error:",
+ " expect count 1 in 'a.b' to be in array, got 2:",
+ " - c: hello world",
+ " - d: foo bar",
+ " - e: bar",
+ " - e: bar",
}, diff)
}
diff --git a/unittest/validators/equal_raw_validator.go b/unittest/validators/equal_raw_validator.go
new file mode 100644
index 000000000..4e7394b25
--- /dev/null
+++ b/unittest/validators/equal_raw_validator.go
@@ -0,0 +1,67 @@
+package validators
+
+import (
+ "reflect"
+
+ "github.com/lrills/helm-unittest/unittest/common"
+)
+
+// EqualRawValidator validate whether the raw value equal to Value
+type EqualRawValidator struct {
+ Value string
+}
+
+func (a EqualRawValidator) failInfo(actual interface{}, not bool) []string {
+ var notAnnotation string
+ if not {
+ notAnnotation = " NOT to equal"
+ }
+ failFormat := `
+Expected` + notAnnotation + `:
+%s`
+
+ expectedYAML := common.TrustedMarshalYAML(a.Value)
+ if not {
+ return splitInfof(failFormat, -1, expectedYAML)
+ }
+
+ actualYAML := common.TrustedMarshalYAML(actual)
+ return splitInfof(
+ failFormat+`
+Actual:
+%s
+Diff:
+%s
+`,
+ -1,
+ expectedYAML,
+ actualYAML,
+ diff(expectedYAML, actualYAML),
+ )
+}
+
+// Validate implement Validatable
+func (a EqualRawValidator) Validate(context *ValidateContext) (bool, []string) {
+ manifests, err := context.getManifests()
+ if err != nil {
+ return false, splitInfof(errorFormat, -1, err.Error())
+ }
+
+ validateSuccess := true
+ validateErrors := make([]string, 0)
+
+ for _, manifest := range manifests {
+ actual := uniformContent(manifest[common.RAW])
+
+ if reflect.DeepEqual(a.Value, actual) == context.Negative {
+ validateSuccess = validateSuccess && false
+ errorMessage := a.failInfo(actual, context.Negative)
+ validateErrors = append(validateErrors, errorMessage...)
+ continue
+ }
+
+ validateSuccess = validateSuccess && true
+ }
+
+ return validateSuccess, validateErrors
+}
diff --git a/unittest/validators/equal_raw_validator_test.go b/unittest/validators/equal_raw_validator_test.go
new file mode 100644
index 000000000..5ccac43da
--- /dev/null
+++ b/unittest/validators/equal_raw_validator_test.go
@@ -0,0 +1,93 @@
+package validators_test
+
+import (
+ "testing"
+
+ . "github.com/lrills/helm-unittest/unittest/validators"
+
+ "github.com/lrills/helm-unittest/unittest/common"
+ "github.com/stretchr/testify/assert"
+)
+
+var docToTestEqualRaw = `
+raw: This is a NOTES.txt document.
+`
+
+func TestEqualRawValidatorWhenOk(t *testing.T) {
+ manifest := makeManifest(docToTestEqualRaw)
+ validator := EqualRawValidator{"This is a NOTES.txt document."}
+
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+}
+
+func TestEqualRawValidatorWhenNegativeAndOk(t *testing.T) {
+ manifest := makeManifest(docToTestEqualRaw)
+
+ validator := EqualRawValidator{"Invalid text."}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ Negative: true,
+ })
+
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+}
+
+func TestEqualRawValidatorWhenFail(t *testing.T) {
+ manifest := makeManifest(docToTestEqualRaw)
+
+ validator := EqualRawValidator{"Invalid text."}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Expected:",
+ " Invalid text.",
+ "Actual:",
+ " This is a NOTES.txt document.",
+ "Diff:",
+ " --- Expected",
+ " +++ Actual",
+ " @@ -1,2 +1,2 @@",
+ " -Invalid text.",
+ " +This is a NOTES.txt document.",
+ }, diff)
+}
+
+func TestEqualRawValidatorWhenNegativeAndFail(t *testing.T) {
+ manifest := makeManifest(docToTestEqualRaw)
+
+ v := EqualRawValidator{"This is a NOTES.txt document."}
+ pass, diff := v.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ Negative: true,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Expected NOT to equal:",
+ " This is a NOTES.txt document.",
+ }, diff)
+}
+
+func TestEqualRawValidatorWhenInvalidIndex(t *testing.T) {
+ manifest := makeManifest(docToTestEqualRaw)
+ validator := EqualRawValidator{"Invalid text."}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ Index: 2,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Error:",
+ " documentIndex 2 out of range",
+ }, diff)
+}
diff --git a/unittest/validators/is_subset_validator.go b/unittest/validators/is_subset_validator.go
new file mode 100644
index 000000000..4df14de12
--- /dev/null
+++ b/unittest/validators/is_subset_validator.go
@@ -0,0 +1,90 @@
+package validators
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/lrills/helm-unittest/unittest/common"
+ "github.com/lrills/helm-unittest/unittest/valueutils"
+ yaml "gopkg.in/yaml.v2"
+)
+
+// IsSubsetValidator validate whether value of Path contains Content
+type IsSubsetValidator struct {
+ Path string
+ Content interface{}
+}
+
+func (v IsSubsetValidator) failInfo(actual interface{}, index int, not bool) []string {
+ var notAnnotation string
+ if not {
+ notAnnotation = " NOT"
+ }
+ containsFailFormat := `
+Path:%s
+Expected` + notAnnotation + ` to contain:
+%s
+Actual:
+%s
+`
+ return splitInfof(
+ containsFailFormat,
+ index,
+ v.Path,
+ common.TrustedMarshalYAML(v.Content),
+ common.TrustedMarshalYAML(actual),
+ )
+}
+
+// Validate implement Validatable
+func (v IsSubsetValidator) Validate(context *ValidateContext) (bool, []string) {
+ manifests, err := context.getManifests()
+ if err != nil {
+ return false, splitInfof(errorFormat, -1, err.Error())
+ }
+
+ validateSuccess := true
+ validateErrors := make([]string, 0)
+
+ for idx, manifest := range manifests {
+ actual, err := valueutils.GetValueOfSetPath(manifest, v.Path)
+ if err != nil {
+ validateSuccess = validateSuccess && false
+ errorMessage := splitInfof(errorFormat, idx, err.Error())
+ validateErrors = append(validateErrors, errorMessage...)
+ continue
+ }
+
+ if actual, ok := actual.(map[interface{}]interface{}); ok {
+ found := false
+
+ for key, value := range actual {
+ ele := map[interface{}]interface{}{key: value}
+ if reflect.DeepEqual(ele, v.Content) {
+ found = true
+ }
+ }
+
+ if found == context.Negative {
+ validateSuccess = validateSuccess && false
+ errorMessage := v.failInfo(actual, idx, context.Negative)
+ validateErrors = append(validateErrors, errorMessage...)
+ continue
+ }
+
+ validateSuccess = validateSuccess && true
+ continue
+ }
+
+ actualYAML, _ := yaml.Marshal(actual)
+ validateSuccess = validateSuccess && false
+ errorMessage := splitInfof(errorFormat, idx, fmt.Sprintf(
+ "expect '%s' to be an object, got:\n%s",
+ v.Path,
+ string(actualYAML),
+ ))
+ validateErrors = append(validateErrors, errorMessage...)
+ }
+
+ return validateSuccess, validateErrors
+}
diff --git a/unittest/validators/is_subset_validator_test.go b/unittest/validators/is_subset_validator_test.go
new file mode 100644
index 000000000..97b04e830
--- /dev/null
+++ b/unittest/validators/is_subset_validator_test.go
@@ -0,0 +1,212 @@
+package validators_test
+
+import (
+ "testing"
+
+ . "github.com/lrills/helm-unittest/unittest/validators"
+
+ "github.com/lrills/helm-unittest/unittest/common"
+ "github.com/stretchr/testify/assert"
+)
+
+var docToTestIsSubset = `
+a:
+ b:
+ c: hello world
+ d: foo bar
+`
+
+func TestIsSubsetValidatorWhenOk(t *testing.T) {
+ manifest := makeManifest(docToTestIsSubset)
+
+ validator := IsSubsetValidator{
+ "a.b",
+ map[interface{}]interface{}{"d": "foo bar"}}
+
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+}
+
+func TestIsSubsetValidatorWhenNegativeAndOk(t *testing.T) {
+ manifest := makeManifest(docToTestIsSubset)
+
+ validator := IsSubsetValidator{
+ "a.b",
+ map[interface{}]interface{}{"d": "hello bar"}}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ Negative: true,
+ })
+
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+}
+
+func TestIsSubsetValidatorWhenFail(t *testing.T) {
+ manifest := makeManifest(docToTestIsSubset)
+
+ validator := IsSubsetValidator{
+ "a.b",
+ map[interface{}]interface{}{"e": "bar bar"},
+ }
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "DocumentIndex: 0",
+ "Path: a.b",
+ "Expected to contain:",
+ " e: bar bar",
+ "Actual:",
+ " c: hello world",
+ " d: foo bar",
+ }, diff)
+}
+
+func TestIsSubsetValidatorMultiManifestWhenFail(t *testing.T) {
+ manifest1 := makeManifest(docToTestIsSubset)
+ extraDoc := `
+a:
+ b:
+ c: hello world
+`
+ manifest2 := makeManifest(extraDoc)
+ manifests := []common.K8sManifest{manifest1, manifest2}
+
+ validator := IsSubsetValidator{
+ "a.b",
+ map[interface{}]interface{}{"d": "foo bar"},
+ }
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: manifests,
+ Index: -1,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "DocumentIndex: 1",
+ "Path: a.b",
+ "Expected to contain:",
+ " d: foo bar",
+ "Actual:",
+ " c: hello world",
+ }, diff)
+}
+
+func TestIsSubsetValidatorMultiManifestWhenBothFail(t *testing.T) {
+ manifest1 := makeManifest(docToTestIsSubset)
+ manifests := []common.K8sManifest{manifest1, manifest1}
+
+ validator := IsSubsetValidator{
+ "a.b",
+ map[interface{}]interface{}{"e": "foo bar"},
+ }
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: manifests,
+ Index: -1,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "DocumentIndex: 0",
+ "Path: a.b",
+ "Expected to contain:",
+ " e: foo bar",
+ "Actual:",
+ " c: hello world",
+ " d: foo bar",
+ "DocumentIndex: 1",
+ "Path: a.b",
+ "Expected to contain:",
+ " e: foo bar",
+ "Actual:",
+ " c: hello world",
+ " d: foo bar",
+ }, diff)
+}
+
+func TestIsSubsetValidatorWhenNegativeAndFail(t *testing.T) {
+ manifest := makeManifest(docToTestIsSubset)
+
+ validator := IsSubsetValidator{
+ "a.b",
+ map[interface{}]interface{}{"d": "foo bar"},
+ }
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ Negative: true,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "DocumentIndex: 0",
+ "Path: a.b",
+ "Expected NOT to contain:",
+ " d: foo bar",
+ "Actual:",
+ " c: hello world",
+ " d: foo bar",
+ }, diff)
+}
+
+func TestIsSubsetValidatorWhenInvalidIndex(t *testing.T) {
+ manifest := makeManifest(docToTestIsSubset)
+
+ validator := IsSubsetValidator{"a.b", common.K8sManifest{"d": "foo bar"}}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ Index: 2,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Error:",
+ " documentIndex 2 out of range",
+ }, diff)
+}
+
+func TestIsSubsetValidatorWhenNotAnObject(t *testing.T) {
+ manifestDocNotObject := `
+a:
+ b:
+ c: hello world
+ d: foo bar
+`
+ manifest := makeManifest(manifestDocNotObject)
+
+ validator := IsSubsetValidator{"a.b.c", common.K8sManifest{"d": "foo bar"}}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "DocumentIndex: 0",
+ "Error:",
+ " expect 'a.b.c' to be an object, got:",
+ " hello world",
+ }, diff)
+}
+
+func TestIsSubsetValidatorWhenInvalidPath(t *testing.T) {
+ manifest := makeManifest("a::error")
+
+ validator := IsSubsetValidator{"a.b", common.K8sManifest{"d": "foo bar"}}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "DocumentIndex: 0",
+ "Error:",
+ " can't get [\"b\"] from a non map type:",
+ " null",
+ }, diff)
+}
diff --git a/unittest/validators/match_regex_raw_validator.go b/unittest/validators/match_regex_raw_validator.go
new file mode 100644
index 000000000..f5436131a
--- /dev/null
+++ b/unittest/validators/match_regex_raw_validator.go
@@ -0,0 +1,58 @@
+package validators
+
+import (
+ "regexp"
+
+ "github.com/lrills/helm-unittest/unittest/common"
+)
+
+// MatchRegexRawValidator validate value of Path match Pattern
+type MatchRegexRawValidator struct {
+ Pattern string
+}
+
+func (v MatchRegexRawValidator) failInfo(actual string, not bool) []string {
+ var notAnnotation = ""
+ if not {
+ notAnnotation = " NOT"
+ }
+ regexFailFormat := `
+Expected` + notAnnotation + ` to match:%s
+Actual:%s
+`
+ return splitInfof(regexFailFormat, -1, v.Pattern, actual)
+}
+
+// Validate implement Validatable
+func (v MatchRegexRawValidator) Validate(context *ValidateContext) (bool, []string) {
+ manifests, err := context.getManifests()
+ if err != nil {
+ return false, splitInfof(errorFormat, -1, err.Error())
+ }
+
+ validateSuccess := true
+ validateErrors := make([]string, 0)
+
+ for _, manifest := range manifests {
+ actual := uniformContent(manifest[common.RAW])
+
+ p, err := regexp.Compile(v.Pattern)
+ if err != nil {
+ validateSuccess = validateSuccess && false
+ errorMessage := splitInfof(errorFormat, -1, err.Error())
+ validateErrors = append(validateErrors, errorMessage...)
+ break
+ }
+
+ if p.MatchString(actual) == context.Negative {
+ validateSuccess = validateSuccess && false
+ errorMessage := v.failInfo(actual, context.Negative)
+ validateErrors = append(validateErrors, errorMessage...)
+ continue
+ }
+
+ validateSuccess = validateSuccess && true
+ }
+
+ return validateSuccess, validateErrors
+}
diff --git a/unittest/validators/match_regex_raw_validator_test.go b/unittest/validators/match_regex_raw_validator_test.go
new file mode 100644
index 000000000..009df13b3
--- /dev/null
+++ b/unittest/validators/match_regex_raw_validator_test.go
@@ -0,0 +1,99 @@
+package validators_test
+
+import (
+ "testing"
+
+ . "github.com/lrills/helm-unittest/unittest/validators"
+
+ "github.com/lrills/helm-unittest/unittest/common"
+ "github.com/stretchr/testify/assert"
+)
+
+var docToTestMatchRegexRaw = `
+raw: |
+ This is a NOTES.txt document.
+`
+
+func TestMatchRegexRawValidatorWhenOk(t *testing.T) {
+ manifest := makeManifest(docToTestMatchRegexRaw)
+
+ validator := MatchRegexRawValidator{"^This"}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+}
+
+func TestMatchRegexRawValidatorWhenNegativeAndOk(t *testing.T) {
+ manifest := makeManifest(docToTestMatchRegexRaw)
+
+ validator := MatchRegexRawValidator{"^foo"}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ Negative: true,
+ })
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+}
+
+func TestMatchRegexRawValidatorWhenRegexCompileFail(t *testing.T) {
+ manifest := common.K8sManifest{"raw": ""}
+
+ validator := MatchRegexRawValidator{"+"}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Error:",
+ " error parsing regexp: missing argument to repetition operator: `+`",
+ }, diff)
+}
+
+func TestMatchRegexRawValidatorWhenMatchFail(t *testing.T) {
+ manifest := makeManifest(docToTestMatchRegexRaw)
+
+ validator := MatchRegexRawValidator{"^foo"}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ })
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Expected to match: ^foo",
+ "Actual: This is a NOTES.txt document.",
+ }, diff)
+}
+
+func TestMatchRegexRawValidatorWhenNegativeAndMatchFail(t *testing.T) {
+ manifest := makeManifest(docToTestMatchRegexRaw)
+
+ validator := MatchRegexRawValidator{"^This"}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ Negative: true,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Expected NOT to match: ^This",
+ "Actual: This is a NOTES.txt document.",
+ }, diff)
+}
+
+func TestMatchRegexRawValidatorWhenInvalidIndex(t *testing.T) {
+ manifest := makeManifest(docToTestMatchRegexRaw)
+
+ validator := MatchRegexRawValidator{"^This"}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{manifest},
+ Index: 2,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Error:",
+ " documentIndex 2 out of range",
+ }, diff)
+}
diff --git a/unittest/validators/snapshot_raw_validator.go b/unittest/validators/snapshot_raw_validator.go
new file mode 100644
index 000000000..623e930a8
--- /dev/null
+++ b/unittest/validators/snapshot_raw_validator.go
@@ -0,0 +1,57 @@
+package validators
+
+import (
+ "strconv"
+
+ "github.com/lrills/helm-unittest/unittest/common"
+ "github.com/lrills/helm-unittest/unittest/snapshot"
+)
+
+// MatchSnapshotRawValidator validate snapshot of value of Path the same as cached
+type MatchSnapshotRawValidator struct{}
+
+func (v MatchSnapshotRawValidator) failInfo(compared *snapshot.CompareResult, not bool) []string {
+ var notAnnotation = ""
+ if not {
+ notAnnotation = " NOT"
+ }
+ snapshotFailFormat := `
+Expected` + notAnnotation + ` to match snapshot ` + strconv.Itoa(int(compared.Index)) + `:
+%s
+`
+ var infoToShow string
+ if not {
+ infoToShow = compared.CachedSnapshot
+ } else {
+ infoToShow = diff(compared.CachedSnapshot, compared.NewSnapshot)
+ }
+ return splitInfof(snapshotFailFormat, -1, infoToShow)
+}
+
+// Validate implement Validatable
+func (v MatchSnapshotRawValidator) Validate(context *ValidateContext) (bool, []string) {
+ manifests, err := context.getManifests()
+ if err != nil {
+ return false, splitInfof(errorFormat, -1, err.Error())
+ }
+
+ validateSuccess := true
+ validateErrors := make([]string, 0)
+
+ for _, manifest := range manifests {
+ actual := uniformContent(manifest[common.RAW])
+
+ result := context.CompareToSnapshot(actual)
+
+ if result.Passed == context.Negative {
+ validateSuccess = validateSuccess && false
+ errorMessage := v.failInfo(result, context.Negative)
+ validateErrors = append(validateErrors, errorMessage...)
+ continue
+ }
+
+ validateSuccess = validateSuccess == true
+ }
+
+ return validateSuccess, validateErrors
+}
diff --git a/unittest/validators/snapshot_raw_validator_test.go b/unittest/validators/snapshot_raw_validator_test.go
new file mode 100644
index 000000000..5e3e9b165
--- /dev/null
+++ b/unittest/validators/snapshot_raw_validator_test.go
@@ -0,0 +1,125 @@
+package validators_test
+
+import (
+ "testing"
+
+ "github.com/lrills/helm-unittest/unittest/common"
+ "github.com/lrills/helm-unittest/unittest/snapshot"
+ . "github.com/lrills/helm-unittest/unittest/validators"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSnapshotRawValidatorWhenOk(t *testing.T) {
+ data := common.K8sManifest{common.RAW: "b"}
+ validator := MatchSnapshotRawValidator{}
+
+ mockComparer := new(mockSnapshotComparer)
+ mockComparer.On("CompareToSnapshot", "b").Return(&snapshot.CompareResult{
+ Passed: true,
+ })
+
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{data},
+ SnapshotComparer: mockComparer,
+ })
+
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+
+ mockComparer.AssertExpectations(t)
+}
+
+func TestSnapshotRawValidatorWhenNegativeAndOk(t *testing.T) {
+ data := common.K8sManifest{common.RAW: "b"}
+ validator := MatchSnapshotRawValidator{}
+
+ mockComparer := new(mockSnapshotComparer)
+ mockComparer.On("CompareToSnapshot", "b").Return(&snapshot.CompareResult{
+ Passed: false,
+ CachedSnapshot: "b\n",
+ NewSnapshot: "x\n",
+ })
+
+ pass, diff := validator.Validate(&ValidateContext{
+ Negative: true,
+ Docs: []common.K8sManifest{data},
+ SnapshotComparer: mockComparer,
+ })
+
+ assert.True(t, pass)
+ assert.Equal(t, []string{}, diff)
+
+ mockComparer.AssertExpectations(t)
+}
+
+func TestSnapshotRawValidatorWhenFail(t *testing.T) {
+ data := common.K8sManifest{common.RAW: "b"}
+ validator := MatchSnapshotRawValidator{}
+
+ mockComparer := new(mockSnapshotComparer)
+ mockComparer.On("CompareToSnapshot", "b").Return(&snapshot.CompareResult{
+ Passed: false,
+ CachedSnapshot: "b\n",
+ NewSnapshot: "x\n",
+ })
+
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{data},
+ SnapshotComparer: mockComparer,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Expected to match snapshot 0:",
+ " --- Expected",
+ " +++ Actual",
+ " @@ -1,2 +1,2 @@",
+ " -b",
+ " +x",
+ }, diff)
+
+ mockComparer.AssertExpectations(t)
+}
+
+func TestSnapshotRawValidatorWhenNegativeAndFail(t *testing.T) {
+ data := common.K8sManifest{common.RAW: "b"}
+ validator := MatchSnapshotRawValidator{}
+
+ cached := "b\n"
+ mockComparer := new(mockSnapshotComparer)
+ mockComparer.On("CompareToSnapshot", "b").Return(&snapshot.CompareResult{
+ Passed: true,
+ CachedSnapshot: cached,
+ NewSnapshot: cached,
+ })
+
+ pass, diff := validator.Validate(&ValidateContext{
+ Negative: true,
+ Docs: []common.K8sManifest{data},
+ SnapshotComparer: mockComparer,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Expected NOT to match snapshot 0:",
+ " b",
+ }, diff)
+
+ mockComparer.AssertExpectations(t)
+}
+
+func TestSnapshotRawValidatorWhenInvalidIndex(t *testing.T) {
+ data := common.K8sManifest{common.RAW: "b"}
+
+ validator := MatchSnapshotRawValidator{}
+ pass, diff := validator.Validate(&ValidateContext{
+ Docs: []common.K8sManifest{data},
+ Index: 2,
+ })
+
+ assert.False(t, pass)
+ assert.Equal(t, []string{
+ "Error:",
+ " documentIndex 2 out of range",
+ }, diff)
+}
diff --git a/unittest/validators/snapshot_validator_test.go b/unittest/validators/snapshot_validator_test.go
index 6df040f30..05b201c51 100644
--- a/unittest/validators/snapshot_validator_test.go
+++ b/unittest/validators/snapshot_validator_test.go
@@ -7,18 +7,8 @@ import (
"github.com/lrills/helm-unittest/unittest/snapshot"
. "github.com/lrills/helm-unittest/unittest/validators"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
)
-type mockSnapshotComparer struct {
- mock.Mock
-}
-
-func (m *mockSnapshotComparer) CompareToSnapshot(content interface{}) *snapshot.CompareResult {
- args := m.Called(content)
- return args.Get(0).(*snapshot.CompareResult)
-}
-
func TestSnapshotValidatorWhenOk(t *testing.T) {
data := common.K8sManifest{"a": "b"}
validator := MatchSnapshotValidator{Path: "a"}