use indoc::indoc;
use super::{parse_ir, parse_ir_with_helpers, schema_accepts_instance, schema_for_values_yaml};
const ENGINE_HELPERS: &str = indoc! {r#"
{{- define "test.tplValues" -}}
{{- $doc := .doc -}}
{{- if and (eq (kindOf $doc) "map") (eq (len $doc) 1) (hasKey $doc "$tplYaml") -}}
{{- $tpl := get $doc "$tplYaml" -}}
{{- toJson (dict "doc" (fromYaml (tpl $tpl .ctx))) -}}
{{- else -}}
{{- toJson (dict "doc" $doc) -}}
{{- end -}}
{{- end -}}
"#};
const ENGINE_SRC: &str = indoc! {r#"
{{- $values := get (include "test.tplValues" (dict "doc" .Values "ctx" $) | fromJson) "doc" }}
{{- $_ := set . "Values" $values }}
apiVersion: v1
kind: Service
metadata:
name: test
spec:
selector:
app: test
ports:
- port: {{ .Values.port }}
"#};
#[test]
fn detected_engine_accepts_program_wrappers_at_value_nodes() {
let schema = schema_for_values_yaml(
parse_ir_with_helpers(ENGINE_SRC, ENGINE_HELPERS),
Some("port: 4222\n"),
);
for (instance, want) in [
(
serde_json::json!({ "port": { "$tplYaml": "{{ add 4000 333 }}" } }),
true,
),
(serde_json::json!({ "port": { "$tplYaml": "4333" } }), true),
(serde_json::json!({ "port": { "$tplYaml": true } }), false),
(
serde_json::json!({ "port": { "$tplYaml": { "$tplYaml": "1" } } }),
false,
),
(
serde_json::json!({ "port": { "$tplYaml": "1", "x": 2 } }),
false,
),
(serde_json::json!({ "port": 4222 }), true),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"wrapper alternatives ride detected engine conventions: \
instance={instance}; want={want}; schema={schema}"
);
}
}
#[test]
fn program_wrappers_do_not_rewrite_truthiness_definitions() {
let source = indoc! {r#"
{{- $values := get (include "test.tplValues" (dict "doc" .Values "ctx" $) | fromJson) "doc" }}
{{- $_ := set . "Values" $values }}
{{- if .Values.port }}
apiVersion: v1
kind: Service
metadata:
name: test
spec:
selector:
app: test
ports:
- port: {{ .Values.port }}
{{- end }}
"#};
let schema = schema_for_values_yaml(
parse_ir_with_helpers(source, ENGINE_HELPERS),
Some("port: 4222\n"),
);
for (instance, want) in [
(serde_json::json!({ "port": { "$tplYaml": "4333" } }), true),
(serde_json::json!({ "port": { "$tplYaml": true } }), false),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"truthiness tests must see the raw wrapper map while the value schema validates its program: \
instance={instance}; want={want}; schema={schema}"
);
}
}
const SPREAD_ENGINE_HELPERS: &str = indoc! {r#"
{{- define "test.tplValues" -}}
{{- $doc := .doc -}}
{{- if and (eq (kindOf $doc) "map") (eq (len $doc) 1) (or (hasKey $doc "$tplYaml") (hasKey $doc "$tplYamlSpread")) -}}
{{- $tpl := get $doc "$tplYaml" -}}
{{- if hasKey $doc "$tplYamlSpread" -}}
{{- if eq .path "/" -}}
{{- fail "cannot spread on the root object" -}}
{{- end -}}
{{- $tpl = get $doc "$tplYamlSpread" -}}
{{- end -}}
{{- toJson (dict "doc" (fromYaml (tpl $tpl .ctx))) -}}
{{- else -}}
{{- toJson (dict "doc" $doc) -}}
{{- end -}}
{{- end -}}
"#};
const SPREAD_ENGINE_SRC: &str = indoc! {r#"
apiVersion: v1
kind: ConfigMap
metadata:
name: test
labels: {{ .Values.labels | toYaml | nindent 4 }}
data:
port: {{ .Values.port }}
{{- $values := get (include "test.tplValues" (dict "doc" .Values "ctx" $ "path" "/") | fromJson) "doc" }}
{{- $_ := set . "Values" $values }}
"#};
#[test]
fn wrapper_program_results_must_be_compatible_with_node_and_parent() {
let schema = schema_for_values_yaml(
parse_ir_with_helpers(SPREAD_ENGINE_SRC, SPREAD_ENGINE_HELPERS),
Some(indoc! {"
port: 4222
labels:
app: test
"}),
);
for (instance, want) in [
(
serde_json::json!({ "labels": { "$tplYaml": "true" } }),
false,
),
(
serde_json::json!({ "labels": { "$tplYaml": "4333" } }),
false,
),
(
serde_json::json!({ "labels": { "$tplYaml": "[a]" } }),
false,
),
(
serde_json::json!({ "labels": { "$tplYaml": "app: x" } }),
true,
),
(
serde_json::json!({ "labels": { "$tplYaml": "{{ .Release.Name }}" } }),
true,
),
(
serde_json::json!({ "labels": { "$tplYamlSpread": "[]" } }),
false,
),
(
serde_json::json!({ "labels": { "$tplYamlSpread": "- a" } }),
false,
),
(
serde_json::json!({ "labels": { "$tplYamlSpread": "audit" } }),
false,
),
(
serde_json::json!({ "labels": { "$tplYamlSpread": "{a: 1}" } }),
true,
),
(
serde_json::json!({ "labels": { "$tplYamlSpread": "null" } }),
true,
),
(
serde_json::json!({ "labels": { "$tplYamlSpread": "{{ .Values.x }}" } }),
true,
),
(serde_json::json!({ "labels": { "app": "x" } }), true),
(
serde_json::json!({ "labels": { "$tplYaml": "true", "x": "y" } }),
true,
),
] {
assert!(
schema_accepts_instance(&schema, &instance) == want,
"wrapper result compatibility: instance={instance}; want={want}; schema={schema}"
);
}
}
#[test]
fn without_an_engine_wrapper_maps_stay_ordinary_objects() {
let src = indoc! {r"
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
port: {{ .Values.port }}
"};
let schema = schema_for_values_yaml(parse_ir(src), Some("port: 4222\n"));
assert!(
schema_accepts_instance(
&schema,
&serde_json::json!({ "port": { "$tplYaml": "4333" } })
),
"without an engine the map is ordinary input, and its safe Go-formatted text renders: schema={schema}"
);
assert!(
!schema_accepts_instance(
&schema,
&serde_json::json!({ "port": { "$tplYaml": "a: b" } })
),
"ordinary maps still obey the plain-scalar text grammar: schema={schema}"
);
}