use std::collections::{BTreeMap, BTreeSet, HashMap};
use helm_schema_ast::{DefineIndex, parse_action_expressions};
use test_util::prelude::sim_assert_eq;
use crate::abstract_value::AbstractValue;
use crate::analysis_db::IrAnalysisDb;
use crate::fragment_expr_eval::FragmentEvalContext;
use crate::helper_meta::HelperOutputMeta;
use crate::{Guard, GuardValue};
use super::*;
macro_rules! values_path {
($path:expr $(,)?) => {
AbstractValue::ValuesPath(helm_schema_core::ValuesPath::parse(&$path))
};
}
fn parse_condition(text: &str) -> Vec<Guard> {
let wrapped = format!("{{{{ {text} }}}}");
let Some(top) = parse_action_expressions(&wrapped).into_iter().next() else {
return Vec::new();
};
condition_context(HashMap::new())
.condition_predicate_expr(&top)
.contract_guards()
.unwrap_or_default()
}
fn parse_condition_with_template_bindings(
text: &str,
template_bindings: HashMap<String, AbstractValue>,
) -> Vec<Guard> {
parse_condition_with_template_facts(text, template_bindings, HashMap::new())
}
#[test]
fn invalid_kind_is_absence_or_null_instead_of_truthiness() {
let invalid = parse_action_expressions(r#"{{ kindIs "invalid" .Values.hostUsers }}"#)
.into_iter()
.next()
.expect("condition expression");
let predicate = condition_context(HashMap::new()).condition_predicate_expr(&invalid);
sim_assert_eq!(
have: predicate,
want: Predicate::Or(vec![
Predicate::from(Guard::Eq {
path: helm_schema_core::ValuesPath::parse("hostUsers"),
value: GuardValue::Null,
}),
Predicate::from(Guard::Absent {
path: helm_schema_core::ValuesPath::parse("hostUsers"),
}),
]),
);
let present = parse_action_expressions(r#"{{ not (kindIs "invalid" .Values.hostUsers) }}"#)
.into_iter()
.next()
.expect("condition expression");
let predicate = condition_context(HashMap::new()).condition_predicate_expr(&present);
assert!(
matches!(predicate, Predicate::Not(ref inner) if matches!(inner.as_ref(), Predicate::Or(_))),
"negating invalid must preserve required-and-non-null semantics: {predicate:#?}"
);
}
#[test]
fn invalid_kind_guard_abstains_for_a_meta_selected_subject() {
let template_bindings = HashMap::from([("selected".to_string(), values_path!("value"))]);
let mut metadata = HelperOutputMeta {
input_identity: true,
..HelperOutputMeta::default()
};
metadata.conjoin_branches(&std::collections::BTreeSet::from([Predicate::truthy_path(
"enabled",
)]));
let template_output_meta = HashMap::from([(
"selected".to_string(),
BTreeMap::from([(helm_schema_core::ValuesPath::parse("value"), metadata)]),
)]);
sim_assert_eq!(
have: parse_condition_with_template_facts(
r#"kindIs "invalid" $selected"#,
template_bindings,
template_output_meta,
),
want: vec![Guard::Truthy {
path: helm_schema_core::ValuesPath::parse("value"),
}],
);
}
fn parse_condition_with_template_facts(
text: &str,
template_bindings: HashMap<String, AbstractValue>,
template_output_meta: HashMap<String, BTreeMap<helm_schema_core::ValuesPath, HelperOutputMeta>>,
) -> Vec<Guard> {
let wrapped = format!("{{{{ {text} }}}}");
let Some(top) = parse_action_expressions(&wrapped).into_iter().next() else {
return Vec::new();
};
condition_context_with_output_meta(template_bindings, template_output_meta)
.condition_predicate_expr(&top)
.contract_guards()
.unwrap_or_default()
}
fn condition_context(
template_bindings: HashMap<String, AbstractValue>,
) -> ValuePathContext<'static> {
condition_context_with_output_meta(template_bindings, HashMap::new())
}
fn condition_context_with_output_meta(
template_bindings: HashMap<String, AbstractValue>,
template_output_meta: HashMap<String, BTreeMap<helm_schema_core::ValuesPath, HelperOutputMeta>>,
) -> ValuePathContext<'static> {
condition_context_with_defines(template_bindings, template_output_meta, DefineIndex::new())
}
fn condition_context_with_defines(
template_bindings: HashMap<String, AbstractValue>,
template_output_meta: HashMap<String, BTreeMap<helm_schema_core::ValuesPath, HelperOutputMeta>>,
defines: DefineIndex,
) -> ValuePathContext<'static> {
let root_bindings = Box::leak(Box::new(HashMap::new()));
let range_domains = Box::leak(Box::new(HashMap::new()));
let get_bindings = Box::leak(Box::new(HashMap::new()));
let template_default_paths = Box::leak(Box::new(HashMap::new()));
let template_output_meta: &'static HashMap<
String,
BTreeMap<helm_schema_core::ValuesPath, HelperOutputMeta>,
> = Box::leak(Box::new(template_output_meta));
let template_scalar_dispatches = Box::leak(Box::new(HashMap::new()));
let template_truthy_reductions = Box::leak(Box::new(HashMap::new()));
let template_truthiness_abstentions = Box::leak(Box::new(BTreeSet::new()));
let defines = Box::leak(Box::new(defines));
let analysis_db = Box::leak(Box::new(IrAnalysisDb::new(defines)));
let typeof_bindings = Box::leak(Box::new(HashMap::new()));
ValuePathContext {
root_bindings,
root_truthy_predicates: Box::leak(Box::new(HashMap::new())),
root_value_dispatches: Box::leak(Box::new(HashMap::new())),
root_field_semantics_on_current_dot: false,
pipeline_bound_bindings: template_bindings.keys().cloned().collect(),
template_bindings,
template_scalar_dispatches,
range_domains,
get_bindings,
template_default_paths,
template_output_meta,
template_truthy_reductions,
template_truthiness_abstentions,
typeof_bindings,
int_cast_bindings: Box::leak(Box::new(HashMap::new())),
fragment_context: FragmentEvalContext::new(analysis_db),
current_dot_fragment: None,
current_dot_binding: None,
}
}
#[test]
fn negated_include_uses_rendered_text_truthiness() {
let mut defines = DefineIndex::new();
defines.add_file_source(
"<inline:rendered-false>",
indoc::indoc! {r#"
{{- define "rendered.false" -}}
{{- if .Values.flag -}}
false
{{- else -}}
false
{{- end -}}
{{- end -}}
"#},
);
let expr = parse_action_expressions(r#"{{ not (include "rendered.false" .) }}"#)
.into_iter()
.next()
.expect("condition expression");
sim_assert_eq!(
have: condition_context_with_defines(HashMap::new(), HashMap::new(), defines)
.condition_predicate_expr(&expr),
want: Predicate::False,
);
}
#[test]
fn truthy_simple_path() {
sim_assert_eq!(
have: parse_condition(".Values.X"),
want: vec![Guard::Truthy { path: helm_schema_core::ValuesPath::parse("X") }],
);
}
#[test]
fn not_simple_path() {
sim_assert_eq!(
have: parse_condition("not .Values.X"),
want: vec![Guard::Not { path: helm_schema_core::ValuesPath::parse("X") }],
);
}
#[test]
fn absent_custom_root_field_is_false_until_set() {
let expr = parse_action_expressions("{{ not .defaultValuesSet }}")
.into_iter()
.next()
.expect("parsed root-field condition");
let mut context = condition_context(HashMap::new());
context.current_dot_binding = Some(AbstractValue::RootContext);
assert!(context.condition_lowering_is_faithful(&expr));
sim_assert_eq!(
have: context.condition_predicate_expr(&expr),
want: Predicate::True,
);
}
#[test]
fn composite_truthiness_is_faithful_only_when_its_exact_decoder_succeeds() {
let undecodable_merge =
AbstractValue::MergedLayers(vec![values_path!("base"), AbstractValue::Unknown]);
let undecodable_selection =
AbstractValue::FirstTruthy(vec![values_path!("primary"), AbstractValue::Unknown]);
let bindings = HashMap::from([
(
"context".to_string(),
AbstractValue::Dict(BTreeMap::from([("merged".to_string(), undecodable_merge)])),
),
("selected".to_string(), undecodable_selection),
]);
let context = condition_context(bindings);
let merged = parse_action_expressions("{{ $context.merged }}")
.into_iter()
.next()
.expect("merged selector condition");
let selected = parse_action_expressions("{{ $selected }}")
.into_iter()
.next()
.expect("first-truthy local condition");
assert!(!context.condition_lowering_is_faithful(&merged));
assert!(!context.condition_lowering_is_faithful(&selected));
assert!(context.condition_lowering_is_usable_for_control(&merged));
assert!(context.condition_lowering_is_usable_for_control(&selected));
}
#[test]
fn decodable_composite_truthiness_remains_faithful() {
let bindings = HashMap::from([
(
"merged".to_string(),
AbstractValue::MergedLayers(vec![values_path!("base"), values_path!("override")]),
),
(
"selected".to_string(),
AbstractValue::FirstTruthy(vec![values_path!("primary"), values_path!("fallback")]),
),
]);
let context = condition_context(bindings);
let merged = parse_action_expressions("{{ $merged }}")
.into_iter()
.next()
.expect("merged local condition");
let selected = parse_action_expressions("{{ $selected }}")
.into_iter()
.next()
.expect("first-truthy local condition");
assert!(context.condition_lowering_is_faithful(&merged));
assert!(context.condition_lowering_is_faithful(&selected));
}
#[test]
fn quoted_empty_membership_preserves_false_and_zero_as_live_values() {
let expr = parse_action_expressions(
r#"{{ not (has (quote .Values.global.logLevel) (list "" (quote ""))) }}"#,
)
.into_iter()
.next()
.expect("condition expression");
sim_assert_eq!(
have: condition_context(HashMap::new()).condition_predicate_expr(&expr),
want: Predicate::Not(Box::new(Predicate::Or(vec![
Predicate::from(Guard::Absent {
path: helm_schema_core::ValuesPath::parse("global.logLevel"),
}),
Predicate::from(Guard::Eq {
path: helm_schema_core::ValuesPath::parse("global.logLevel"),
value: GuardValue::Null,
}),
Predicate::from(Guard::Eq {
path: helm_schema_core::ValuesPath::parse("global.logLevel"),
value: GuardValue::string(""),
}),
]))),
);
}
#[test]
fn or_with_two_paths_emits_or_guard() {
sim_assert_eq!(
have: parse_condition("or .Values.A .Values.B"),
want: vec![Guard::Or {
paths: vec![
helm_schema_core::ValuesPath::parse("A"),
helm_schema_core::ValuesPath::parse("B"),
],
}],
);
}
#[test]
fn or_paths_are_sorted() {
sim_assert_eq!(
have: parse_condition("or .Values.z .Values.a"),
want: vec![Guard::Or {
paths: vec![
helm_schema_core::ValuesPath::parse("a"),
helm_schema_core::ValuesPath::parse("z"),
],
}],
);
}
#[test]
fn or_with_nested_helper_calls() {
sim_assert_eq!(
have: parse_condition("or (has .Values.A 1) (has .Values.B 2)"),
want: vec![Guard::Or {
paths: vec![
helm_schema_core::ValuesPath::parse("A"),
helm_schema_core::ValuesPath::parse("B"),
],
}],
);
}
#[test]
fn or_with_equality_preserves_typed_alternative() {
sim_assert_eq!(
have: parse_condition(r#"or (eq .Values.mode "prod") .Values.enabled"#),
want: vec![Guard::AnyOf {
alternatives: vec![
vec![Guard::Truthy {
path: helm_schema_core::ValuesPath::parse("enabled"),
}],
vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("mode"),
value: GuardValue::string("prod"),
}],
],
}],
);
}
#[test]
fn or_with_nested_and_preserves_conjunctive_alternative() {
sim_assert_eq!(
have: parse_condition(r#"or (and .Values.a .Values.b) (eq .Values.mode "prod")"#),
want: vec![Guard::AnyOf {
alternatives: vec![
vec![
Guard::Truthy { path: helm_schema_core::ValuesPath::parse("a") },
Guard::Truthy { path: helm_schema_core::ValuesPath::parse("b") },
],
vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("mode"),
value: GuardValue::string("prod"),
}],
],
}],
);
}
#[test]
fn eq_with_string_literal() {
sim_assert_eq!(
have: parse_condition(r#"eq .Values.X "value""#),
want: vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("X"),
value: GuardValue::string("value"),
}],
);
}
#[test]
fn eq_with_string_literal_containing_phantom_path() {
sim_assert_eq!(
have: parse_condition(r#"eq .Values.X ".Values.fake""#),
want: vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("X"),
value: GuardValue::string(".Values.fake"),
}],
);
}
#[test]
fn eq_with_bool_literal_preserves_exact_comparison() {
sim_assert_eq!(
have: parse_condition("eq .Values.enabled false"),
want: vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("enabled"),
value: GuardValue::Bool(false),
}],
);
}
#[test]
fn eq_with_int_literal_preserves_exact_comparison() {
sim_assert_eq!(
have: parse_condition("eq .Values.replicas 3"),
want: vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("replicas"),
value: GuardValue::Int(3),
}],
);
}
#[test]
fn eq_with_nil_literal_preserves_exact_comparison() {
sim_assert_eq!(
have: parse_condition("eq .Values.image.tag nil"),
want: vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("image.tag"),
value: GuardValue::Null,
}],
);
}
#[test]
fn eq_compare_two_values_falls_through_to_truthy() {
sim_assert_eq!(
have: parse_condition("eq .Values.X .Values.Y"),
want: vec![
Guard::Truthy { path: helm_schema_core::ValuesPath::parse("X") },
Guard::Truthy { path: helm_schema_core::ValuesPath::parse("Y") },
],
);
}
#[test]
fn ne_with_string_literal_emits_not_eq() {
sim_assert_eq!(
have: parse_condition(r#"ne .Values.X "value""#),
want: vec![Guard::NotEq {
path: helm_schema_core::ValuesPath::parse("X"),
value: GuardValue::string("value"),
}],
);
}
#[test]
fn not_eq_literal_projects_to_not_eq() {
sim_assert_eq!(
have: parse_condition(r#"not (eq .Values.mode "disabled")"#),
want: vec![Guard::NotEq {
path: helm_schema_core::ValuesPath::parse("mode"),
value: GuardValue::string("disabled"),
}],
);
}
#[test]
fn not_ne_literal_projects_to_eq() {
sim_assert_eq!(
have: parse_condition(r#"not (ne .Values.mode "disabled")"#),
want: vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("mode"),
value: GuardValue::string("disabled"),
}],
);
}
#[test]
fn and_falls_through_to_per_path_truthy() {
sim_assert_eq!(
have: parse_condition("and .Values.A .Values.B"),
want: vec![
Guard::Truthy { path: helm_schema_core::ValuesPath::parse("A") },
Guard::Truthy { path: helm_schema_core::ValuesPath::parse("B") },
],
);
}
#[test]
fn and_with_parens_falls_through_to_per_path_truthy() {
sim_assert_eq!(
have: parse_condition("and (.Values.A) (.Values.B)"),
want: vec![
Guard::Truthy { path: helm_schema_core::ValuesPath::parse("A") },
Guard::Truthy { path: helm_schema_core::ValuesPath::parse("B") },
],
);
}
#[test]
fn and_preserves_nested_not_guard() {
sim_assert_eq!(
have: parse_condition(
"and .Values.prometheus.enabled (not .Values.prometheus.podmonitor.enabled)"
),
want: vec![
Guard::Truthy {
path: helm_schema_core::ValuesPath::parse("prometheus.enabled")
},
Guard::Not {
path: helm_schema_core::ValuesPath::parse("prometheus.podmonitor.enabled")
},
],
);
}
#[test]
fn and_preserves_nested_or_guard() {
sim_assert_eq!(
have: parse_condition(
"and .Values.ldap.enabled (or .Values.ldap.bind_password .Values.ldap.bindpw)"
),
want: vec![
Guard::Truthy {
path: helm_schema_core::ValuesPath::parse("ldap.enabled")
},
Guard::Or {
paths: vec![
helm_schema_core::ValuesPath::parse("ldap.bind_password"),
helm_schema_core::ValuesPath::parse("ldap.bindpw"),
]
},
],
);
}
#[test]
fn empty_path_is_falsey_guard() {
sim_assert_eq!(
have: parse_condition("empty .Values.service.loadBalancerIP"),
want: vec![Guard::Not {
path: helm_schema_core::ValuesPath::parse("service.loadBalancerIP")
}],
);
}
#[test]
fn not_empty_path_is_truthy_guard() {
sim_assert_eq!(
have: parse_condition("not (empty .Values.service.loadBalancerIP)"),
want: vec![Guard::Truthy {
path: helm_schema_core::ValuesPath::parse("service.loadBalancerIP")
}],
);
}
#[test]
fn not_or_paths_uses_demorgan_negated_guards() {
sim_assert_eq!(
have: parse_condition("not (or .Values.serviceMonitor.enabled .Values.podMonitor.enabled)"),
want: vec![
Guard::Not {
path: helm_schema_core::ValuesPath::parse("podMonitor.enabled")
},
Guard::Not {
path: helm_schema_core::ValuesPath::parse("serviceMonitor.enabled")
},
],
);
}
#[test]
fn empty_condition_returns_empty() {
assert!(parse_condition("").is_empty());
assert!(parse_condition(" ").is_empty());
}
#[test]
fn condition_without_values_reference_returns_empty() {
assert!(parse_condition(".Chart.Name").is_empty());
assert!(parse_condition("not (empty $var)").is_empty());
}
#[test]
fn has_key_on_known_dict_is_structural_not_value_truthy() {
let template_bindings = HashMap::from([(
"arg".to_string(),
AbstractValue::Dict(BTreeMap::from([
("customLabels".to_string(), values_path!("commonLabels")),
("context".to_string(), AbstractValue::RootContext),
])),
)]);
sim_assert_eq!(
have: parse_condition_with_template_bindings(
r#"and (hasKey $arg "customLabels") (hasKey $arg "context")"#,
template_bindings,
),
want: Vec::<Guard>::new(),
);
}
#[test]
fn eq_value_preserves_literal_dot_star_substring() {
sim_assert_eq!(
have: parse_condition(r#"eq .Values.X "match.*foo""#),
want: vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("X"),
value: GuardValue::string("match.*foo"),
}],
);
}
#[test]
fn eq_value_preserves_dot_values_substring_inside_string() {
sim_assert_eq!(
have: parse_condition(r#"eq .Values.X ".Values.fake""#),
want: vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("X"),
value: GuardValue::string(".Values.fake"),
}],
);
}
#[test]
fn alias_comparison_preserves_typed_predicates() {
let aliases = HashMap::from([("mode".to_string(), values_path!("service.type"))]);
sim_assert_eq!(
have: parse_condition_with_template_bindings(r#"eq $mode "ClusterIP""#, aliases),
want: vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("service.type"),
value: GuardValue::string("ClusterIP"),
}],
);
}
#[test]
fn output_meta_comparison_preserves_typed_predicates() {
let template_bindings = HashMap::from([("customUser".to_string(), AbstractValue::Unknown)]);
let template_output_meta = HashMap::from([(
"customUser".to_string(),
BTreeMap::from([
(
helm_schema_core::ValuesPath::parse("auth.username"),
HelperOutputMeta::default(),
),
(
helm_schema_core::ValuesPath::parse("global.postgresql.auth.username"),
HelperOutputMeta::default(),
),
]),
)]);
sim_assert_eq!(
have: parse_condition_with_template_facts(
r#"ne $customUser "postgres""#,
template_bindings,
template_output_meta,
),
want: vec![
Guard::NotEq {
path: helm_schema_core::ValuesPath::parse("auth.username"),
value: GuardValue::string("postgres"),
},
Guard::NotEq {
path: helm_schema_core::ValuesPath::parse("global.postgresql.auth.username"),
value: GuardValue::string("postgres"),
},
],
);
}
#[test]
fn defaulted_binding_comparison_carries_the_fallback_arm() {
let path = "serviceMonitor.renderMode";
let context = || {
let mut meta = HelperOutputMeta::default();
meta.predicates
.insert(std::collections::BTreeSet::from([Predicate::truthy_path(
path.to_string(),
)]));
meta.default_fallback = Some(GuardValue::string("skipIfMissing"));
condition_context_with_output_meta(
HashMap::from([("mode".to_string(), values_path!(path))]),
HashMap::from([(
"mode".to_string(),
BTreeMap::from([(helm_schema_core::ValuesPath::parse(path), meta)]),
)]),
)
};
let parse = |text: &str| {
let wrapped = format!("{{{{ {text} }}}}");
parse_action_expressions(&wrapped)
.into_iter()
.next()
.expect("condition expression")
};
let truthy = Predicate::truthy_path(path.to_string());
sim_assert_eq!(
have: context().condition_predicate_expr(&parse(r#"eq $mode "skipIfMissing""#)),
want: Predicate::Or(vec![
Predicate::And(vec![
truthy.clone(),
Predicate::from(Guard::Eq {
path: helm_schema_core::ValuesPath::parse(path),
value: GuardValue::string("skipIfMissing"),
}),
]),
truthy.negated(),
]),
);
sim_assert_eq!(
have: context().condition_predicate_expr(&parse(r#"eq $mode "alwaysRender""#)),
want: Predicate::And(vec![
truthy.clone(),
Predicate::from(Guard::Eq {
path: helm_schema_core::ValuesPath::parse(path),
value: GuardValue::string("alwaysRender"),
}),
]),
);
sim_assert_eq!(
have: context().condition_predicate_expr(&parse(r#"ne $mode "alwaysRender""#)),
want: Predicate::Or(vec![
Predicate::And(vec![
truthy.clone(),
Predicate::from(Guard::NotEq {
path: helm_schema_core::ValuesPath::parse(path),
value: GuardValue::string("alwaysRender"),
}),
]),
truthy.negated(),
]),
);
sim_assert_eq!(
have: context().condition_predicate_expr(&parse(r#"ne $mode "skipIfMissing""#)),
want: Predicate::And(vec![
truthy.clone(),
Predicate::from(Guard::NotEq {
path: helm_schema_core::ValuesPath::parse(path),
value: GuardValue::string("skipIfMissing"),
}),
]),
);
}
#[test]
fn alias_or_predicate_projects_to_path_disjunction() {
let aliases = HashMap::from([(
"annotations".to_string(),
AbstractValue::choice(vec![
values_path!("service.annotations"),
values_path!("global.annotations"),
])
.expect("choice has paths"),
)]);
sim_assert_eq!(
have: parse_condition_with_template_bindings("or $annotations .Values.service.labels", aliases),
want: vec![Guard::Or {
paths: vec![
helm_schema_core::ValuesPath::parse("global.annotations"),
helm_schema_core::ValuesPath::parse("service.annotations"),
helm_schema_core::ValuesPath::parse("service.labels"),
],
}],
);
}
#[test]
fn with_predicates_preserve_header_projection_semantics() {
let predicate = Predicate::all(vec![
Predicate::truthy_path("service.enabled"),
Predicate::from(Guard::Eq {
path: helm_schema_core::ValuesPath::parse("service.type"),
value: GuardValue::string("ClusterIP"),
}),
Predicate::Or(vec![
Predicate::truthy_path("service.annotations"),
Predicate::truthy_path("global.annotations"),
]),
Predicate::truthy_path("service.disabled").negated(),
]);
let with_predicate = Predicate::all(predicate.with_context_predicates());
sim_assert_eq!(
have: with_predicate.contract_guards(),
want: Some(vec![
Guard::With {
path: helm_schema_core::ValuesPath::parse("service.enabled"),
},
Guard::With {
path: helm_schema_core::ValuesPath::parse("service.type"),
},
Guard::Eq {
path: helm_schema_core::ValuesPath::parse("service.type"),
value: GuardValue::string("ClusterIP"),
},
Guard::With {
path: helm_schema_core::ValuesPath::parse("service.annotations"),
},
Guard::With {
path: helm_schema_core::ValuesPath::parse("global.annotations"),
},
Guard::Or {
paths: vec![
helm_schema_core::ValuesPath::parse("global.annotations"),
helm_schema_core::ValuesPath::parse("service.annotations"),
],
},
Guard::With {
path: helm_schema_core::ValuesPath::parse("service.disabled"),
},
Guard::Not {
path: helm_schema_core::ValuesPath::parse("service.disabled"),
},
])
);
sim_assert_eq!(
have: Predicate::all(Predicate::False.with_context_predicates()),
want: Predicate::False,
);
}
#[test]
fn files_get_printf_condition_decodes_to_finite_name_disjunction() {
let mut defines = DefineIndex::new();
defines.add_file_source("files/profile-demo.yaml", "a: 1\n");
defines.add_file_source("files/profile-ambient.yaml", "b: 2\n");
defines.add_file_source("templates/other.yaml", "kind: ConfigMap\n");
let defines = Box::leak(Box::new(defines));
let analysis_db = Box::leak(Box::new(IrAnalysisDb::new(defines)));
let context = ValuePathContext {
root_bindings: Box::leak(Box::new(HashMap::new())),
root_truthy_predicates: Box::leak(Box::new(HashMap::new())),
root_value_dispatches: Box::leak(Box::new(HashMap::new())),
root_field_semantics_on_current_dot: false,
pipeline_bound_bindings: std::collections::HashSet::new(),
template_bindings: HashMap::new(),
template_scalar_dispatches: Box::leak(Box::new(HashMap::new())),
range_domains: Box::leak(Box::new(HashMap::new())),
get_bindings: Box::leak(Box::new(HashMap::new())),
template_default_paths: Box::leak(Box::new(HashMap::new())),
template_output_meta: Box::leak(Box::new(HashMap::new())),
template_truthy_reductions: Box::leak(Box::new(HashMap::new())),
template_truthiness_abstentions: Box::leak(Box::new(BTreeSet::new())),
typeof_bindings: Box::leak(Box::new(HashMap::new())),
int_cast_bindings: Box::leak(Box::new(HashMap::new())),
fragment_context: FragmentEvalContext::new(analysis_db),
current_dot_fragment: None,
current_dot_binding: None,
};
let wrapped = r#"{{ .Files.Get (printf "files/profile-%s.yaml" .Values.profile) }}"#;
let top = parse_action_expressions(wrapped)
.into_iter()
.next()
.expect("parsed condition expression");
let guards = context.condition_predicate_expr(&top).contract_guards();
sim_assert_eq!(
have: guards,
want: Some(vec![Guard::AnyOf {
alternatives: vec![
vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("profile"),
value: GuardValue::string("ambient"),
}],
vec![Guard::Eq {
path: helm_schema_core::ValuesPath::parse("profile"),
value: GuardValue::string("demo"),
}],
],
}]),
);
}