use indoc::indoc;
use super::{parse_ir, schema_accepts_instance, schema_for_values_yaml};
#[test]
fn prefixed_argument_splice_keeps_fallback_typed_inputs_open() {
let src = indoc! {r#"
{{- if .Values.ctrl.create }}
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
args: --log-level={{ .Values.logLevel | default "info" }}
{{- end }}
"#};
let schema = schema_for_values_yaml(
parse_ir(src),
Some(indoc! {"
ctrl:
create: true
logLevel: info
"}),
);
for instance in [
serde_json::json!({ "ctrl": { "create": true }, "logLevel": { "a": "b" } }),
serde_json::json!({ "ctrl": { "create": true }, "logLevel": ["a"] }),
serde_json::json!({ "ctrl": { "create": true }, "logLevel": "info" }),
serde_json::json!({ "ctrl": { "create": true }, "logLevel": false }),
] {
assert!(
schema_accepts_instance(&schema, &instance),
"an embedded splice totally formats every input: instance={instance}; schema={schema}"
);
}
}
#[test]
fn token_initial_splice_excludes_lists() {
let src = indoc! {r"
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
image: {{ .Values.tempo.registry }}/{{ .Values.tempo.repository }}:{{ .Values.tempo.tag }}
"};
let schema = schema_for_values_yaml(
parse_ir(src),
Some(indoc! {"
tempo:
registry: docker.io
repository: grafana/tempo
tag: latest
"}),
);
for (instance, want) in [
(serde_json::json!({ "tempo": { "registry": ["a"] } }), false),
(
serde_json::json!({ "tempo": { "registry": "docker.io" } }),
true,
),
(
serde_json::json!({ "tempo": { "registry": { "a": "b" } } }),
true,
),
(
serde_json::json!({ "tempo": { "repository": ["a"] } }),
true,
),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"a token-initial list opens a flow sequence and breaks the token: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn token_initial_splice_survives_sibling_default_arm_split() {
let src = indoc! {r"
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: test
spec:
template:
spec:
containers:
- args:
- -config.file=/conf/tempo.yaml
image: {{ .Values.tempo.registry }}/{{ .Values.tempo.repository }}:{{ .Values.tempo.tag | default .Chart.AppVersion }}
name: tempo
"};
let schema = schema_for_values_yaml(
parse_ir(src),
Some(indoc! {"
tempo:
registry: docker.io
repository: grafana/tempo
tag: latest
"}),
);
let instance = serde_json::json!({ "tempo": { "registry": ["a"] } });
assert!(
!schema_accepts_instance(&schema, &instance),
"instance={instance}; schema={schema}"
);
}
#[test]
fn double_quoted_splice_excludes_invalid_quoted_content() {
let src = indoc! {r#"
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"
"#};
let schema = schema_for_values_yaml(
parse_ir(src),
Some(indoc! {"
image:
registry: ghcr.io
repository: op
tag: v1
"}),
);
for (instance, want) in [
(
serde_json::json!({ "image": { "registry": "bad\"quote" } }),
false,
),
(
serde_json::json!({ "image": { "registry": "back\\slash" } }),
false,
),
(
serde_json::json!({ "image": { "registry": "trail\\" } }),
false,
),
(
serde_json::json!({ "image": { "registry": "esc\\\"ok" } }),
true,
),
(
serde_json::json!({ "image": { "registry": "esc\\\\ok" } }),
true,
),
(
serde_json::json!({ "image": { "registry": "ghcr.io" } }),
true,
),
(serde_json::json!({ "image": { "registry": 7 } }), true),
(
serde_json::json!({ "image": { "tag": "no\"quotes\"allowed" } }),
false,
),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"only invalid double-quoted content corrupts the token: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn single_quoted_splice_excludes_undoubled_apostrophes() {
let src = indoc! {r"
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
level: 'trace|debug|{{ .Values.defaultLevel }}'
"};
let schema = schema_for_values_yaml(parse_ir(src), Some("defaultLevel: info\n"));
for (instance, want) in [
(serde_json::json!({ "defaultLevel": "a'b" }), false),
(serde_json::json!({ "defaultLevel": "a''b" }), true),
(serde_json::json!({ "defaultLevel": "info" }), true),
(serde_json::json!({ "defaultLevel": 7 }), true),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"an undoubled apostrophe corrupts the single-quoted token: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn single_quoted_sequence_item_excludes_undoubled_apostrophes() {
let src = indoc! {r"
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: main
args:
- '-c config.json'
- '--log-level {{ .Values.defaultLevel }}'
"};
let schema = schema_for_values_yaml(parse_ir(src), Some("defaultLevel: info\n"));
for (instance, want) in [
(serde_json::json!({ "defaultLevel": "a'b" }), false),
(serde_json::json!({ "defaultLevel": "info" }), true),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"a single-quoted item claims the quoted contract: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn single_quoted_item_survives_undecodable_sibling_arms() {
let src = indoc! {r#"
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: main
args:
{{- if and (.Values.debug.enabled) (has "envoy" (splitList " " .Values.debug.verbose)) }}
- '--log-level trace'
{{- else if .Values.defaultLevel }}
- '--log-level {{ .Values.defaultLevel }}'
{{- else }}
- '--log-level info'
{{- end }}
"#};
let schema = schema_for_values_yaml(
parse_ir(src),
Some(indoc! {"
debug:
enabled: false
verbose: ~
defaultLevel: info
"}),
);
for (instance, want) in [
(
serde_json::json!({ "debug": { "enabled": false }, "defaultLevel": "a'b" }),
false,
),
(
serde_json::json!({ "debug": { "enabled": false }, "defaultLevel": "info" }),
true,
),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"the quoted contract holds under undecodable sibling arms: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn flow_style_quoted_splice_keeps_the_quoted_contract() {
let src = indoc! {r#"
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
hosts: [ "clustermesh.apiserver.{{ .Values.domain }}" ]
"#};
let schema = schema_for_values_yaml(parse_ir(src), Some("domain: mesh.cilium.io\n"));
for (instance, want) in [
(serde_json::json!({ "domain": "a\"b" }), false),
(serde_json::json!({ "domain": "mesh.cilium.io" }), true),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"flow-content quoting keeps the double-quoted contract: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn double_quoted_splice_before_inline_region_keeps_the_contract() {
let src = indoc! {r#"
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: main
env:
- name: FOLDER
value: "{{ .Values.folder }}{{- with .Values.defaultFolderName }}/{{ . }}{{- end }}"
"#};
let schema = schema_for_values_yaml(
parse_ir(src),
Some(indoc! {"
folder: /tmp/dashboards
defaultFolderName: ~
"}),
);
for (instance, want) in [
(serde_json::json!({ "folder": "a\"b" }), false),
(serde_json::json!({ "folder": "a\\qb" }), false),
(serde_json::json!({ "folder": "a\\\"b" }), true),
(serde_json::json!({ "folder": "/tmp/dashboards" }), true),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"the quoted contract holds before an inline region: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn flow_quoted_splice_after_range_variable_keeps_the_contract() {
let src = indoc! {r#"
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
hostAliases:
{{- range $cluster := .Values.clusters }}
- ip: 10.0.0.1
hostnames: [ "{{ $cluster.name }}.{{ $.Values.domain }}" ]
{{- end }}
"#};
let schema = schema_for_values_yaml(
parse_ir(src),
Some(indoc! {"
clusters: []
domain: mesh.cilium.io
"}),
);
for (instance, want) in [
(
serde_json::json!({ "clusters": [{ "name": "c1" }], "domain": "a\"b" }),
false,
),
(
serde_json::json!({ "clusters": [{ "name": "a\"b" }], "domain": "mesh.cilium.io" }),
false,
),
(
serde_json::json!({ "clusters": [{ "name": "c1" }], "domain": "mesh.cilium.io" }),
true,
),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"flow quoting binds ranged and root-scoped splices alike: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn same_line_yaml_serialized_value_rejects_structured_members() {
let src = indoc! {r"
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
{{- range .Values.zoneFiles }}
{{ .filename }}: {{ toYaml .contents | indent 4 }}
{{- end }}
"};
let schema = schema_for_values_yaml(parse_ir(src), Some("zoneFiles: []\n"));
for (instance, want) in [
(
serde_json::json!({ "zoneFiles": [{ "filename": "db.local", "contents": "zone data" }] }),
true,
),
(
serde_json::json!({ "zoneFiles": [{ "filename": "db.local", "contents": "multi\nline" }] }),
true,
),
(
serde_json::json!({ "zoneFiles": [{ "filename": "db.local", "contents": { "a": 1 } }] }),
false,
),
(
serde_json::json!({ "zoneFiles": [{ "filename": "db.local", "contents": ["a"] }] }),
false,
),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"structured members break the same-line serialized slot: \
instance={instance}; schema={schema}"
);
}
}
#[test]
fn double_quoted_splice_composites_require_safe_nested_strings() {
let src = indoc! {r#"
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
image: "{{ .Values.image.registry }}/ui:v1"
"#};
let schema = schema_for_values_yaml(
parse_ir(src),
Some(indoc! {"
image:
registry: ghcr.io
"}),
);
for (instance, want) in [
(
serde_json::json!({ "image": { "registry": { "x": "a\"b" } } }),
false,
),
(
serde_json::json!({ "image": { "registry": ["a\"b"] } }),
false,
),
(
serde_json::json!({ "image": { "registry": { "a\"b": "v" } } }),
false,
),
(
serde_json::json!({ "image": { "registry": { "x": { "y": "a\"b" } } } }),
false,
),
(
serde_json::json!({ "image": { "registry": { "x": ["a\"b"] } } }),
false,
),
(
serde_json::json!({ "image": { "registry": { "x": "ok" } } }),
true,
),
(
serde_json::json!({ "image": { "registry": ["ok", 7, true] } }),
true,
),
(
serde_json::json!({ "image": { "registry": { "x": { "y": 7 } } } }),
true,
),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"composite quoted splices constrain nested strings: \
instance={instance}; want={want}; schema={schema}"
);
}
}
#[test]
fn single_quoted_splice_composites_require_safe_nested_strings() {
let src = indoc! {r"
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: main
args:
- '--log-level {{ .Values.defaultLevel }}'
"};
let schema = schema_for_values_yaml(parse_ir(src), Some("defaultLevel: info\n"));
for (instance, want) in [
(serde_json::json!({ "defaultLevel": { "x": "a'b" } }), false),
(serde_json::json!({ "defaultLevel": ["a'b"] }), false),
(serde_json::json!({ "defaultLevel": { "x": "a''b" } }), true),
(serde_json::json!({ "defaultLevel": [7, true] }), true),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"composite single-quoted splices constrain nested strings: \
instance={instance}; want={want}; schema={schema}"
);
}
}