use color_eyre::eyre;
use indoc::indoc;
#[path = "common/chart_instances.rs"]
mod chart_instances;
#[path = "common/helm_samples.rs"]
mod helm_samples;
#[path = "common/schema_roundtrip.rs"]
mod schema_roundtrip;
#[path = "common/values_yaml.rs"]
mod values_yaml;
#[test]
fn nats_json_decoded_extra_resources_exclude_integer_iteration() -> eyre::Result<()> {
let schema = schema_roundtrip::generate_chart_schema_for_path("nats")?;
let validator = jsonschema::validator_for(&schema).expect("schema validator");
let resource = serde_json::json!({
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": { "name": "extra" },
"data": { "key": "value" }
});
let valid = chart_instances::with_override(
"nats",
serde_json::json!({ "extraResources": [resource] }),
)?;
assert!(
validator.is_valid(&valid),
"a list of Kubernetes resources survives the JSON roundtrip and renders"
);
let invalid =
chart_instances::with_override("nats", serde_json::json!({ "extraResources": 7 }))?;
assert!(
!validator.is_valid(&invalid),
"JSON decoding turns the raw integer into a non-iterable number"
);
Ok(())
}
#[test]
fn nats_tpl_yaml_sentinels_stay_nested_and_do_not_seed_root_properties() -> eyre::Result<()> {
let schema = schema_roundtrip::generate_chart_schema_for_path("nats")?;
assert!(
schema.pointer("/properties/$tplYaml").is_none()
&& schema.pointer("/properties/$tplYamlSpread").is_none(),
"the recursive tplYaml walker must not turn its nested sentinel keys into root values properties"
);
helm_samples::assert_generated_schema_accepts_helm_samples_for_path(
"nats",
&schema,
&[helm_samples::HelmValidationSample {
name: "nested tplYaml sentinel",
values_yaml: Some(indoc! {r"
extraResources:
- apiVersion: v1
kind: ConfigMap
metadata:
name:
$tplYaml: '{{ .Release.Name | quote }}'
"}),
}],
)?;
Ok(())
}
#[test]
fn nats_wrapper_results_must_be_compatible_with_their_sinks() -> eyre::Result<()> {
let schema = schema_roundtrip::generate_chart_schema_for_path("nats")?;
let validator = jsonschema::validator_for(&schema).expect("schema validator");
for (overrides, want) in [
(
serde_json::json!({ "extraResources": [{ "$tplYaml": "true" }] }),
false,
),
(
serde_json::json!({ "extraResources": [{ "$tplYaml": "4333" }] }),
false,
),
(
serde_json::json!({ "extraResources": [{ "$tplYaml": "audit" }] }),
false,
),
(
serde_json::json!({ "extraResources": [{ "$tplYaml": "[a]" }] }),
false,
),
(
serde_json::json!({ "extraResources": [{ "$tplYaml": "{{ .Values.global }}" }] }),
true,
),
(
serde_json::json!({ "extraResources": [{ "$tplYaml": true }] }),
false,
),
(
serde_json::json!({ "extraResources": [{ "$tplYamlSpread": "{a: 1}" }] }),
false,
),
(
serde_json::json!({ "extraResources": [{ "$tplYamlSpread": "audit" }] }),
false,
),
(
serde_json::json!({ "extraResources": [{ "$tplYamlSpread":
"- {apiVersion: v1, kind: ConfigMap, metadata: {name: x}}" }] }),
true,
),
(
serde_json::json!({
"podTemplate": { "topologySpreadConstraints": { "$tplYamlSpread": "[]" } }
}),
false,
),
(
serde_json::json!({
"podTemplate": { "topologySpreadConstraints": { "$tplYamlSpread": "7" } }
}),
false,
),
(
serde_json::json!({
"podTemplate": { "topologySpreadConstraints": { "$tplYamlSpread": "{a: 1}" } }
}),
true,
),
] {
let instance = chart_instances::with_override("nats", overrides.clone())?;
assert!(
validator.is_valid(&instance) == want,
"wrapper results intersect their sinks: overrides={overrides}; want={want}"
);
}
assert!(
!validator.is_valid(&serde_json::json!({ "$tplYamlSpread": "a: 1" })),
"a singleton spread wrapper at the values root aborts rendering"
);
assert!(
validator.is_valid(&serde_json::json!({ "$tplYaml": "a: 1" })),
"a singleton replace wrapper at the values root is substituted"
);
Ok(())
}
#[test]
fn nats_program_wrappers_inhabit_typed_leaves() -> eyre::Result<()> {
let schema = schema_roundtrip::generate_chart_schema_for_path("nats")?;
let validator = jsonschema::validator_for(&schema).expect("schema validator");
for (overrides, want) in [
(
serde_json::json!({
"podTemplate": { "topologySpreadConstraints": { "$tplYaml": "{}" } }
}),
true,
),
(
serde_json::json!({
"podTemplate": { "topologySpreadConstraints": { "$tplYaml": true } }
}),
false,
),
(
serde_json::json!({
"podTemplate": { "topologySpreadConstraints": { "$tplYaml": "{}", "x": 1 } }
}),
false,
),
(
serde_json::json!({ "podTemplate": { "topologySpreadConstraints": 7 } }),
false,
),
] {
let instance = chart_instances::with_override("nats", overrides.clone())?;
assert!(
validator.is_valid(&instance) == want,
"program wrappers inhabit typed leaves: overrides={overrides}; want={want}"
);
}
Ok(())
}
#[test]
fn nats_jsonpatch_ops_bind_through_the_helper_range() -> eyre::Result<()> {
let schema = schema_roundtrip::generate_chart_schema_for_path("nats")?;
let validator = jsonschema::validator_for(&schema).expect("schema validator");
for (overrides, want) in [
(
serde_json::json!({ "service": { "patch": [
{ "op": "add", "path": "/metadata/labels/x", "value": "y" }
] } }),
true,
),
(serde_json::json!({ "service": { "patch": [] } }), true),
(
serde_json::json!({ "service": { "patch": [
{ "op": "bogus", "path": "/x" }
] } }),
false,
),
(
serde_json::json!({ "service": { "patch": [{ "path": "/x" }] } }),
false,
),
(
serde_json::json!({ "service": { "patch": [
{ "$tplYamlSpread": "- {op: add, path: /metadata/labels/x, value: y}" }
] } }),
true,
),
] {
let instance = chart_instances::with_override("nats", overrides.clone())?;
assert!(
validator.is_valid(&instance) == want,
"jsonpatch ops bind through the helper range: overrides={overrides}; want={want}"
);
}
Ok(())
}
#[test]
fn nats_jsonpatch_per_op_requirements_bind_through_the_helper_range() -> eyre::Result<()> {
let schema = schema_roundtrip::generate_chart_schema_for_path("nats")?;
let validator = jsonschema::validator_for(&schema).expect("schema validator");
for (patch, want) in [
(
serde_json::json!({ "op": "copy", "path": "/spec/selector2", "from": "/spec/selector" }),
true,
),
(
serde_json::json!({ "op": "move", "path": "/spec/selector2", "from": "/spec/selector" }),
true,
),
(
serde_json::json!({ "op": "remove", "path": "/spec/selector" }),
true,
),
(
serde_json::json!({ "op": "add", "path": "/metadata/labels/x", "value": "y" }),
true,
),
(
serde_json::json!({ "op": "copy", "path": "/spec/selector2" }),
false,
),
(
serde_json::json!({ "op": "move", "path": "/spec/selector2" }),
false,
),
(
serde_json::json!({ "op": "add", "path": "/metadata/labels/x" }),
false,
),
(
serde_json::json!({ "op": "test", "path": "/spec/selector" }),
false,
),
] {
let overrides = serde_json::json!({ "service": { "patch": [patch] } });
let instance = chart_instances::with_override("nats", overrides.clone())?;
assert!(
validator.is_valid(&instance) == want,
"per-op requirements bind through the helper range: overrides={overrides}; want={want}"
);
}
Ok(())
}
#[test]
fn nats_pre_rewrite_strict_consumers_reject_wrapper_programs() -> eyre::Result<()> {
let schema = schema_roundtrip::generate_chart_schema_for_path("nats")?;
let validator = jsonschema::validator_for(&schema).expect("schema validator");
for (overrides, want) in [
(
serde_json::json!({ "nameOverride": { "$tplYaml": "x" } }),
false,
),
(
serde_json::json!({ "fullnameOverride":
{ "$tplYaml": "{{ .Release.Name | quote }}" } }),
false,
),
(serde_json::json!({ "nameOverride": "plain" }), true),
(
serde_json::json!({ "configMap": { "name":
{ "$tplYaml": "{{ .Release.Name | quote }}" } } }),
true,
),
] {
let instance = chart_instances::with_override("nats", overrides.clone())?;
assert!(
validator.is_valid(&instance) == want,
"pre-rewrite strict consumers exclude wrappers: overrides={overrides}; want={want}"
);
}
Ok(())
}