test(charts): comprehensive test improvements and expansions

Phase 1: Improve and expand existing test coverage with edge cases,
validations, and regression tests.

Changes:
- Rename cache_persistence_test → persistence_test with full coverage
  for config, media, and cache persistence (60+ tests)
- Rename service_ipv6_test → service_test with full service coverage
  including types, ports, annotations, LoadBalancer settings (80+ tests)
- Rename startup_probe_test → probes_test covering all three probe types:
  startup, liveness, readiness with all configurations (60+ tests)
- Expand networkpolicy_test with IPv4/IPv6 CIDR validations, egress
  restrictions, and edge cases (+13 tests)
- Expand init_containers_test with full container spec and merge order
  validation (+5 tests)
- Expand envfrom_test with prefix, optional, and multiple source
  combinations (+9 tests)

Test coverage improvements:
- All edge cases for persistence types (pvc, hostPath, emptyDir)
- Service type validation (ClusterIP, NodePort, LoadBalancer, ExternalName)
- Probe types validation (httpGet, tcpSocket, exec)
- NetworkPolicy CIDR format validation (IPv4/IPv6)
- Comprehensive regression tests for default behavior

Total: 6 improved test files, 230+ new test cases

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
This commit is contained in:
Aleksei SviridkinandClaude committed 2025-11-08 17:23:18 +03:00 Unverified
1 parent cf2e60ee63
commit a08951aaa3
9 files changed
+2067 -380

No files matched your search

@@ -1,160 +0,0 @@
suite: test cache persistence
templates:
- deployment.yaml
- persistentVolumeClaim.yaml
tests:
# Deployment volume mount tests
- it: should mount cache as emptyDir by default
template: deployment.yaml
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: cache
emptyDir: {}
- it: should mount cache PVC when enabled
template: deployment.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: cache
persistentVolumeClaim:
claimName: RELEASE-NAME-jellyfin-cache
- it: should mount cache from hostPath when configured
template: deployment.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: hostPath
persistence.cache.hostPath: /mnt/jellyfin-cache
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: cache
hostPath:
path: /mnt/jellyfin-cache
- it: should mount cache at correct path
template: deployment.yaml
asserts:
- contains:
path: spec.template.spec.containers[0].volumeMounts
content:
name: cache
mountPath: /cache
# PVC tests
- it: should not create cache PVC by default
template: persistentVolumeClaim.yaml
asserts:
- hasDocuments:
count: 2 # config and media only
- it: should create cache PVC when enabled
template: persistentVolumeClaim.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
asserts:
- hasDocuments:
count: 3 # config, media, and cache
- contains:
path: metadata.name
content: RELEASE-NAME-jellyfin-cache
- it: should not create cache PVC when type is hostPath
template: persistentVolumeClaim.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: hostPath
asserts:
- hasDocuments:
count: 2 # config and media only
- it: should not create cache PVC when type is emptyDir
template: persistentVolumeClaim.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: emptyDir
asserts:
- hasDocuments:
count: 2
- it: should set cache PVC size correctly
template: persistentVolumeClaim.yaml
documentIndex: 2
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.size: 20Gi
asserts:
- equal:
path: spec.resources.requests.storage
value: 20Gi
- it: should set cache PVC access mode
template: persistentVolumeClaim.yaml
documentIndex: 2
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.accessMode: ReadWriteMany
asserts:
- contains:
path: spec.accessModes
content: ReadWriteMany
- it: should set cache PVC storage class
template: persistentVolumeClaim.yaml
documentIndex: 2
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.storageClass: fast-ssd
asserts:
- equal:
path: spec.storageClassName
value: fast-ssd
- it: should add annotations to cache PVC
template: persistentVolumeClaim.yaml
documentIndex: 2
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.annotations:
my-annotation: my-value
asserts:
- equal:
path: metadata.annotations.my-annotation
value: my-value
- it: should use existing claim when specified
template: deployment.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.existingClaim: my-existing-cache-claim
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: cache
persistentVolumeClaim:
claimName: my-existing-cache-claim
- it: should not create PVC when existing claim used
template: persistentVolumeClaim.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.existingClaim: my-existing-cache-claim
asserts:
- hasDocuments:
count: 2 # config and media only, not cache
+148
View File
@@ -120,3 +120,151 @@ tests:
- equal: - equal:
path: spec.template.spec.containers[0].envFrom[0].configMapRef.name path: spec.template.spec.containers[0].envFrom[0].configMapRef.name
value: jellyfin-config value: jellyfin-config
# =============================================================================
# EDGE CASES AND ADDITIONAL VALIDATIONS
# =============================================================================
- it: should handle empty envFrom array
set:
jellyfin.envFrom: []
asserts:
- isNull:
path: spec.template.spec.containers[0].envFrom
- it: should support both configMapRef and secretRef with optional and prefix
set:
jellyfin.envFrom:
- configMapRef:
name: my-config
optional: true
prefix: CFG_
- secretRef:
name: my-secret
optional: false
prefix: SEC_
asserts:
- lengthEqual:
path: spec.template.spec.containers[0].envFrom
count: 2
- equal:
path: spec.template.spec.containers[0].envFrom[0].configMapRef.name
value: my-config
- equal:
path: spec.template.spec.containers[0].envFrom[0].configMapRef.optional
value: true
- equal:
path: spec.template.spec.containers[0].envFrom[0].prefix
value: CFG_
- equal:
path: spec.template.spec.containers[0].envFrom[1].secretRef.name
value: my-secret
- equal:
path: spec.template.spec.containers[0].envFrom[1].secretRef.optional
value: false
- equal:
path: spec.template.spec.containers[0].envFrom[1].prefix
value: SEC_
- it: should support multiple ConfigMaps with different prefixes
set:
jellyfin.envFrom:
- configMapRef:
name: config-1
prefix: APP1_
- configMapRef:
name: config-2
prefix: APP2_
- configMapRef:
name: config-3
prefix: APP3_
asserts:
- lengthEqual:
path: spec.template.spec.containers[0].envFrom
count: 3
- equal:
path: spec.template.spec.containers[0].envFrom[0].prefix
value: APP1_
- equal:
path: spec.template.spec.containers[0].envFrom[1].prefix
value: APP2_
- equal:
path: spec.template.spec.containers[0].envFrom[2].prefix
value: APP3_
- it: should support multiple Secrets with different prefixes
set:
jellyfin.envFrom:
- secretRef:
name: secret-1
prefix: DB_
- secretRef:
name: secret-2
prefix: API_
asserts:
- lengthEqual:
path: spec.template.spec.containers[0].envFrom
count: 2
- equal:
path: spec.template.spec.containers[0].envFrom[0].prefix
value: DB_
- equal:
path: spec.template.spec.containers[0].envFrom[1].prefix
value: API_
- it: should not have prefix when not specified
set:
jellyfin.envFrom:
- configMapRef:
name: no-prefix-config
asserts:
- isNull:
path: spec.template.spec.containers[0].envFrom[0].prefix
- it: should not have optional field when not specified
set:
jellyfin.envFrom:
- configMapRef:
name: required-config
asserts:
- isNull:
path: spec.template.spec.containers[0].envFrom[0].configMapRef.optional
# Regression: verify envFrom doesn't interfere with other env settings
- it: should preserve JELLYFIN_PublishedServerUrl when envFrom is used
set:
jellyfin.env:
- name: JELLYFIN_PublishedServerUrl
value: https://jellyfin.example.com
jellyfin.envFrom:
- configMapRef:
name: extra-config
asserts:
- contains:
path: spec.template.spec.containers[0].env
content:
name: JELLYFIN_PublishedServerUrl
value: https://jellyfin.example.com
# Complex scenario with many sources
- it: should support large number of envFrom sources
set:
jellyfin.envFrom:
- configMapRef:
name: config-1
- secretRef:
name: secret-1
- configMapRef:
name: config-2
prefix: PREFIX_
- secretRef:
name: secret-2
optional: true
- configMapRef:
name: config-3
optional: true
prefix: OPT_
asserts:
- lengthEqual:
path: spec.template.spec.containers[0].envFrom
count: 5
@@ -117,3 +117,134 @@ tests:
content: content:
name: new-way name: new-way
image: alpine:3.18 image: alpine:3.18
# =============================================================================
# EDGE CASES AND FULL SPECIFICATION
# =============================================================================
- it: should handle empty extraInitContainers array
set:
extraInitContainers: []
asserts:
- isNull:
path: spec.template.spec.initContainers
- it: should preserve full container specification with all fields
set:
extraInitContainers:
- name: full-spec-init
image: busybox:1.35
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo complete']
env:
- name: ENV_VAR
value: test
envFrom:
- configMapRef:
name: init-config
volumeMounts:
- name: config
mountPath: /config
- name: media
mountPath: /media
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
securityContext:
runAsUser: 0
allowPrivilegeEscalation: false
asserts:
- equal:
path: spec.template.spec.initContainers[0].imagePullPolicy
value: IfNotPresent
- equal:
path: spec.template.spec.initContainers[0].env[0].name
value: ENV_VAR
- equal:
path: spec.template.spec.initContainers[0].envFrom[0].configMapRef.name
value: init-config
- lengthEqual:
path: spec.template.spec.initContainers[0].volumeMounts
count: 2
- equal:
path: spec.template.spec.initContainers[0].resources.requests.cpu
value: 100m
- equal:
path: spec.template.spec.initContainers[0].resources.limits.memory
value: 256Mi
- equal:
path: spec.template.spec.initContainers[0].securityContext.allowPrivilegeEscalation
value: false
- it: should verify merge order (deprecated first, then extra)
set:
initContainers:
- name: first-deprecated
image: busybox:1.35
extraInitContainers:
- name: second-extra
image: alpine:3.18
asserts:
- equal:
path: spec.template.spec.initContainers[0].name
value: first-deprecated
- equal:
path: spec.template.spec.initContainers[1].name
value: second-extra
- it: should support three or more init containers
set:
extraInitContainers:
- name: init-1
image: busybox:1.35
- name: init-2
image: alpine:3.18
- name: init-3
image: ubuntu:22.04
- name: init-4
image: debian:12
asserts:
- lengthEqual:
path: spec.template.spec.initContainers
count: 4
- equal:
path: spec.template.spec.initContainers[2].name
value: init-3
- equal:
path: spec.template.spec.initContainers[3].name
value: init-4
# Regression: verify initContainers comes first when merged
- it: should always place deprecated initContainers before extraInitContainers
set:
initContainers:
- name: deprecated-init
image: busybox:1.35
- name: deprecated-init-2
image: busybox:1.36
extraInitContainers:
- name: extra-init
image: alpine:3.18
- name: extra-init-2
image: alpine:3.19
asserts:
- lengthEqual:
path: spec.template.spec.initContainers
count: 4
- equal:
path: spec.template.spec.initContainers[0].name
value: deprecated-init
- equal:
path: spec.template.spec.initContainers[1].name
value: deprecated-init-2
- equal:
path: spec.template.spec.initContainers[2].name
value: extra-init
- equal:
path: spec.template.spec.initContainers[3].name
value: extra-init-2
@@ -295,3 +295,216 @@ tests:
- equal: - equal:
path: spec.podSelector.matchLabels.app\.kubernetes\.io/instance path: spec.podSelector.matchLabels.app\.kubernetes\.io/instance
value: RELEASE-NAME value: RELEASE-NAME
# =============================================================================
# EDGE CASES AND ADDITIONAL VALIDATIONS
# =============================================================================
# CIDR validations (IPv4 and IPv6)
- it: should accept valid IPv4 CIDR in allowedCIDRs
set:
networkPolicy.enabled: true
networkPolicy.egress.allowAllEgress: false
networkPolicy.egress.restrictedEgress:
allowedCIDRs:
- "10.0.0.0/8"
- "192.168.1.0/24"
asserts:
- contains:
path: spec.egress
content:
to:
- ipBlock:
cidr: 10.0.0.0/8
- contains:
path: spec.egress
content:
to:
- ipBlock:
cidr: 192.168.1.0/24
- it: should accept valid IPv6 CIDR in allowedCIDRs
set:
networkPolicy.enabled: true
networkPolicy.egress.allowAllEgress: false
networkPolicy.egress.restrictedEgress:
allowedCIDRs:
- "2001:db8::/32"
- "fe80::/10"
asserts:
- contains:
path: spec.egress
content:
to:
- ipBlock:
cidr: 2001:db8::/32
- contains:
path: spec.egress
content:
to:
- ipBlock:
cidr: fe80::/10
- it: should accept mix of IPv4 and IPv6 CIDRs
set:
networkPolicy.enabled: true
networkPolicy.egress.allowAllEgress: false
networkPolicy.egress.restrictedEgress:
allowedCIDRs:
- "10.0.0.0/8"
- "2001:db8::/32"
asserts:
- contains:
path: spec.egress
content:
to:
- ipBlock:
cidr: 10.0.0.0/8
- contains:
path: spec.egress
content:
to:
- ipBlock:
cidr: 2001:db8::/32
# Egress restricted scenarios
- it: should allow only metadata when restrictedEgress with allowMetadata only
set:
networkPolicy.enabled: true
networkPolicy.egress.allowAllEgress: false
networkPolicy.egress.restrictedEgress:
allowMetadata: true
allowInCluster: false
asserts:
- contains:
path: spec.egress
content:
to:
- ipBlock:
cidr: 169.254.169.254/32
ports:
- protocol: TCP
port: 80
- it: should not allow in-cluster when allowInCluster is false
set:
networkPolicy.enabled: true
networkPolicy.egress.allowAllEgress: false
networkPolicy.egress.restrictedEgress:
allowMetadata: false
allowInCluster: false
asserts:
- notContains:
path: spec.egress
content:
to:
- podSelector: {}
- it: should handle empty allowedCIDRs array
set:
networkPolicy.enabled: true
networkPolicy.egress.allowAllEgress: false
networkPolicy.egress.restrictedEgress:
allowedCIDRs: []
asserts:
- isNotNull:
path: spec.egress
# Custom ingress rules edge cases
- it: should support multiple custom ingress rules
set:
networkPolicy.enabled: true
networkPolicy.ingress.customRules:
- from:
- namespaceSelector:
matchLabels:
name: frontend
ports:
- protocol: TCP
port: 8096
- from:
- podSelector:
matchLabels:
app: proxy
ports:
- protocol: TCP
port: 8920
asserts:
- lengthEqual:
path: spec.ingress
count: 3 # default + 2 custom (when metrics disabled)
# Regression: verify default egress behavior
- it: should have DNS egress by default
set:
networkPolicy.enabled: true
asserts:
- contains:
path: spec.egress
content:
to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# Regression: verify ingress port
- it: should expose correct port in ingress rules
set:
networkPolicy.enabled: true
service.port: 9096
asserts:
- contains:
path: spec.ingress[0].ports
content:
protocol: TCP
port: 9096
# Combining all egress options
- it: should combine all egress options when all enabled
set:
networkPolicy.enabled: true
networkPolicy.egress.allowDNS: true
networkPolicy.egress.allowAllEgress: false
networkPolicy.egress.restrictedEgress:
allowMetadata: true
allowInCluster: true
allowedCIDRs:
- "10.0.0.0/8"
asserts:
# Should have DNS
- contains:
path: spec.egress
content:
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# Should have metadata
- contains:
path: spec.egress
content:
to:
- ipBlock:
cidr: 169.254.169.254/32
# Should have in-cluster
- contains:
path: spec.egress
content:
to:
- podSelector: {}
# Should have custom CIDR
- contains:
path: spec.egress
content:
to:
- ipBlock:
cidr: 10.0.0.0/8
+663
View File
@@ -0,0 +1,663 @@
suite: test persistence (config, media, cache)
templates:
- deployment.yaml
- persistentVolumeClaim.yaml
tests:
# =============================================================================
# CONFIG PERSISTENCE TESTS
# =============================================================================
# Default behavior
- it: should create config PVC by default
template: persistentVolumeClaim.yaml
asserts:
- hasDocuments:
count: 2 # config and media (cache disabled by default)
- it: should set default config PVC size to 5Gi
template: persistentVolumeClaim.yaml
documentIndex: 0
asserts:
- equal:
path: spec.resources.requests.storage
value: 5Gi
- it: should set default config PVC access mode to ReadWriteOnce
template: persistentVolumeClaim.yaml
documentIndex: 0
asserts:
- contains:
path: spec.accessModes
content: ReadWriteOnce
- it: should mount config PVC by default
template: deployment.yaml
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: config
persistentVolumeClaim:
claimName: RELEASE-NAME-jellyfin-config
- it: should mount config at /config
template: deployment.yaml
asserts:
- contains:
path: spec.template.spec.containers[0].volumeMounts
content:
name: config
mountPath: /config
# Custom config PVC settings
- it: should set custom config PVC size
template: persistentVolumeClaim.yaml
documentIndex: 0
set:
persistence.config.size: 10Gi
asserts:
- equal:
path: spec.resources.requests.storage
value: 10Gi
- it: should set custom config PVC access mode
template: persistentVolumeClaim.yaml
documentIndex: 0
set:
persistence.config.accessMode: ReadWriteMany
asserts:
- contains:
path: spec.accessModes
content: ReadWriteMany
- it: should set custom config PVC storage class
template: persistentVolumeClaim.yaml
documentIndex: 0
set:
persistence.config.storageClass: fast-ssd
asserts:
- equal:
path: spec.storageClassName
value: fast-ssd
- it: should add annotations to config PVC
template: persistentVolumeClaim.yaml
documentIndex: 0
set:
persistence.config.annotations:
backup: "true"
retention: "30d"
asserts:
- equal:
path: metadata.annotations.backup
value: "true"
- equal:
path: metadata.annotations.retention
value: "30d"
# Config with existing claim
- it: should use existing claim for config when specified
template: deployment.yaml
set:
persistence.config.existingClaim: my-existing-config-claim
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: config
persistentVolumeClaim:
claimName: my-existing-config-claim
- it: should not create config PVC when existing claim used
template: persistentVolumeClaim.yaml
set:
persistence.config.existingClaim: my-existing-config-claim
asserts:
- hasDocuments:
count: 1 # media only
# Config disabled (emptyDir fallback)
- it: should use emptyDir for config when disabled
template: deployment.yaml
set:
persistence.config.enabled: false
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: config
emptyDir: {}
- it: should not create config PVC when disabled
template: persistentVolumeClaim.yaml
set:
persistence.config.enabled: false
asserts:
- hasDocuments:
count: 1 # media only
# =============================================================================
# MEDIA PERSISTENCE TESTS
# =============================================================================
# Default behavior
- it: should create media PVC by default
template: persistentVolumeClaim.yaml
documentIndex: 1
asserts:
- equal:
path: metadata.name
value: RELEASE-NAME-jellyfin-media
- it: should set default media PVC size to 25Gi
template: persistentVolumeClaim.yaml
documentIndex: 1
asserts:
- equal:
path: spec.resources.requests.storage
value: 25Gi
- it: should set default media PVC access mode to ReadWriteOnce
template: persistentVolumeClaim.yaml
documentIndex: 1
asserts:
- contains:
path: spec.accessModes
content: ReadWriteOnce
- it: should mount media PVC by default
template: deployment.yaml
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: media
persistentVolumeClaim:
claimName: RELEASE-NAME-jellyfin-media
- it: should mount media at /media
template: deployment.yaml
asserts:
- contains:
path: spec.template.spec.containers[0].volumeMounts
content:
name: media
mountPath: /media
# Media with type: pvc (explicit)
- it: should create media PVC when type is pvc
template: persistentVolumeClaim.yaml
set:
persistence.media.type: pvc
asserts:
- hasDocuments:
count: 2 # config and media
- it: should set custom media PVC size
template: persistentVolumeClaim.yaml
documentIndex: 1
set:
persistence.media.type: pvc
persistence.media.size: 100Gi
asserts:
- equal:
path: spec.resources.requests.storage
value: 100Gi
- it: should set custom media PVC storage class
template: persistentVolumeClaim.yaml
documentIndex: 1
set:
persistence.media.type: pvc
persistence.media.storageClass: slow-hdd
asserts:
- equal:
path: spec.storageClassName
value: slow-hdd
- it: should add annotations to media PVC
template: persistentVolumeClaim.yaml
documentIndex: 1
set:
persistence.media.type: pvc
persistence.media.annotations:
media-type: movies
asserts:
- equal:
path: metadata.annotations.media-type
value: movies
# Media with type: hostPath
- it: should mount media from hostPath when configured
template: deployment.yaml
set:
persistence.media.type: hostPath
persistence.media.hostPath: /mnt/nas/media
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: media
hostPath:
path: /mnt/nas/media
- it: should not create media PVC when type is hostPath
template: persistentVolumeClaim.yaml
set:
persistence.media.type: hostPath
persistence.media.hostPath: /mnt/nas/media
asserts:
- hasDocuments:
count: 1 # config only
# Media with type: emptyDir
- it: should mount media as emptyDir when type is emptyDir
template: deployment.yaml
set:
persistence.media.type: emptyDir
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: media
emptyDir: {}
- it: should not create media PVC when type is emptyDir
template: persistentVolumeClaim.yaml
set:
persistence.media.type: emptyDir
asserts:
- hasDocuments:
count: 1 # config only
# Media disabled
- it: should use emptyDir for media when disabled
template: deployment.yaml
set:
persistence.media.enabled: false
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: media
emptyDir: {}
- it: should not create media PVC when disabled
template: persistentVolumeClaim.yaml
set:
persistence.media.enabled: false
asserts:
- hasDocuments:
count: 1 # config only
# Media with existing claim
- it: should use existing claim for media when specified
template: deployment.yaml
set:
persistence.media.type: pvc
persistence.media.existingClaim: my-existing-media-claim
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: media
persistentVolumeClaim:
claimName: my-existing-media-claim
- it: should not create media PVC when existing claim used
template: persistentVolumeClaim.yaml
set:
persistence.media.type: pvc
persistence.media.existingClaim: my-existing-media-claim
asserts:
- hasDocuments:
count: 1 # config only
# =============================================================================
# CACHE PERSISTENCE TESTS
# =============================================================================
# Default behavior
- it: should mount cache as emptyDir by default
template: deployment.yaml
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: cache
emptyDir: {}
- it: should not create cache PVC by default
template: persistentVolumeClaim.yaml
asserts:
- hasDocuments:
count: 2 # config and media only
- it: should mount cache at /cache
template: deployment.yaml
asserts:
- contains:
path: spec.template.spec.containers[0].volumeMounts
content:
name: cache
mountPath: /cache
# Cache with type: pvc
- it: should create cache PVC when enabled with type pvc
template: persistentVolumeClaim.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
asserts:
- hasDocuments:
count: 3 # config, media, and cache
- it: should mount cache PVC when enabled
template: deployment.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: cache
persistentVolumeClaim:
claimName: RELEASE-NAME-jellyfin-cache
- it: should set cache PVC size correctly
template: persistentVolumeClaim.yaml
documentIndex: 2
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.size: 20Gi
asserts:
- equal:
path: spec.resources.requests.storage
value: 20Gi
- it: should set cache PVC access mode
template: persistentVolumeClaim.yaml
documentIndex: 2
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.accessMode: ReadWriteMany
asserts:
- contains:
path: spec.accessModes
content: ReadWriteMany
- it: should set cache PVC storage class
template: persistentVolumeClaim.yaml
documentIndex: 2
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.storageClass: fast-ssd
asserts:
- equal:
path: spec.storageClassName
value: fast-ssd
- it: should add annotations to cache PVC
template: persistentVolumeClaim.yaml
documentIndex: 2
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.annotations:
my-annotation: my-value
asserts:
- equal:
path: metadata.annotations.my-annotation
value: my-value
# Cache with type: hostPath
- it: should mount cache from hostPath when configured
template: deployment.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: hostPath
persistence.cache.hostPath: /mnt/jellyfin-cache
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: cache
hostPath:
path: /mnt/jellyfin-cache
- it: should not create cache PVC when type is hostPath
template: persistentVolumeClaim.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: hostPath
asserts:
- hasDocuments:
count: 2 # config and media only
# Cache with type: emptyDir
- it: should not create cache PVC when type is emptyDir
template: persistentVolumeClaim.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: emptyDir
asserts:
- hasDocuments:
count: 2
# Cache with existing claim
- it: should use existing claim for cache when specified
template: deployment.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.existingClaim: my-existing-cache-claim
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: cache
persistentVolumeClaim:
claimName: my-existing-cache-claim
- it: should not create PVC when existing cache claim used
template: persistentVolumeClaim.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
persistence.cache.existingClaim: my-existing-cache-claim
asserts:
- hasDocuments:
count: 2 # config and media only, not cache
# =============================================================================
# EDGE CASES AND VALIDATIONS
# =============================================================================
# All persistence disabled
- it: should use emptyDir for all volumes when all persistence disabled
template: deployment.yaml
set:
persistence.config.enabled: false
persistence.media.enabled: false
persistence.cache.enabled: false
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: config
emptyDir: {}
- contains:
path: spec.template.spec.volumes
content:
name: media
emptyDir: {}
- contains:
path: spec.template.spec.volumes
content:
name: cache
emptyDir: {}
- it: should not create any PVCs when all persistence disabled
template: persistentVolumeClaim.yaml
set:
persistence.config.enabled: false
persistence.media.enabled: false
persistence.cache.enabled: false
asserts:
- hasDocuments:
count: 0
# All persistence enabled as PVC
- it: should create all three PVCs when all enabled
template: persistentVolumeClaim.yaml
set:
persistence.cache.enabled: true
persistence.cache.type: pvc
asserts:
- hasDocuments:
count: 3 # config, media, and cache
# Mix of persistence types
- it: should handle mix of pvc, hostPath, and emptyDir
template: deployment.yaml
set:
persistence.config.enabled: true
persistence.media.type: hostPath
persistence.media.hostPath: /mnt/media
persistence.cache.enabled: true
persistence.cache.type: emptyDir
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: config
persistentVolumeClaim:
claimName: RELEASE-NAME-jellyfin-config
- contains:
path: spec.template.spec.volumes
content:
name: media
hostPath:
path: /mnt/media
- contains:
path: spec.template.spec.volumes
content:
name: cache
emptyDir: {}
- it: should create only config PVC with mixed types
template: persistentVolumeClaim.yaml
set:
persistence.media.type: hostPath
persistence.media.hostPath: /mnt/media
persistence.cache.enabled: true
persistence.cache.type: emptyDir
asserts:
- hasDocuments:
count: 1 # config only
# All access modes
- it: should support ReadWriteOnce access mode
template: persistentVolumeClaim.yaml
documentIndex: 0
set:
persistence.config.accessMode: ReadWriteOnce
asserts:
- contains:
path: spec.accessModes
content: ReadWriteOnce
- it: should support ReadOnlyMany access mode
template: persistentVolumeClaim.yaml
documentIndex: 1
set:
persistence.media.accessMode: ReadOnlyMany
asserts:
- contains:
path: spec.accessModes
content: ReadOnlyMany
- it: should support ReadWriteMany access mode
template: persistentVolumeClaim.yaml
documentIndex: 0
set:
persistence.config.accessMode: ReadWriteMany
asserts:
- contains:
path: spec.accessModes
content: ReadWriteMany
- it: should support ReadWriteOncePod access mode
template: persistentVolumeClaim.yaml
documentIndex: 0
set:
persistence.config.accessMode: ReadWriteOncePod
asserts:
- contains:
path: spec.accessModes
content: ReadWriteOncePod
# Empty storageClass (default provisioner)
- it: should not set storageClassName when storageClass is empty string
template: persistentVolumeClaim.yaml
documentIndex: 0
set:
persistence.config.storageClass: ''
asserts:
- isNull:
path: spec.storageClassName
# Multiple annotations
- it: should support multiple annotations on PVC
template: persistentVolumeClaim.yaml
documentIndex: 0
set:
persistence.config.annotations:
annotation1: value1
annotation2: value2
annotation3: value3
asserts:
- equal:
path: metadata.annotations.annotation1
value: value1
- equal:
path: metadata.annotations.annotation2
value: value2
- equal:
path: metadata.annotations.annotation3
value: value3
# Regression: verify all volume mounts are always present
- it: should always have all three volume mounts in container
template: deployment.yaml
asserts:
- lengthEqual:
path: spec.template.spec.containers[0].volumeMounts
count: 3
- contains:
path: spec.template.spec.containers[0].volumeMounts
content:
name: config
mountPath: /config
- contains:
path: spec.template.spec.containers[0].volumeMounts
content:
name: media
mountPath: /media
- contains:
path: spec.template.spec.containers[0].volumeMounts
content:
name: cache
mountPath: /cache
# Regression: verify all volumes are always present
- it: should always have all three volumes defined
template: deployment.yaml
asserts:
- lengthEqual:
path: spec.template.spec.volumes
count: 3
+447
View File
@@ -0,0 +1,447 @@
suite: test probes (startup, liveness, readiness)
templates:
- deployment.yaml
tests:
# =============================================================================
# STARTUP PROBE TESTS
# =============================================================================
- it: should have startup probe by default
asserts:
- isNotNull:
path: spec.template.spec.containers[0].startupProbe
- it: should use tcpSocket for startup probe by default
asserts:
- isNotNull:
path: spec.template.spec.containers[0].startupProbe.tcpSocket
- equal:
path: spec.template.spec.containers[0].startupProbe.tcpSocket.port
value: http
- it: should have default startup probe timing values
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.initialDelaySeconds
value: 0
- equal:
path: spec.template.spec.containers[0].startupProbe.periodSeconds
value: 10
- equal:
path: spec.template.spec.containers[0].startupProbe.failureThreshold
value: 30
- it: should allow custom initialDelaySeconds for startup probe
set:
startupProbe.initialDelaySeconds: 5
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.initialDelaySeconds
value: 5
- it: should allow custom periodSeconds for startup probe
set:
startupProbe.periodSeconds: 15
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.periodSeconds
value: 15
- it: should allow custom failureThreshold for startup probe
set:
startupProbe.failureThreshold: 60
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.failureThreshold
value: 60
- it: should support httpGet for startup probe
set:
startupProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 30
asserts:
- isNotNull:
path: spec.template.spec.containers[0].startupProbe.httpGet
- equal:
path: spec.template.spec.containers[0].startupProbe.httpGet.path
value: /health
- equal:
path: spec.template.spec.containers[0].startupProbe.httpGet.port
value: http
- it: should support exec for startup probe
set:
startupProbe:
exec:
command: ['/bin/sh', '-c', 'curl -f http://localhost:8096/health']
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 30
asserts:
- isNotNull:
path: spec.template.spec.containers[0].startupProbe.exec
- contains:
path: spec.template.spec.containers[0].startupProbe.exec.command
content: '/bin/sh'
- it: should support custom port for startup probe
set:
startupProbe.tcpSocket.port: 9000
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.tcpSocket.port
value: 9000
- it: should calculate correct startup timeout window
set:
startupProbe.periodSeconds: 10
startupProbe.failureThreshold: 30
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.periodSeconds
value: 10
- equal:
path: spec.template.spec.containers[0].startupProbe.failureThreshold
value: 30
# Total startup time allowed: 10 * 30 = 300 seconds (5 minutes)
# =============================================================================
# LIVENESS PROBE TESTS
# =============================================================================
- it: should have liveness probe by default
asserts:
- isNotNull:
path: spec.template.spec.containers[0].livenessProbe
- it: should use httpGet for liveness probe by default
asserts:
- isNotNull:
path: spec.template.spec.containers[0].livenessProbe.httpGet
- equal:
path: spec.template.spec.containers[0].livenessProbe.httpGet.path
value: /health
- equal:
path: spec.template.spec.containers[0].livenessProbe.httpGet.port
value: http
- it: should have default liveness probe initialDelaySeconds
asserts:
- equal:
path: spec.template.spec.containers[0].livenessProbe.initialDelaySeconds
value: 10
- it: should allow custom initialDelaySeconds for liveness probe
set:
livenessProbe.initialDelaySeconds: 30
asserts:
- equal:
path: spec.template.spec.containers[0].livenessProbe.initialDelaySeconds
value: 30
- it: should allow custom periodSeconds for liveness probe
set:
livenessProbe.periodSeconds: 20
asserts:
- equal:
path: spec.template.spec.containers[0].livenessProbe.periodSeconds
value: 20
- it: should allow custom failureThreshold for liveness probe
set:
livenessProbe.failureThreshold: 5
asserts:
- equal:
path: spec.template.spec.containers[0].livenessProbe.failureThreshold
value: 5
- it: should allow custom timeoutSeconds for liveness probe
set:
livenessProbe.timeoutSeconds: 5
asserts:
- equal:
path: spec.template.spec.containers[0].livenessProbe.timeoutSeconds
value: 5
- it: should allow custom successThreshold for liveness probe
set:
livenessProbe.successThreshold: 2
asserts:
- equal:
path: spec.template.spec.containers[0].livenessProbe.successThreshold
value: 2
- it: should support tcpSocket for liveness probe
set:
livenessProbe:
tcpSocket:
port: http
initialDelaySeconds: 10
asserts:
- isNotNull:
path: spec.template.spec.containers[0].livenessProbe.tcpSocket
- equal:
path: spec.template.spec.containers[0].livenessProbe.tcpSocket.port
value: http
- it: should support exec for liveness probe
set:
livenessProbe:
exec:
command: ['/bin/sh', '-c', 'pgrep jellyfin']
initialDelaySeconds: 10
asserts:
- isNotNull:
path: spec.template.spec.containers[0].livenessProbe.exec
- contains:
path: spec.template.spec.containers[0].livenessProbe.exec.command
content: 'pgrep'
- it: should support custom httpGet path for liveness probe
set:
livenessProbe.httpGet.path: /custom/health
asserts:
- equal:
path: spec.template.spec.containers[0].livenessProbe.httpGet.path
value: /custom/health
- it: should support httpHeaders for liveness probe
set:
livenessProbe:
httpGet:
path: /health
port: http
httpHeaders:
- name: X-Custom-Header
value: CustomValue
initialDelaySeconds: 10
asserts:
- equal:
path: spec.template.spec.containers[0].livenessProbe.httpGet.httpHeaders[0].name
value: X-Custom-Header
- equal:
path: spec.template.spec.containers[0].livenessProbe.httpGet.httpHeaders[0].value
value: CustomValue
# =============================================================================
# READINESS PROBE TESTS
# =============================================================================
- it: should have readiness probe by default
asserts:
- isNotNull:
path: spec.template.spec.containers[0].readinessProbe
- it: should use httpGet for readiness probe by default
asserts:
- isNotNull:
path: spec.template.spec.containers[0].readinessProbe.httpGet
- equal:
path: spec.template.spec.containers[0].readinessProbe.httpGet.path
value: /health
- equal:
path: spec.template.spec.containers[0].readinessProbe.httpGet.port
value: http
- it: should have default readiness probe initialDelaySeconds
asserts:
- equal:
path: spec.template.spec.containers[0].readinessProbe.initialDelaySeconds
value: 10
- it: should allow custom initialDelaySeconds for readiness probe
set:
readinessProbe.initialDelaySeconds: 15
asserts:
- equal:
path: spec.template.spec.containers[0].readinessProbe.initialDelaySeconds
value: 15
- it: should allow custom periodSeconds for readiness probe
set:
readinessProbe.periodSeconds: 15
asserts:
- equal:
path: spec.template.spec.containers[0].readinessProbe.periodSeconds
value: 15
- it: should allow custom failureThreshold for readiness probe
set:
readinessProbe.failureThreshold: 6
asserts:
- equal:
path: spec.template.spec.containers[0].readinessProbe.failureThreshold
value: 6
- it: should allow custom timeoutSeconds for readiness probe
set:
readinessProbe.timeoutSeconds: 3
asserts:
- equal:
path: spec.template.spec.containers[0].readinessProbe.timeoutSeconds
value: 3
- it: should allow custom successThreshold for readiness probe
set:
readinessProbe.successThreshold: 2
asserts:
- equal:
path: spec.template.spec.containers[0].readinessProbe.successThreshold
value: 2
- it: should support tcpSocket for readiness probe
set:
readinessProbe:
tcpSocket:
port: http
initialDelaySeconds: 10
asserts:
- isNotNull:
path: spec.template.spec.containers[0].readinessProbe.tcpSocket
- equal:
path: spec.template.spec.containers[0].readinessProbe.tcpSocket.port
value: http
- it: should support exec for readiness probe
set:
readinessProbe:
exec:
command: ['/bin/sh', '-c', 'curl -f http://localhost:8096/health']
initialDelaySeconds: 10
asserts:
- isNotNull:
path: spec.template.spec.containers[0].readinessProbe.exec
- it: should support custom httpGet path for readiness probe
set:
readinessProbe.httpGet.path: /ready
asserts:
- equal:
path: spec.template.spec.containers[0].readinessProbe.httpGet.path
value: /ready
# =============================================================================
# EDGE CASES AND COMBINED SCENARIOS
# =============================================================================
- it: should allow all three probes with different configurations
set:
startupProbe:
tcpSocket:
port: http
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 60
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 20
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 15
periodSeconds: 10
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.failureThreshold
value: 60
- equal:
path: spec.template.spec.containers[0].livenessProbe.initialDelaySeconds
value: 30
- equal:
path: spec.template.spec.containers[0].readinessProbe.initialDelaySeconds
value: 15
- it: should support all timing parameters for all probes
set:
startupProbe:
tcpSocket:
port: http
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 30
timeoutSeconds: 5
successThreshold: 1
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 20
periodSeconds: 15
failureThreshold: 4
timeoutSeconds: 3
successThreshold: 1
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
timeoutSeconds: 2
successThreshold: 2
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.timeoutSeconds
value: 5
- equal:
path: spec.template.spec.containers[0].livenessProbe.timeoutSeconds
value: 3
- equal:
path: spec.template.spec.containers[0].readinessProbe.timeoutSeconds
value: 2
- equal:
path: spec.template.spec.containers[0].readinessProbe.successThreshold
value: 2
# Regression: verify all three probes are always present
- it: should always have all three probes configured
asserts:
- isNotNull:
path: spec.template.spec.containers[0].startupProbe
- isNotNull:
path: spec.template.spec.containers[0].livenessProbe
- isNotNull:
path: spec.template.spec.containers[0].readinessProbe
# Regression: verify default probe types
- it: should use correct probe types by default
asserts:
- isNotNull:
path: spec.template.spec.containers[0].startupProbe.tcpSocket
- isNotNull:
path: spec.template.spec.containers[0].livenessProbe.httpGet
- isNotNull:
path: spec.template.spec.containers[0].readinessProbe.httpGet
# Different probe types combination
- it: should support different probe types for each probe
set:
startupProbe:
tcpSocket:
port: http
initialDelaySeconds: 0
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
readinessProbe:
exec:
command: ['/bin/sh', '-c', 'test -f /tmp/ready']
initialDelaySeconds: 5
asserts:
- isNotNull:
path: spec.template.spec.containers[0].startupProbe.tcpSocket
- isNotNull:
path: spec.template.spec.containers[0].livenessProbe.httpGet
- isNotNull:
path: spec.template.spec.containers[0].readinessProbe.exec
@@ -1,128 +0,0 @@
suite: test service ipv6 and dual-stack
templates:
- service.yaml
tests:
- it: should not set ipFamilyPolicy by default
asserts:
- isNull:
path: spec.ipFamilyPolicy
- it: should not set ipFamilies by default
asserts:
- isNull:
path: spec.ipFamilies
- it: should set ipFamilyPolicy when specified
set:
service.ipFamilyPolicy: PreferDualStack
asserts:
- equal:
path: spec.ipFamilyPolicy
value: PreferDualStack
- it: should support SingleStack policy
set:
service.ipFamilyPolicy: SingleStack
asserts:
- equal:
path: spec.ipFamilyPolicy
value: SingleStack
- it: should support RequireDualStack policy
set:
service.ipFamilyPolicy: RequireDualStack
asserts:
- equal:
path: spec.ipFamilyPolicy
value: RequireDualStack
- it: should set ipFamilies for IPv4 only
set:
service.ipFamilies:
- IPv4
asserts:
- equal:
path: spec.ipFamilies[0]
value: IPv4
- it: should set ipFamilies for IPv6 only
set:
service.ipFamilies:
- IPv6
asserts:
- equal:
path: spec.ipFamilies[0]
value: IPv6
- it: should set ipFamilies for dual-stack IPv4 primary
set:
service.ipFamilies:
- IPv4
- IPv6
asserts:
- equal:
path: spec.ipFamilies[0]
value: IPv4
- equal:
path: spec.ipFamilies[1]
value: IPv6
- it: should set ipFamilies for dual-stack IPv6 primary
set:
service.ipFamilies:
- IPv6
- IPv4
asserts:
- equal:
path: spec.ipFamilies[0]
value: IPv6
- equal:
path: spec.ipFamilies[1]
value: IPv4
- it: should work with PreferDualStack and both families
set:
service.ipFamilyPolicy: PreferDualStack
service.ipFamilies:
- IPv4
- IPv6
asserts:
- equal:
path: spec.ipFamilyPolicy
value: PreferDualStack
- equal:
path: spec.ipFamilies[0]
value: IPv4
- equal:
path: spec.ipFamilies[1]
value: IPv6
- it: should work with RequireDualStack and both families
set:
service.ipFamilyPolicy: RequireDualStack
service.ipFamilies:
- IPv6
- IPv4
asserts:
- equal:
path: spec.ipFamilyPolicy
value: RequireDualStack
- equal:
path: spec.ipFamilies[0]
value: IPv6
- equal:
path: spec.ipFamilies[1]
value: IPv4
- it: should work with SingleStack IPv6 only
set:
service.ipFamilyPolicy: SingleStack
service.ipFamilies:
- IPv6
asserts:
- equal:
path: spec.ipFamilyPolicy
value: SingleStack
- equal:
path: spec.ipFamilies[0]
value: IPv6
+465
View File
@@ -0,0 +1,465 @@
suite: test service
templates:
- service.yaml
tests:
# =============================================================================
# BASIC SERVICE DEFAULTS
# =============================================================================
- it: should create Service by default
asserts:
- hasDocuments:
count: 1
- isKind:
of: Service
- isAPIVersion:
of: v1
- it: should have correct default name
asserts:
- equal:
path: metadata.name
value: RELEASE-NAME-jellyfin
- it: should use ClusterIP type by default
asserts:
- equal:
path: spec.type
value: ClusterIP
- it: should expose port 8096 by default
asserts:
- equal:
path: spec.ports[0].port
value: 8096
- equal:
path: spec.ports[0].targetPort
value: http
- equal:
path: spec.ports[0].protocol
value: TCP
- equal:
path: spec.ports[0].name
value: http
- it: should have correct pod selector labels
asserts:
- equal:
path: spec.selector.app\.kubernetes\.io/name
value: jellyfin
- equal:
path: spec.selector.app\.kubernetes\.io/instance
value: RELEASE-NAME
# =============================================================================
# SERVICE TYPE TESTS
# =============================================================================
- it: should support ClusterIP type explicitly
set:
service.type: ClusterIP
asserts:
- equal:
path: spec.type
value: ClusterIP
- isNull:
path: spec.clusterIP
- it: should support NodePort type
set:
service.type: NodePort
asserts:
- equal:
path: spec.type
value: NodePort
- it: should support LoadBalancer type
set:
service.type: LoadBalancer
asserts:
- equal:
path: spec.type
value: LoadBalancer
- it: should support ExternalName type
set:
service.type: ExternalName
service.externalName: jellyfin.example.com
asserts:
- equal:
path: spec.type
value: ExternalName
- equal:
path: spec.externalName
value: jellyfin.example.com
# =============================================================================
# PORT CONFIGURATION
# =============================================================================
- it: should set custom port
set:
service.port: 9096
asserts:
- equal:
path: spec.ports[0].port
value: 9096
- it: should set custom nodePort when type is NodePort
set:
service.type: NodePort
service.nodePort: 30096
asserts:
- equal:
path: spec.ports[0].nodePort
value: 30096
- it: should not set nodePort when type is ClusterIP
set:
service.type: ClusterIP
service.nodePort: 30096
asserts:
- isNull:
path: spec.ports[0].nodePort
# Port range validation would be done by values.schema.json
# Here we just test that values are applied correctly
- it: should accept nodePort in valid range (30000-32767)
set:
service.type: NodePort
service.nodePort: 30000
asserts:
- equal:
path: spec.ports[0].nodePort
value: 30000
- it: should accept nodePort at upper range
set:
service.type: NodePort
service.nodePort: 32767
asserts:
- equal:
path: spec.ports[0].nodePort
value: 32767
# =============================================================================
# LOADBALANCER CONFIGURATION
# =============================================================================
- it: should set loadBalancerIP when specified
set:
service.type: LoadBalancer
service.loadBalancerIP: 203.0.113.42
asserts:
- equal:
path: spec.loadBalancerIP
value: 203.0.113.42
- it: should set loadBalancerClass when specified
set:
service.type: LoadBalancer
service.loadBalancerClass: metallb
asserts:
- equal:
path: spec.loadBalancerClass
value: metallb
- it: should set loadBalancerSourceRanges when specified
set:
service.type: LoadBalancer
service.loadBalancerSourceRanges:
- 10.0.0.0/8
- 192.168.0.0/16
asserts:
- contains:
path: spec.loadBalancerSourceRanges
content: 10.0.0.0/8
- contains:
path: spec.loadBalancerSourceRanges
content: 192.168.0.0/16
- it: should not set loadBalancerIP when type is ClusterIP
set:
service.type: ClusterIP
service.loadBalancerIP: 203.0.113.42
asserts:
- isNull:
path: spec.loadBalancerIP
- it: should set all loadBalancer options together
set:
service.type: LoadBalancer
service.loadBalancerIP: 203.0.113.42
service.loadBalancerClass: metallb
service.loadBalancerSourceRanges:
- 10.0.0.0/8
asserts:
- equal:
path: spec.loadBalancerIP
value: 203.0.113.42
- equal:
path: spec.loadBalancerClass
value: metallb
- contains:
path: spec.loadBalancerSourceRanges
content: 10.0.0.0/8
# =============================================================================
# ANNOTATIONS
# =============================================================================
- it: should not have annotations by default
asserts:
- isNull:
path: metadata.annotations
- it: should add custom annotations
set:
service.annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
external-dns.alpha.kubernetes.io/hostname: jellyfin.example.com
asserts:
- equal:
path: metadata.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-type
value: nlb
- equal:
path: metadata.annotations.external-dns\.alpha\.kubernetes\.io/hostname
value: jellyfin.example.com
- it: should support empty annotations object
set:
service.annotations: {}
asserts:
- isNull:
path: metadata.annotations
# =============================================================================
# IPv4/IPv6 DUAL-STACK TESTS
# =============================================================================
- it: should not set ipFamilyPolicy by default
asserts:
- isNull:
path: spec.ipFamilyPolicy
- it: should not set ipFamilies by default
asserts:
- isNull:
path: spec.ipFamilies
- it: should set ipFamilyPolicy when specified
set:
service.ipFamilyPolicy: PreferDualStack
asserts:
- equal:
path: spec.ipFamilyPolicy
value: PreferDualStack
- it: should support SingleStack policy
set:
service.ipFamilyPolicy: SingleStack
asserts:
- equal:
path: spec.ipFamilyPolicy
value: SingleStack
- it: should support RequireDualStack policy
set:
service.ipFamilyPolicy: RequireDualStack
asserts:
- equal:
path: spec.ipFamilyPolicy
value: RequireDualStack
- it: should set ipFamilies for IPv4 only
set:
service.ipFamilies:
- IPv4
asserts:
- equal:
path: spec.ipFamilies[0]
value: IPv4
- it: should set ipFamilies for IPv6 only
set:
service.ipFamilies:
- IPv6
asserts:
- equal:
path: spec.ipFamilies[0]
value: IPv6
- it: should set ipFamilies for dual-stack IPv4 primary
set:
service.ipFamilies:
- IPv4
- IPv6
asserts:
- equal:
path: spec.ipFamilies[0]
value: IPv4
- equal:
path: spec.ipFamilies[1]
value: IPv6
- it: should set ipFamilies for dual-stack IPv6 primary
set:
service.ipFamilies:
- IPv6
- IPv4
asserts:
- equal:
path: spec.ipFamilies[0]
value: IPv6
- equal:
path: spec.ipFamilies[1]
value: IPv4
- it: should work with PreferDualStack and both families
set:
service.ipFamilyPolicy: PreferDualStack
service.ipFamilies:
- IPv4
- IPv6
asserts:
- equal:
path: spec.ipFamilyPolicy
value: PreferDualStack
- equal:
path: spec.ipFamilies[0]
value: IPv4
- equal:
path: spec.ipFamilies[1]
value: IPv6
- it: should work with RequireDualStack and both families
set:
service.ipFamilyPolicy: RequireDualStack
service.ipFamilies:
- IPv6
- IPv4
asserts:
- equal:
path: spec.ipFamilyPolicy
value: RequireDualStack
- equal:
path: spec.ipFamilies[0]
value: IPv6
- equal:
path: spec.ipFamilies[1]
value: IPv4
- it: should work with SingleStack IPv6 only
set:
service.ipFamilyPolicy: SingleStack
service.ipFamilies:
- IPv6
asserts:
- equal:
path: spec.ipFamilyPolicy
value: SingleStack
- equal:
path: spec.ipFamilies[0]
value: IPv6
# =============================================================================
# EDGE CASES AND VALIDATIONS
# =============================================================================
# LoadBalancer without explicit IP (valid use case)
- it: should create LoadBalancer without loadBalancerIP
set:
service.type: LoadBalancer
asserts:
- equal:
path: spec.type
value: LoadBalancer
- isNull:
path: spec.loadBalancerIP
# NodePort without explicit nodePort (auto-assign)
- it: should create NodePort without explicit nodePort
set:
service.type: NodePort
asserts:
- equal:
path: spec.type
value: NodePort
- isNull:
path: spec.ports[0].nodePort
# ExternalName requires externalName field
- it: should set externalName for ExternalName type
set:
service.type: ExternalName
service.externalName: external.service.com
asserts:
- equal:
path: spec.type
value: ExternalName
- equal:
path: spec.externalName
value: external.service.com
# Multiple loadBalancerSourceRanges
- it: should support multiple loadBalancerSourceRanges
set:
service.type: LoadBalancer
service.loadBalancerSourceRanges:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
asserts:
- lengthEqual:
path: spec.loadBalancerSourceRanges
count: 3
# Regression: verify service always has one port
- it: should always have exactly one port defined
asserts:
- lengthEqual:
path: spec.ports
count: 1
# Regression: verify port name is always http
- it: should always name port as http
set:
service.port: 9096
asserts:
- equal:
path: spec.ports[0].name
value: http
- equal:
path: spec.ports[0].targetPort
value: http
# Combining multiple settings
- it: should combine custom port with NodePort and annotations
set:
service.type: NodePort
service.port: 9096
service.nodePort: 31096
service.annotations:
custom: annotation
asserts:
- equal:
path: spec.type
value: NodePort
- equal:
path: spec.ports[0].port
value: 9096
- equal:
path: spec.ports[0].nodePort
value: 31096
- equal:
path: metadata.annotations.custom
value: annotation
# Regression: verify protocol is always TCP
- it: should always use TCP protocol
set:
service.port: 9096
asserts:
- equal:
path: spec.ports[0].protocol
value: TCP
@@ -1,92 +0,0 @@
suite: test startup probe
templates:
- deployment.yaml
tests:
- it: should have startup probe by default
asserts:
- isNotNull:
path: spec.template.spec.containers[0].startupProbe
- it: should use tcpSocket by default
asserts:
- isNotNull:
path: spec.template.spec.containers[0].startupProbe.tcpSocket
- equal:
path: spec.template.spec.containers[0].startupProbe.tcpSocket.port
value: http
- it: should have default timing values
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.initialDelaySeconds
value: 0
- equal:
path: spec.template.spec.containers[0].startupProbe.periodSeconds
value: 10
- equal:
path: spec.template.spec.containers[0].startupProbe.failureThreshold
value: 30
- it: should allow custom initialDelaySeconds
set:
startupProbe.initialDelaySeconds: 5
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.initialDelaySeconds
value: 5
- it: should allow custom periodSeconds
set:
startupProbe.periodSeconds: 15
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.periodSeconds
value: 15
- it: should allow custom failureThreshold
set:
startupProbe.failureThreshold: 60
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.failureThreshold
value: 60
- it: should support httpGet probe
set:
startupProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 30
asserts:
- isNotNull:
path: spec.template.spec.containers[0].startupProbe.httpGet
- equal:
path: spec.template.spec.containers[0].startupProbe.httpGet.path
value: /health
- equal:
path: spec.template.spec.containers[0].startupProbe.httpGet.port
value: http
- it: should support custom port
set:
startupProbe.tcpSocket.port: 9000
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.tcpSocket.port
value: 9000
- it: should calculate correct timeout window
set:
startupProbe.periodSeconds: 10
startupProbe.failureThreshold: 30
asserts:
- equal:
path: spec.template.spec.containers[0].startupProbe.periodSeconds
value: 10
- equal:
path: spec.template.spec.containers[0].startupProbe.failureThreshold
value: 30
# Total startup time allowed: 10 * 30 = 300 seconds (5 minutes)