use super::*;
use indoc::indoc;
#[test]
fn default_literal_fallback_keeps_helm_empty_inputs_open_for_parsers() {
let src = indoc! {r#"
{{- if semverCompare ">=1.8" (default "1.8" .Values.upgradeCompatibility) }}
apiVersion: v1
kind: ConfigMap
metadata:
name: test
{{- end }}
"#};
let schema = schema_for_values_yaml(parse_ir(src), None);
for instance in [
serde_json::json!({}),
serde_json::json!({ "upgradeCompatibility": null }),
serde_json::json!({ "upgradeCompatibility": false }),
serde_json::json!({ "upgradeCompatibility": 0 }),
serde_json::json!({ "upgradeCompatibility": "" }),
serde_json::json!({ "upgradeCompatibility": {} }),
serde_json::json!({ "upgradeCompatibility": [] }),
serde_json::json!({ "upgradeCompatibility": "1.14" }),
serde_json::json!({ "upgradeCompatibility": "v1.2.3-rc.1" }),
] {
assert!(
schema_accepts_instance(&schema, &instance),
"a Helm-empty input selects the fallback and never reaches the parser: \
instance={instance}; schema={schema}"
);
}
for instance in [
serde_json::json!({ "upgradeCompatibility": "garbage" }),
serde_json::json!({ "upgradeCompatibility": { "a": 1 } }),
serde_json::json!({ "upgradeCompatibility": [1] }),
serde_json::json!({ "upgradeCompatibility": true }),
] {
assert!(
!schema_accepts_instance(&schema, &instance),
"a truthy non-semver input survives selection and aborts semverCompare: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn helper_default_fallback_keeps_helm_empty_inputs_open_for_string_consumers() {
let helpers = indoc! {r#"
{{- define "chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "chart.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
"#};
let src = indoc! {r#"
{{- if .Values.config.create }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "chart.fullname" . }}
labels:
app: {{ include "chart.name" . }}
{{- end }}
"#};
let schema = schema_for_values_yaml(
parse_ir_with_helpers(src, helpers),
Some(indoc! {r#"
nameOverride: ""
config:
create: true
"#}),
);
for instance in [
serde_json::json!({ "config": { "create": true } }),
serde_json::json!({ "nameOverride": null, "config": { "create": true } }),
serde_json::json!({ "nameOverride": false, "config": { "create": false } }),
serde_json::json!({ "nameOverride": "", "config": { "create": true } }),
serde_json::json!({ "nameOverride": {}, "config": { "create": false } }),
serde_json::json!({ "nameOverride": "custom-name", "config": { "create": true } }),
serde_json::json!({ "nameOverride": false, "config": { "create": true } }),
serde_json::json!({ "nameOverride": {}, "config": { "create": true } }),
serde_json::json!({ "nameOverride": "custom", "config": { "create": true } }),
] {
assert!(
schema_accepts_instance(&schema, &instance),
"Helm-empty overrides take the chart-name fallback and render: \
instance={instance}; schema={schema}"
);
}
assert!(
!schema_accepts_instance(
&schema,
&serde_json::json!({ "nameOverride": { "a": 1 }, "config": { "create": true } })
),
"a live truthy map survives selection and aborts trunc: {schema}"
);
}
#[test]
fn truthy_guarded_read_keeps_helm_falsy_inputs_open() {
let helpers = indoc! {r#"
{{- define "chart.namespace" -}}
{{- if .Values.namespaceOverride -}}
{{- .Values.namespaceOverride -}}
{{- else -}}
{{- .Release.Namespace -}}
{{- end -}}
{{- end -}}
"#};
let src = indoc! {r#"
{{- if .Values.rbac.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: test
namespace: {{ include "chart.namespace" . }}
{{- end }}
"#};
let schema = schema_for_values_yaml(
parse_ir_with_helpers(src, helpers),
Some(indoc! {r#"
namespaceOverride: ""
rbac:
create: true
"#}),
);
for instance in [
serde_json::json!({ "namespaceOverride": false, "rbac": { "create": true } }),
serde_json::json!({ "namespaceOverride": "", "rbac": { "create": true } }),
serde_json::json!({ "namespaceOverride": {}, "rbac": { "create": true } }),
serde_json::json!({ "namespaceOverride": "custom-ns", "rbac": { "create": true } }),
serde_json::json!({ "namespaceOverride": false, "rbac": { "create": false } }),
serde_json::json!({ "namespaceOverride": "ns", "rbac": { "create": true } }),
] {
assert!(
schema_accepts_instance(&schema, &instance),
"falsy overrides skip the truthy arm and render the release namespace: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn liveness_gated_helper_keeps_helm_empty_fallback_inputs_open() {
let helpers = indoc! {r#"
{{- define "cloudnative-pg.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
"#};
let src = indoc! {r#"
{{- if .Values.config.create }}
apiVersion: v1
kind: ConfigMap
metadata:
name: config-name
data:
chartname: {{ include "cloudnative-pg.name" . }}
{{- end }}
"#};
let schema = schema_for_values_yaml(
parse_ir_with_helpers(src, helpers),
Some(indoc! {r#"
nameOverride: ""
fullnameOverride: ""
namespaceOverride: ""
config:
create: true
"#}),
);
for instance in [
serde_json::json!({ "nameOverride": false, "config": { "create": true } }),
serde_json::json!({ "nameOverride": {}, "config": { "create": true } }),
] {
assert!(
schema_accepts_instance(&schema, &instance),
"helm-empty stays open: instance={instance}; schema={schema}"
);
}
}
#[test]
fn trim_suffix_projects_the_parser_domain_through_the_affix_preimage() {
let helpers = indoc! {r#"
{{- define "repro.agentVersion" -}}
{{- $version := .Values.agents.tag | toString | trimSuffix "-jmx" -}}
{{- if semverCompare "<7.67.0" $version -}}
{{- fail "agent versions before 7.67.0 are not supported" -}}
{{- end -}}
{{- $version -}}
{{- end -}}
"#};
let src = indoc! {r#"
apiVersion: v1
kind: ConfigMap
metadata:
name: cm
data:
version: {{ include "repro.agentVersion" . | quote }}
"#};
let values_yaml = indoc! {r#"
agents:
tag: "7.80.1"
"#};
let schema = schema_for_values_yaml(parse_ir_with_helpers(src, helpers), Some(values_yaml));
for (tag, want, label) in [
(serde_json::json!("7.80.1"), true, "a plain version parses"),
(
serde_json::json!("7.80.1-jmx"),
true,
"the jmx-suffixed version is trimmed before parsing",
),
(
serde_json::json!("junk"),
false,
"a non-version aborts the parser",
),
(
serde_json::json!("junk-jmx"),
false,
"trimming the suffix still leaves a non-version",
),
(
serde_json::json!("v-jmx-7.80.1"),
false,
"a mid-string token no longer exempts the raw value",
),
] {
let instance = serde_json::json!({ "agents": { "tag": tag } });
assert!(
schema_accepts_instance(&schema, &instance) == want,
"trim-suffix parser preimage ({label}): instance={instance}; schema={schema}"
);
}
}
#[test]
fn falsy_reassignment_to_another_source_scopes_the_parser_to_truthy_values() {
let helpers = indoc! {r#"
{{- define "repro.agentVersion" -}}
{{- $version := .Values.agents.tag | toString | trimSuffix "-jmx" -}}
{{- if eq $version "latest" -}}
{{- $version = "7.80.1" -}}
{{- end -}}
{{- $version -}}
{{- end -}}
{{- define "repro.gatewayImage" -}}
{{- $imageTag := .Values.gw.tag -}}
{{- if not $imageTag -}}
{{- $imageTag = include "repro.agentVersion" . -}}
{{- end -}}
{{- $imageTag = $imageTag | toString -}}
{{- if semverCompare "<7.67.0" $imageTag -}}
{{- fail "agent versions before 7.67.0 are not supported" -}}
{{- end -}}
{{- $imageTag -}}
{{- end -}}
"#};
let src = indoc! {r#"
{{- if .Values.gw.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: gw
spec:
template:
spec:
containers:
- name: gw
image: "agent:{{ include "repro.gatewayImage" . }}"
{{- end }}
"#};
let values_yaml = indoc! {r#"
gw:
enabled: false
tag: ""
agents:
tag: "7.80.1"
"#};
let ir = parse_ir_with_helpers(src, helpers);
let schema = schema_for_values_yaml(ir, Some(values_yaml));
for (tag, want) in [
(serde_json::json!(""), true),
(serde_json::json!(null), true),
(serde_json::json!("7.70.0"), true),
(serde_json::json!("junk"), false),
] {
let instance = serde_json::json!({
"gw": { "enabled": true, "tag": tag },
"agents": { "tag": "7.80.1" },
});
assert!(
schema_accepts_instance(&schema, &instance) == want,
"falsy-reassignment parser scoping: instance={instance}; schema={schema}"
);
}
}
#[test]
fn digest_strip_and_v_trim_compose_the_semver_bound_exactly() {
let src = indoc! {r#"
{{- if ne .Values.tag "latest" }}
{{- if regexReplaceAll "@.*$" .Values.tag "" | trimPrefix "v" | semverCompare "<0.9.0" }}
{{- fail "tag must be >= v0.9.0" }}
{{- end }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
name: cm
data:
tag: {{ .Values.tag | quote }}
"#};
let values_yaml = indoc! {r#"
tag: "latest"
"#};
let schema = schema_for_values_yaml(parse_ir(src), Some(values_yaml));
for (tag, want, label) in [
(
serde_json::json!("v0.13.5"),
true,
"a valid version renders",
),
(
serde_json::json!("v0.13.5@sha256:abc"),
true,
"the digest suffix is stripped before parsing",
),
(serde_json::json!("0.9.0"), true, "the bound itself passes"),
(
serde_json::json!("latest"),
true,
"the ne-excluded sentinel never reaches the gate",
),
(
serde_json::json!("garbage"),
false,
"an unparsable core aborts semverCompare",
),
(
serde_json::json!("v0.1.0"),
false,
"a version below the bound hits the fail",
),
(
serde_json::json!("0.1.0@sha256:abc"),
false,
"the bound applies to the digest-stripped core",
),
(
serde_json::json!("vv0.8.0"),
false,
"one trimmed v still leaves a below-bound version",
),
] {
let instance = serde_json::json!({ "tag": tag });
assert!(
schema_accepts_instance(&schema, &instance) == want,
"digest-strip/v-trim semver bound ({label}): instance={instance}; schema={schema}"
);
}
}