#![allow(clippy::module_name_repetitions)]
use caixa_core::{Caixa, MappingExt, kube_metadata_str_field, lareira_chart_name};
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("{0}")]
NotAServico(#[from] caixa_core::KindMismatch),
#[error("{0}")]
UnsupportedServicoCount(#[from] caixa_core::ServicoCountMismatch),
#[error("computeunit yaml missing required field: {0}")]
MissingField(&'static str),
#[error("yaml: {0}")]
Yaml(#[from] serde_yaml::Error),
#[error("render: {0}")]
Render(#[from] caixa_core::RenderError),
}
pub use caixa_core::DEFAULT_NAMESPACE;
pub use caixa_core::DEFAULT_FLUX_RECONCILE_INTERVAL;
pub use caixa_core::DEFAULT_FLUX_CHART_SOURCE_SUBPATH;
pub use caixa_core::FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT;
pub use caixa_core::FLUX_HELMRELEASE_KEY_RETRIES;
pub use caixa_core::FLUX_HELMRELEASE_KEY_REMEDIATION;
pub use caixa_core::FLUX_HELMRELEASE_KEY_INSTALL;
pub use caixa_core::FLUX_HELMRELEASE_KEY_UPGRADE;
pub use caixa_core::FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE;
pub use caixa_core::FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT;
pub use caixa_core::FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE;
pub use caixa_core::FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT;
pub use caixa_core::FLUX_KUSTOMIZATION_KEY_PRUNE;
pub use caixa_core::FLUX_KUSTOMIZATION_PRUNE_DEFAULT;
pub use caixa_core::CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT;
pub use caixa_core::FLUX_KUSTOMIZATION_KEY_PATH;
pub use caixa_core::flux_kustomization_source_subtree;
pub use caixa_core::FLUX_KUSTOMIZATION_KEY_TIMEOUT;
pub use caixa_core::DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT;
pub use caixa_core::DEFAULT_FLUX_SYSTEM_NAMESPACE;
pub use caixa_core::FLUX_HELMRELEASE_API_VERSION;
pub use caixa_core::FLUX_GITREPOSITORY_API_VERSION;
pub use caixa_core::FLUX_KUSTOMIZATION_API_VERSION;
pub use caixa_core::FLUX_KIND_GIT_REPOSITORY;
pub use caixa_core::FLUX_KIND_HELM_RELEASE;
pub use caixa_core::FLUX_KIND_KUSTOMIZATION;
pub use caixa_core::FLUX_KEY_CHART;
pub use caixa_core::FLUX_HELMCHART_TEMPLATE_KEY_CHART;
pub use caixa_core::FLUX_KEY_SOURCE_REF;
pub use caixa_core::FLUX_KEY_VALUES;
pub use caixa_core::FLUX_KEY_HEALTH_CHECKS;
pub use caixa_core::FLUX_KEY_INTERVAL;
pub use caixa_core::FLUX_GITREPOSITORY_REF_KEY_TAG;
pub use caixa_core::FLUX_GITREPOSITORY_REF_KEY_BRANCH;
pub use caixa_core::FLUX_GITREPOSITORY_REF_KEY_COMMIT;
pub use caixa_core::FLUX_GITREPOSITORY_KEY_REF;
pub use caixa_core::FLUX_GITREPOSITORY_KEY_URL;
pub use caixa_core::FLUX_HELMRELEASE_YAML_FILENAME;
pub use caixa_core::FLUX_GITREPOSITORY_YAML_FILENAME;
pub use caixa_core::FLUX_KUSTOMIZATION_YAML_FILENAME;
pub use caixa_core::DEFAULT_LIBRARY_NAME;
pub use caixa_core::HELM_VALUES_KEY_ENABLED;
pub use caixa_core::KUBE_KEY_SPEC;
pub use caixa_core::KUBE_KEY_METADATA;
pub use caixa_core::KUBE_KEY_KIND;
pub use caixa_core::KUBE_KEY_API_VERSION;
pub use caixa_core::KUBE_KEY_NAMESPACE;
pub use caixa_core::KUBE_KEY_NAME;
pub use caixa_core::FLEET_PROGRAMS_KEY_PROGRAMS;
pub use caixa_core::FLEET_PROGRAMS_KEY_NAME;
pub use caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE;
pub use caixa_core::COMPUTEUNIT_SPEC_KEY_TRIGGER;
pub use caixa_core::COMPUTEUNIT_SPEC_KEY_CAPABILITIES;
pub use caixa_core::COMPUTEUNIT_MODULE_KEY_SOURCE;
pub use caixa_core::servico_spec_and_m2_overlay_entries;
pub fn programs_yaml_entry(
caixa: &Caixa,
computeunit_yaml: &serde_yaml::Value,
) -> Result<serde_yaml::Value, Error> {
caixa_core::require_v0_servico_shape::<Error>(caixa)?;
let spec = computeunit_yaml
.get(KUBE_KEY_SPEC)
.ok_or(Error::MissingField(KUBE_KEY_SPEC))?;
let namespace = kube_metadata_str_field(computeunit_yaml, KUBE_KEY_NAMESPACE)
.unwrap_or(DEFAULT_NAMESPACE)
.to_string();
let mut entry = serde_yaml::Mapping::new();
entry.insert_string(FLEET_PROGRAMS_KEY_NAME, caixa.nome().to_string());
entry.insert_string(KUBE_KEY_NAMESPACE, namespace);
for (k, v) in caixa_core::servico_spec_and_m2_overlay_entries(caixa, spec)? {
entry.entry_str_key(&k).or_insert(v);
}
Ok(serde_yaml::Value::Mapping(entry))
}
pub fn upsert_into_helmrelease_programs(
helmrelease: serde_yaml::Value,
new_entry: serde_yaml::Value,
) -> Result<(serde_yaml::Value, bool), Error> {
let serde_yaml::Value::Mapping(mut root) = helmrelease else {
return Err(Error::MissingField(
"expected mapping at root of HelmRelease",
));
};
let spec = root
.get_mut(KUBE_KEY_SPEC)
.ok_or(Error::MissingField(KUBE_KEY_SPEC))?;
let serde_yaml::Value::Mapping(spec_map) = spec else {
return Err(Error::MissingField("spec must be a mapping"));
};
let values_map = spec_map
.entry_or_default_mapping(FLUX_KEY_VALUES)
.ok_or(Error::MissingField("spec.values must be a mapping"))?;
let arr = values_map
.entry_or_default_sequence(FLEET_PROGRAMS_KEY_PROGRAMS)
.ok_or(Error::MissingField(
"spec.values.programs must be a sequence",
))?;
let inserted = caixa_core::upsert_named_entry(arr, new_entry, FLEET_PROGRAMS_KEY_NAME, || {
Error::MissingField(FLEET_PROGRAMS_KEY_NAME)
})?;
Ok((serde_yaml::Value::Mapping(root), inserted))
}
pub fn upsert_into_programs_yaml(
programs_yaml: serde_yaml::Value,
new_entry: serde_yaml::Value,
) -> Result<(serde_yaml::Value, bool), Error> {
let serde_yaml::Value::Mapping(mut root) = programs_yaml else {
return Err(Error::MissingField(
"expected mapping at root of values.yaml",
));
};
let arr = root
.entry_or_default_sequence(FLEET_PROGRAMS_KEY_PROGRAMS)
.ok_or(Error::MissingField("programs must be a sequence"))?;
let inserted = caixa_core::upsert_named_entry(arr, new_entry, FLEET_PROGRAMS_KEY_NAME, || {
Error::MissingField(FLEET_PROGRAMS_KEY_NAME)
})?;
Ok((serde_yaml::Value::Mapping(root), inserted))
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterBundleOpts {
pub cluster: String,
pub namespace: String,
pub interval: String,
pub chart_path: String,
pub git_url: String,
pub git_ref: GitRefSpec,
}
#[derive(Debug, Clone, Serialize, Deserialize, gen_platform::IsVariant)]
#[serde(rename_all = "camelCase")]
pub enum GitRefSpec {
Tag(String),
Branch(String),
Commit(String),
}
impl GitRefSpec {
#[must_use]
pub const fn ref_field_name(&self) -> &'static str {
match self {
GitRefSpec::Tag(_) => FLUX_GITREPOSITORY_REF_KEY_TAG,
GitRefSpec::Branch(_) => FLUX_GITREPOSITORY_REF_KEY_BRANCH,
GitRefSpec::Commit(_) => FLUX_GITREPOSITORY_REF_KEY_COMMIT,
}
}
#[must_use]
pub fn ref_value(&self) -> &str {
match self {
GitRefSpec::Tag(t) | GitRefSpec::Branch(t) | GitRefSpec::Commit(t) => t.as_str(),
}
}
}
impl ClusterBundleOpts {
#[must_use]
pub fn for_caixa(caixa: &Caixa, cluster: impl Into<String>) -> Self {
Self {
cluster: cluster.into(),
namespace: DEFAULT_NAMESPACE.into(),
interval: DEFAULT_FLUX_RECONCILE_INTERVAL.into(),
chart_path: DEFAULT_FLUX_CHART_SOURCE_SUBPATH.into(),
git_url: caixa.repositorio().map(str::to_owned).unwrap_or_else(|| {
format!(
"https://github.com/{org}/{nome}",
org = caixa_core::DEFAULT_PLEME_GIT_ORG,
nome = caixa.nome(),
)
}),
git_ref: GitRefSpec::Tag(format!(
"{prefix}{versao}",
prefix = caixa_core::DEFAULT_PUBLISH_TAG_PREFIX,
versao = caixa.versao(),
)),
}
}
}
pub type BundleFile = caixa_core::RenderedFile;
pub fn cluster_bundle(caixa: &Caixa, opts: &ClusterBundleOpts) -> Result<Vec<BundleFile>, Error> {
caixa_core::require_v0_servico_shape::<Error>(caixa)?;
let name = caixa.nome().to_string();
let chart_name = lareira_chart_name(&name);
let gitref_field = format!(
" {field}: {value:?}",
field = opts.git_ref.ref_field_name(),
value = opts.git_ref.ref_value(),
);
let gitrepo = format!(
"---\n\
# Source — pinned to {tag_human}, rendered by caixa-flux.\n\
{api_version_key}: {api_version}\n\
{kind_key}: {kind}\n\
{metadata_key}:\n \
{name_key}: {name}\n \
{namespace_key}: {namespace}\n\
{spec_key}:\n \
{interval_key}: {interval}\n \
{url_key}: {url}\n \
{ref_key}:\n\
{gitref_field}\n",
api_version_key = KUBE_KEY_API_VERSION,
api_version = FLUX_GITREPOSITORY_API_VERSION,
kind_key = KUBE_KEY_KIND,
kind = FLUX_KIND_GIT_REPOSITORY,
metadata_key = KUBE_KEY_METADATA,
name_key = KUBE_KEY_NAME,
namespace_key = KUBE_KEY_NAMESPACE,
spec_key = KUBE_KEY_SPEC,
tag_human = format!(
"{field} {value}",
field = opts.git_ref.ref_field_name(),
value = opts.git_ref.ref_value(),
),
name = name,
namespace = opts.namespace,
interval_key = FLUX_KEY_INTERVAL,
interval = opts.interval,
url_key = FLUX_GITREPOSITORY_KEY_URL,
url = opts.git_url,
ref_key = FLUX_GITREPOSITORY_KEY_REF,
gitref_field = gitref_field,
);
let helmrelease = format!(
"---\n\
# HelmRelease consumes the chart caixa-helm renders for this\n\
# caixa Servico. Per-cluster values are injected here.\n\
{api_version_key}: {api_version}\n\
{kind_key}: {kind}\n\
{metadata_key}:\n \
{name_key}: {name}\n \
{namespace_key}: {namespace}\n\
{spec_key}:\n \
{interval_key}: {interval}\n \
{chart_key}:\n \
{spec_key}:\n \
{chart_name_key}: {chart_path}\n \
{source_ref_key}:\n \
{kind_key}: {source_kind}\n \
{name_key}: {name}\n \
{namespace_key}: {namespace}\n \
{install_key}:\n \
{create_namespace_key}: {create_namespace_default}\n \
{remediation_key}:\n \
{retries_key}: {retries_default}\n \
{upgrade_key}:\n \
{remediation_key}:\n \
{retries_key}: {retries_default}\n \
{remediate_last_failure_key}: {remediate_last_failure_default}\n \
{values_key}:\n \
{library_name}:\n \
{enabled_key}: {lareira_enabled_default}\n",
api_version_key = KUBE_KEY_API_VERSION,
api_version = FLUX_HELMRELEASE_API_VERSION,
kind_key = KUBE_KEY_KIND,
kind = FLUX_KIND_HELM_RELEASE,
source_kind = FLUX_KIND_GIT_REPOSITORY,
metadata_key = KUBE_KEY_METADATA,
name_key = KUBE_KEY_NAME,
namespace_key = KUBE_KEY_NAMESPACE,
spec_key = KUBE_KEY_SPEC,
name = name,
namespace = opts.namespace,
interval_key = FLUX_KEY_INTERVAL,
interval = opts.interval,
chart_key = FLUX_KEY_CHART,
chart_name_key = FLUX_HELMCHART_TEMPLATE_KEY_CHART,
chart_path = opts.chart_path,
source_ref_key = FLUX_KEY_SOURCE_REF,
values_key = FLUX_KEY_VALUES,
library_name = DEFAULT_LIBRARY_NAME,
enabled_key = HELM_VALUES_KEY_ENABLED,
retries_default = FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT,
retries_key = FLUX_HELMRELEASE_KEY_RETRIES,
remediation_key = FLUX_HELMRELEASE_KEY_REMEDIATION,
install_key = FLUX_HELMRELEASE_KEY_INSTALL,
upgrade_key = FLUX_HELMRELEASE_KEY_UPGRADE,
remediate_last_failure_key = FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE,
remediate_last_failure_default = FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT,
create_namespace_key = FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE,
create_namespace_default = FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT,
lareira_enabled_default = CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT,
);
let source_subtree = flux_kustomization_source_subtree(&opts.cluster, &name);
let kustomization = format!(
"---\n\
# Flux Kustomization that pins the GitRepository + HelmRelease.\n\
# Paired path: pleme-io/k8s/clusters/{cluster}/services/{name}/\n\
{api_version_key}: {kustomization_api_version}\n\
{kind_key}: {kind}\n\
{metadata_key}:\n \
{name_key}: {name}\n \
{namespace_key}: {flux_system}\n\
{spec_key}:\n \
{interval_key}: {interval}\n \
{prune_key}: {prune_default}\n \
{source_ref_key}:\n \
{kind_key}: {source_kind}\n \
{name_key}: {flux_system}\n \
{path_key}: {source_subtree}\n \
{health_checks_key}:\n \
- {api_version_key}: {api_version}\n \
{kind_key}: {health_kind}\n \
{name_key}: {name}\n \
{namespace_key}: {namespace}\n \
{timeout_key}: {timeout_default}\n",
api_version_key = KUBE_KEY_API_VERSION,
kustomization_api_version = FLUX_KUSTOMIZATION_API_VERSION,
kind_key = KUBE_KEY_KIND,
kind = FLUX_KIND_KUSTOMIZATION,
source_kind = FLUX_KIND_GIT_REPOSITORY,
health_kind = FLUX_KIND_HELM_RELEASE,
metadata_key = KUBE_KEY_METADATA,
name_key = KUBE_KEY_NAME,
namespace_key = KUBE_KEY_NAMESPACE,
spec_key = KUBE_KEY_SPEC,
api_version = FLUX_HELMRELEASE_API_VERSION,
name = name,
namespace = opts.namespace,
interval_key = FLUX_KEY_INTERVAL,
interval = opts.interval,
cluster = opts.cluster,
flux_system = DEFAULT_FLUX_SYSTEM_NAMESPACE,
source_ref_key = FLUX_KEY_SOURCE_REF,
health_checks_key = FLUX_KEY_HEALTH_CHECKS,
prune_key = FLUX_KUSTOMIZATION_KEY_PRUNE,
prune_default = FLUX_KUSTOMIZATION_PRUNE_DEFAULT,
path_key = FLUX_KUSTOMIZATION_KEY_PATH,
timeout_key = FLUX_KUSTOMIZATION_KEY_TIMEOUT,
timeout_default = DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT,
source_subtree = source_subtree,
);
let _ = chart_name;
Ok(vec![
BundleFile::new(FLUX_GITREPOSITORY_YAML_FILENAME, gitrepo),
BundleFile::new(FLUX_HELMRELEASE_YAML_FILENAME, helmrelease),
BundleFile::new(FLUX_KUSTOMIZATION_YAML_FILENAME, kustomization),
])
}
#[cfg(test)]
mod tests {
use super::*;
use caixa_core::{
Caixa, CaixaKind, M2_BEHAVIOR_KEY_ON_INIT, M2_KEY_BEHAVIOR, M2_KEY_LIMITS,
M2_KEY_UPGRADE_FROM, M2_LIMITS_KEY_CPU, M2_LIMITS_KEY_MEMORY, M2_UPGRADE_FROM_KEY_FROM,
kube_root_str_field,
};
fn sample_caixa() -> Caixa {
Caixa {
nome: "hello-rio".into(),
versao: "0.1.0".into(),
kind: CaixaKind::Servico,
edicao: Some("2026".into()),
descricao: Some("Canonical Rust→wasm32-wasip2 caixa Servico.".into()),
repositorio: Some("https://github.com/pleme-io/hello-rio".into()),
licenca: Some("MIT".into()),
autores: vec!["pleme-io".into()],
etiquetas: vec!["hello-world".into()],
deps: vec![],
deps_dev: vec![],
exe: vec![],
bibliotecas: vec![],
servicos: vec!["servicos/hello-rio.computeunit.yaml".into()],
limits: None,
behavior: None,
upgrade_from: vec![],
estrategia: None,
max_restarts: None,
restart_window: None,
children: vec![],
membros: vec![],
contratos: vec![],
politicas: None,
placement: None,
entrada: None,
ci: None,
}
}
fn sample_cu_yaml() -> serde_yaml::Value {
serde_yaml::from_str(
r#"
apiVersion: wasm.pleme.io/v1alpha1
kind: ComputeUnit
metadata:
name: hello-rio
namespace: tatara-system
spec:
module:
source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0
trigger:
service:
port: 8080
paths: ["/", "/hello", "/healthz"]
capabilities:
- http-in:0.0.0.0:8080
- env
"#,
)
.unwrap()
}
#[test]
fn default_namespace_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"DEFAULT_NAMESPACE",
DEFAULT_NAMESPACE,
caixa_core::DEFAULT_NAMESPACE,
);
}
#[test]
fn default_flux_reconcile_interval_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"DEFAULT_FLUX_RECONCILE_INTERVAL",
DEFAULT_FLUX_RECONCILE_INTERVAL,
caixa_core::DEFAULT_FLUX_RECONCILE_INTERVAL,
);
}
#[test]
fn cluster_bundle_opts_for_caixa_seeds_interval_from_lifted_default() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
assert_eq!(
opts.interval, DEFAULT_FLUX_RECONCILE_INTERVAL,
"the substrate's per-caixa Flux v2 reconcile-cadence default \
seed must resolve to the lifted \
`DEFAULT_FLUX_RECONCILE_INTERVAL` scalar — drift here \
silently splits the substrate's per-caixa convergence-\
freshness contract between the operator-facing canonical \
default and the per-caixa seeded reconcile-schedule"
);
}
#[test]
fn default_flux_chart_source_subpath_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"DEFAULT_FLUX_CHART_SOURCE_SUBPATH",
DEFAULT_FLUX_CHART_SOURCE_SUBPATH,
caixa_core::DEFAULT_FLUX_CHART_SOURCE_SUBPATH,
);
}
#[test]
fn cluster_bundle_opts_for_caixa_seeds_chart_path_from_lifted_default() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
assert_eq!(
opts.chart_path, DEFAULT_FLUX_CHART_SOURCE_SUBPATH,
"the substrate's per-caixa Flux v2 chart-directory-in-git-\
source default seed must resolve to the lifted \
`DEFAULT_FLUX_CHART_SOURCE_SUBPATH` scalar — drift here \
silently splits the substrate's per-caixa chart-open \
contract between the operator-facing canonical default \
and the per-caixa seeded chart-directory pointer"
);
}
#[test]
fn cluster_bundle_opts_for_caixa_git_url_fallback_routes_through_caixa_nome_accessor() {
let mut caixa = sample_caixa();
caixa.repositorio = None;
let opts = ClusterBundleOpts::for_caixa(&caixa, "rio");
let expected = format!(
"https://github.com/{org}/{nome}",
org = caixa_core::DEFAULT_PLEME_GIT_ORG,
nome = caixa.nome(),
);
assert_eq!(
opts.git_url, expected,
"the substrate's per-caixa `:repositorio`-null pleme-org \
github URL fallback composer must derive its trailing \
`<nome>` path segment through the typed \
`caixa_core::Caixa::nome` accessor — a regression that \
re-inlines `caixa.nome` at the fallback site silently \
splits the parent-Caixa identity between the FluxCD \
`GitRepository` `spec.url` `<org>/<nome>` clone target \
and every other per-Caixa identity consumer the sibling \
caixa-helm / caixa-mesh renderers emit"
);
}
#[test]
fn flux_helmrelease_remediation_retries_default_re_export_points_at_caixa_core_canonical() {
assert_eq!(
FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT,
caixa_core::FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT,
"`caixa_flux::FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT` \
must be the value-identical re-export of the canonical \
`caixa_core::FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT` — \
drift here silently splits the substrate's per-caixa Flux \
v2 `HelmRelease` remediation-retries ceiling between the \
canonical caixa-core declaration and the renderer's threaded \
seed"
);
}
#[test]
fn flux_helmrelease_key_retries_re_export_points_at_caixa_core_canonical() {
assert_eq!(
FLUX_HELMRELEASE_KEY_RETRIES,
caixa_core::FLUX_HELMRELEASE_KEY_RETRIES
);
assert!(
std::ptr::eq(
FLUX_HELMRELEASE_KEY_RETRIES.as_ptr(),
caixa_core::FLUX_HELMRELEASE_KEY_RETRIES.as_ptr(),
),
"FLUX_HELMRELEASE_KEY_RETRIES must be a re-export of \
caixa_core::FLUX_HELMRELEASE_KEY_RETRIES, not a sibling `pub const` \
that happens to carry the same string — drift between the two is \
the canonical footgun this lift closes"
);
}
#[test]
fn flux_helmrelease_key_remediation_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_HELMRELEASE_KEY_REMEDIATION",
FLUX_HELMRELEASE_KEY_REMEDIATION,
caixa_core::FLUX_HELMRELEASE_KEY_REMEDIATION,
);
}
#[test]
fn flux_helmrelease_key_remediation_pins_canonical_remediation_string() {
assert_eq!(FLUX_HELMRELEASE_KEY_REMEDIATION, "remediation");
}
#[test]
fn flux_helmrelease_key_install_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_HELMRELEASE_KEY_INSTALL",
FLUX_HELMRELEASE_KEY_INSTALL,
caixa_core::FLUX_HELMRELEASE_KEY_INSTALL,
);
}
#[test]
fn flux_helmrelease_key_install_pins_canonical_install_string() {
assert_eq!(FLUX_HELMRELEASE_KEY_INSTALL, "install");
}
#[test]
fn flux_helmrelease_key_upgrade_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_HELMRELEASE_KEY_UPGRADE",
FLUX_HELMRELEASE_KEY_UPGRADE,
caixa_core::FLUX_HELMRELEASE_KEY_UPGRADE,
);
}
#[test]
fn flux_helmrelease_key_upgrade_pins_canonical_upgrade_string() {
assert_eq!(FLUX_HELMRELEASE_KEY_UPGRADE, "upgrade");
}
#[test]
fn flux_helmrelease_key_retries_pins_canonical_retries_string() {
assert_eq!(FLUX_HELMRELEASE_KEY_RETRIES, "retries");
}
#[test]
fn cluster_bundle_helmrelease_install_remediation_retries_pins_lifted_default() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let install_retries = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_HELMRELEASE_KEY_INSTALL))
.and_then(|i| i.get(FLUX_HELMRELEASE_KEY_REMEDIATION))
.and_then(|r| r.get(FLUX_HELMRELEASE_KEY_RETRIES))
.and_then(|v| v.as_u64())
.expect(
"spec.install.remediation.retries scalar present; drift on \
this axis silently splits the substrate's canonical retry-\
ceiling between the install-path first-time chart apply and \
the canonical `FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`",
);
assert_eq!(
install_retries,
u64::from(FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT),
"spec.install.remediation.retries must carry the lifted \
`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT` scalar — \
drift here silently splits the substrate's canonical retry-\
ceiling between the operator-facing canonical default and \
the install-path retry cap the Flux v2 helm-controller \
consumes at first-time chart apply time"
);
}
#[test]
fn cluster_bundle_helmrelease_upgrade_remediation_retries_pins_lifted_default() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let upgrade_retries = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_HELMRELEASE_KEY_UPGRADE))
.and_then(|u| u.get(FLUX_HELMRELEASE_KEY_REMEDIATION))
.and_then(|r| r.get(FLUX_HELMRELEASE_KEY_RETRIES))
.and_then(|v| v.as_u64())
.expect(
"spec.upgrade.remediation.retries scalar present; drift on \
this axis silently splits the substrate's canonical retry-\
ceiling between the upgrade-path per-version chart re-apply \
and the canonical `FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`",
);
assert_eq!(
upgrade_retries,
u64::from(FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT),
"spec.upgrade.remediation.retries must carry the lifted \
`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT` scalar — \
drift here silently splits the substrate's canonical retry-\
ceiling between the operator-facing canonical default and \
the upgrade-path retry cap the Flux v2 helm-controller \
consumes on every subsequent per-caixa-version chart re-apply"
);
}
#[test]
fn cluster_bundle_helmrelease_upgrade_remediation_remediate_last_failure_pins_lifted_true() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let remediate_last_failure = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_HELMRELEASE_KEY_UPGRADE))
.and_then(|u| u.get(FLUX_HELMRELEASE_KEY_REMEDIATION))
.and_then(|r| r.get(FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE))
.and_then(|v| v.as_bool())
.expect(
"spec.upgrade.remediation.remediateLastFailure boolean scalar \
present; drift on this axis silently drops the substrate's \
chosen post-retry-exhaustion rollback semantic from every \
emitted per-caixa `HelmRelease` document, leaving every \
terminally-failed upgrade in the failed state without \
rolling back to the prior last-known-good release",
);
assert_eq!(
remediate_last_failure, FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT,
"spec.upgrade.remediation.remediateLastFailure must carry the \
lifted `FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT` \
scalar — drift silently splits the substrate's canonical \
post-retry-exhaustion rollback semantic between the operator-\
facing canonical default and the per-caixa `HelmRelease` \
document's per-CR upgrade-path remediation-toggle the Flux \
v2 helm-controller's per-CR upgrade-path remediation loop \
keys off to trigger the prior-release rollback pipeline once \
the paired retry-cap ceiling has been exhausted, and every \
terminally-failed per-caixa upgrade sits in the failed state \
indefinitely with no diagnostic naming the remediation-\
toggle-drift root cause"
);
}
#[test]
fn flux_helmrelease_key_remediate_last_failure_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE",
FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE,
caixa_core::FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE,
);
}
#[test]
fn flux_helmrelease_remediate_last_failure_default_re_export_matches_caixa_core_canonical_value()
{
assert_eq!(
FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT,
caixa_core::FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT,
"FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT re-export \
must resolve to the canonical caixa_core value — drift \
silently splits the substrate's canonical post-retry-\
exhaustion rollback default between the two `pub const` \
declarations, and every rendered per-caixa `helmrelease.yaml` \
would emit a different per-CR upgrade-path remediation-toggle \
scalar than every downstream consumer (the future M4 \
`mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-\
Aplicacao `HelmRelease` synthesis, the future admission-\
webhook floor) reads at admit / reconcile time"
);
assert!(
FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT,
"FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT must remain \
`true` — the substrate's canonical \"no chart apply leaves \
a per-caixa CR in a stalled, unremediated state\" \
(MESH-COMPOSITION.md §V) guarantee requires every emitted \
per-caixa `HelmRelease` opt into the helm-controller's per-CR \
upgrade-path post-retry-exhaustion rollback pipeline; a drift \
to `false` here silently leaves every terminally-failed \
upgrade parked at `Ready: False` without rolling back to the \
prior last-known-good release"
);
}
#[test]
fn cluster_bundle_helmrelease_install_create_namespace_pins_lifted_true() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let create_namespace = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_HELMRELEASE_KEY_INSTALL))
.and_then(|i| i.get(FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE))
.and_then(|v| v.as_bool())
.expect(
"spec.install.createNamespace boolean scalar present; drift \
on this axis silently drops the substrate's chosen first-\
apply namespace-seeder semantic from every emitted per-\
caixa `HelmRelease` document, leaving every first-time \
per-caixa chart apply against a fresh cluster refused by \
the helm-controller because the target namespace was not \
pre-provisioned by an out-of-band pipeline",
);
assert_eq!(
create_namespace, FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT,
"spec.install.createNamespace must carry the lifted \
`FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT` scalar — drift \
silently splits the substrate's canonical first-apply \
namespace-seeder semantic between the operator-facing \
canonical default and the per-caixa `HelmRelease` document's \
per-CR install-path namespace-seeder-toggle the Flux v2 \
helm-controller's per-CR install-path pre-apply loop keys \
off to materialize the target namespace before the first \
chart apply, and every first-time per-caixa Servico chart \
apply against a fresh cluster is refused with no diagnostic \
naming the seeder-toggle-drift root cause"
);
}
#[test]
fn flux_helmrelease_key_create_namespace_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE",
FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE,
caixa_core::FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE,
);
}
#[test]
fn flux_helmrelease_create_namespace_default_re_export_matches_caixa_core_canonical_value() {
assert_eq!(
FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT,
caixa_core::FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT,
"FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT re-export must \
resolve to the canonical caixa_core value — drift silently \
splits the substrate's canonical first-apply namespace-\
seeder default between the two `pub const` declarations, \
and every rendered per-caixa `helmrelease.yaml` would emit a \
different per-CR install-path namespace-seeder-toggle scalar \
than every downstream consumer (the future M4 \
`mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-\
Aplicacao `HelmRelease` synthesis, the future admission-\
webhook floor) reads at admit / reconcile time"
);
assert!(
FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT,
"FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT must remain `true` \
— the substrate's canonical \"no per-caixa Servico apply is \
blocked on manual namespace preprovisioning\" \
(MESH-COMPOSITION.md §V install-path-fluency) guarantee \
requires every emitted per-caixa `HelmRelease` opt into the \
helm-controller's per-CR install-path pre-apply namespace-\
seeder pipeline; a drift to `false` here silently refuses \
every first-time per-caixa chart apply against a fresh \
cluster whose target namespace has not been pre-provisioned \
by an out-of-band pipeline"
);
}
#[test]
fn cluster_bundle_lareira_enabled_default_re_export_matches_caixa_core_canonical_value() {
assert_eq!(
CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT,
caixa_core::CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT,
"CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT re-export must \
resolve to the canonical caixa_core value — drift silently \
splits the substrate's canonical force-on-under-composition \
child-chart-enablement default between the two `pub const` \
declarations, and every rendered per-caixa `helmrelease.yaml` \
would emit a different per-values-overlay child-chart-\
enablement toggle than every downstream consumer (the future \
M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-\
Aplicacao `HelmRelease` synthesis) reads at admit / reconcile \
time"
);
assert!(
CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT,
"CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT must remain `true` — \
the `cluster_bundle` composition path is the substrate-side \
opt-in path where the operator has already asserted per-caixa \
cluster-scoped ownership by materializing a per-caixa \
GitRepository + HelmRelease + Kustomization trio, so the \
overlay must force the child chart on by seeding \
`enabled: true` under the `values.<library>` wrap; a drift to \
`false` here silently no-ops every per-caixa lareira child \
chart at the per-cluster `HelmRelease` apply step, leaving \
the paired standalone-chart-side `enabled: false` per-chart \
default un-overridden"
);
}
#[test]
fn cluster_bundle_kustomization_prune_pins_lifted_true() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let ks = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&ks.contents).expect("kustomization.yaml parses as YAML");
let prune = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KUSTOMIZATION_KEY_PRUNE))
.and_then(|v| v.as_bool())
.expect(
"spec.prune boolean scalar present; drift on this axis \
silently drops the substrate's chosen sweep-what-you-\
removed semantic from every emitted per-caixa \
`Kustomization` document, leaving per-caixa resources \
the source manifest set previously reconciled but no \
longer carries dangling in the cluster",
);
assert_eq!(
prune, FLUX_KUSTOMIZATION_PRUNE_DEFAULT,
"spec.prune must carry the lifted \
`FLUX_KUSTOMIZATION_PRUNE_DEFAULT` scalar — drift silently \
splits the substrate's canonical sweep-what-you-removed \
semantic between the operator-facing canonical default and \
the per-caixa `Kustomization` document's per-CR garbage-\
collection-toggle the Flux v2 kustomize-controller's per-CR \
reconcile loop keys off to garbage-collect per-caixa resources \
removed from the source manifest set between reconciles, and \
every per-caixa `Kustomization` reconcile leaves orphaned \
resources dangling in the cluster with no diagnostic naming \
the toggle-drift root cause"
);
}
#[test]
fn flux_kustomization_prune_default_re_export_matches_caixa_core_canonical_value() {
assert_eq!(
FLUX_KUSTOMIZATION_PRUNE_DEFAULT,
caixa_core::FLUX_KUSTOMIZATION_PRUNE_DEFAULT,
"FLUX_KUSTOMIZATION_PRUNE_DEFAULT re-export must resolve to \
the canonical caixa_core value — drift silently splits the \
substrate's canonical sweep-what-you-removed default \
between the two `pub const` declarations, and every rendered \
per-caixa `kustomization.yaml` would emit a different \
per-CR garbage-collection-toggle scalar than every downstream \
consumer (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` \
CR materializer's per-Aplicacao `Kustomization` synthesis, \
the future admission-webhook floor) reads at admit / \
reconcile time"
);
assert!(
FLUX_KUSTOMIZATION_PRUNE_DEFAULT,
"FLUX_KUSTOMIZATION_PRUNE_DEFAULT must remain `true` — the \
substrate's canonical sweep-what-you-removed semantic \
(CAIXA-SDLC.md §V author-to-live-convergence guarantee) \
requires every emitted per-caixa `Kustomization` opt into \
the kustomize-controller's per-CR resource-tracking \
garbage-collection loop; a drift to `false` here silently \
leaves orphaned resources dangling in every cluster the \
substrate reconciles into"
);
}
#[test]
fn flux_kustomization_key_prune_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KUSTOMIZATION_KEY_PRUNE",
FLUX_KUSTOMIZATION_KEY_PRUNE,
caixa_core::FLUX_KUSTOMIZATION_KEY_PRUNE,
);
}
#[test]
fn cluster_bundle_kustomization_path_pins_lifted_sub_tree() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let ks = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&ks.contents).expect("kustomization.yaml parses as YAML");
let path = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KUSTOMIZATION_KEY_PATH))
.and_then(|v| v.as_str())
.expect(
"spec.path string scalar present; drift on this axis \
silently unbinds every per-caixa Kustomization from \
its paired per-caixa sub-tree of the pleme-io k8s \
repository, and the kustomize-controller either \
defaults to reconciling the GitRepository root or \
refuses to reconcile at all",
);
let expected = flux_kustomization_source_subtree(&opts.cluster, sample_caixa().nome());
assert_eq!(
path, expected,
"spec.path must carry the substrate's canonical per-cluster \
/ per-caixa sub-tree seed — drift silently unbinds every \
per-caixa Kustomization from its paired sub-tree of the \
pleme-io k8s repository"
);
}
#[test]
fn flux_kustomization_key_path_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KUSTOMIZATION_KEY_PATH",
FLUX_KUSTOMIZATION_KEY_PATH,
caixa_core::FLUX_KUSTOMIZATION_KEY_PATH,
);
}
#[test]
fn flux_kustomization_source_subtree_re_export_matches_caixa_core_canonical_output() {
assert_eq!(
flux_kustomization_source_subtree("rio", "hello-rio"),
caixa_core::flux_kustomization_source_subtree("rio", "hello-rio"),
);
assert_eq!(
flux_kustomization_source_subtree("rio", "hello-rio"),
"./clusters/rio/services/hello-rio",
);
assert_eq!(
flux_kustomization_source_subtree("paris", "cart"),
caixa_core::flux_kustomization_source_subtree("paris", "cart"),
);
}
#[test]
fn cluster_bundle_kustomization_spec_path_uses_lifted_composer() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let ks = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&ks.contents).expect("kustomization.yaml parses as YAML");
let emitted = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KUSTOMIZATION_KEY_PATH))
.and_then(|v| v.as_str())
.expect("spec.path string scalar present")
.to_owned();
let composed = flux_kustomization_source_subtree(&opts.cluster, sample_caixa().nome());
assert_eq!(
emitted, composed,
"spec.path emit must byte-equal the lifted \
flux_kustomization_source_subtree composer's output — drift \
at either half silently splits the per-cluster / per-caixa \
sub-tree seed axis at emit time from the canonical composer"
);
}
#[test]
fn sample_caixa_nome_accessor_byte_equals_raw_field() {
let fixture = sample_caixa();
assert_eq!(
fixture.nome(),
fixture.nome.as_str(),
"Caixa::nome() must borrow the same bytes as the raw \
`nome: String` field storage; any implementation drift \
here silently splits every caixa-flux composer-fixture \
navigation site that routes through the accessor from \
the storage-side field the peer production emit path \
still reads verbatim"
);
}
#[test]
fn cluster_bundle_kustomization_timeout_pins_lifted_default() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let ks = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&ks.contents).expect("kustomization.yaml parses as YAML");
let timeout = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KUSTOMIZATION_KEY_TIMEOUT))
.and_then(|v| v.as_str())
.expect(
"spec.timeout string scalar present; drift on this axis \
silently strips the substrate's chosen reconcile-ceiling \
declaration from every emitted per-caixa `Kustomization` \
document, letting the kustomize-controller fall back to \
the upstream Flux v2 controller-side default cap",
);
assert_eq!(
timeout, DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT,
"spec.timeout must carry the substrate's canonical Flux v2 \
per-`Kustomization`-CR reconcile wall-clock cap seed — drift \
silently strips the substrate's chosen reconcile-ceiling \
declaration from every emitted per-caixa `Kustomization` \
document"
);
}
#[test]
fn flux_kustomization_key_timeout_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KUSTOMIZATION_KEY_TIMEOUT",
FLUX_KUSTOMIZATION_KEY_TIMEOUT,
caixa_core::FLUX_KUSTOMIZATION_KEY_TIMEOUT,
);
}
#[test]
fn default_flux_kustomization_timeout_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT",
DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT,
caixa_core::DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT,
);
}
#[test]
fn kube_key_spec_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"KUBE_KEY_SPEC",
KUBE_KEY_SPEC,
caixa_core::KUBE_KEY_SPEC,
);
}
#[test]
fn kube_key_metadata_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"KUBE_KEY_METADATA",
KUBE_KEY_METADATA,
caixa_core::KUBE_KEY_METADATA,
);
}
#[test]
fn kube_key_kind_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"KUBE_KEY_KIND",
KUBE_KEY_KIND,
caixa_core::KUBE_KEY_KIND,
);
}
#[test]
fn kube_key_api_version_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"KUBE_KEY_API_VERSION",
KUBE_KEY_API_VERSION,
caixa_core::KUBE_KEY_API_VERSION,
);
}
#[test]
fn kube_key_namespace_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"KUBE_KEY_NAMESPACE",
KUBE_KEY_NAMESPACE,
caixa_core::KUBE_KEY_NAMESPACE,
);
}
#[test]
fn fleet_programs_key_programs_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLEET_PROGRAMS_KEY_PROGRAMS",
FLEET_PROGRAMS_KEY_PROGRAMS,
caixa_core::FLEET_PROGRAMS_KEY_PROGRAMS,
);
}
#[test]
fn fleet_programs_key_name_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLEET_PROGRAMS_KEY_NAME",
FLEET_PROGRAMS_KEY_NAME,
caixa_core::FLEET_PROGRAMS_KEY_NAME,
);
}
#[test]
fn computeunit_spec_key_module_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"COMPUTEUNIT_SPEC_KEY_MODULE",
COMPUTEUNIT_SPEC_KEY_MODULE,
caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE,
);
}
#[test]
fn computeunit_spec_key_trigger_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"COMPUTEUNIT_SPEC_KEY_TRIGGER",
COMPUTEUNIT_SPEC_KEY_TRIGGER,
caixa_core::COMPUTEUNIT_SPEC_KEY_TRIGGER,
);
}
#[test]
fn computeunit_spec_key_capabilities_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"COMPUTEUNIT_SPEC_KEY_CAPABILITIES",
COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
caixa_core::COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
);
}
#[test]
fn computeunit_module_key_source_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"COMPUTEUNIT_MODULE_KEY_SOURCE",
COMPUTEUNIT_MODULE_KEY_SOURCE,
caixa_core::COMPUTEUNIT_MODULE_KEY_SOURCE,
);
}
#[test]
fn programs_yaml_entry_round_trips() {
let entry = programs_yaml_entry(&sample_caixa(), &sample_cu_yaml()).unwrap();
assert_eq!(
entry.get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()),
Some("hello-rio")
);
assert_eq!(
entry.get(KUBE_KEY_NAMESPACE).and_then(|n| n.as_str()),
Some(DEFAULT_NAMESPACE)
);
assert!(entry.get(COMPUTEUNIT_SPEC_KEY_MODULE).is_some());
assert!(entry.get(COMPUTEUNIT_SPEC_KEY_TRIGGER).is_some());
assert!(entry.get(COMPUTEUNIT_SPEC_KEY_CAPABILITIES).is_some());
assert!(
entry
.get(COMPUTEUNIT_SPEC_KEY_MODULE)
.and_then(|m| m.get(COMPUTEUNIT_MODULE_KEY_SOURCE))
.is_some(),
"module.source must propagate verbatim"
);
}
#[test]
fn programs_yaml_entry_falls_back_to_default_namespace() {
let cu: serde_yaml::Value = serde_yaml::from_str(
r#"
apiVersion: wasm.pleme.io/v1alpha1
kind: ComputeUnit
metadata:
name: hello-rio
spec:
module:
source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0
"#,
)
.unwrap();
let entry = programs_yaml_entry(&sample_caixa(), &cu).unwrap();
assert_eq!(
entry.get(KUBE_KEY_NAMESPACE).and_then(|n| n.as_str()),
Some(DEFAULT_NAMESPACE)
);
}
#[test]
fn programs_yaml_entry_refuses_non_servico() {
let mut c = sample_caixa();
c.kind = CaixaKind::Biblioteca;
c.servicos = vec![];
let err = programs_yaml_entry(&c, &sample_cu_yaml()).unwrap_err();
assert!(matches!(err, Error::NotAServico(_)));
}
#[test]
fn kind_mismatch_error_names_offending_caixa_nome() {
let mut c = sample_caixa();
c.kind = CaixaKind::Biblioteca;
c.servicos = vec![];
let err = programs_yaml_entry(&c, &sample_cu_yaml()).unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("hello-rio"),
"kind-mismatch diagnostic must name the offending caixa nome \
(got: {msg:?})"
);
assert!(
msg.contains("Servico"),
"diagnostic must name the expected kind (got: {msg:?})"
);
assert!(
msg.contains("Biblioteca"),
"diagnostic must name the actual kind (got: {msg:?})"
);
}
#[test]
fn cluster_bundle_kind_mismatch_names_offending_caixa_nome() {
let mut c = sample_caixa();
c.kind = CaixaKind::Aplicacao;
c.servicos = vec![];
let opts = ClusterBundleOpts::for_caixa(&c, "rio");
let err = cluster_bundle(&c, &opts).unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("hello-rio"),
"cluster_bundle's kind-mismatch must also name the caixa \
nome (got: {msg:?})"
);
match err {
Error::NotAServico(km) => {
assert_eq!(km.nome, "hello-rio");
assert_eq!(km.expected, CaixaKind::Servico);
assert_eq!(km.actual, CaixaKind::Aplicacao);
}
other => panic!("expected Error::NotAServico, got {other:?}"),
}
}
#[test]
fn servico_count_mismatch_carries_typed_view_with_nome() {
let mut c = sample_caixa();
c.servicos = vec![
"servicos/hello-rio.computeunit.yaml".into(),
"servicos/extra.computeunit.yaml".into(),
];
let err = programs_yaml_entry(&c, &sample_cu_yaml()).unwrap_err();
match err {
Error::UnsupportedServicoCount(scm) => {
assert_eq!(scm.nome, "hello-rio");
assert_eq!(scm.count, 2);
}
other => panic!("expected Error::UnsupportedServicoCount, got {other:?}"),
}
}
#[test]
fn servico_count_mismatch_diagnostic_names_offending_caixa_nome() {
let mut c = sample_caixa();
c.servicos = vec![];
let err = programs_yaml_entry(&c, &sample_cu_yaml()).unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("hello-rio"),
":servicos-count-mismatch diagnostic must name the offending caixa nome \
(got: {msg:?})"
);
assert!(
msg.contains("0"),
"diagnostic must name the actual count (got: {msg:?})"
);
assert!(
msg.contains(":servicos"),
"diagnostic must name the offending field axis (got: {msg:?})"
);
}
#[test]
fn cluster_bundle_servico_count_mismatch_carries_typed_view_with_nome() {
let mut c = sample_caixa();
c.servicos = vec![
"servicos/hello-rio.computeunit.yaml".into(),
"servicos/extra.computeunit.yaml".into(),
];
let opts = ClusterBundleOpts::for_caixa(&c, "rio");
let err = cluster_bundle(&c, &opts).unwrap_err();
match err {
Error::UnsupportedServicoCount(scm) => {
assert_eq!(scm.nome, "hello-rio");
assert_eq!(scm.count, 2);
}
other => panic!("expected Error::UnsupportedServicoCount, got {other:?}"),
}
}
#[test]
fn cluster_bundle_servico_count_mismatch_diagnostic_names_offending_caixa_nome() {
let mut c = sample_caixa();
c.servicos = vec![];
let opts = ClusterBundleOpts::for_caixa(&c, "rio");
let err = cluster_bundle(&c, &opts).unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("hello-rio"),
"cluster_bundle's :servicos-count-mismatch must name the offending caixa nome \
(got: {msg:?})"
);
assert!(
msg.contains("0"),
"diagnostic must name the actual count (got: {msg:?})"
);
assert!(
msg.contains(":servicos"),
"diagnostic must name the offending field axis (got: {msg:?})"
);
}
#[test]
fn upsert_inserts_new_entry() {
let initial: serde_yaml::Value = serde_yaml::from_str(
r#"
enabled: true
defaultNamespace: tatara-system
programs: []
"#,
)
.unwrap();
let entry = programs_yaml_entry(&sample_caixa(), &sample_cu_yaml()).unwrap();
let (modified, inserted) = upsert_into_programs_yaml(initial, entry).unwrap();
assert!(inserted, "first time should be insert");
let arr = modified
.get(FLEET_PROGRAMS_KEY_PROGRAMS)
.unwrap()
.as_sequence()
.unwrap();
assert_eq!(arr.len(), 1);
assert_eq!(
arr[0].get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()),
Some("hello-rio")
);
}
#[test]
fn upsert_replaces_existing_entry() {
let initial: serde_yaml::Value = serde_yaml::from_str(
r#"
enabled: true
defaultNamespace: tatara-system
programs:
- name: hello-rio
namespace: tatara-system
module:
source: oci://ghcr.io/pleme-io/hello-rio:v0.0.1
- name: other
namespace: tatara-system
module: { source: github:foo/bar }
"#,
)
.unwrap();
let entry = programs_yaml_entry(&sample_caixa(), &sample_cu_yaml()).unwrap();
let (modified, inserted) = upsert_into_programs_yaml(initial, entry).unwrap();
assert!(!inserted, "second time should be replace");
let arr = modified
.get(FLEET_PROGRAMS_KEY_PROGRAMS)
.unwrap()
.as_sequence()
.unwrap();
assert_eq!(arr.len(), 2, "no new entry added");
let updated_module = arr[0]
.get(COMPUTEUNIT_SPEC_KEY_MODULE)
.unwrap()
.get(COMPUTEUNIT_MODULE_KEY_SOURCE)
.and_then(|s| s.as_str());
assert_eq!(
updated_module,
Some("oci://ghcr.io/pleme-io/hello-rio:v0.1.0")
);
}
#[test]
fn upsert_helmrelease_inserts_under_spec_values_programs() {
let initial: serde_yaml::Value = serde_yaml::from_str(
r#"
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: rio-fleet-programs
namespace: tatara-system
spec:
interval: 30m
chart:
spec:
chart: lareira-fleet-programs
values:
enabled: true
defaultNamespace: tatara-system
programs:
- name: existing
module: { source: github:foo/bar }
"#,
)
.unwrap();
let entry = programs_yaml_entry(&sample_caixa(), &sample_cu_yaml()).unwrap();
let (modified, inserted) = upsert_into_helmrelease_programs(initial, entry).unwrap();
assert!(inserted);
let arr = modified
.get(KUBE_KEY_SPEC)
.unwrap()
.get(FLUX_KEY_VALUES)
.unwrap()
.get(FLEET_PROGRAMS_KEY_PROGRAMS)
.unwrap()
.as_sequence()
.unwrap();
assert_eq!(arr.len(), 2);
assert_eq!(
arr[1].get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()),
Some("hello-rio")
);
}
#[test]
fn upsert_helmrelease_replaces_existing() {
let initial: serde_yaml::Value = serde_yaml::from_str(
r#"
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata: { name: rio-fleet-programs }
spec:
values:
programs:
- name: hello-rio
module: { source: oci://ghcr.io/pleme-io/hello-rio:v0.0.1 }
- name: other
module: { source: github:foo/bar }
"#,
)
.unwrap();
let entry = programs_yaml_entry(&sample_caixa(), &sample_cu_yaml()).unwrap();
let (modified, inserted) = upsert_into_helmrelease_programs(initial, entry).unwrap();
assert!(!inserted);
let arr = modified
.get(KUBE_KEY_SPEC)
.unwrap()
.get(FLUX_KEY_VALUES)
.unwrap()
.get(FLEET_PROGRAMS_KEY_PROGRAMS)
.unwrap()
.as_sequence()
.unwrap();
assert_eq!(arr.len(), 2);
let updated = arr[0]
.get(COMPUTEUNIT_SPEC_KEY_MODULE)
.unwrap()
.get(COMPUTEUNIT_MODULE_KEY_SOURCE)
.and_then(|s| s.as_str());
assert_eq!(updated, Some("oci://ghcr.io/pleme-io/hello-rio:v0.1.0"));
}
#[test]
fn limits_slot_propagates_into_programs_yaml_entry() {
use caixa_core::LimitsSpec;
use std::time::Duration;
let mut c = sample_caixa();
c.limits = Some(LimitsSpec {
memory: Some(64 * 1024 * 1024),
fuel: Some(1_000_000),
wall_clock: Some(Duration::from_secs(30)),
cpu: Some(500),
});
let entry = programs_yaml_entry(&c, &sample_cu_yaml()).unwrap();
let limits = entry.get(M2_KEY_LIMITS).expect("limits propagates");
assert_eq!(
limits.get(M2_LIMITS_KEY_MEMORY).and_then(|m| m.as_str()),
Some("64MiB")
);
assert_eq!(
limits.get(M2_LIMITS_KEY_CPU).and_then(|m| m.as_str()),
Some("500m")
);
}
#[test]
fn behavior_slot_propagates_into_programs_yaml_entry() {
use caixa_core::BehaviorSpec;
use std::path::PathBuf;
let mut c = sample_caixa();
c.behavior = Some(BehaviorSpec {
on_init: Some(PathBuf::from("lib/init.lisp")),
on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
..Default::default()
});
let entry = programs_yaml_entry(&c, &sample_cu_yaml()).unwrap();
let behavior = entry.get(M2_KEY_BEHAVIOR).expect("behavior propagates");
assert_eq!(
behavior
.get(M2_BEHAVIOR_KEY_ON_INIT)
.and_then(|v| v.as_str()),
Some("lib/init.lisp")
);
}
#[test]
fn upgrade_from_slot_propagates_into_programs_yaml_entry() {
use caixa_core::{UpgradeFromEntry, UpgradeInstruction};
let mut c = sample_caixa();
c.upgrade_from = vec![UpgradeFromEntry {
from: "0.0.9".into(),
instructions: vec![UpgradeInstruction::SoftPurge {
module: "hello-rio-old".into(),
}],
}];
let entry = programs_yaml_entry(&c, &sample_cu_yaml()).unwrap();
let upgrade_from = entry
.get(M2_KEY_UPGRADE_FROM)
.and_then(|u| u.as_sequence())
.expect("upgradeFrom propagates as a sequence");
assert_eq!(upgrade_from.len(), 1);
assert_eq!(
upgrade_from[0]
.get(M2_UPGRADE_FROM_KEY_FROM)
.and_then(|f| f.as_str()),
Some("0.0.9")
);
}
#[test]
fn empty_m2_slots_do_not_appear_in_programs_yaml_entry() {
let entry = programs_yaml_entry(&sample_caixa(), &sample_cu_yaml()).unwrap();
assert!(entry.get(M2_KEY_LIMITS).is_none());
assert!(entry.get(M2_KEY_BEHAVIOR).is_none());
assert!(entry.get(M2_KEY_UPGRADE_FROM).is_none());
}
#[test]
fn cluster_bundle_three_files() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
assert_eq!(files.len(), 3);
let names: Vec<_> = files
.iter()
.map(|f| f.path.to_string_lossy().to_string())
.collect();
assert!(names.contains(&FLUX_GITREPOSITORY_YAML_FILENAME.to_string()));
assert!(names.contains(&FLUX_HELMRELEASE_YAML_FILENAME.to_string()));
assert!(names.contains(&FLUX_KUSTOMIZATION_YAML_FILENAME.to_string()));
let kust = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.unwrap();
assert!(kust.contents.contains("./clusters/rio/services/hello-rio"));
let gitrepo = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.unwrap();
assert!(gitrepo.contents.contains("v0.1.0"));
}
#[test]
fn default_library_name_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"DEFAULT_LIBRARY_NAME",
DEFAULT_LIBRARY_NAME,
caixa_core::DEFAULT_LIBRARY_NAME,
);
}
#[test]
fn cluster_bundle_helmrelease_values_wrap_key_uses_lifted_constant() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let values = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_VALUES))
.and_then(|v| v.as_mapping())
.expect("spec.values mapping present");
assert!(
values.get(DEFAULT_LIBRARY_NAME).is_some(),
"spec.values must wrap under the lifted DEFAULT_LIBRARY_NAME \
({DEFAULT_LIBRARY_NAME:?}); a drifted literal here silently \
routes per-cluster overrides nowhere at helm template time"
);
let wrapped = values
.get(DEFAULT_LIBRARY_NAME)
.and_then(|v| v.as_mapping())
.expect("wrapped library mapping");
assert_eq!(
wrapped
.get(HELM_VALUES_KEY_ENABLED)
.and_then(|v| v.as_bool()),
Some(CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT),
"cluster_bundle helmrelease.yaml `spec.values.<library>.enabled` \
overlay must resolve to the lifted \
CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT — a drifted inline \
literal here would silently disagree with the substrate-side \
child-chart force-on-under-composition seed"
);
}
#[test]
fn cluster_bundle_helmrelease_wrap_key_pins_canonical_pleme_computeunit_string() {
assert_eq!(DEFAULT_LIBRARY_NAME, "pleme-computeunit");
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.unwrap();
assert!(
hr.contents.contains("pleme-computeunit:"),
"helmrelease.yaml must spell the canonical library-chart wrap \
key under spec.values (got: {contents:?})",
contents = hr.contents
);
}
#[test]
fn helm_values_key_enabled_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"HELM_VALUES_KEY_ENABLED",
HELM_VALUES_KEY_ENABLED,
caixa_core::HELM_VALUES_KEY_ENABLED,
);
}
#[test]
fn default_flux_system_namespace_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"DEFAULT_FLUX_SYSTEM_NAMESPACE",
DEFAULT_FLUX_SYSTEM_NAMESPACE,
caixa_core::DEFAULT_FLUX_SYSTEM_NAMESPACE,
);
}
#[test]
fn cluster_bundle_kustomization_uses_lifted_flux_system_namespace() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let kust = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&kust.contents).expect("kustomization.yaml parses as YAML");
assert_eq!(
kube_metadata_str_field(&parsed, KUBE_KEY_NAMESPACE),
Some(DEFAULT_FLUX_SYSTEM_NAMESPACE),
"kustomization.yaml metadata.namespace must spell the lifted \
DEFAULT_FLUX_SYSTEM_NAMESPACE ({DEFAULT_FLUX_SYSTEM_NAMESPACE:?}); \
a drifted literal here silently places the Kustomization outside \
the bootstrap kustomize-controller's watch window"
);
assert_eq!(
parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_SOURCE_REF))
.and_then(|r| r.get("name"))
.and_then(|n| n.as_str()),
Some(DEFAULT_FLUX_SYSTEM_NAMESPACE),
"kustomization.yaml spec.sourceRef.name must spell the lifted \
DEFAULT_FLUX_SYSTEM_NAMESPACE ({DEFAULT_FLUX_SYSTEM_NAMESPACE:?}); \
a drifted literal here dangles the reference at a GitRepository \
that doesn't exist in the rebranded installation namespace"
);
}
#[test]
fn cluster_bundle_kustomization_pins_canonical_flux_system_string() {
assert_eq!(DEFAULT_FLUX_SYSTEM_NAMESPACE, "flux-system");
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let kust = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.unwrap();
assert!(
kust.contents.contains("namespace: flux-system\n"),
"kustomization.yaml must spell the canonical FluxCD \
installation namespace at metadata.namespace (got: {contents:?})",
contents = kust.contents
);
assert!(
kust.contents.contains("name: flux-system\n"),
"kustomization.yaml must spell the canonical FluxCD \
installation namespace at spec.sourceRef.name (got: {contents:?})",
contents = kust.contents
);
}
#[test]
fn cluster_bundle_helmrelease_uses_lifted_flux_api_version() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
assert_eq!(
kube_root_str_field(&parsed, KUBE_KEY_API_VERSION),
Some(FLUX_HELMRELEASE_API_VERSION),
"helmrelease.yaml apiVersion must spell the lifted \
FLUX_HELMRELEASE_API_VERSION ({FLUX_HELMRELEASE_API_VERSION:?}); \
a drifted literal here routes the HelmRelease outside the Flux v2 \
helm-controller's Watches",
);
}
#[test]
fn cluster_bundle_kustomization_health_check_uses_lifted_flux_api_version() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let kust = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&kust.contents).expect("kustomization.yaml parses as YAML");
let health_checks = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_HEALTH_CHECKS))
.and_then(|h| h.as_sequence())
.expect("kustomization.yaml spec.healthChecks present");
assert!(
!health_checks.is_empty(),
"kustomization.yaml spec.healthChecks must carry at least one \
entry — the rendered Kustomization gates on its sibling \
HelmRelease's health by construction",
);
for (i, entry) in health_checks.iter().enumerate() {
assert_eq!(
kube_root_str_field(entry, KUBE_KEY_API_VERSION),
Some(FLUX_HELMRELEASE_API_VERSION),
"kustomization.yaml spec.healthChecks[{i}].apiVersion must \
spell the lifted FLUX_HELMRELEASE_API_VERSION \
({FLUX_HELMRELEASE_API_VERSION:?}); a drifted literal here \
dangles the per-resource health-gate at apply time",
);
}
}
#[test]
fn cluster_bundle_helmrelease_pins_canonical_flux_v2_api_version_string() {
assert_eq!(FLUX_HELMRELEASE_API_VERSION, "helm.toolkit.fluxcd.io/v2");
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.unwrap();
assert!(
hr.contents
.contains("apiVersion: helm.toolkit.fluxcd.io/v2\n"),
"helmrelease.yaml must spell the canonical Flux v2 HelmRelease \
apiVersion at the top-level apiVersion axis (got: {contents:?})",
contents = hr.contents,
);
let kust = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.unwrap();
assert!(
kust.contents
.contains("apiVersion: helm.toolkit.fluxcd.io/v2\n"),
"kustomization.yaml must spell the canonical Flux v2 HelmRelease \
apiVersion at spec.healthChecks[].apiVersion (got: {contents:?})",
contents = kust.contents,
);
}
#[test]
fn cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix() {
let caixa = sample_caixa();
let opts = ClusterBundleOpts::for_caixa(&caixa, "rio");
match &opts.git_ref {
GitRefSpec::Tag(tag) => {
assert!(
tag.starts_with(caixa_core::DEFAULT_PUBLISH_TAG_PREFIX),
"default git_ref tag must start with the lifted \
caixa_core::DEFAULT_PUBLISH_TAG_PREFIX (got: {tag:?})"
);
assert_eq!(
tag,
&format!(
"{prefix}{versao}",
prefix = caixa_core::DEFAULT_PUBLISH_TAG_PREFIX,
versao = caixa.versao(),
),
"default git_ref tag must compose the lifted prefix \
against the caixa's :versao verbatim"
);
}
other => panic!("expected GitRefSpec::Tag, got {other:?}"),
}
let files = cluster_bundle(&caixa, &opts).unwrap();
let gr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.expect("gitrepository.yaml present");
let expected_tag = format!(
"{prefix}{versao}",
prefix = caixa_core::DEFAULT_PUBLISH_TAG_PREFIX,
versao = caixa.versao(),
);
assert!(
gr.contents.contains(&format!("tag: {expected_tag:?}")),
"gitrepository.yaml must spell the lifted-prefix-composed tag \
at ref.tag (expected: {expected_tag:?}, got: {contents:?})",
contents = gr.contents
);
}
#[test]
fn cluster_bundle_default_git_tag_versao_routes_through_caixa_versao_accessor() {
let caixa = sample_caixa();
let opts = ClusterBundleOpts::for_caixa(&caixa, "rio");
let expected_tag = format!(
"{prefix}{versao}",
prefix = caixa_core::DEFAULT_PUBLISH_TAG_PREFIX,
versao = caixa.versao(),
);
match &opts.git_ref {
GitRefSpec::Tag(tag) => assert_eq!(
tag, &expected_tag,
"default git_ref tag must derive its `{{versao}}` \
scalar through the typed `caixa_core::Caixa::versao` \
accessor — a regression that re-inlines \
`caixa.versao` at the constructor site silently \
splits the FluxCD `GitRepository` clone-target \
`ref.tag` from every other per-Caixa \
version-identity consumer"
),
other => panic!("expected GitRefSpec::Tag, got {other:?}"),
}
let files = cluster_bundle(&caixa, &opts).unwrap();
let gr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.expect("gitrepository.yaml present");
assert!(
gr.contents.contains(&format!("tag: {expected_tag:?}")),
"gitrepository.yaml must spell the accessor-derived tag \
at ref.tag (expected: {expected_tag:?}, got: {contents:?}) \
— a regression that re-inlines `caixa.versao` at the \
[`cluster_bundle`] format-string site silently splits \
the on-disk Flux v2 `GitRepository` YAML from the \
substrate's per-caixa version-identity dispatch",
contents = gr.contents
);
}
#[test]
fn flux_gitrepository_api_version_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_GITREPOSITORY_API_VERSION",
FLUX_GITREPOSITORY_API_VERSION,
caixa_core::FLUX_GITREPOSITORY_API_VERSION,
);
}
#[test]
fn cluster_bundle_gitrepository_uses_lifted_flux_api_version() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let gr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.expect("gitrepository.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&gr.contents).expect("gitrepository.yaml parses as YAML");
assert_eq!(
kube_root_str_field(&parsed, KUBE_KEY_API_VERSION),
Some(FLUX_GITREPOSITORY_API_VERSION),
"gitrepository.yaml apiVersion must spell the lifted \
FLUX_GITREPOSITORY_API_VERSION ({FLUX_GITREPOSITORY_API_VERSION:?}); \
a drifted literal here routes the GitRepository outside the Flux v2 \
source-controller's Watches",
);
}
#[test]
fn cluster_bundle_gitrepository_pins_canonical_flux_v1_api_version_string() {
assert_eq!(
FLUX_GITREPOSITORY_API_VERSION,
"source.toolkit.fluxcd.io/v1"
);
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let gr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.unwrap();
assert!(
gr.contents
.contains("apiVersion: source.toolkit.fluxcd.io/v1\n"),
"gitrepository.yaml must spell the canonical Flux v2 GitRepository \
apiVersion at the top-level apiVersion axis (got: {contents:?})",
contents = gr.contents,
);
}
#[test]
fn flux_kustomization_api_version_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KUSTOMIZATION_API_VERSION",
FLUX_KUSTOMIZATION_API_VERSION,
caixa_core::FLUX_KUSTOMIZATION_API_VERSION,
);
}
#[test]
fn cluster_bundle_kustomization_uses_lifted_flux_api_version() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let kz = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&kz.contents).expect("kustomization.yaml parses as YAML");
assert_eq!(
kube_root_str_field(&parsed, KUBE_KEY_API_VERSION),
Some(FLUX_KUSTOMIZATION_API_VERSION),
"kustomization.yaml apiVersion must spell the lifted \
FLUX_KUSTOMIZATION_API_VERSION ({FLUX_KUSTOMIZATION_API_VERSION:?}); \
a drifted literal here routes the Kustomization outside the Flux v2 \
kustomize-controller's Watches",
);
}
#[test]
fn cluster_bundle_kustomization_pins_canonical_flux_v1_api_version_string() {
assert_eq!(
FLUX_KUSTOMIZATION_API_VERSION,
"kustomize.toolkit.fluxcd.io/v1"
);
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let kz = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.unwrap();
assert!(
kz.contents
.contains("apiVersion: kustomize.toolkit.fluxcd.io/v1\n"),
"kustomization.yaml must spell the canonical Flux v2 Kustomization \
apiVersion at the top-level apiVersion axis (got: {contents:?})",
contents = kz.contents,
);
}
#[test]
fn cluster_bundle_every_flux_cr_carries_top_level_api_version_label_from_lifted_key() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let label_prefix = format!("{KUBE_KEY_API_VERSION}: ");
for filename in [
FLUX_GITREPOSITORY_YAML_FILENAME,
FLUX_HELMRELEASE_YAML_FILENAME,
FLUX_KUSTOMIZATION_YAML_FILENAME,
] {
let f = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(filename))
.unwrap_or_else(|| panic!("{filename} present"));
assert!(
f.contents.contains(&label_prefix),
"{filename} must carry the top-level {label_prefix:?} YAML label \
composed from the lifted KUBE_KEY_API_VERSION ({KUBE_KEY_API_VERSION:?}); \
a drifted inline label here silently rebrands the per-CR \
CRD-group/version-axis discriminator away from the lifted key \
(got: {contents:?})",
contents = f.contents,
);
}
}
#[test]
fn cluster_bundle_every_flux_cr_carries_top_level_kind_label_from_lifted_key() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let label_prefix = format!("{KUBE_KEY_KIND}: ");
for filename in [
FLUX_GITREPOSITORY_YAML_FILENAME,
FLUX_HELMRELEASE_YAML_FILENAME,
FLUX_KUSTOMIZATION_YAML_FILENAME,
] {
let f = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(filename))
.unwrap_or_else(|| panic!("{filename} present"));
assert!(
f.contents.contains(&label_prefix),
"{filename} must carry the top-level {label_prefix:?} YAML label \
composed from the lifted KUBE_KEY_KIND ({KUBE_KEY_KIND:?}); \
a drifted inline label here silently rebrands the per-CR \
CRD-`kind`-discriminator-axis away from the lifted key \
(got: {contents:?})",
contents = f.contents,
);
}
}
#[test]
fn cluster_bundle_every_flux_cr_carries_top_level_metadata_label_from_lifted_key() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let label_prefix = format!("{KUBE_KEY_METADATA}:");
for filename in [
FLUX_GITREPOSITORY_YAML_FILENAME,
FLUX_HELMRELEASE_YAML_FILENAME,
FLUX_KUSTOMIZATION_YAML_FILENAME,
] {
let f = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(filename))
.unwrap_or_else(|| panic!("{filename} present"));
assert!(
f.contents.contains(&label_prefix),
"{filename} must carry the top-level {label_prefix:?} YAML label \
composed from the lifted KUBE_KEY_METADATA ({KUBE_KEY_METADATA:?}); \
a drifted inline label here silently rebrands the per-CR \
ObjectMeta-block-scope axis away from the lifted key \
(got: {contents:?})",
contents = f.contents,
);
}
}
#[test]
fn cluster_bundle_every_flux_cr_carries_top_level_spec_label_from_lifted_key() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let label_prefix = format!("{KUBE_KEY_SPEC}:");
for filename in [
FLUX_GITREPOSITORY_YAML_FILENAME,
FLUX_HELMRELEASE_YAML_FILENAME,
FLUX_KUSTOMIZATION_YAML_FILENAME,
] {
let f = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(filename))
.unwrap_or_else(|| panic!("{filename} present"));
assert!(
f.contents.contains(&label_prefix),
"{filename} must carry the top-level {label_prefix:?} YAML label \
composed from the lifted KUBE_KEY_SPEC ({KUBE_KEY_SPEC:?}); \
a drifted inline label here silently rebrands the per-CR \
spec-block-scope axis away from the lifted key \
(got: {contents:?})",
contents = f.contents,
);
}
}
#[test]
fn cluster_bundle_every_flux_cr_carries_metadata_namespace_label_from_lifted_key() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let label_prefix = format!("{KUBE_KEY_NAMESPACE}:");
for filename in [
FLUX_GITREPOSITORY_YAML_FILENAME,
FLUX_HELMRELEASE_YAML_FILENAME,
FLUX_KUSTOMIZATION_YAML_FILENAME,
] {
let f = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(filename))
.unwrap_or_else(|| panic!("{filename} present"));
assert!(
f.contents.contains(&label_prefix),
"{filename} must carry the {label_prefix:?} YAML label \
composed from the lifted KUBE_KEY_NAMESPACE ({KUBE_KEY_NAMESPACE:?}); \
a drifted inline label here silently rebrands the per-CR \
metadata.namespace axis away from the lifted key \
(got: {contents:?})",
contents = f.contents,
);
}
}
#[test]
fn cluster_bundle_every_flux_cr_carries_metadata_name_label_from_lifted_key() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let label_prefix = format!("{KUBE_KEY_NAME}:");
for filename in [
FLUX_GITREPOSITORY_YAML_FILENAME,
FLUX_HELMRELEASE_YAML_FILENAME,
FLUX_KUSTOMIZATION_YAML_FILENAME,
] {
let f = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(filename))
.unwrap_or_else(|| panic!("{filename} present"));
assert!(
f.contents.contains(&label_prefix),
"{filename} must carry the {label_prefix:?} YAML label \
composed from the lifted KUBE_KEY_NAME ({KUBE_KEY_NAME:?}); \
a drifted inline label here silently rebrands the per-CR \
metadata.name axis away from the lifted key \
(got: {contents:?})",
contents = f.contents,
);
}
}
#[test]
fn flux_kind_git_repository_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KIND_GIT_REPOSITORY",
FLUX_KIND_GIT_REPOSITORY,
caixa_core::FLUX_KIND_GIT_REPOSITORY,
);
}
#[test]
fn cluster_bundle_gitrepository_kind_uses_lifted_flux_kind_git_repository() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let gr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.expect("gitrepository.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&gr.contents).expect("gitrepository.yaml parses as YAML");
assert_eq!(
kube_root_str_field(&parsed, KUBE_KEY_KIND),
Some(FLUX_KIND_GIT_REPOSITORY),
"gitrepository.yaml top-level kind must spell the lifted \
FLUX_KIND_GIT_REPOSITORY ({FLUX_KIND_GIT_REPOSITORY:?}); a drifted \
literal here routes the GitRepository outside the Flux v2 \
source-controller's CRD registration",
);
}
#[test]
fn cluster_bundle_helmrelease_source_ref_kind_uses_lifted_flux_kind_git_repository() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let source_ref_kind = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_CHART))
.and_then(|c| c.get(KUBE_KEY_SPEC))
.and_then(|s| s.get(FLUX_KEY_SOURCE_REF))
.and_then(|r| r.get(KUBE_KEY_KIND))
.and_then(|k| k.as_str())
.expect("helmrelease.yaml spec.chart.spec.sourceRef.kind present");
assert_eq!(
source_ref_kind, FLUX_KIND_GIT_REPOSITORY,
"helmrelease.yaml spec.chart.spec.sourceRef.kind must spell the \
lifted FLUX_KIND_GIT_REPOSITORY ({FLUX_KIND_GIT_REPOSITORY:?}); a \
drifted literal here dangles the HelmRelease's chart sourceRef \
at the Flux v2 source-controller's CRD registration",
);
}
#[test]
fn cluster_bundle_kustomization_source_ref_kind_uses_lifted_flux_kind_git_repository() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let kz = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&kz.contents).expect("kustomization.yaml parses as YAML");
let source_ref_kind = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_SOURCE_REF))
.and_then(|r| r.get(KUBE_KEY_KIND))
.and_then(|k| k.as_str())
.expect("kustomization.yaml spec.sourceRef.kind present");
assert_eq!(
source_ref_kind, FLUX_KIND_GIT_REPOSITORY,
"kustomization.yaml spec.sourceRef.kind must spell the lifted \
FLUX_KIND_GIT_REPOSITORY ({FLUX_KIND_GIT_REPOSITORY:?}); a drifted \
literal here dangles the parent Kustomization's sourceRef at \
the Flux v2 source-controller's CRD registration",
);
}
#[test]
fn cluster_bundle_three_git_repository_kind_axes_share_one_lifted_constant() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let gr_kind = serde_yaml::from_str::<serde_yaml::Value>(
&files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.unwrap()
.contents,
)
.unwrap()
.get(KUBE_KEY_KIND)
.and_then(|v| v.as_str())
.map(String::from)
.unwrap();
let hr_source_kind = serde_yaml::from_str::<serde_yaml::Value>(
&files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.unwrap()
.contents,
)
.unwrap()
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_CHART))
.and_then(|c| c.get(KUBE_KEY_SPEC))
.and_then(|s| s.get(FLUX_KEY_SOURCE_REF))
.and_then(|r| r.get(KUBE_KEY_KIND))
.and_then(|v| v.as_str())
.map(String::from)
.unwrap();
let kz_source_kind = serde_yaml::from_str::<serde_yaml::Value>(
&files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.unwrap()
.contents,
)
.unwrap()
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_SOURCE_REF))
.and_then(|r| r.get(KUBE_KEY_KIND))
.and_then(|v| v.as_str())
.map(String::from)
.unwrap();
assert_eq!(gr_kind, FLUX_KIND_GIT_REPOSITORY);
assert_eq!(hr_source_kind, FLUX_KIND_GIT_REPOSITORY);
assert_eq!(kz_source_kind, FLUX_KIND_GIT_REPOSITORY);
assert_eq!(
gr_kind, hr_source_kind,
"gitrepository.yaml top-level kind and helmrelease.yaml \
spec.chart.spec.sourceRef.kind must spell the same lifted \
constant — drift here dangles the HelmRelease's chart sourceRef"
);
assert_eq!(
gr_kind, kz_source_kind,
"gitrepository.yaml top-level kind and kustomization.yaml \
spec.sourceRef.kind must spell the same lifted constant — \
drift here dangles the parent Kustomization's sourceRef"
);
}
#[test]
fn flux_kind_helm_release_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KIND_HELM_RELEASE",
FLUX_KIND_HELM_RELEASE,
caixa_core::FLUX_KIND_HELM_RELEASE,
);
}
#[test]
fn cluster_bundle_helmrelease_kind_uses_lifted_flux_kind_helm_release() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
assert_eq!(
kube_root_str_field(&parsed, KUBE_KEY_KIND),
Some(FLUX_KIND_HELM_RELEASE),
"helmrelease.yaml top-level kind must spell the lifted \
FLUX_KIND_HELM_RELEASE ({FLUX_KIND_HELM_RELEASE:?}); a drifted \
literal here routes the HelmRelease outside the Flux v2 \
helm-controller's CRD registration",
);
}
#[test]
fn cluster_bundle_kustomization_health_check_kind_uses_lifted_flux_kind_helm_release() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let kz = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&kz.contents).expect("kustomization.yaml parses as YAML");
let health_checks = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_HEALTH_CHECKS))
.and_then(|h| h.as_sequence())
.expect("kustomization.yaml spec.healthChecks present");
assert!(
!health_checks.is_empty(),
"kustomization.yaml spec.healthChecks must carry at least one \
entry — the HelmRelease health gate is the canonical pleme-io \
Flux bundle invariant"
);
let health_kind = health_checks[0]
.get(KUBE_KEY_KIND)
.and_then(|k| k.as_str())
.expect("kustomization.yaml spec.healthChecks[0].kind present");
assert_eq!(
health_kind, FLUX_KIND_HELM_RELEASE,
"kustomization.yaml spec.healthChecks[0].kind must spell the \
lifted FLUX_KIND_HELM_RELEASE ({FLUX_KIND_HELM_RELEASE:?}); a \
drifted literal here dangles the parent Kustomization at \
`Reconciling` forever at the Flux v2 kustomize-controller's \
health-gate evaluation",
);
}
#[test]
fn cluster_bundle_two_helm_release_kind_axes_share_one_lifted_constant() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr_kind = serde_yaml::from_str::<serde_yaml::Value>(
&files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.unwrap()
.contents,
)
.unwrap()
.get(KUBE_KEY_KIND)
.and_then(|v| v.as_str())
.map(String::from)
.unwrap();
let kz_health_kind = serde_yaml::from_str::<serde_yaml::Value>(
&files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.unwrap()
.contents,
)
.unwrap()
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_HEALTH_CHECKS))
.and_then(|h| h.as_sequence())
.and_then(|seq| seq.first())
.and_then(|e| e.get(KUBE_KEY_KIND))
.and_then(|v| v.as_str())
.map(String::from)
.unwrap();
assert_eq!(hr_kind, FLUX_KIND_HELM_RELEASE);
assert_eq!(kz_health_kind, FLUX_KIND_HELM_RELEASE);
assert_eq!(
hr_kind, kz_health_kind,
"helmrelease.yaml top-level kind and kustomization.yaml \
spec.healthChecks[].kind must spell the same lifted constant \
— drift here dangles the parent Kustomization's health gate \
at the Flux v2 kustomize-controller"
);
}
#[test]
fn flux_kind_kustomization_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KIND_KUSTOMIZATION",
FLUX_KIND_KUSTOMIZATION,
caixa_core::FLUX_KIND_KUSTOMIZATION,
);
}
#[test]
fn flux_key_source_ref_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KEY_SOURCE_REF",
FLUX_KEY_SOURCE_REF,
caixa_core::FLUX_KEY_SOURCE_REF,
);
}
#[test]
fn cluster_bundle_helmrelease_spec_chart_spec_source_ref_key_pins_lifted_flux_key_source_ref() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let source_ref = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_CHART))
.and_then(|c| c.get(KUBE_KEY_SPEC))
.and_then(|s| s.get(FLUX_KEY_SOURCE_REF))
.and_then(|r| r.as_mapping())
.expect("spec.chart.spec.<FLUX_KEY_SOURCE_REF> mapping present");
assert!(
!source_ref.is_empty(),
"spec.chart.spec.<FLUX_KEY_SOURCE_REF> ({FLUX_KEY_SOURCE_REF:?}) \
must carry the `(kind, name, namespace)` reference triple; drift \
on this container-axis key silently dangles the HelmRelease's \
chart resolution at the Flux v2 source-controller's CRD \
registration"
);
}
#[test]
fn cluster_bundle_kustomization_spec_source_ref_key_pins_lifted_flux_key_source_ref() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let kz = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&kz.contents).expect("kustomization.yaml parses as YAML");
let source_ref = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_SOURCE_REF))
.and_then(|r| r.as_mapping())
.expect("spec.<FLUX_KEY_SOURCE_REF> mapping present");
assert!(
!source_ref.is_empty(),
"spec.<FLUX_KEY_SOURCE_REF> ({FLUX_KEY_SOURCE_REF:?}) must \
carry the `(kind, name)` reference pair pointing at the \
cluster's bootstrap GitRepository; drift on this \
container-axis key silently dangles the parent \
Kustomization's source resolution at the Flux v2 \
source-controller's CRD registration"
);
}
#[test]
fn flux_key_values_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KEY_VALUES",
FLUX_KEY_VALUES,
caixa_core::FLUX_KEY_VALUES,
);
}
#[test]
fn cluster_bundle_helmrelease_values_block_uses_lifted_flux_key_values() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let values = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_VALUES))
.and_then(|v| v.as_mapping())
.expect("spec.<FLUX_KEY_VALUES> mapping present");
assert!(
!values.is_empty(),
"spec.<FLUX_KEY_VALUES> ({FLUX_KEY_VALUES:?}) must carry \
at least the lifted-[`DEFAULT_LIBRARY_NAME`]-wrapped per-\
cluster override block; drift on this block-body-axis key \
silently routes overrides nowhere at helm-controller \
reconcile time"
);
}
#[test]
fn cluster_bundle_kustomization_kind_uses_lifted_flux_kind_kustomization() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let kz = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&kz.contents).expect("kustomization.yaml parses as YAML");
assert_eq!(
kube_root_str_field(&parsed, KUBE_KEY_KIND),
Some(FLUX_KIND_KUSTOMIZATION),
"kustomization.yaml top-level kind must spell the lifted \
FLUX_KIND_KUSTOMIZATION ({FLUX_KIND_KUSTOMIZATION:?}); a drifted \
literal here routes the Kustomization outside the Flux v2 \
kustomize-controller's CRD registration",
);
}
#[test]
fn flux_key_chart_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KEY_CHART",
FLUX_KEY_CHART,
caixa_core::FLUX_KEY_CHART,
);
}
#[test]
fn cluster_bundle_helmrelease_chart_block_uses_lifted_flux_key_chart() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let chart = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_CHART))
.and_then(|v| v.as_mapping())
.expect("spec.<FLUX_KEY_CHART> mapping present");
assert!(
!chart.is_empty(),
"spec.<FLUX_KEY_CHART> ({FLUX_KEY_CHART:?}) must carry \
at least the nested `HelmChartTemplate.spec` sub-document; \
drift on this container-axis key silently dangles the \
whole chart-template block at helm-controller reconcile time"
);
}
#[test]
fn flux_helmchart_template_key_chart_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_HELMCHART_TEMPLATE_KEY_CHART",
FLUX_HELMCHART_TEMPLATE_KEY_CHART,
caixa_core::FLUX_HELMCHART_TEMPLATE_KEY_CHART,
);
}
#[test]
fn cluster_bundle_helmrelease_chart_name_leaf_uses_lifted_flux_helmchart_template_key_chart() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let chart_name = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_CHART))
.and_then(|c| c.get(KUBE_KEY_SPEC))
.and_then(|s| s.get(FLUX_HELMCHART_TEMPLATE_KEY_CHART))
.and_then(|n| n.as_str())
.expect("spec.chart.spec.<FLUX_HELMCHART_TEMPLATE_KEY_CHART> scalar present");
assert_eq!(
chart_name,
opts.chart_path,
"spec.chart.spec.<FLUX_HELMCHART_TEMPLATE_KEY_CHART> \
({FLUX_HELMCHART_TEMPLATE_KEY_CHART:?}) leaf-scalar must carry the \
ClusterBundleOpts.chart_path verbatim ({expected:?}); a drifted \
key here silently dangles the chart-artifact resolution at the \
Flux v2 helm-controller's per-CR reconcile-time chart-lookup axis",
expected = opts.chart_path,
);
}
#[test]
fn flux_key_health_checks_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KEY_HEALTH_CHECKS",
FLUX_KEY_HEALTH_CHECKS,
caixa_core::FLUX_KEY_HEALTH_CHECKS,
);
}
#[test]
fn cluster_bundle_kustomization_health_checks_block_uses_lifted_flux_key_health_checks() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
let kz = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&kz.contents).expect("kustomization.yaml parses as YAML");
let health_checks = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_HEALTH_CHECKS))
.and_then(|v| v.as_sequence())
.expect("spec.<FLUX_KEY_HEALTH_CHECKS> sequence present");
assert!(
!health_checks.is_empty(),
"spec.<FLUX_KEY_HEALTH_CHECKS> ({FLUX_KEY_HEALTH_CHECKS:?}) must \
carry at least one `[]NamespacedObjectKindReference` entry; \
drift on this container-axis key silently dangles the whole \
health-gate at kustomize-controller reconcile time and freezes \
the parent Kustomization at `Reconciling`"
);
}
#[test]
fn flux_key_interval_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KEY_INTERVAL",
FLUX_KEY_INTERVAL,
caixa_core::FLUX_KEY_INTERVAL,
);
}
#[test]
fn flux_gitrepository_ref_key_tag_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_GITREPOSITORY_REF_KEY_TAG",
FLUX_GITREPOSITORY_REF_KEY_TAG,
caixa_core::FLUX_GITREPOSITORY_REF_KEY_TAG,
);
}
#[test]
fn flux_gitrepository_ref_key_branch_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_GITREPOSITORY_REF_KEY_BRANCH",
FLUX_GITREPOSITORY_REF_KEY_BRANCH,
caixa_core::FLUX_GITREPOSITORY_REF_KEY_BRANCH,
);
}
#[test]
fn flux_gitrepository_ref_key_commit_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_GITREPOSITORY_REF_KEY_COMMIT",
FLUX_GITREPOSITORY_REF_KEY_COMMIT,
caixa_core::FLUX_GITREPOSITORY_REF_KEY_COMMIT,
);
}
#[test]
fn flux_gitrepository_key_ref_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_GITREPOSITORY_KEY_REF",
FLUX_GITREPOSITORY_KEY_REF,
caixa_core::FLUX_GITREPOSITORY_KEY_REF,
);
}
#[test]
fn cluster_bundle_gitrepository_spec_ref_key_pins_lifted_flux_gitrepository_key_ref() {
let caixa = sample_caixa();
let opts = ClusterBundleOpts::for_caixa(&caixa, "rio");
let files = cluster_bundle(&caixa, &opts).expect("bundle renders");
let gr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.expect("gitrepository.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&gr.contents).expect("gitrepository.yaml parses as YAML");
assert!(
parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_GITREPOSITORY_KEY_REF))
.is_some(),
"rendered gitrepository.yaml must carry its ref-selection \
container-axis at the lifted FLUX_GITREPOSITORY_KEY_REF key \
verbatim (got: {:?})",
gr.contents
);
}
#[test]
fn flux_gitrepository_key_url_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_GITREPOSITORY_KEY_URL",
FLUX_GITREPOSITORY_KEY_URL,
caixa_core::FLUX_GITREPOSITORY_KEY_URL,
);
}
#[test]
fn cluster_bundle_gitrepository_spec_url_key_pins_lifted_flux_gitrepository_key_url() {
let caixa = sample_caixa();
let opts = ClusterBundleOpts::for_caixa(&caixa, "rio");
let files = cluster_bundle(&caixa, &opts).expect("bundle renders");
let gr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.expect("gitrepository.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&gr.contents).expect("gitrepository.yaml parses as YAML");
let url = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_GITREPOSITORY_KEY_URL))
.and_then(|u| u.as_str())
.expect(
"rendered gitrepository.yaml must carry its remote-URL leaf-scalar \
axis at the lifted FLUX_GITREPOSITORY_KEY_URL key verbatim",
);
assert!(
!url.is_empty(),
"spec.url leaf-scalar must resolve to a non-empty git-remote clone \
target — a drifted key would collapse the readback to None, an \
empty string would break the source-controller's per-CR clone step"
);
}
#[test]
fn gitrefspec_ref_field_name_dispatches_per_variant_onto_lifted_consts() {
assert_eq!(
GitRefSpec::Tag("v0.1.0".into()).ref_field_name(),
FLUX_GITREPOSITORY_REF_KEY_TAG,
);
assert_eq!(
GitRefSpec::Branch("main".into()).ref_field_name(),
FLUX_GITREPOSITORY_REF_KEY_BRANCH,
);
assert_eq!(
GitRefSpec::Commit("deadbeef".into()).ref_field_name(),
FLUX_GITREPOSITORY_REF_KEY_COMMIT,
);
}
#[test]
fn gitrefspec_ref_value_extracts_underlying_scalar_per_variant() {
assert_eq!(GitRefSpec::Tag("v0.1.0".into()).ref_value(), "v0.1.0");
assert_eq!(GitRefSpec::Branch("main".into()).ref_value(), "main");
assert_eq!(
GitRefSpec::Commit("deadbeef".into()).ref_value(),
"deadbeef",
);
}
#[test]
fn gitrefspec_is_variant_predicates_partition_the_arm_set() {
let rows: [(GitRefSpec, [bool; 3]); 3] = [
(GitRefSpec::Tag("v0.1.0".into()), [true, false, false]),
(GitRefSpec::Branch("main".into()), [false, true, false]),
(GitRefSpec::Commit("deadbeef".into()), [false, false, true]),
];
for (variant, expected) in rows {
let observed = [variant.is_tag(), variant.is_branch(), variant.is_commit()];
assert_eq!(
observed, expected,
"GitRefSpec::{variant:?} is_* predicates must partition \
the arm set (tag, branch, commit); got {observed:?}"
);
}
}
#[test]
fn gitrefspec_is_variant_predicates_agree_with_matches() {
let variants = [
GitRefSpec::Tag("v0.1.0".into()),
GitRefSpec::Branch("main".into()),
GitRefSpec::Commit("deadbeef".into()),
];
for variant in variants {
assert_eq!(
variant.is_tag(),
matches!(variant, GitRefSpec::Tag(_)),
"GitRefSpec::{variant:?}.is_tag() must agree with \
matches!(_, GitRefSpec::Tag(_))",
);
assert_eq!(
variant.is_branch(),
matches!(variant, GitRefSpec::Branch(_)),
"GitRefSpec::{variant:?}.is_branch() must agree with \
matches!(_, GitRefSpec::Branch(_))",
);
assert_eq!(
variant.is_commit(),
matches!(variant, GitRefSpec::Commit(_)),
"GitRefSpec::{variant:?}.is_commit() must agree with \
matches!(_, GitRefSpec::Commit(_))",
);
}
}
#[test]
fn cluster_bundle_gitref_field_composes_lifted_dispatch_byte_shape() {
let cases = [
(GitRefSpec::Tag("v0.1.0".into()), " tag: \"v0.1.0\""),
(GitRefSpec::Branch("main".into()), " branch: \"main\""),
(
GitRefSpec::Commit("deadbeef".into()),
" commit: \"deadbeef\"",
),
];
for (git_ref, expected) in cases {
let composed = format!(
" {field}: {value:?}",
field = git_ref.ref_field_name(),
value = git_ref.ref_value(),
);
assert_eq!(
composed, expected,
"GitRefSpec::{git_ref:?} must compose to the prior \
inline byte-shape via the lifted dispatch",
);
}
}
#[test]
fn cluster_bundle_gitref_narrator_composes_lifted_dispatch_byte_shape() {
let cases = [
(GitRefSpec::Tag("v0.1.0".into()), "tag v0.1.0"),
(GitRefSpec::Branch("main".into()), "branch main"),
(GitRefSpec::Commit("deadbeef".into()), "commit deadbeef"),
];
for (git_ref, expected) in cases {
let composed = format!(
"{field} {value}",
field = git_ref.ref_field_name(),
value = git_ref.ref_value(),
);
assert_eq!(
composed, expected,
"GitRefSpec::{git_ref:?} narrator prose must compose \
to the prior inline byte-shape via the lifted dispatch",
);
}
}
#[test]
fn cluster_bundle_gitrepo_yaml_carries_lifted_sub_selector_keys() {
let caixa = sample_caixa();
let cases: [(GitRefSpec, &str, &str); 3] = [
(
GitRefSpec::Tag("v0.1.0".into()),
FLUX_GITREPOSITORY_REF_KEY_TAG,
"v0.1.0",
),
(
GitRefSpec::Branch("main".into()),
FLUX_GITREPOSITORY_REF_KEY_BRANCH,
"main",
),
(
GitRefSpec::Commit("deadbeef".into()),
FLUX_GITREPOSITORY_REF_KEY_COMMIT,
"deadbeef",
),
];
for (git_ref, expected_key, expected_value) in cases {
let mut opts = ClusterBundleOpts::for_caixa(&caixa, "rio");
opts.git_ref = git_ref.clone();
let files = cluster_bundle(&caixa, &opts).expect("bundle renders");
let gr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.expect("gitrepository.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&gr.contents).expect("gitrepository.yaml parses as YAML");
let sub_selector = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_GITREPOSITORY_KEY_REF))
.and_then(|r| r.get(expected_key))
.and_then(|v| v.as_str())
.unwrap_or_else(|| {
panic!(
"spec.ref.{expected_key:?} missing or non-string \
for GitRefSpec::{git_ref:?}: {contents:?}",
contents = gr.contents,
)
});
assert_eq!(
sub_selector, expected_value,
"spec.ref.{expected_key:?} must carry the paired \
scalar for GitRefSpec::{git_ref:?}",
);
}
}
#[test]
fn flux_helmrelease_yaml_filename_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_HELMRELEASE_YAML_FILENAME",
FLUX_HELMRELEASE_YAML_FILENAME,
caixa_core::FLUX_HELMRELEASE_YAML_FILENAME,
);
}
#[test]
fn flux_gitrepository_yaml_filename_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_GITREPOSITORY_YAML_FILENAME",
FLUX_GITREPOSITORY_YAML_FILENAME,
caixa_core::FLUX_GITREPOSITORY_YAML_FILENAME,
);
}
#[test]
fn flux_kustomization_yaml_filename_re_export_points_at_caixa_core_canonical() {
caixa_core::assert_str_reexport_identity(
"FLUX_KUSTOMIZATION_YAML_FILENAME",
FLUX_KUSTOMIZATION_YAML_FILENAME,
caixa_core::FLUX_KUSTOMIZATION_YAML_FILENAME,
);
}
#[test]
fn cluster_bundle_every_flux_cr_carries_lifted_flux_key_interval_scalar() {
let opts = ClusterBundleOpts::for_caixa(&sample_caixa(), "rio");
let files = cluster_bundle(&sample_caixa(), &opts).unwrap();
for filename in [
FLUX_GITREPOSITORY_YAML_FILENAME,
FLUX_HELMRELEASE_YAML_FILENAME,
FLUX_KUSTOMIZATION_YAML_FILENAME,
] {
let doc = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(filename))
.unwrap_or_else(|| panic!("{filename} present"));
let parsed: serde_yaml::Value = serde_yaml::from_str(&doc.contents)
.unwrap_or_else(|_| panic!("{filename} parses as YAML"));
let interval = parsed
.get(KUBE_KEY_SPEC)
.and_then(|s| s.get(FLUX_KEY_INTERVAL))
.and_then(|v| v.as_str())
.unwrap_or_else(|| {
panic!(
"{filename} spec.<FLUX_KEY_INTERVAL> ({FLUX_KEY_INTERVAL:?}) \
scalar present; drift on this axis silently drops the \
per-CR reconcile schedule from the Flux v2 controller's \
per-CR watch registration",
)
});
assert!(
!interval.is_empty(),
"{filename} spec.<FLUX_KEY_INTERVAL> ({FLUX_KEY_INTERVAL:?}) must \
carry a non-empty duration scalar; the Flux v2 controller's \
per-CR reconcile loop rejects an empty cadence at admission",
);
assert_eq!(
interval, opts.interval,
"{filename} spec.<FLUX_KEY_INTERVAL> ({FLUX_KEY_INTERVAL:?}) \
must carry the same duration scalar the [`ClusterBundleOpts`] \
seeded — drift here silently splits the per-CR reconcile \
schedule across the three Flux v2 controllers",
);
}
}
#[test]
fn bundle_file_alias_resolves_to_caixa_core_rendered_file() {
let canonical: caixa_core::RenderedFile = caixa_core::RenderedFile {
path: std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME),
contents: String::new(),
};
let aliased: BundleFile = canonical.clone();
assert_eq!(aliased, canonical);
let via_alias = BundleFile {
path: std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME),
contents: "kind: HelmRelease\n".to_string(),
};
assert_eq!(
via_alias.path.to_string_lossy(),
FLUX_HELMRELEASE_YAML_FILENAME
);
}
#[test]
fn bundle_file_new_constructor_travels_through_alias_to_canonical() {
let via_alias_new: BundleFile =
BundleFile::new(FLUX_GITREPOSITORY_YAML_FILENAME, "kind: GitRepository\n");
let via_canonical_new = caixa_core::RenderedFile::new(
FLUX_GITREPOSITORY_YAML_FILENAME,
String::from("kind: GitRepository\n"),
);
assert_eq!(via_alias_new, via_canonical_new);
assert_eq!(
via_alias_new.path,
std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME),
);
assert_eq!(via_alias_new.contents, "kind: GitRepository\n");
}
#[test]
fn programs_yaml_entry_name_field_routes_through_caixa_nome_accessor() {
let caixa = sample_caixa();
let entry = programs_yaml_entry(&caixa, &sample_cu_yaml()).unwrap();
let emitted = entry
.get(FLEET_PROGRAMS_KEY_NAME)
.and_then(|n| n.as_str())
.expect(
"programs.yaml entry must carry a `name:` scalar — drift here \
silently splits the substrate-operator-side fleet-programs \
aggregator's per-entry identity from every peer read-side \
consumer of `Caixa::nome`",
);
assert_eq!(
emitted,
caixa.nome(),
"programs.yaml `entry[FLEET_PROGRAMS_KEY_NAME]` must derive from \
the typed `caixa_core::Caixa::nome` accessor byte-for-byte — a \
regression that re-inlines `caixa.nome.clone()` at the emit site \
silently splits the aggregator-path per-entry `name:` axis from \
every future accessor extension (namespace-qualified rewrite, \
per-cluster alias table, `:nome-suffix` overlay) that lands on \
the accessor",
);
}
#[test]
fn cluster_bundle_gitrepository_metadata_name_routes_through_caixa_nome_accessor() {
let caixa = sample_caixa();
let opts = ClusterBundleOpts::for_caixa(&caixa, "rio");
let files = cluster_bundle(&caixa, &opts).unwrap();
let gr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_GITREPOSITORY_YAML_FILENAME))
.expect("gitrepository.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&gr.contents).expect("gitrepository.yaml parses as YAML");
let emitted = kube_metadata_str_field(&parsed, KUBE_KEY_NAME).expect(
"gitrepository.yaml `metadata.name` scalar present — drift here \
silently orphans the source-controller-side per-Servico \
GitRepository CR from every peer `spec.sourceRef.name` binding",
);
assert_eq!(
emitted,
caixa.nome(),
"gitrepository.yaml `metadata.name` must derive from the typed \
`caixa_core::Caixa::nome` accessor byte-for-byte — a regression \
that re-inlines `caixa.nome.clone()` at the per-bundle \
`let name` binding silently splits the source-controller-side \
per-Servico GitRepository CR's identity from every future \
accessor extension (namespace-qualified rewrite, per-cluster \
alias table, `:nome-suffix` overlay) that lands on the accessor",
);
}
#[test]
fn cluster_bundle_helmrelease_metadata_name_routes_through_caixa_nome_accessor() {
let caixa = sample_caixa();
let opts = ClusterBundleOpts::for_caixa(&caixa, "rio");
let files = cluster_bundle(&caixa, &opts).unwrap();
let hr = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME))
.expect("helmrelease.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&hr.contents).expect("helmrelease.yaml parses as YAML");
let emitted = kube_metadata_str_field(&parsed, KUBE_KEY_NAME).expect(
"helmrelease.yaml `metadata.name` scalar present — drift here \
silently orphans the helm-controller-side per-Servico \
HelmRelease CR from every peer Kustomization \
`spec.healthChecks[].name` binding",
);
assert_eq!(
emitted,
caixa.nome(),
"helmrelease.yaml `metadata.name` must derive from the typed \
`caixa_core::Caixa::nome` accessor byte-for-byte — a regression \
that re-inlines `caixa.nome.clone()` at the per-bundle \
`let name` binding silently splits the helm-controller-side \
per-Servico HelmRelease CR's identity from every future \
accessor extension (namespace-qualified rewrite, per-cluster \
alias table, `:nome-suffix` overlay) that lands on the accessor",
);
}
#[test]
fn cluster_bundle_kustomization_metadata_name_routes_through_caixa_nome_accessor() {
let caixa = sample_caixa();
let opts = ClusterBundleOpts::for_caixa(&caixa, "rio");
let files = cluster_bundle(&caixa, &opts).unwrap();
let k = files
.iter()
.find(|f| f.path == std::path::PathBuf::from(FLUX_KUSTOMIZATION_YAML_FILENAME))
.expect("kustomization.yaml present");
let parsed: serde_yaml::Value =
serde_yaml::from_str(&k.contents).expect("kustomization.yaml parses as YAML");
let emitted = kube_metadata_str_field(&parsed, KUBE_KEY_NAME).expect(
"kustomization.yaml `metadata.name` scalar present — drift here \
silently splits the kustomize-controller-side per-Servico \
Kustomization CR's prune / reconcile decisions from every peer \
HelmRelease CR's paired identity",
);
assert_eq!(
emitted,
caixa.nome(),
"kustomization.yaml `metadata.name` must derive from the typed \
`caixa_core::Caixa::nome` accessor byte-for-byte — a regression \
that re-inlines `caixa.nome.clone()` at the per-bundle \
`let name` binding silently splits the kustomize-controller-\
side per-Servico Kustomization CR's identity from every future \
accessor extension (namespace-qualified rewrite, per-cluster \
alias table, `:nome-suffix` overlay) that lands on the accessor",
);
}
}