caixa_helm/lib.rs
1//! caixa-helm — typed renderer that emits a per-program `lareira-<name>`
2//! Helm chart from a [`Caixa`] manifest plus its `servicos/<name>.computeunit.yaml`.
3//!
4//! ## Output shape
5//!
6//! Every chart emitted here mirrors the canonical
7//! `pleme-io/helmworks/charts/lareira-<name>/` layout, which is *thin*:
8//!
9//! Chart.yaml ; metadata + dependency on pleme-computeunit
10//! values.yaml ; pleme-computeunit values block (the typed L2 ComputeUnit shape)
11//! README.md ; one-line elevator pitch for the chart
12//!
13//! There are no `templates/` — the rendering is delegated to the
14//! `pleme-computeunit` library chart in helmworks (per `theory/META-FRAMEWORK.md`
15//! §I, Layer 3 → Layer 2 transformation). caixa-helm's job is to derive the
16//! values block from a Caixa, not to render Kubernetes objects directly.
17//!
18//! ## Why a separate crate
19//!
20//! Same pattern as [`caixa_flake`] (renders flake.nix) and [`caixa_pangea`]
21//! (renders pangea Ruby) — `caixa-<target>` crates take a typed Caixa and
22//! emit the canonical source for `<target>`. Naming is uniform across the
23//! workspace.
24//!
25//! ## V0 contract
26//!
27//! ```rust,ignore
28//! use caixa_core::Caixa;
29//! use caixa_helm::{ChartDir, render_chart_for_servico};
30//!
31//! let caixa: Caixa = Caixa::from_lisp(src)?;
32//! let cu_yaml: serde_yaml::Value =
33//! serde_yaml::from_str(std::fs::read_to_string("servicos/hello-rio.computeunit.yaml")?)?;
34//! let dir: ChartDir = render_chart_for_servico(&caixa, &cu_yaml)?;
35//! dir.write_to(std::path::Path::new("/tmp/lareira-hello-rio"))?;
36//! ```
37//!
38//! ## What this is NOT
39//!
40//! - Not a chart for the `caixa-operator` itself — that lives in
41//! `pleme-io/caixa/operator-chart/`.
42//! - Not a Helm CLI wrapper — emitting bytes only; consumers (`feira chart`,
43//! eventually) drive the I/O.
44//! - Not a renderer of K8s resources — `pleme-computeunit` library chart owns
45//! the templates that turn this values block into ComputeUnit + Service +
46//! ScaledObject + ConfigMap.
47
48#![allow(clippy::module_name_repetitions)]
49
50use std::collections::BTreeMap;
51use std::path::Path;
52
53use caixa_core::{Caixa, MappingExt};
54use serde::{Deserialize, Serialize};
55use thiserror::Error;
56
57/// Errors caixa-helm can raise.
58#[derive(Debug, Error)]
59pub enum Error {
60 /// The caixa's `:kind` doesn't match what `caixa-helm` targets
61 /// (this renderer only emits per-program `lareira-<nome>` charts
62 /// for `:kind Servico`). Lifted from a prior `NotAServico(CaixaKind)`
63 /// arm to wrap [`caixa_core::KindMismatch`] so the diagnostic
64 /// names the offending caixa's `:nome` (not just its kind),
65 /// shared verbatim with `caixa-flux` and `caixa-mesh`.
66 #[error("{0}")]
67 NotAServico(#[from] caixa_core::KindMismatch),
68 /// The caixa's `:servicos` list doesn't carry exactly one entry —
69 /// the V0 contract every Servico-kind caixa satisfies (one
70 /// ComputeUnit YAML pointer per Servico, matching the one Helm
71 /// chart this renderer emits). Lifted from a prior
72 /// `UnsupportedServicoCount(usize)` arm to wrap
73 /// [`caixa_core::ServicoCountMismatch`] so the diagnostic names
74 /// the offending caixa's `:nome` (not just the count), shared
75 /// verbatim with `caixa-flux` (the peer per-Servico renderer
76 /// running the same V0 invariant on the programs.yaml-entry axis).
77 #[error("{0}")]
78 UnsupportedServicoCount(#[from] caixa_core::ServicoCountMismatch),
79 #[error("computeunit yaml missing required field: {0}")]
80 MissingField(&'static str),
81 #[error("yaml: {0}")]
82 Yaml(#[from] serde_yaml::Error),
83 #[error("render: {0}")]
84 Render(#[from] caixa_core::RenderError),
85 #[error("io: {0}")]
86 Io(#[from] std::io::Error),
87}
88
89/// One file in the rendered chart — `(path, contents)` pair every
90/// [`render_chart_for_servico`]-rendered `lareira-<nome>` chart-tree
91/// leaf lands at (`Chart.yaml`, `values.yaml`, `README.md`).
92///
93/// Type-aliased to the canonical [`caixa_core::RenderedFile`] so the
94/// substrate-side "one rendered leaf artifact" shape lives at one
95/// struct definition across every per-target renderer — the peer
96/// [`caixa_flux::BundleFile`] alias resolves to the same canonical, so
97/// a future rebrand on either axis (a per-artifact hash / provenance
98/// field addition, a per-artifact write-mode discriminator once
99/// per-cluster-writer sandboxing lands) lands at one caixa-core `pub
100/// struct RenderedFile` edit and reaches both crates by construction.
101/// Prior to this lift both crates carried an inline `pub struct
102/// <Xxx>File { pub path: PathBuf, pub contents: String }` with
103/// identical `#[derive(Debug, Clone, PartialEq, Eq)]` shapes and no
104/// per-type impls; a future per-target renderer (`caixa-otel`, the
105/// future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer, the
106/// future per-Supervisor reconciler renderer) would have carried a
107/// third and fourth clone of the same record. Type aliases preserve
108/// every existing struct-literal construction site
109/// (`ChartFile { path, contents }`), every field-access site (`f.path`,
110/// `f.contents`), and every derive-fed navigator by construction —
111/// Rust type aliases inherit the aliased type's `#[derive]`-generated
112/// `Debug`/`Clone`/`PartialEq`/`Eq` impls with no per-alias glue.
113pub type ChartFile = caixa_core::RenderedFile;
114
115/// The rendered chart — a flat list of files, plus the chart name.
116#[derive(Debug, Clone, PartialEq, Eq)]
117pub struct ChartDir {
118 /// Chart name — e.g. `lareira-hello-rio`. Used as the output dir name.
119 pub name: String,
120 pub files: Vec<ChartFile>,
121}
122
123impl ChartDir {
124 /// Write every file to `<dest>/<self.name>/`. Creates parent dirs.
125 pub fn write_to(&self, dest: &Path) -> Result<(), Error> {
126 let root = dest.join(&self.name);
127 std::fs::create_dir_all(&root)?;
128 for f in &self.files {
129 let target = root.join(&f.path);
130 if let Some(parent) = target.parent() {
131 std::fs::create_dir_all(parent)?;
132 }
133 std::fs::write(&target, &f.contents)?;
134 }
135 Ok(())
136 }
137}
138
139/// Top-level `Chart.yaml` shape for a generated lareira-<name> chart.
140///
141/// Mirrors `helmworks/charts/lareira-hello-world/Chart.yaml` 1:1 in
142/// structural slots — versions, deps, keywords, maintainers.
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
144pub struct ChartYaml {
145 #[serde(rename = "apiVersion")]
146 pub api_version: String,
147 pub name: String,
148 pub description: String,
149 #[serde(rename = "type")]
150 pub chart_type: String,
151 pub version: String,
152 #[serde(rename = "appVersion")]
153 pub app_version: String,
154 #[serde(default, skip_serializing_if = "Vec::is_empty")]
155 pub keywords: Vec<String>,
156 #[serde(default, skip_serializing_if = "Vec::is_empty")]
157 pub maintainers: Vec<Maintainer>,
158 #[serde(default, skip_serializing_if = "Option::is_none")]
159 pub home: Option<String>,
160 pub dependencies: Vec<ChartDependency>,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
164pub struct Maintainer {
165 pub name: String,
166 #[serde(default, skip_serializing_if = "Option::is_none")]
167 pub email: Option<String>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
171pub struct ChartDependency {
172 pub name: String,
173 pub version: String,
174 pub repository: String,
175 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub alias: Option<String>,
177}
178
179/// Canonical Helm 3 `Chart.yaml` `dependencies[0].repository` chart-source
180/// URL every rendered `lareira-<nome>` chart declares against the
181/// substrate-canonical [`DEFAULT_LIBRARY_NAME`] library chart — the
182/// `file://` per-chart-dep resolver scheme pointing at the sibling
183/// helmworks directory on disk that `helm dependency build` vendors the
184/// library chart bytes from. Re-export of the lifted
185/// [`caixa_core::DEFAULT_LIBRARY_REPO`] so the load-bearing per-dep
186/// resolver URL lives in exactly one place across every caixa renderer —
187/// the caixa-helm `RenderOpts::library_repo` default here + every future
188/// substrate-side per-Servico renderer consumer (the future M4
189/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Servico
190/// library-chart-dep repo resolver, the future per-Aplicacao library-
191/// chart's per-Servico per-dep repo emitter, the future per-cluster
192/// per-registry chart-source mirror the operator pins through a future
193/// [`caixa_flux::ClusterBundleOpts`]-scoped library-repo override) reach
194/// through one `&'static str` by construction. Same shape as the peer
195/// [`DEFAULT_LIBRARY_NAME`] (41438dc) re-export on the sibling per-dep
196/// `Chart.yaml` `dependencies[0].name` axis — extends the substrate-
197/// primitive-residence discipline onto the paired per-dep repository
198/// axis every emitted per-Servico `Chart.yaml` `dependencies[0]` entry
199/// carries.
200pub use caixa_core::DEFAULT_LIBRARY_REPO;
201pub const DEFAULT_LIBRARY_VERSION: &str = "~0.1.0";
202/// Canonical Helm library-chart name every `lareira-<nome>` chart depends
203/// on — re-export of the lifted [`caixa_core::DEFAULT_LIBRARY_NAME`] so
204/// the load-bearing string lives in exactly one place across every
205/// caixa renderer (caixa-helm's `RenderOpts::library_name` default
206/// here + caixa-flux's `cluster_bundle` `helmrelease.yaml` wrap key).
207/// A future per-edition library-chart fork — every entry on the
208/// absorption-roadmap that names a per-cluster / per-namespace /
209/// per-tenant variant of the canonical library chart — reaches both
210/// consumers through one `&'static str` by construction. Same shape
211/// as the [`caixa_core::DEFAULT_NAMESPACE`] (a085b26) /
212/// [`caixa_core::DEFAULT_SERVICO_PORT`] (1e22add) lifts on the peer
213/// canonical-K8s-axis-constant surface.
214pub use caixa_core::DEFAULT_LIBRARY_NAME;
215
216/// Canonical substrate-side per-[`caixa_core::Caixa`] author-omitted
217/// `:licenca` SPDX-shaped license-expression fallback every
218/// [`build_readme`]-emitted `lareira-<nome>` chart `README.md` `## License`
219/// section body seeds when the author-omitted `:licenca` slot lands past
220/// the [`caixa_core::Caixa::licenca`] `Option<&str>` accessor's `None` arm.
221/// Re-export of the lifted [`caixa_core::CAIXA_LICENCA_DEFAULT`] so the
222/// load-bearing byte-string lives in exactly one place across the
223/// substrate — the caixa-helm `build_readme` fold here + every future
224/// substrate-side per-`Caixa` registry-facing renderer consumer (the M4
225/// `Chart.yaml annotations["artifacthub.io/license"]` emitter the
226/// [`caixa_core::Caixa::validate_licenca`] docstring roadmap names) reach
227/// through one `&'static str` by construction. Same shape as the peer
228/// [`caixa_core::DEFAULT_LIBRARY_NAME`] / [`caixa_core::DEFAULT_NAMESPACE`]
229/// / [`caixa_core::DEFAULT_SERVICO_PORT`] lifts on the sibling
230/// canonical-load-bearing-scalar surface.
231pub use caixa_core::CAIXA_LICENCA_DEFAULT;
232
233/// Canonical Helm 3 `Chart.yaml` `apiVersion` every rendered
234/// `lareira-<nome>` chart declares. Re-export of the lifted
235/// [`caixa_core::HELM_CHART_API_VERSION`] so the Helm-side
236/// chart-schema apiVersion — the discriminator the Helm binary's
237/// chart-schema parser (`helm dependency build`, `helm lint`,
238/// `helm template`) consults to select the schema that reads the
239/// rendered Chart.yaml — lives in exactly one place across every
240/// caixa renderer. The single production-code call site consuming
241/// it is [`build_chart_yaml`]'s `api_version` field assignment; a
242/// drifted local `pub const HELM_CHART_API_VERSION: &str = "…"` at
243/// this crate (or any sibling per-chart-schema renderer the
244/// absorption roadmap acknowledges — the future per-Aplicacao
245/// library chart, the future per-cluster snapshot chart) would
246/// silently reroute the rendered Chart.yaml through a stale
247/// chart-schema parser at `helm template` time far from the
248/// rebrand commit's source, so the equality + `&'static` static-data
249/// identity pin
250/// (`helm_chart_api_version_re_export_points_at_caixa_core_canonical`)
251/// closes the drift footgun at caixa-helm build time. Same shape as
252/// the [`DEFAULT_LIBRARY_NAME`] / [`KUBE_KEY_SPEC`] re-exports on the
253/// sibling canonical-Helm-load-bearing-string / canonical-K8s-CR-key
254/// axes.
255pub use caixa_core::HELM_CHART_API_VERSION;
256
257/// Canonical Helm 3 `Chart.yaml` `type` field per-chart-kind
258/// discriminator scalar-value every rendered `lareira-<nome>` chart
259/// declares. Re-export of the lifted
260/// [`caixa_core::HELM_CHART_TYPE_APPLICATION`] so the Helm chart-schema
261/// per-chart-kind discriminator — the scalar Helm's per-release install-
262/// shape dispatch loop keys off to select the per-chart-kind install
263/// pathway — lives in exactly one place across every caixa renderer.
264/// The single production-code call site consuming it is
265/// [`build_chart_yaml`]'s `chart_type` field assignment (the sole
266/// emitter site the prior inline `"application".into()` literal sat at);
267/// a drifted local `pub const HELM_CHART_TYPE_APPLICATION: &str = "…"`
268/// at this crate (or any sibling per-chart renderer the absorption
269/// roadmap acknowledges — the future per-Aplicacao library chart, the
270/// future per-cluster snapshot chart) would surface as one of two
271/// silent failure modes at `helm install` time: a value outside the
272/// schema's admitted set (`{"application", "library"}`) that Helm's
273/// chart-schema parser silently treats as the default `application`
274/// shape (masking the schema violation with no process-log signal), or
275/// an accidental collapse onto the sibling `"library"` shape that Helm
276/// refuses to install directly ("Error: library charts cannot be
277/// installed") with no field naming the chart-kind-drift root cause.
278/// The equality + `&'static` static-data identity pin
279/// (`helm_chart_type_application_re_export_points_at_caixa_core_canonical`)
280/// closes the drift footgun at caixa-helm build time. Peer to the
281/// [`HELM_CHART_API_VERSION`] re-export on the sibling canonical-Helm-
282/// chart-schema-axis — completes the per-Chart.yaml `(apiVersion, type)`
283/// canonical-scalar-axis re-export pair every rendered `lareira-<nome>`
284/// chart declares at its top-level Chart.yaml body.
285pub use caixa_core::HELM_CHART_TYPE_APPLICATION;
286
287/// Canonical Helm 3 `Chart.yaml` `type` field per-chart-kind discriminator
288/// scalar-value the sibling library-chart shape lands on — re-export of
289/// the lifted [`caixa_core::HELM_CHART_TYPE_LIBRARY`] so the second and
290/// only other arm of the Helm chart-schema's closed set `{"application",
291/// "library"}` lives in exactly one place across every caixa renderer.
292/// No production emitter here consumes it today — the caixa-helm
293/// renderer emits per-Servico `application`-typed `lareira-<nome>`
294/// charts, and the sibling [`DEFAULT_LIBRARY_NAME`] `pleme-computeunit`
295/// library chart is out-of-tree at `pleme-io/helmworks` (not this
296/// crate's authority) — but every future substrate-side per-chart-kind
297/// classifier and every emitter for the future per-Aplicacao library
298/// chart the [`HELM_CHART_TYPE_APPLICATION`] docstring names as a
299/// trajectory item reads the same `&'static str` by construction.
300/// Peer to the [`HELM_CHART_TYPE_APPLICATION`] re-export on the sibling
301/// closed-set arm — completes the two-arm re-export pair of the Helm
302/// chart-schema's per-chart-kind axis at the caixa-helm surface so
303/// consumers reaching for either shape read from one canonical source
304/// per arm. The paired identity pins
305/// (`helm_chart_type_library_re_export_points_at_caixa_core_canonical`)
306/// close the drift footgun at caixa-helm build time.
307pub use caixa_core::HELM_CHART_TYPE_LIBRARY;
308
309/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
310/// per-chart-kind discriminator field — re-export of the lifted
311/// [`caixa_core::HELM_CHART_KEY_TYPE`] so the top-level per-chart-kind
312/// discriminator YAML key the sibling [`HELM_CHART_TYPE_APPLICATION`] /
313/// [`HELM_CHART_TYPE_LIBRARY`] axis-value re-exports carry the closed-
314/// set admitted scalars for lives in exactly one place across every
315/// caixa renderer. The production emitter today is [`ChartYaml`]'s
316/// `chart_type` field `#[serde(rename = "type")]` attribute — Rust's
317/// attribute grammar admits only string literals so the const cannot
318/// substitute for the literal syntactically, but the drift-detection
319/// pin at
320/// [`tests::chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
321/// round-trips a rendered `Chart.yaml` through
322/// `serde_yaml::from_str::<serde_yaml::Value>` and asserts the top-
323/// level `Mapping::get(HELM_CHART_KEY_TYPE)` resolves, closing the
324/// drift the attribute-literal-only grammar leaves silent (a future
325/// refactor that dropped the `#[serde(rename = "type")]` attribute
326/// would silently serialize the field as Rust's default snake_case
327/// `chart_type:`, which Helm's chart-schema parser silently ignores
328/// as an unknown top-level key, defaulting the per-chart-kind axis
329/// to `application` with no process-log drift-signal). Peer to the
330/// [`HELM_CHART_TYPE_APPLICATION`] / [`HELM_CHART_TYPE_LIBRARY`]
331/// re-exports on the sibling per-chart-kind axis-value canonical
332/// surface — completes the per-Chart.yaml per-chart-kind
333/// discriminator axis's `(key, value-set)` canonical re-export trio
334/// at the caixa-helm surface.
335pub use caixa_core::HELM_CHART_KEY_TYPE;
336
337/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
338/// per-chart underlying-application-version field — re-export of the
339/// lifted [`caixa_core::HELM_CHART_KEY_APP_VERSION`] so the top-level
340/// per-chart-app-version YAML key the [`ChartYaml`] `app_version`
341/// field's `#[serde(rename = "appVersion")]` attribute encodes lives
342/// in exactly one place across every caixa renderer. Rust's attribute
343/// grammar admits only string literals so the const cannot substitute
344/// for the literal syntactically, but the drift-detection pin at
345/// [`tests::chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`]
346/// round-trips a rendered `Chart.yaml` and asserts the top-level
347/// `Mapping::get(HELM_CHART_KEY_APP_VERSION)` resolves, closing the
348/// drift the attribute-literal-only grammar leaves silent (a future
349/// refactor that dropped the `#[serde(rename = "appVersion")]`
350/// attribute would silently serialize the field as Rust's default
351/// snake_case `app_version:`, which Helm's chart-schema parser
352/// silently drops from the parsed chart-metadata shape, and every
353/// downstream Artifact Hub / `helm search` per-chart index falls back
354/// to "no application version" for the rendered chart far from the
355/// drift site). Peer to [`HELM_CHART_KEY_TYPE`] on the sibling
356/// per-Chart.yaml top-level YAML axis-key re-export surface —
357/// completes the per-Chart.yaml top-level YAML axis-key re-export
358/// pair at the caixa-helm surface for the two serde-rename-literal-
359/// only axes on this crate's [`ChartYaml`] struct that Rust's
360/// attribute-argument grammar leaves un-substitutable syntactically
361/// (the third top-level axis-key `apiVersion` re-exports through
362/// [`HELM_CHART_KEY_API_VERSION`] below, whose byte-shape coincides
363/// with the sibling [`caixa_core::KUBE_KEY_API_VERSION`] by Helm's
364/// design decision to inherit the K8s CR top-level shape verbatim —
365/// the paired substrate-side
366/// [`caixa_core`-side
367/// `helm_chart_key_api_version_matches_kube_key_api_version`] pin
368/// makes the byte-shape coincidence load-bearing rather than
369/// accidental).
370pub use caixa_core::HELM_CHART_KEY_APP_VERSION;
371
372/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
373/// per-chart chart-schema-apiVersion field — re-export of the lifted
374/// [`caixa_core::HELM_CHART_KEY_API_VERSION`] so the top-level chart-
375/// schema-apiVersion YAML key the [`ChartYaml`] `api_version` field's
376/// `#[serde(rename = "apiVersion")]` attribute encodes lives in
377/// exactly one place across every caixa renderer. Rust's attribute
378/// grammar admits only string literals so the const cannot substitute
379/// for the literal syntactically, but the drift-detection pin at
380/// [`tests::chart_yaml_serializes_api_version_axis_under_lifted_helm_chart_key_api_version`]
381/// round-trips a rendered `Chart.yaml` and asserts the top-level
382/// `Mapping::get(HELM_CHART_KEY_API_VERSION)` resolves, closing the
383/// drift the attribute-literal-only grammar leaves silent (a future
384/// refactor that dropped the `#[serde(rename = "apiVersion")]`
385/// attribute would silently serialize the field as Rust's default
386/// snake_case `api_version:`, which Helm's chart-schema parser
387/// rejects at `helm lint` / `helm dependency build` / `helm template`
388/// time with an "apiVersion is required" error far from the drift
389/// site). Peer to [`HELM_CHART_KEY_TYPE`] / [`HELM_CHART_KEY_APP_VERSION`]
390/// on the sibling per-Chart.yaml top-level YAML axis-key re-export
391/// surface — completes the per-Chart.yaml top-level YAML axis-key
392/// re-export trio at the caixa-helm surface for the three serde-
393/// rename-literal-only axes on this crate's [`ChartYaml`] struct.
394pub use caixa_core::HELM_CHART_KEY_API_VERSION;
395
396/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
397/// per-chart dependency-list field — re-export of the lifted
398/// [`caixa_core::HELM_CHART_KEY_DEPENDENCIES`] so the load-bearing serde
399/// field-name at [`ChartYaml`]'s `dependencies` field (the parent
400/// list-container the already-re-exported per-`dependencies[]`-entry
401/// sub-mapping tetrad [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
402/// [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
403/// [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
404/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] mounts one level down under)
405/// lives in exactly one place across every caixa renderer. The Rust
406/// field name and the wire key coincide by default (no
407/// `#[serde(rename)]` attribute today), so the const doesn't substitute
408/// for the field-name syntactically at the struct definition, but the
409/// drift-detection pin at
410/// [`tests::chart_yaml_serializes_dependencies_axis_under_lifted_helm_chart_key_dependencies`]
411/// round-trips a rendered `Chart.yaml` through
412/// `serde_yaml::from_str::<serde_yaml::Value>` and asserts the top-
413/// level `Mapping::get(HELM_CHART_KEY_DEPENDENCIES)` resolves — closing
414/// the drift a future field rename (`dependencies` → `deps` /
415/// `chartDependencies`) or a `#[serde(rename_all = "camelCase")]`
416/// attribute addition on [`ChartYaml`] would otherwise leave silent
417/// (Helm's chart-schema parser silently drops the entire dep list from
418/// the parsed chart-metadata, `helm dependency build` finds no chart to
419/// vendor, and every rendered `lareira-<nome>` chart's install fails at
420/// apply time far from the drift site). Peer to
421/// [`HELM_CHART_KEY_TYPE`] / [`HELM_CHART_KEY_APP_VERSION`] /
422/// [`HELM_CHART_KEY_API_VERSION`] on the sibling per-Chart.yaml
423/// top-level YAML axis-key re-export surface — extends the per-
424/// Chart.yaml top-level YAML axis-key re-export trio at the caixa-helm
425/// surface onto the fourth top-level axis-key, the parent list-
426/// container whose per-entry sub-mapping tetrad is already re-exported
427/// under [`HELM_CHART_DEPENDENCY_KEY_*`].
428pub use caixa_core::HELM_CHART_KEY_DEPENDENCIES;
429
430/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
431/// YAML axis-key naming the per-dep chart-name field — re-export of the
432/// lifted [`caixa_core::HELM_CHART_DEPENDENCY_KEY_NAME`] so the load-
433/// bearing serde field-name at [`ChartDependency`]'s `name` field lives
434/// in exactly one place across every caixa renderer. Peer to
435/// [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
436/// [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
437/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] on the sibling per-dep sub-key
438/// axes. The drift-detection round-trip pin at
439/// [`tests::chart_dependency_serializes_tetrad_under_lifted_helm_chart_dependency_keys`]
440/// serializes a fully-populated [`ChartDependency`] and asserts each of
441/// the four per-dep sub-mapping wire keys resolves — closing the drift
442/// a rename of the Rust field or a `#[serde(rename_all)]` attribute
443/// addition would otherwise leave silent.
444pub use caixa_core::HELM_CHART_DEPENDENCY_KEY_NAME;
445
446/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
447/// YAML axis-key naming the per-dep chart-version-constraint field —
448/// re-export of the lifted [`caixa_core::HELM_CHART_DEPENDENCY_KEY_VERSION`]
449/// so the load-bearing serde field-name at [`ChartDependency`]'s
450/// `version` field lives in exactly one place across every caixa
451/// renderer. Peer to [`HELM_CHART_DEPENDENCY_KEY_NAME`] on the sibling
452/// per-dep sub-key axes. See [`HELM_CHART_DEPENDENCY_KEY_NAME`] for the
453/// shared per-entry-sub-mapping lift rationale.
454pub use caixa_core::HELM_CHART_DEPENDENCY_KEY_VERSION;
455
456/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
457/// YAML axis-key naming the per-dep chart-registry URL field — re-export
458/// of the lifted [`caixa_core::HELM_CHART_DEPENDENCY_KEY_REPOSITORY`]
459/// so the load-bearing serde field-name at [`ChartDependency`]'s
460/// `repository` field lives in exactly one place across every caixa
461/// renderer. Peer to [`HELM_CHART_DEPENDENCY_KEY_NAME`] on the sibling
462/// per-dep sub-key axes.
463pub use caixa_core::HELM_CHART_DEPENDENCY_KEY_REPOSITORY;
464
465/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
466/// YAML axis-key naming the per-dep chart-alias override field —
467/// re-export of the lifted [`caixa_core::HELM_CHART_DEPENDENCY_KEY_ALIAS`]
468/// so the load-bearing serde field-name at [`ChartDependency`]'s
469/// `alias` field lives in exactly one place across every caixa
470/// renderer. Peer to [`HELM_CHART_DEPENDENCY_KEY_NAME`] on the sibling
471/// per-dep sub-key axes.
472pub use caixa_core::HELM_CHART_DEPENDENCY_KEY_ALIAS;
473
474/// Canonical Helm 3 per-chart-directory metadata-file filename every
475/// rendered `lareira-<nome>` chart carries at its top-level directory —
476/// re-export of the lifted [`caixa_core::HELM_CHART_YAML_FILENAME`] so
477/// the fixed lookup name Helm's chart-schema parser (`helm dependency
478/// build`, `helm lint`, `helm template`, `helm install`) consults at
479/// chart-open time to locate the per-chart schema-body scalars
480/// ([`HELM_CHART_API_VERSION`], [`HELM_CHART_TYPE_APPLICATION`], the
481/// name/version/dependencies fields) lives in exactly one place across
482/// every caixa renderer. The single production-code call site
483/// consuming it is [`render_chart_for_servico`]'s `ChartDir` assembly
484/// where the metadata file's per-`ChartFile` `path` axis is set (the
485/// sole emitter site the prior inline `PathBuf::from("Chart.yaml")`
486/// literal sat at); every test-side round-trip navigator that reaches
487/// into the rendered `ChartDir` by the metadata filename (the
488/// per-chart-metadata-field sweep tests +
489/// [`ChartDir::write_to`] post-write existence pin) now consults the
490/// same `&'static str`, so a rebrand of the Helm 3 metadata-file axis
491/// (any per-fork `Chartfile.yaml` / Helm 4 metadata-file rename the
492/// upstream packaging spec might adopt) lands at one const and reaches
493/// every consumer by construction. A drifted local `pub const
494/// HELM_CHART_YAML_FILENAME: &str = "…"` at this crate — the canonical
495/// drift footgun where a sibling local `pub const` could happen to
496/// carry the same string at the source while pointing at a different
497/// `&'static` allocation — surfaces as one of two silent failure modes
498/// at chart-consumption time: Helm's chart-schema parser refuses to
499/// open the rendered chart-directory ("Error: Chart.yaml file is
500/// missing") far from the drift commit, or the sibling
501/// [`caixa_flux::cluster_bundle`]'s future per-chart-directory
502/// resolver — a per-cluster snapshot bundle that re-lists the
503/// chart-dir contents by filename — silently returns `None` at
504/// cluster-side `feira app deploy` time. The equality + `&'static`
505/// static-data identity pin
506/// (`helm_chart_yaml_filename_re_export_points_at_caixa_core_canonical`)
507/// closes the drift footgun at caixa-helm build time. Peer to the
508/// [`HELM_CHART_API_VERSION`] / [`HELM_CHART_TYPE_APPLICATION`]
509/// re-exports on the sibling canonical-Helm-chart-schema-body-axis
510/// surface — completes the per-`lareira-<nome>`-chart-directory
511/// `(filename, apiVersion, type)` canonical-scalar-axis re-export
512/// triple every rendered chart declares at its top-level metadata file.
513pub use caixa_core::HELM_CHART_YAML_FILENAME;
514
515/// Canonical Helm 3 per-chart-directory values-file filename every
516/// rendered `lareira-<nome>` chart carries at its top-level directory —
517/// re-export of the lifted [`caixa_core::HELM_VALUES_YAML_FILENAME`] so
518/// the fixed lookup name Helm's chart-schema parser (`helm dependency
519/// build`, `helm lint`, `helm template`, `helm install`) consults at
520/// chart-open time to locate the per-chart values block that
521/// [`HELM_VALUES_KEY_ENABLED`] toggles under its
522/// [`DEFAULT_LIBRARY_NAME`] wrap key lives in exactly one place across
523/// every caixa renderer. The single production-code call site
524/// consuming it is [`render_chart_for_servico`]'s `ChartDir` assembly
525/// where the values file's per-`ChartFile` `path` axis is set (the
526/// sole emitter site the prior inline `PathBuf::from("values.yaml")`
527/// literal sat at); every test-side round-trip navigator that reaches
528/// into the rendered `ChartDir` by the values filename (the
529/// per-chart-values-field sweep tests +
530/// [`ChartDir::write_to`] post-write existence pin) now consults the
531/// same `&'static str`, so a rebrand of the Helm 3 values-file axis
532/// (any per-fork `defaults.yaml` / Helm 4 values-file rename the
533/// upstream packaging spec might adopt) lands at one const and reaches
534/// every consumer by construction. A drifted local `pub const
535/// HELM_VALUES_YAML_FILENAME: &str = "…"` at this crate — the canonical
536/// drift footgun where a sibling local `pub const` could happen to
537/// carry the same string at the source while pointing at a different
538/// `&'static` allocation — surfaces as one of two silent failure modes
539/// at chart-consumption time: Helm's per-chart values-loader silently
540/// falls back to the empty values block (`helm template` emits the
541/// library chart under its admission-time defaults, the workload comes
542/// up disabled or without any per-Servico M2 overlay applied) far from
543/// the drift commit, or the sibling
544/// [`caixa_flux::cluster_bundle`]'s future per-chart-directory
545/// resolver — a per-cluster snapshot bundle that re-lists the
546/// chart-dir contents by filename to route per-cluster values overlays
547/// through the canonical values file — silently returns `None` at
548/// cluster-side `feira app deploy` time. The equality + `&'static`
549/// static-data identity pin
550/// (`helm_values_yaml_filename_re_export_points_at_caixa_core_canonical`)
551/// closes the drift footgun at caixa-helm build time. Peer to the
552/// [`HELM_CHART_YAML_FILENAME`] re-export on the sibling
553/// canonical-Helm-per-chart-directory-metadata-file-axis surface —
554/// completes the per-`lareira-<nome>`-chart-directory
555/// `(Chart.yaml, values.yaml)` canonical-per-chart-directory-filename-
556/// axis re-export pair every rendered chart declares as its two
557/// schema-load-bearing `ChartDir::files` entries.
558pub use caixa_core::HELM_VALUES_YAML_FILENAME;
559
560/// Canonical `lareira-<nome>` chart-directory human-facing readme
561/// filename every rendered chart carries at its top-level directory —
562/// re-export of the lifted [`caixa_core::HELM_CHART_README_FILENAME`] so
563/// the third leg of the canonical `{Chart.yaml, values.yaml, README.md}`
564/// per-`lareira-<nome>` chart-directory `ChartFile` triple lives in
565/// exactly one place across every caixa renderer. The single
566/// production-code call site consuming it is
567/// [`render_chart_for_servico`]'s `ChartDir` assembly where the readme
568/// file's per-`ChartFile` `path` axis is set (the sole emitter site the
569/// prior inline `"README.md"` string literal sat at); every test-side
570/// round-trip navigator that reaches into the rendered `ChartDir` by
571/// the readme filename (the [`render_chart_for_servico`] files-vec-
572/// membership pin + the [`ChartDir::write_to`] post-write existence
573/// pin — two sites) now consults the same `&'static str`. A drifted
574/// local `pub const HELM_CHART_README_FILENAME: &str = "…"` at this
575/// crate — the canonical drift footgun where a sibling local
576/// `pub const` could happen to carry the same string at the source
577/// while pointing at a different `&'static` allocation — surfaces as
578/// GitHub / Artifact Hub / any downstream per-chart README-surfacing
579/// UI silently falling back to "no README available" for the rendered
580/// `lareira-<nome>` chart far from the drift commit's source, with no
581/// field naming the readme-filename-drift root cause. The equality +
582/// `&'static` static-data identity pin
583/// (`helm_chart_readme_filename_re_export_points_at_caixa_core_canonical`)
584/// closes the drift footgun at caixa-helm build time. Peer to the
585/// [`HELM_CHART_YAML_FILENAME`] / [`HELM_VALUES_YAML_FILENAME`]
586/// re-exports on the sibling canonical-Helm-per-chart-directory-
587/// filename axes — completes the per-`lareira-<nome>`-chart-directory
588/// `(Chart.yaml, values.yaml, README.md)` canonical-per-chart-directory-
589/// filename-axis re-export triple every rendered chart declares as its
590/// three `ChartDir::files` entries.
591pub use caixa_core::HELM_CHART_README_FILENAME;
592
593/// Canonical K8s CR top-level `spec` key. Re-export of the canonical
594/// [`caixa_core::KUBE_KEY_SPEC`] so the per-kind body key lives in
595/// exactly one place across every caixa renderer — caixa-helm's
596/// `build_values_yaml` (the upstream ComputeUnit YAML's `spec.*` axis
597/// the rendered `lareira-<nome>` chart's values block re-routes
598/// through the library alias) now consults the same `&'static str` as
599/// the peer caixa-flux / caixa-mesh renderers' `KUBE_KEY_SPEC`
600/// re-exports. The prior inline `"spec"` literal at the production-
601/// code call site would have let a typo (e.g. `"Spec"`, `"specs"`,
602/// `"spec_"`) silently emit a values block that drops every typed
603/// ComputeUnit-side field (`module`, `trigger`, `capabilities`,
604/// `resources`, `serviceAccount`) at the rendered chart's landing
605/// site — the `Error::MissingField("spec")` diagnostic now threads
606/// the same `&'static str` through the diagnostic surface so the
607/// error message stays byte-identical to the key it failed to find.
608/// Same shape as the [`DEFAULT_LIBRARY_NAME`] re-export on the
609/// sibling canonical-Helm-load-bearing-string axis.
610pub use caixa_core::KUBE_KEY_SPEC;
611
612/// Canonical `pleme-computeunit` library-chart values-block enable-toggle
613/// key — re-export of the lifted [`caixa_core::HELM_VALUES_KEY_ENABLED`]
614/// so the values-block toggle every rendered `lareira-<nome>` chart's
615/// values.yaml carries under its [`DEFAULT_LIBRARY_NAME`] wrap key lives
616/// in exactly one place across every caixa renderer. The single
617/// production-code call site consuming it is [`build_values_yaml`]'s
618/// `block.insert(HELM_VALUES_KEY_ENABLED.to_string(), …)` (formerly an
619/// inline `"enabled".to_string()` literal at `caixa-helm/src/lib.rs:389`);
620/// the peer test-fixture navigators pinning the default-off round-trip
621/// (`values_yaml_wraps_under_pleme_computeunit_key`,
622/// `values_yaml_wrap_key_follows_library_name_override`) also consult the
623/// re-export so a rebrand of the library-chart's per-values enable-toggle
624/// axis lands at one const and reaches every consumer by construction.
625/// A drifted local `pub const HELM_VALUES_KEY_ENABLED: &str = "…"` (or
626/// any sibling per-renderer variant that inlined a stale
627/// `"enabled"` / `"enable"` / `"disabled"` literal) would silently emit
628/// a values block whose per-values enable-toggle lands under one key
629/// while [`caixa_flux::cluster_bundle`]'s `HelmRelease`
630/// `spec.values.<library>.enabled` per-cluster override lands under
631/// another — Helm's per-values merge treats them as sibling scalars, the
632/// enable-toggle the library chart's own template consults never sees the
633/// flip, and the workload silently comes up with the library chart's
634/// admission-time defaults instead of the per-cluster override the
635/// operator set. Same shape as the [`DEFAULT_LIBRARY_NAME`] /
636/// [`KUBE_KEY_SPEC`] / [`HELM_CHART_API_VERSION`] re-exports on the
637/// sibling canonical-Helm-load-bearing-string / canonical-K8s-CR-body-
638/// key / canonical-Helm-chart-schema-apiVersion axes.
639pub use caixa_core::HELM_VALUES_KEY_ENABLED;
640
641/// Canonical substrate-side default for the
642/// `values.<library>.enabled` scalar-value toggle every
643/// [`render_chart_for_servico`]-emitted standalone `lareira-<nome>`
644/// chart's `values.yaml` document seeds inside its per-caixa
645/// [`DEFAULT_LIBRARY_NAME`] wrap block to leave the paired
646/// [`DEFAULT_LIBRARY_NAME`] child chart opted-out at the per-cluster
647/// `helm template` / `helm install` apply step. Re-export of the
648/// canonical [`caixa_core::STANDALONE_LAREIRA_ENABLED_DEFAULT`] so the
649/// substrate-side default the standalone per-chart path seeds under
650/// the sibling [`HELM_VALUES_KEY_ENABLED`] leaf-scalar-key lives in
651/// exactly one place across every caixa renderer. Consumed by
652/// [`RenderOpts::default`]'s `enabled_default` field seed (formerly
653/// an inline `false` scalar-value literal at
654/// `caixa-helm/src/lib.rs:700`); the peer test-fixture navigators
655/// pinning the default-off round-trip also consult the re-export so a
656/// rebrand of the per-values-block child-chart-enablement-toggle scalar
657/// on the standalone per-chart path lands at one const and reaches
658/// every consumer by construction. Semantically distinct from — and
659/// inverse of — the peer
660/// [`caixa_flux::CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] on the
661/// composition per-cluster-`HelmRelease` values-overlay path (which
662/// force-ons the child chart under the substrate-side composition
663/// path); the two peer scalar-value defaults name mirror-symmetric
664/// per-path child-chart-enablement-toggle-scalar-value defaults at the
665/// exact same `values.<library>.enabled` sub-block position on the
666/// standalone per-chart-`values.yaml` path (this re-export) and the
667/// composition per-cluster-`HelmRelease` values-overlay path (the peer
668/// re-export). Same shape as the [`DEFAULT_LIBRARY_NAME`] /
669/// [`KUBE_KEY_SPEC`] / [`HELM_VALUES_KEY_ENABLED`] re-exports on the
670/// sibling canonical-Helm-load-bearing-string / canonical-K8s-CR-body-
671/// key / canonical-Helm-per-values-block-enable-toggle-key axes. See
672/// [`caixa_core::STANDALONE_LAREIRA_ENABLED_DEFAULT`] for the full lift
673/// rationale.
674pub use caixa_core::STANDALONE_LAREIRA_ENABLED_DEFAULT;
675
676/// Local re-export of the canonical
677/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE`] — the
678/// `wasm.pleme.io/v1alpha1/ComputeUnit` CRD per-CR wasm-module-reference
679/// `spec.module` sub-block key every rendered `values.yaml`'s
680/// [`DEFAULT_LIBRARY_NAME`]-wrapped block carries so the
681/// `pleme-computeunit` library chart's per-Servico module-source axis
682/// binds to the exact source the caixa.lisp's `:servicos` fixture
683/// pins. Two per-values drift-detection navigators in this crate's
684/// test module (the canonical-wrap-key round-trip + the
685/// `library-name`-override wrap-key round-trip) now consult the same
686/// `&'static str` as the peer caixa-flux writer's per-Servico
687/// `programs[]`-entry module-source navigators. Same re-export shape
688/// as the peer [`HELM_VALUES_KEY_ENABLED`] / [`KUBE_KEY_SPEC`]
689/// surfaces on the sibling canonical-Helm-load-bearing-string /
690/// canonical-K8s-CR-body-key axes — extends the discipline the
691/// M2-typed-slot / K8s-CR key re-export families establish onto the
692/// substrate-side ComputeUnit-CRD per-`spec.*` sub-block axis. See
693/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE`] for the full lift
694/// rationale.
695pub use caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE;
696
697/// Local re-export of the canonical
698/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_TRIGGER`] — the
699/// `wasm.pleme.io/v1alpha1/ComputeUnit` CRD per-CR invocation-shape
700/// `spec.trigger` sub-block key every rendered `values.yaml`'s
701/// [`DEFAULT_LIBRARY_NAME`]-wrapped block carries so the
702/// `pleme-computeunit` library chart's per-Servico
703/// `trigger.service.{port, paths, breathability}` routing binds to
704/// the exact axis the caixa.lisp's `:servicos` fixture pins. Peer of
705/// [`COMPUTEUNIT_SPEC_KEY_MODULE`] on the same ComputeUnit CRD
706/// per-`spec.*` sub-block axis — see
707/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_TRIGGER`] for the full lift
708/// rationale.
709pub use caixa_core::COMPUTEUNIT_SPEC_KEY_TRIGGER;
710
711/// Local re-export of the canonical
712/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_CAPABILITIES`] — the
713/// `wasm.pleme.io/v1alpha1/ComputeUnit` CRD per-CR WASI-capability-list
714/// `spec.capabilities` sub-block key every rendered `values.yaml`'s
715/// [`DEFAULT_LIBRARY_NAME`]-wrapped block carries so the
716/// `pleme-computeunit` library chart's per-Servico WASI-preview-2
717/// capability-token binding fires exactly against the axis the
718/// caixa.lisp's `:servicos` fixture pins. Peer of
719/// [`COMPUTEUNIT_SPEC_KEY_MODULE`] and [`COMPUTEUNIT_SPEC_KEY_TRIGGER`]
720/// on the same ComputeUnit CRD per-`spec.*` sub-block axis — completes
721/// the substrate-side ComputeUnit-CRD per-`spec.*` sub-block re-export
722/// triple in this crate. See
723/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_CAPABILITIES`] for the full lift
724/// rationale.
725pub use caixa_core::COMPUTEUNIT_SPEC_KEY_CAPABILITIES;
726
727/// Local re-export of the canonical
728/// [`caixa_core::servico_spec_and_m2_overlay_entries`] — the composed
729/// per-Servico value-block splice helper this crate's
730/// [`build_values_yaml`] and the peer
731/// [`caixa_flux::programs_yaml_entry`] both now route their two-step
732/// `spec.*` field-splice + M2 typed-slot overlay through. The single
733/// production-code call site consuming it is [`build_values_yaml`]'s
734/// inner splice loop (formerly two hand-written for-loops chained
735/// around `string_keyed_entries` + `servico_m2_overlay`); re-exported
736/// so the shared composition contract lives in exactly one place
737/// across both per-Servico renderers — a future author reading
738/// `caixa_helm::build_values_yaml` finds the composition helper
739/// immediately without an extra `use caixa_core::…` line, and a
740/// rebrand of the composition axis (e.g. a swap of the `or_insert`
741/// precedence rule once per-Aplicacao operator overrides land)
742/// reaches both renderers through one canonical `&'static` function
743/// pointer. Same shape as the peer [`COMPUTEUNIT_SPEC_KEY_MODULE`] /
744/// [`COMPUTEUNIT_SPEC_KEY_TRIGGER`] / [`COMPUTEUNIT_SPEC_KEY_CAPABILITIES`]
745/// re-exports on the sibling canonical-ComputeUnit-CRD `spec.*` axis
746/// — extends the shared-composition discipline the per-`spec.*`
747/// sub-block re-export triple establishes onto the composed
748/// spec.*+M2 splice axis every per-Servico renderer navigates.
749pub use caixa_core::servico_spec_and_m2_overlay_entries;
750
751/// Knobs that don't come from the Caixa manifest.
752#[derive(Debug, Clone)]
753pub struct RenderOpts {
754 /// Where the library chart lives. Default = `file://../pleme-computeunit`.
755 pub library_repo: String,
756 pub library_version: String,
757 pub library_name: String,
758 /// Whether the rendered values block is `enabled: false` by default
759 /// (matching `lareira-hello-world` so cluster operators flip it on
760 /// per-cluster). Default: [`STANDALONE_LAREIRA_ENABLED_DEFAULT`]
761 /// (`false` — the substrate's chosen standalone per-chart
762 /// opt-out seed, inverse of the composition per-cluster-`HelmRelease`
763 /// values-overlay path's [`caixa_flux::CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`]
764 /// force-on).
765 pub enabled_default: bool,
766}
767
768impl Default for RenderOpts {
769 fn default() -> Self {
770 Self {
771 library_repo: DEFAULT_LIBRARY_REPO.into(),
772 library_version: DEFAULT_LIBRARY_VERSION.into(),
773 library_name: DEFAULT_LIBRARY_NAME.into(),
774 enabled_default: STANDALONE_LAREIRA_ENABLED_DEFAULT,
775 }
776 }
777}
778
779/// Render a per-program lareira-<name> chart from a Caixa Servico + its
780/// loaded ComputeUnit YAML.
781///
782/// The ComputeUnit YAML is passed in as a `serde_yaml::Value` because the
783/// authoritative schema lives in the wasm-operator's CRD — we don't want
784/// caixa-helm to drift from that schema. It's enough that we can locate
785/// `spec` and pass it through.
786pub fn render_chart_for_servico(
787 caixa: &Caixa,
788 computeunit_yaml: &serde_yaml::Value,
789) -> Result<ChartDir, Error> {
790 render_chart_for_servico_with(caixa, computeunit_yaml, &RenderOpts::default())
791}
792
793/// `render_chart_for_servico` with explicit options.
794pub fn render_chart_for_servico_with(
795 caixa: &Caixa,
796 computeunit_yaml: &serde_yaml::Value,
797 opts: &RenderOpts,
798) -> Result<ChartDir, Error> {
799 caixa_core::require_v0_servico_shape::<Error>(caixa)?;
800
801 // Route the per-`ChartDir.name` `lareira-<nome>` chart-identity
802 // composer through the substrate-canonical
803 // [`caixa_core::Caixa::lareira_chart_name`] resolved-chart-name
804 // dispatch rather than the two-step
805 // [`caixa_core::lareira_chart_name`]-of-[`caixa_core::Caixa::nome`]
806 // open-coded compose — the load-bearing per-Servico Helm chart
807 // identity now reaches through exactly one typed dispatch on the
808 // substrate primitive. Peer of the sibling
809 // [`Caixa::canonical_git_url`] (124f864) / [`Caixa::publish_tag`]
810 // (07e05b8) resolved-composers on the paired per-`Caixa` published-
811 // artifact-identity axis — every substrate-side consumer that
812 // resolves "which chart does this caixa publish under?" now travels
813 // through one accessor rather than a per-caller two-step compose.
814 // Pinned by the drift-detection test
815 // [`chart_dir_name_routes_through_caixa_lareira_chart_name_accessor`]
816 // in the tests module.
817 let chart_name = caixa.lareira_chart_name();
818 let chart_yaml = build_chart_yaml(caixa, &chart_name, opts);
819 let values_yaml = build_values_yaml(caixa, computeunit_yaml, opts)?;
820 let readme = build_readme(caixa, &chart_name);
821
822 // Each per-artifact leaf routes through the canonical
823 // [`caixa_core::RenderedFile::new`] `impl Into<PathBuf>` /
824 // `impl Into<String>` constructor (re-exported by the peer
825 // [`ChartFile`] alias since Rust inherent methods travel through
826 // type aliases to the aliased type at name resolution). The prior
827 // three inline `ChartFile { path: PathBuf::from(FILENAME_CONST),
828 // contents: <body> }` blocks each re-derived the same
829 // `PathBuf::from(&str)` wrap + the same two-field assembly — a
830 // byte-identical duplicate of the peer [`caixa_flux::cluster_bundle`]
831 // Flux v2 CR trio's three per-CR emit sites. Sweeping both trios
832 // onto [`RenderedFile::new`] collapses the six substrate-side
833 // per-artifact-construction sites onto one canonical constructor,
834 // so a future rebrand on the record shape (a per-artifact hash /
835 // provenance field addition, a per-artifact write-mode discriminator
836 // once per-cluster-writer sandboxing lands, the
837 // [`caixa_core::is_sandboxed_relative_path`] discipline the
838 // [`RenderedFile`] docstring acknowledges is not yet run at emit
839 // time) reaches every per-target renderer through one caixa-core
840 // edit instead of a coordinated six-site rewrite.
841 Ok(ChartDir {
842 name: chart_name,
843 files: vec![
844 ChartFile::new(
845 HELM_CHART_YAML_FILENAME,
846 serde_yaml::to_string(&chart_yaml)?,
847 ),
848 ChartFile::new(HELM_VALUES_YAML_FILENAME, values_yaml),
849 ChartFile::new(HELM_CHART_README_FILENAME, readme),
850 ],
851 })
852}
853
854fn build_chart_yaml(caixa: &Caixa, chart_name: &str, opts: &RenderOpts) -> ChartYaml {
855 let description = caixa
856 .descricao()
857 .map(str::to_owned)
858 .unwrap_or_else(|| format!("Generated chart for caixa Servico {}", caixa.nome()));
859 let keywords: Vec<String> = caixa
860 .etiquetas()
861 .iter()
862 .cloned()
863 .chain(
864 caixa_core::LAREIRA_CHART_KEYWORDS
865 .iter()
866 .copied()
867 .map(String::from),
868 )
869 .collect::<Vec<_>>()
870 .into_iter()
871 .collect::<std::collections::BTreeSet<_>>()
872 .into_iter()
873 .collect();
874 let maintainers = caixa
875 .autores()
876 .iter()
877 .map(|a| Maintainer {
878 name: a.clone(),
879 email: None,
880 })
881 .collect();
882 // Canonical typed `String`-carry of the per-`Caixa` `:versao`
883 // universal-axis SemVer-2 pinned-version scalar into the two
884 // per-`Chart.yaml` version-carrier fields Helm's chart-schema
885 // parser routes per-chart identity through — `Chart.yaml`'s
886 // top-level `version:` (the axis Helm's per-chart resolver keys
887 // per-release reconciliation off, the paired `HelmRelease`
888 // `spec.chart.spec.version` binds through, and every `helm
889 // template <chart>` / `helm install <release> <chart>` /
890 // `helm upgrade <release> <chart> --version` invocation names
891 // through) and `Chart.yaml`'s top-level `appVersion:` (the
892 // axis Helm chart-consumers key per-application-version
893 // documentation / release-note / OCI-tag / operator-side
894 // per-Caixa CR revision off), both routing through the typed
895 // [`caixa_core::Caixa::versao`] accessor's canonical
896 // `to_string()` extension of `&self.versao`. Peer of the
897 // sibling 4a363bf / 54bf2f3 `caixa.nome.clone()` converges
898 // on the outer-Caixa `:nome` `String`-carry axis in caixa-flux
899 // / caixa-mesh — this converges the last unlifted per-Caixa
900 // `.versao.clone()` raw-field `String`-carry axis in
901 // caixa-helm on the same "one typed dispatch per axis"
902 // discipline.
903 let versao = caixa.versao().to_string();
904 ChartYaml {
905 api_version: HELM_CHART_API_VERSION.into(),
906 name: chart_name.into(),
907 description,
908 chart_type: HELM_CHART_TYPE_APPLICATION.into(),
909 version: versao.clone(),
910 app_version: versao,
911 keywords,
912 maintainers,
913 home: caixa.repositorio().map(str::to_owned),
914 dependencies: vec![ChartDependency {
915 name: opts.library_name.clone(),
916 version: opts.library_version.clone(),
917 repository: opts.library_repo.clone(),
918 alias: None,
919 }],
920 }
921}
922
923fn build_values_yaml(
924 caixa: &Caixa,
925 computeunit_yaml: &serde_yaml::Value,
926 opts: &RenderOpts,
927) -> Result<String, Error> {
928 // The library chart consumes its values under the key matching its
929 // Helm chart `dependencies[].name` (Helm's per-dep alias convention
930 // — when no `alias:` is set on the dependency, values are scoped
931 // under the dependency's `name`). This renderer wires both axes
932 // through the same `opts.library_name`: the chart's dep `name:`
933 // (build_chart_yaml at line 277) and this site's values wrap key
934 // both consult one `&str`, so a future fork that overrides
935 // `RenderOpts::library_name` to point at `acme-computeunit` /
936 // `pleme-computeunit-mirror` / the future per-edition library name
937 // reaches both axes by construction. Until this lift landed the
938 // wrap key was hardcoded `"pleme-computeunit"` while the dep name
939 // followed `opts.library_name`, so an override silently emitted
940 // values keyed under one name (the literal) while the rendered
941 // Chart.yaml's dep was declared under another (the override) —
942 // Helm's per-dep values router would route nothing to the
943 // configured dependency at `helm template` / `helm install` time,
944 // and every typed value the values block carries (`enabled`,
945 // `module`, `trigger`, the M2 overlay's `:limits`/`:behavior`/
946 // `:upgrade-from`) would silently no-op at the rendered chart's
947 // landing site. The wrap key now reads from the same `&str` the
948 // dep name reads from, structurally closing the drift footgun
949 // peer with the [`caixa_core::DEFAULT_NAMESPACE`] /
950 // [`caixa_core::DEFAULT_SERVICO_PORT`] lifts on the sibling
951 // canonical-K8s-axis constants (where two production-code call
952 // sites of the same load-bearing value would drift apart on
953 // any rebrand without a shared source of truth).
954 let library_alias = opts.library_name.as_str();
955 let spec = computeunit_yaml
956 .get(KUBE_KEY_SPEC)
957 .ok_or(Error::MissingField(KUBE_KEY_SPEC))?;
958
959 // Prepend a comment header so the file is human-friendly.
960 let header = format!(
961 "# Auto-generated by caixa-helm from caixa.lisp + servicos/{nome}.computeunit.yaml.\n\
962 # Edits to this file are overwritten by `feira chart`.\n\
963 #\n\
964 # `{library_alias}:` is the alias under which the library chart\n\
965 # in pleme-io/helmworks/charts/{library_alias} consumes its values.\n\n",
966 nome = caixa.nome()
967 );
968
969 let mut block = BTreeMap::new();
970 block.insert(
971 HELM_VALUES_KEY_ENABLED.to_string(),
972 serde_yaml::Value::Bool(opts.enabled_default),
973 );
974 // Two-step per-Servico value-block splice — the `spec.*` field
975 // splice (module / trigger / capabilities / config / resources /
976 // serviceAccount) and the M2 typed-slot overlay (limits / behavior
977 // / upgradeFrom, `or_insert` semantics so `spec.*` wins on
978 // collision) now route through the canonical
979 // [`caixa_core::servico_spec_and_m2_overlay_entries`] composition
980 // helper — the two prior inline for-loops chained around
981 // `string_keyed_entries` + `servico_m2_overlay` this call site
982 // (and the peer [`caixa_flux::programs_yaml_entry`] site) each
983 // re-derived collapse onto one canonical composition, so a future
984 // change to the per-Servico splice / overlay shape (the M4 typed
985 // per-edge policy overlay slot addition MESH-COMPOSITION §III.2 #3
986 // acknowledges, a change to the precedence rule once per-Aplicacao
987 // operator overrides land, a canonicalization pass on the merged
988 // key set) reaches both renderers by construction instead of a
989 // coordinated two-file rewrite. See the helper's docstring for the
990 // full lift rationale. The target `BTreeMap` re-sorts by key on
991 // insert, so the final rendered values block stays byte-identical
992 // to the prior inline block's alphabetical shape.
993 for (k, v) in caixa_core::servico_spec_and_m2_overlay_entries(caixa, spec)? {
994 block.entry(k).or_insert(v);
995 }
996
997 let mut wrapped = serde_yaml::Mapping::new();
998 wrapped.insert_str_key(library_alias, serde_yaml::to_value(block)?);
999 let body = serde_yaml::to_string(&serde_yaml::Value::Mapping(wrapped))?;
1000 Ok(format!("{header}{body}"))
1001}
1002
1003fn build_readme(caixa: &Caixa, chart_name: &str) -> String {
1004 let descricao = caixa
1005 .descricao()
1006 .map(str::to_owned)
1007 .unwrap_or_else(|| format!("caixa Servico {}", caixa.nome()));
1008 // Route the per-`README.md` `## Origin` line's `{repo}` interpolation
1009 // through the substrate-canonical [`caixa_core::Caixa::canonical_git_url`]
1010 // resolved-git-URL composer (124f864) rather than the prior
1011 // `caixa.repositorio().unwrap_or(caixa.nome())` two-arm inline fallback.
1012 // The prior shape's `:repositorio`-null arm folded to `caixa.nome()`
1013 // verbatim, so the rendered README's `## Origin` line emitted the
1014 // meaningless `Generated by `caixa-helm` from `<nome>/caixa.lisp``
1015 // scalar on every `:repositorio`-null caixa — a bare `<nome>` prefix
1016 // that told a downstream reader nothing about where the caixa's
1017 // source actually lives, and disagreed on that same axis with the
1018 // sibling [`caixa_flux::ClusterBundleOpts::for_caixa`] `git_url`
1019 // seed which had already routed through the resolved-URL composer
1020 // and emitted the canonical `https://github.com/{DEFAULT_PLEME_GIT_ORG}/<nome>`
1021 // pleme-org fallback on the same author-omitted-`:repositorio` case.
1022 // The 124f864 commit body explicitly anticipated this converge (the
1023 // "same accessor consumed by every renderer" convergence surface it
1024 // called out as the live-behavior-correcting fold distinct from that
1025 // commit's pure byte-preserving convergence). Post-lift the two
1026 // renderers agree byte-for-byte on the resolved-URL substrate
1027 // primitive on both arms, and the README's `## Origin` line names a
1028 // concrete pleme-org URL on the fallback path.
1029 format!(
1030 "# {chart_name}\n\
1031 \n\
1032 {descricao}\n\
1033 \n\
1034 ## Origin\n\
1035 \n\
1036 Generated by `caixa-helm` from `{repo}/caixa.lisp` v{versao}.\n\
1037 Edits here are overwritten by `feira chart`.\n\
1038 \n\
1039 ## Install\n\
1040 \n\
1041 ```bash\n\
1042 helm dependency build\n\
1043 helm template {chart_name} . --values values.yaml\n\
1044 ```\n\
1045 \n\
1046 ## License\n\
1047 \n\
1048 {license}.\n",
1049 chart_name = chart_name,
1050 descricao = descricao,
1051 repo = caixa.canonical_git_url(),
1052 versao = caixa.versao(),
1053 // Route the per-`README.md` `## License` line's `{license}`
1054 // interpolation through the substrate-canonical
1055 // [`caixa_core::CAIXA_LICENCA_DEFAULT`] typed `pub const` rather
1056 // than the prior raw `"MIT"` byte literal fallback arm — one
1057 // source of truth for the substrate-side per-`Caixa`
1058 // author-omitted-`:licenca` SPDX-shaped license-expression
1059 // fallback that every substrate-side consumer of the
1060 // author-omitted `:licenca` slot degrades onto. Peer of the
1061 // sibling `## Origin` line's `{repo}` interpolation already
1062 // routed through [`caixa_core::Caixa::canonical_git_url`]
1063 // (124f864) on the paired per-`README.md` universal-axis
1064 // fallback surface — the two typed dispatches jointly close
1065 // the `lareira-<nome>` chart's `README.md` universal-axis
1066 // author-omitted-slot fallback resolution at the substrate
1067 // primitive. Same "close the substrate-side default at one
1068 // canonical arm on the substrate primitive, converge every
1069 // prior open-coded caller onto the arm" discipline the sibling
1070 // M3 per-`:placement :estrategia`
1071 // [`caixa_core::PLACEMENT_ESTRATEGIA_DEFAULT`] (29c21ca) and
1072 // the M2 per-`:supervisor` default set carry on the paired
1073 // typed-slot-default axes, extended to the outer top-level
1074 // [`caixa_core::Caixa`] universal-axis `:licenca` fallback.
1075 // Pinned by
1076 // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
1077 // in the tests module.
1078 license = caixa.licenca().unwrap_or(caixa_core::CAIXA_LICENCA_DEFAULT),
1079 )
1080}
1081
1082#[cfg(test)]
1083mod tests {
1084 use super::*;
1085 use caixa_core::{
1086 Caixa, CaixaKind, M2_BEHAVIOR_KEY_ON_CALL, M2_BEHAVIOR_KEY_ON_INIT, M2_KEY_BEHAVIOR,
1087 M2_KEY_LIMITS, M2_KEY_UPGRADE_FROM, M2_LIMITS_KEY_CPU, M2_LIMITS_KEY_FUEL,
1088 M2_LIMITS_KEY_MEMORY, M2_LIMITS_KEY_WALL_CLOCK, find_file_by_path, kube_has, kube_str,
1089 kube_u64, mapping_string_keys, parse_yaml_at_path, parse_yaml_at_path_as,
1090 };
1091 use std::path::PathBuf;
1092
1093 fn sample_caixa() -> Caixa {
1094 Caixa {
1095 nome: "hello-rio".into(),
1096 versao: "0.1.0".into(),
1097 kind: CaixaKind::Servico,
1098 edicao: Some("2026".into()),
1099 descricao: Some("Canonical Rust→wasm32-wasip2 caixa Servico.".into()),
1100 repositorio: Some("github:pleme-io/hello-rio".into()),
1101 licenca: Some("MIT".into()),
1102 autores: vec!["pleme-io".into()],
1103 etiquetas: vec!["hello-world".into(), "wasm".into(), "rust".into()],
1104 deps: vec![],
1105 deps_dev: vec![],
1106 exe: vec![],
1107 bibliotecas: vec![],
1108 servicos: vec!["servicos/hello-rio.computeunit.yaml".into()],
1109 limits: None,
1110 behavior: None,
1111 upgrade_from: vec![],
1112 estrategia: None,
1113 max_restarts: None,
1114 restart_window: None,
1115 children: vec![],
1116 membros: vec![],
1117 contratos: vec![],
1118 politicas: None,
1119 placement: None,
1120 entrada: None,
1121 ci: None,
1122 }
1123 }
1124
1125 fn sample_cu_yaml() -> serde_yaml::Value {
1126 serde_yaml::from_str(
1127 r#"
1128apiVersion: wasm.pleme.io/v1alpha1
1129kind: ComputeUnit
1130metadata:
1131 name: hello-rio
1132spec:
1133 module:
1134 source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0
1135 trigger:
1136 service:
1137 port: 8080
1138 paths: ["/", "/hello", "/healthz"]
1139 breathability:
1140 enabled: true
1141 minReplicas: 0
1142 maxReplicas: 5
1143 cooldownPeriod: 600
1144 capabilities:
1145 - http-in:0.0.0.0:8080
1146 - env
1147"#,
1148 )
1149 .unwrap()
1150 }
1151
1152 #[test]
1153 fn renders_three_files() {
1154 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1155 assert_eq!(dir.name, "lareira-hello-rio");
1156 let names: Vec<_> = dir
1157 .files
1158 .iter()
1159 .map(|f| f.path.to_string_lossy().to_string())
1160 .collect();
1161 assert!(names.contains(&HELM_CHART_YAML_FILENAME.to_string()));
1162 assert!(names.contains(&HELM_VALUES_YAML_FILENAME.to_string()));
1163 assert!(names.contains(&HELM_CHART_README_FILENAME.to_string()));
1164 }
1165
1166 #[test]
1167 fn chart_yaml_metadata_propagates() {
1168 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1169 let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
1170 assert_eq!(chart.api_version, "v2");
1171 assert_eq!(chart.name, "lareira-hello-rio");
1172 assert_eq!(chart.version, "0.1.0");
1173 assert_eq!(chart.app_version, "0.1.0");
1174 assert_eq!(chart.dependencies.len(), 1);
1175 assert_eq!(chart.dependencies[0].name, DEFAULT_LIBRARY_NAME);
1176 assert!(chart.keywords.contains(&"caixa-servico".to_string()));
1177 assert!(chart.keywords.contains(&"hello-world".to_string()));
1178 assert_eq!(chart.maintainers[0].name, "pleme-io");
1179 }
1180
1181 #[test]
1182 fn chart_yaml_keywords_union_pins_every_lareira_chart_keywords_entry() {
1183 // Structural pin: `build_chart_yaml`'s substrate-fixed
1184 // chart-keyword union routes through the canonical
1185 // `caixa_core::LAREIRA_CHART_KEYWORDS` array — every rendered
1186 // `lareira-<nome>` chart's emitted `Chart.yaml` `keywords:`
1187 // sequence carries every substrate-fixed entry the array
1188 // declares. A future substrate-fixed keyword addition
1189 // (an `"opentelemetry"` entry once the caixa-otel collector-
1190 // pipeline chart lands, a `"lunatic"` entry once the wasm-
1191 // process-runtime marker lands, a `"gen_server"` entry once
1192 // the OTP-shape callback marker lands per the
1193 // [`caixa_core::behavior`] surface) that lands in the array
1194 // reaches this crate's production emit site by construction
1195 // through the shared `&[&str]` reference — a drift where the
1196 // production emit at `build_chart_yaml` re-inlines the pre-
1197 // lift `["lareira", "wasm", "tatara-lisp", "caixa-servico"]`
1198 // literal set (or drops a per-entry axis on a rebrand
1199 // sweep) fails this pin at caixa-helm build time rather than
1200 // surfacing as a `helm search hub caixa-servico` miss on the
1201 // Artifact Hub keyword-search index at chart-publish time
1202 // downstream. Peer to
1203 // [`chart_yaml_metadata_propagates`] on the
1204 // per-`Chart.yaml`-body-field propagation surface.
1205 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1206 let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
1207 for keyword in caixa_core::LAREIRA_CHART_KEYWORDS {
1208 assert!(
1209 chart.keywords.contains(&(*keyword).to_string()),
1210 "rendered Chart.yaml keywords {:?} must contain the \
1211 substrate-fixed LAREIRA_CHART_KEYWORDS entry {keyword:?}",
1212 chart.keywords,
1213 );
1214 }
1215 }
1216
1217 #[test]
1218 fn values_yaml_wraps_under_pleme_computeunit_key() {
1219 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1220 let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1221 let cu_block = parsed
1222 .get(DEFAULT_LIBRARY_NAME)
1223 .expect("must wrap under DEFAULT_LIBRARY_NAME");
1224 assert_eq!(
1225 cu_block.get(HELM_VALUES_KEY_ENABLED),
1226 Some(&serde_yaml::Value::Bool(false))
1227 );
1228 assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_MODULE));
1229 assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_TRIGGER));
1230 assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_CAPABILITIES));
1231 }
1232
1233 #[test]
1234 fn values_yaml_wrap_key_follows_library_name_override() {
1235 // Pinning the canonical alignment between the Helm chart's
1236 // `dependencies[].name` axis (build_chart_yaml at line 277) and
1237 // the values block's wrap key (build_values_yaml at the
1238 // `wrapped.insert(...)` site): both consult the same
1239 // `opts.library_name`, so an override on either axis reaches the
1240 // other by construction. Helm's per-dep alias convention — when
1241 // no `alias:` is set on a dependency, values are scoped under
1242 // its `name:` — makes wrap-key drift a silent value-routing
1243 // no-op at `helm template` / `helm install` time, so the
1244 // structural pin is load-bearing.
1245 let opts = RenderOpts {
1246 library_name: "acme-computeunit".into(),
1247 ..RenderOpts::default()
1248 };
1249 let dir = render_chart_for_servico_with(&sample_caixa(), &sample_cu_yaml(), &opts).unwrap();
1250 let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1251 assert!(
1252 kube_has(&parsed, "acme-computeunit"),
1253 "values wrap key must follow opts.library_name override \
1254 (got top-level keys: {keys:?})",
1255 keys = parsed
1256 .as_mapping()
1257 .map(mapping_string_keys)
1258 .unwrap_or_default()
1259 );
1260 assert!(
1261 !kube_has(&parsed, DEFAULT_LIBRARY_NAME),
1262 "values wrap key must not retain the default `{DEFAULT_LIBRARY_NAME}` literal \
1263 when opts.library_name overrides it"
1264 );
1265 let cu_block = parsed.get("acme-computeunit").unwrap();
1266 assert_eq!(
1267 cu_block.get(HELM_VALUES_KEY_ENABLED),
1268 Some(&serde_yaml::Value::Bool(false))
1269 );
1270 assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_MODULE));
1271 assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_TRIGGER));
1272 assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_CAPABILITIES));
1273 }
1274
1275 #[test]
1276 fn values_yaml_wrap_key_matches_chart_dependency_name() {
1277 // The structural invariant the lift defends: every rendered
1278 // chart's values.yaml wrap key equals its Chart.yaml
1279 // `dependencies[0].name`. Sweeping the canonical default + a
1280 // typed override on the same axis pins the alignment across the
1281 // accepted set of `RenderOpts::library_name` values rather than
1282 // at a single canonical literal.
1283 for library_name in [DEFAULT_LIBRARY_NAME, "acme-computeunit", "fork-pleme-cu"] {
1284 let opts = RenderOpts {
1285 library_name: library_name.into(),
1286 ..RenderOpts::default()
1287 };
1288 let dir =
1289 render_chart_for_servico_with(&sample_caixa(), &sample_cu_yaml(), &opts).unwrap();
1290 let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
1291 let dep_name = &chart.dependencies[0].name;
1292 let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1293 assert!(
1294 parsed.get(dep_name.as_str()).is_some(),
1295 "values.yaml wrap key must match Chart.yaml dependencies[0].name {dep_name:?} \
1296 (library_name = {library_name:?}); Helm's per-dep alias convention scopes \
1297 values under the dep's `name` when no `alias:` is set, so any drift between \
1298 the two axes silently routes the values block nowhere"
1299 );
1300 }
1301 }
1302
1303 #[test]
1304 fn values_yaml_header_comment_follows_library_name_override() {
1305 // The human-facing values.yaml header's `<library_alias>:` /
1306 // `pleme-io/helmworks/charts/<library_alias>` references both
1307 // resolve through `opts.library_name`, peer with the wrap key
1308 // itself, so an override leaves the header self-consistent
1309 // with the rendered structure rather than naming a drifted
1310 // default literal.
1311 let opts = RenderOpts {
1312 library_name: "acme-computeunit".into(),
1313 ..RenderOpts::default()
1314 };
1315 let dir = render_chart_for_servico_with(&sample_caixa(), &sample_cu_yaml(), &opts).unwrap();
1316 let values = find_file_by_path(&dir.files, HELM_VALUES_YAML_FILENAME).unwrap();
1317 assert!(
1318 values.contents.contains("`acme-computeunit:`"),
1319 "header must name the overriding library alias verbatim \
1320 (got: {contents:?})",
1321 contents = values.contents
1322 );
1323 assert!(
1324 values
1325 .contents
1326 .contains("pleme-io/helmworks/charts/acme-computeunit"),
1327 "header's helmworks path must follow the overriding library alias \
1328 (got: {contents:?})",
1329 contents = values.contents
1330 );
1331 assert!(
1332 !values.contents.contains("`pleme-computeunit:`"),
1333 "header must not retain the default library alias literal \
1334 when overridden (got: {contents:?})",
1335 contents = values.contents
1336 );
1337 }
1338
1339 #[test]
1340 fn refuses_non_servico() {
1341 let mut c = sample_caixa();
1342 c.kind = CaixaKind::Biblioteca;
1343 c.servicos = vec![];
1344 let err = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap_err();
1345 assert!(matches!(err, Error::NotAServico(_)));
1346 }
1347
1348 #[test]
1349 fn kind_mismatch_error_names_offending_caixa_nome() {
1350 // Pinning the lifted [`caixa_core::KindMismatch`] view's
1351 // load-bearing property: a kind-mismatched caixa surfaces a
1352 // diagnostic that *names the offending caixa* (`hello-rio`),
1353 // not just the rejected kind. Before the lift the renderer
1354 // raised `Error::NotAServico(CaixaKind::Biblioteca)` whose
1355 // Display said "caixa :kind must be Servico for caixa-helm
1356 // rendering, got Biblioteca" — the user had to grep their
1357 // source tree for which caixa.lisp triggered it. After the
1358 // lift the wrapped KindMismatch carries the `:nome`, the
1359 // renderer's `#[error("{0}")]` arm prints it through, and
1360 // the diagnostic is self-locating.
1361 let mut c = sample_caixa();
1362 c.kind = CaixaKind::Biblioteca;
1363 c.servicos = vec![];
1364 let err = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap_err();
1365 let msg = format!("{err}");
1366 assert!(
1367 msg.contains("hello-rio"),
1368 "kind-mismatch diagnostic must name the offending caixa nome \
1369 (got: {msg:?})"
1370 );
1371 assert!(
1372 msg.contains("Servico"),
1373 "diagnostic must name the expected kind (got: {msg:?})"
1374 );
1375 assert!(
1376 msg.contains("Biblioteca"),
1377 "diagnostic must name the actual kind (got: {msg:?})"
1378 );
1379 }
1380
1381 #[test]
1382 fn kind_mismatch_carries_typed_view_via_from_conversion() {
1383 // The renderer's `Error::NotAServico` variant wraps the typed
1384 // [`caixa_core::KindMismatch`] view via `#[from]`, so the `?`
1385 // operator at the call site converts without manual glue.
1386 // Pinning the typed payload (not just the variant) so a
1387 // future refactor can't silently switch the variant to a
1388 // raw-`CaixaKind` payload (which would regress the lift's
1389 // shared-shape contract with caixa-flux + caixa-mesh).
1390 let mut c = sample_caixa();
1391 c.kind = CaixaKind::Aplicacao;
1392 c.servicos = vec![];
1393 let err = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap_err();
1394 match err {
1395 Error::NotAServico(km) => {
1396 assert_eq!(km.nome, "hello-rio");
1397 assert_eq!(km.expected, CaixaKind::Servico);
1398 assert_eq!(km.actual, CaixaKind::Aplicacao);
1399 }
1400 other => panic!("expected Error::NotAServico, got {other:?}"),
1401 }
1402 }
1403
1404 #[test]
1405 fn servico_count_mismatch_carries_typed_view_with_nome() {
1406 // Peer to the [`KindMismatch`]-lift pin above on the V0
1407 // `:servicos`-singularity axis: a Servico-kind caixa whose
1408 // `:servicos` list is non-singleton fails
1409 // [`render_chart_for_servico`] with the renderer's
1410 // `Error::UnsupportedServicoCount` variant wrapping the typed
1411 // [`caixa_core::ServicoCountMismatch`] view (carrying the
1412 // offending caixa's `:nome` + the actual count). Before the
1413 // lift the variant carried only `usize` — the user had to grep
1414 // their source tree for which `caixa.lisp` triggered it; after
1415 // the lift the wrapped typed view names the offending caixa
1416 // verbatim. Pins both the variant routing (via `#[from]`) and
1417 // the typed payload so a future refactor can't silently switch
1418 // back to the raw-`usize` payload (which would regress the
1419 // shared-shape contract with caixa-flux on the peer
1420 // programs.yaml-entry path).
1421 let mut c = sample_caixa();
1422 c.servicos = vec![
1423 "servicos/hello-rio.computeunit.yaml".into(),
1424 "servicos/extra.computeunit.yaml".into(),
1425 ];
1426 let err = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap_err();
1427 match err {
1428 Error::UnsupportedServicoCount(scm) => {
1429 assert_eq!(scm.nome, "hello-rio");
1430 assert_eq!(scm.count, 2);
1431 }
1432 other => panic!("expected Error::UnsupportedServicoCount, got {other:?}"),
1433 }
1434 }
1435
1436 #[test]
1437 fn servico_count_mismatch_diagnostic_names_offending_caixa_nome() {
1438 // The renderer's `#[error("{0}")] UnsupportedServicoCount(
1439 // #[from] ServicoCountMismatch)` arm prints the typed view's
1440 // Display through verbatim, so the offending caixa's `:nome`
1441 // appears in the rendered diagnostic. Pinning the
1442 // self-locating property end-to-end (renderer entry-point →
1443 // typed view's Display → final diagnostic string) so a future
1444 // refactor that re-wraps the variant in a Display impl that
1445 // drops the `:nome` surfaces here as a test failure rather
1446 // than as silent fragmentation of the diagnostic. Peer to the
1447 // `kind_mismatch_error_names_offending_caixa_nome` test above
1448 // on the sibling V0 Servico-shape axis.
1449 let mut c = sample_caixa();
1450 c.servicos = vec![
1451 "servicos/hello-rio.computeunit.yaml".into(),
1452 "servicos/extra.computeunit.yaml".into(),
1453 ];
1454 let err = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap_err();
1455 let msg = format!("{err}");
1456 assert!(
1457 msg.contains("hello-rio"),
1458 ":servicos-count-mismatch diagnostic must name the offending caixa nome \
1459 (got: {msg:?})"
1460 );
1461 assert!(
1462 msg.contains("2"),
1463 "diagnostic must name the actual count (got: {msg:?})"
1464 );
1465 assert!(
1466 msg.contains(":servicos"),
1467 "diagnostic must name the offending field axis (got: {msg:?})"
1468 );
1469 }
1470
1471 #[test]
1472 fn limits_slot_propagates_into_values_block() {
1473 use caixa_core::LimitsSpec;
1474 use std::time::Duration;
1475 let mut c = sample_caixa();
1476 c.limits = Some(LimitsSpec {
1477 memory: Some(64 * 1024 * 1024),
1478 fuel: Some(1_000_000),
1479 wall_clock: Some(Duration::from_secs(30)),
1480 cpu: Some(500),
1481 });
1482 let dir = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap();
1483 let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1484 let cu_block = parsed.get(DEFAULT_LIBRARY_NAME).unwrap();
1485 let limits = cu_block.get(M2_KEY_LIMITS).expect("limits must propagate");
1486 assert_eq!(kube_str(limits, M2_LIMITS_KEY_MEMORY), Some("64MiB"));
1487 assert_eq!(kube_u64(limits, M2_LIMITS_KEY_FUEL), Some(1_000_000));
1488 assert_eq!(kube_str(limits, M2_LIMITS_KEY_WALL_CLOCK), Some("30s"));
1489 assert_eq!(kube_str(limits, M2_LIMITS_KEY_CPU), Some("500m"));
1490 }
1491
1492 #[test]
1493 fn behavior_slot_propagates_into_values_block() {
1494 use caixa_core::BehaviorSpec;
1495 let mut c = sample_caixa();
1496 c.behavior = Some(BehaviorSpec {
1497 on_init: Some(PathBuf::from("lib/init.lisp")),
1498 on_call: Some(PathBuf::from("lib/handlers.lisp")),
1499 ..Default::default()
1500 });
1501 let dir = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap();
1502 let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1503 let cu_block = parsed.get(DEFAULT_LIBRARY_NAME).unwrap();
1504 let behavior = cu_block
1505 .get(M2_KEY_BEHAVIOR)
1506 .expect("behavior must propagate");
1507 assert_eq!(
1508 kube_str(behavior, M2_BEHAVIOR_KEY_ON_INIT),
1509 Some("lib/init.lisp")
1510 );
1511 assert_eq!(
1512 kube_str(behavior, M2_BEHAVIOR_KEY_ON_CALL),
1513 Some("lib/handlers.lisp")
1514 );
1515 }
1516
1517 #[test]
1518 fn upgrade_from_slot_propagates_into_values_block() {
1519 use caixa_core::{UpgradeFromEntry, UpgradeInstruction};
1520 let mut c = sample_caixa();
1521 c.upgrade_from = vec![UpgradeFromEntry {
1522 from: "0.0.9".into(),
1523 instructions: vec![UpgradeInstruction::LoadModule {
1524 module: "hello-rio".into(),
1525 }],
1526 }];
1527 let dir = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap();
1528 let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1529 let cu_block = parsed.get(DEFAULT_LIBRARY_NAME).unwrap();
1530 assert!(kube_has(cu_block, M2_KEY_UPGRADE_FROM));
1531 }
1532
1533 #[test]
1534 fn empty_m2_slots_do_not_appear() {
1535 // Existing caixa with no M2 slots → values.yaml carries no
1536 // limits/behavior/upgradeFrom keys (forward-compat invariant).
1537 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1538 let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1539 let cu_block = parsed.get(DEFAULT_LIBRARY_NAME).unwrap();
1540 assert!(!kube_has(cu_block, M2_KEY_LIMITS));
1541 assert!(!kube_has(cu_block, M2_KEY_BEHAVIOR));
1542 assert!(!kube_has(cu_block, M2_KEY_UPGRADE_FROM));
1543 }
1544
1545 #[test]
1546 fn write_to_creates_files() {
1547 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1548 let tmp = tempfile::tempdir().unwrap();
1549 dir.write_to(tmp.path()).unwrap();
1550 let chart_root = tmp.path().join("lareira-hello-rio");
1551 assert!(chart_root.join(HELM_CHART_YAML_FILENAME).exists());
1552 assert!(chart_root.join(HELM_VALUES_YAML_FILENAME).exists());
1553 assert!(chart_root.join(HELM_CHART_README_FILENAME).exists());
1554 }
1555
1556 #[test]
1557 fn default_library_name_re_export_points_at_caixa_core_canonical() {
1558 // The renderer's `pub const DEFAULT_LIBRARY_NAME` was lifted to a
1559 // re-export of [`caixa_core::DEFAULT_LIBRARY_NAME`] so the Helm
1560 // library-chart name lives in exactly one place across every
1561 // caixa renderer (caixa-helm's `RenderOpts::library_name`
1562 // default here + caixa-flux's `cluster_bundle` `helmrelease.yaml`
1563 // wrap key on the sibling deploy-path crate). Pin the equality
1564 // here so any local re-introduction of a sibling `pub const
1565 // DEFAULT_LIBRARY_NAME: &str = "…"` (the canonical drift footgun
1566 // the prior `DEFAULT_NAMESPACE` / `DEFAULT_SERVICO_PORT` lift
1567 // commits' bodies acknowledged as the recurring shape) is a
1568 // build-time test failure naming the offending drift, not a
1569 // silent apply-time wrap-key mismatch routing the per-cluster
1570 // `enabled: true` override nowhere on `helm template` /
1571 // `helm install`. Peer to
1572 // `caixa_flux::tests::default_library_name_re_export_points_at_caixa_core_canonical`
1573 // on the sibling renderer crate.
1574 caixa_core::assert_str_reexport_identity(
1575 "DEFAULT_LIBRARY_NAME",
1576 DEFAULT_LIBRARY_NAME,
1577 caixa_core::DEFAULT_LIBRARY_NAME,
1578 );
1579 }
1580
1581 #[test]
1582 fn default_library_repo_re_export_points_at_caixa_core_canonical() {
1583 // The renderer's `pub const DEFAULT_LIBRARY_REPO: &str =
1584 // "file://../pleme-computeunit"` was lifted to a re-export of
1585 // [`caixa_core::DEFAULT_LIBRARY_REPO`] so the Helm 3
1586 // `Chart.yaml` `dependencies[0].repository` chart-source URL
1587 // every rendered `lareira-<nome>` chart declares against the
1588 // substrate-canonical [`DEFAULT_LIBRARY_NAME`] library chart
1589 // lives in exactly one place across every caixa renderer
1590 // (caixa-helm's `RenderOpts::library_repo` default here + every
1591 // future substrate-side per-Servico renderer consumer the
1592 // caixa-core [`caixa_core::DEFAULT_LIBRARY_REPO`] docstring
1593 // enumerates). Pin the equality + `&'static` static-data
1594 // identity here so any local re-introduction of a sibling
1595 // `pub const DEFAULT_LIBRARY_REPO: &str = "…"` at this crate —
1596 // the canonical drift footgun where a sibling local `pub const`
1597 // could happen to carry the same bytes at the source while
1598 // pointing at a different `&'static str` allocation (silently
1599 // splitting the substrate-canonical per-dep resolver URL from
1600 // the caixa-core-owned canonical), or, worse, could drift on a
1601 // future scheme migration (`file://` → `oci://` once
1602 // `pleme-io/helmworks/charts` lands as an OCI-registry-backed
1603 // chart-source) that landed on the caixa-core canonical without
1604 // rebrand of the sibling local — is a build-time test failure
1605 // naming the offending drift, not a silent apply-time
1606 // per-chart-dep vendoring floor mismatch surfacing at operator-
1607 // side `helm dependency build` time far from the drift site.
1608 // Peer to the sibling
1609 // [`default_library_name_re_export_points_at_caixa_core_canonical`]
1610 // pin on the sibling per-dep name-axis re-export.
1611 caixa_core::assert_str_reexport_identity(
1612 "DEFAULT_LIBRARY_REPO",
1613 DEFAULT_LIBRARY_REPO,
1614 caixa_core::DEFAULT_LIBRARY_REPO,
1615 );
1616 }
1617
1618 #[test]
1619 fn kube_key_spec_re_export_points_at_caixa_core_canonical() {
1620 // The renderer's `KUBE_KEY_SPEC` was lifted from the production-
1621 // code inline `"spec"` literal at `build_values_yaml`'s
1622 // `computeunit_yaml.get("spec")` ComputeUnit-side spec read (+
1623 // its matching `Error::MissingField("spec")` diagnostic) to a
1624 // re-export of [`caixa_core::KUBE_KEY_SPEC`] so the canonical
1625 // K8s-CR top-level spec-axis string lives in exactly one place
1626 // across every caixa renderer. Pin the equality + static-data
1627 // identity here so any local re-introduction of a sibling
1628 // `pub const KUBE_KEY_SPEC: &str = "…"` (the canonical drift
1629 // footgun where a sibling local `pub const` could happen to
1630 // carry the same string at the source while pointing at a
1631 // different `&'static` allocation) is a build-time test
1632 // failure naming the offending drift. Peer to
1633 // [`default_library_name_re_export_points_at_caixa_core_canonical`]
1634 // on the sibling re-export axis +
1635 // `caixa_flux::tests::kube_key_spec_re_export_points_at_caixa_core_canonical`
1636 // / `caixa_mesh::tests::kube_key_spec_re_export_points_at_caixa_core_canonical`
1637 // on the sibling renderer crates.
1638 caixa_core::assert_str_reexport_identity(
1639 "KUBE_KEY_SPEC",
1640 KUBE_KEY_SPEC,
1641 caixa_core::KUBE_KEY_SPEC,
1642 );
1643 }
1644
1645 #[test]
1646 fn helm_chart_api_version_re_export_points_at_caixa_core_canonical() {
1647 // The renderer's `HELM_CHART_API_VERSION` was lifted from the
1648 // production-code inline `"v2".into()` literal at
1649 // [`build_chart_yaml`]'s `api_version` field assignment (formerly
1650 // `caixa-helm/src/lib.rs:298`) to a re-export of
1651 // [`caixa_core::HELM_CHART_API_VERSION`] so the Helm 3
1652 // chart-schema apiVersion the rendered Chart.yaml declares lives
1653 // in exactly one place across every caixa renderer. Pin the
1654 // equality + `&'static` static-data identity here so any local
1655 // re-introduction of a sibling `pub const HELM_CHART_API_VERSION:
1656 // &str = "…"` at this crate — the canonical drift footgun where
1657 // a sibling local `pub const` could happen to carry the same
1658 // string at the source while pointing at a different `&'static`
1659 // allocation — is a build-time test failure naming the offending
1660 // drift, not a silent chart-schema-parser reroute at
1661 // `helm template` time far from the drift site. Peer to
1662 // [`kube_key_spec_re_export_points_at_caixa_core_canonical`] /
1663 // [`default_library_name_re_export_points_at_caixa_core_canonical`]
1664 // on the sibling re-export axes.
1665 caixa_core::assert_str_reexport_identity(
1666 "HELM_CHART_API_VERSION",
1667 HELM_CHART_API_VERSION,
1668 caixa_core::HELM_CHART_API_VERSION,
1669 );
1670 }
1671
1672 #[test]
1673 fn chart_yaml_uses_lifted_helm_chart_api_version() {
1674 // Fail-before-pass-after pin on the production-code
1675 // substitution: [`build_chart_yaml`]'s `api_version` field
1676 // consults the lifted [`HELM_CHART_API_VERSION`] re-export at
1677 // its assignment site, so the rendered Chart.yaml's top-level
1678 // `apiVersion` axis is byte-identical to the canonical constant
1679 // by construction. Before the lift the field carried an inline
1680 // `"v2".into()` literal at [`build_chart_yaml`]; a future
1681 // refactor that accidentally reverted the substitution — or
1682 // any parallel per-renderer variant that inlined a stale
1683 // Helm 2 `"v1"` literal — would silently reroute the rendered
1684 // Chart.yaml through the wrong chart-schema parser at
1685 // `helm dependency build` / `helm template` time, so this pin
1686 // trips at caixa-helm build time. Peer to
1687 // `values_yaml_wrap_key_matches_chart_dependency_name` on the
1688 // sibling structural-cross-axis-invariant surface.
1689 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1690 let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
1691 assert_eq!(
1692 chart.api_version, HELM_CHART_API_VERSION,
1693 "rendered Chart.yaml `apiVersion` must equal the lifted \
1694 HELM_CHART_API_VERSION verbatim — a drifted value silently \
1695 reroutes the rendered chart through the wrong Helm chart-schema \
1696 parser at `helm template` time"
1697 );
1698 }
1699
1700 #[test]
1701 fn helm_chart_type_application_re_export_points_at_caixa_core_canonical() {
1702 // The renderer's `HELM_CHART_TYPE_APPLICATION` was lifted from
1703 // the production-code inline `"application".into()` literal at
1704 // [`build_chart_yaml`]'s `chart_type` field assignment (formerly
1705 // `caixa-helm/src/lib.rs:354`) to a re-export of
1706 // [`caixa_core::HELM_CHART_TYPE_APPLICATION`] so the Helm 3
1707 // chart-schema per-chart-kind discriminator scalar-value the
1708 // rendered `lareira-<nome>` chart declares lives in exactly one
1709 // place across every caixa renderer. Pin the equality +
1710 // `&'static` static-data identity here so any local
1711 // re-introduction of a sibling `pub const
1712 // HELM_CHART_TYPE_APPLICATION: &str = "…"` at this crate — the
1713 // canonical drift footgun where a sibling local `pub const`
1714 // could happen to carry the same string at the source while
1715 // pointing at a different `&'static` allocation — is a
1716 // build-time test failure naming the offending drift, not a
1717 // silent per-release install-shape dispatch reroute at
1718 // `helm install` time far from the drift site. Peer to
1719 // [`helm_chart_api_version_re_export_points_at_caixa_core_canonical`]
1720 // on the sibling canonical-Helm-chart-schema-axis re-export
1721 // surface — completes the per-Chart.yaml `(apiVersion, type)`
1722 // canonical-scalar-axis re-export pair every rendered
1723 // `lareira-<nome>` chart declares at its top-level Chart.yaml
1724 // body.
1725 caixa_core::assert_str_reexport_identity(
1726 "HELM_CHART_TYPE_APPLICATION",
1727 HELM_CHART_TYPE_APPLICATION,
1728 caixa_core::HELM_CHART_TYPE_APPLICATION,
1729 );
1730 }
1731
1732 #[test]
1733 fn helm_chart_type_library_re_export_points_at_caixa_core_canonical() {
1734 // Re-export identity pin on the peer closed-set arm the
1735 // renderer's `HELM_CHART_TYPE_LIBRARY` alias resolves to. Peer
1736 // of `helm_chart_type_application_re_export_points_at_caixa_core_canonical`
1737 // on the sibling closed-set arm — the two pins together enshrine
1738 // the two-arm `{"application", "library"}` closed set at the
1739 // caixa-helm re-export surface as byte-identical `&'static`
1740 // static-data views onto the canonical caixa-core lifts, so any
1741 // local re-introduction of a sibling `pub const
1742 // HELM_CHART_TYPE_LIBRARY: &str = "…"` at this crate (the same
1743 // drift footgun the peer pin closes on the sibling arm) is a
1744 // build-time test failure naming the offending drift. The pin
1745 // also structurally forbids the two arms from converging on the
1746 // same `&'static` allocation — a future rebrand that
1747 // accidentally aliased `HELM_CHART_TYPE_LIBRARY` at the
1748 // [`caixa_core::HELM_CHART_TYPE_APPLICATION`] canonical would
1749 // pass this identity check but trip the caixa-core-side
1750 // `helm_chart_type_application_and_library_are_distinct` pin
1751 // paired to the two arms' distinctness contract.
1752 caixa_core::assert_str_reexport_identity(
1753 "HELM_CHART_TYPE_LIBRARY",
1754 HELM_CHART_TYPE_LIBRARY,
1755 caixa_core::HELM_CHART_TYPE_LIBRARY,
1756 );
1757 }
1758
1759 #[test]
1760 fn chart_yaml_uses_lifted_helm_chart_type_application() {
1761 // Fail-before-pass-after pin on the production-code substitution:
1762 // [`build_chart_yaml`]'s `chart_type` field consults the lifted
1763 // [`HELM_CHART_TYPE_APPLICATION`] re-export at its assignment
1764 // site, so the rendered Chart.yaml's top-level `type` axis is
1765 // byte-identical to the canonical constant by construction.
1766 // Before the lift the field carried an inline `"application".into()`
1767 // literal at [`build_chart_yaml`]; a future refactor that
1768 // accidentally reverted the substitution — or any parallel
1769 // per-renderer variant that inlined a `"library"` literal (the
1770 // sibling closed-set value from the Helm chart-schema's
1771 // per-chart-kind enum) — would silently reroute the rendered
1772 // Chart.yaml through the wrong per-release install-shape
1773 // dispatch at `helm install` time (Helm refuses to install a
1774 // `library` chart directly), so this pin trips at caixa-helm
1775 // build time. Peer to `chart_yaml_uses_lifted_helm_chart_api_version`
1776 // on the sibling per-Chart.yaml top-level `(apiVersion, type)`
1777 // canonical-scalar-axis pin pair — extends the per-Chart.yaml
1778 // top-level canonical-scalar-axis production-emit-pin
1779 // discipline from the `apiVersion` half onto the sibling `type`
1780 // half.
1781 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1782 let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
1783 assert_eq!(
1784 chart.chart_type, HELM_CHART_TYPE_APPLICATION,
1785 "rendered Chart.yaml `type` must equal the lifted \
1786 HELM_CHART_TYPE_APPLICATION verbatim — a drifted value \
1787 silently reroutes the rendered chart through the wrong \
1788 per-release install-shape dispatch at `helm install` time \
1789 (Helm refuses to install a `library` chart directly, or \
1790 silently treats an unrecognized value as the default \
1791 `application` shape masking the schema violation)"
1792 );
1793 }
1794
1795 #[test]
1796 fn helm_chart_key_type_re_export_points_at_caixa_core_canonical() {
1797 // The renderer's `HELM_CHART_KEY_TYPE` was lifted from the
1798 // sole production-side inline `"type"` literal at [`ChartYaml`]'s
1799 // `chart_type` field `#[serde(rename = "type")]` attribute
1800 // (formerly `caixa-helm/src/lib.rs:149`) to a re-export of
1801 // [`caixa_core::HELM_CHART_KEY_TYPE`] so the Helm 3 top-level
1802 // per-chart-kind discriminator YAML axis-key lives in exactly
1803 // one place across every caixa renderer. Pin the equality +
1804 // `&'static` static-data identity here so any local
1805 // re-introduction of a sibling `pub const HELM_CHART_KEY_TYPE:
1806 // &str = "…"` at this crate — the canonical drift footgun
1807 // where a sibling local `pub const` could happen to carry the
1808 // same string at the source while pointing at a different
1809 // `&'static` allocation — is a build-time test failure naming
1810 // the offending drift, not a silent Helm-chart-schema-parser
1811 // per-chart-kind-defaulting reroute at `helm dependency build`
1812 // / `helm lint` / `helm template` / `helm install` time far
1813 // from the drift site. Peer to
1814 // [`helm_chart_type_application_re_export_points_at_caixa_core_canonical`]
1815 // / [`helm_chart_type_library_re_export_points_at_caixa_core_canonical`]
1816 // on the sibling per-chart-kind axis-value re-export surface —
1817 // completes the per-Chart.yaml per-chart-kind discriminator
1818 // axis's `(key, value-set)` canonical re-export trio at the
1819 // caixa-helm surface.
1820 caixa_core::assert_str_reexport_identity(
1821 "HELM_CHART_KEY_TYPE",
1822 HELM_CHART_KEY_TYPE,
1823 caixa_core::HELM_CHART_KEY_TYPE,
1824 );
1825 }
1826
1827 #[test]
1828 fn helm_chart_key_app_version_re_export_points_at_caixa_core_canonical() {
1829 // The renderer's `HELM_CHART_KEY_APP_VERSION` was lifted from
1830 // the sole production-side inline `"appVersion"` literal at
1831 // [`ChartYaml`]'s `app_version` field
1832 // `#[serde(rename = "appVersion")]` attribute (formerly
1833 // `caixa-helm/src/lib.rs:152`) to a re-export of
1834 // [`caixa_core::HELM_CHART_KEY_APP_VERSION`] so the Helm 3
1835 // top-level per-chart-app-version YAML axis-key lives in
1836 // exactly one place across every caixa renderer. Pin the
1837 // equality + `&'static` static-data identity here so any
1838 // local re-introduction of a sibling `pub const
1839 // HELM_CHART_KEY_APP_VERSION: &str = "…"` at this crate — the
1840 // canonical drift footgun where a sibling local `pub const`
1841 // could happen to carry the same string at the source while
1842 // pointing at a different `&'static` allocation — is a
1843 // build-time test failure naming the offending drift, not a
1844 // silent Helm-chart-schema-parser field-drop at every
1845 // downstream Artifact Hub / `helm search` chart-consumer far
1846 // from the drift site. Peer to
1847 // [`helm_chart_key_type_re_export_points_at_caixa_core_canonical`]
1848 // on the sibling per-Chart.yaml top-level YAML axis-key
1849 // re-export surface — completes the per-Chart.yaml top-level
1850 // YAML axis-key re-export pair at the caixa-helm surface for
1851 // the two serde-rename-literal-only axes.
1852 caixa_core::assert_str_reexport_identity(
1853 "HELM_CHART_KEY_APP_VERSION",
1854 HELM_CHART_KEY_APP_VERSION,
1855 caixa_core::HELM_CHART_KEY_APP_VERSION,
1856 );
1857 }
1858
1859 #[test]
1860 fn chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type() {
1861 // Fail-before-pass-after drift-detection pin on the
1862 // `#[serde(rename = "type")]` attribute at [`ChartYaml`]'s
1863 // `chart_type` field. Rust's attribute grammar admits only
1864 // string literals so the lifted [`HELM_CHART_KEY_TYPE`]
1865 // constant cannot substitute for the literal syntactically at
1866 // the attribute-argument site — a future refactor that
1867 // dropped the `#[serde(rename = "type")]` attribute (or
1868 // changed the target key to `"Type"` / `"kind"` /
1869 // `"chartType"`) would silently serialize the field under
1870 // Rust's default snake_case `chart_type:` key, which Helm's
1871 // chart-schema parser silently ignores as an unknown top-
1872 // level key, defaulting the per-chart-kind axis to
1873 // `application` with no process-log signal. This pin closes
1874 // the drift by round-tripping a rendered `Chart.yaml` through
1875 // `serde_yaml::from_str::<serde_yaml::Value>` and asserting
1876 // the top-level `Mapping::get(HELM_CHART_KEY_TYPE)` resolves
1877 // (rather than serializing through the [`ChartYaml`]-typed
1878 // deserializer that would silently absorb the rename drift
1879 // via `#[serde(default)]` fall-through at the struct-side).
1880 // Peer to
1881 // [`chart_yaml_uses_lifted_helm_chart_type_application`] on
1882 // the sibling per-Chart.yaml per-chart-kind axis-value
1883 // production-emit pin — the two pins together enforce the
1884 // full `(key, value)` production-emit pair at the caixa-helm
1885 // surface for the per-Chart.yaml per-chart-kind discriminator
1886 // axis.
1887 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1888 let doc = parse_yaml_at_path(&dir.files, HELM_CHART_YAML_FILENAME);
1889 let mapping = doc.as_mapping().expect(
1890 "rendered Chart.yaml must be a top-level YAML mapping per \
1891 the Helm 3 chart-schema shape",
1892 );
1893 assert!(
1894 kube_has(mapping, HELM_CHART_KEY_TYPE),
1895 "rendered Chart.yaml must carry a top-level {HELM_CHART_KEY_TYPE:?} \
1896 axis-key — a drift on the `#[serde(rename = {HELM_CHART_KEY_TYPE:?})]` \
1897 attribute at ChartYaml's `chart_type` field silently reroutes the \
1898 per-chart-kind discriminator axis through Rust's default snake_case \
1899 serialization (`chart_type:`), which Helm's chart-schema parser \
1900 silently ignores as an unknown top-level key (defaulting the \
1901 per-chart-kind axis to `application` with no process-log signal)"
1902 );
1903 }
1904
1905 #[test]
1906 fn chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version() {
1907 // Fail-before-pass-after drift-detection pin on the
1908 // `#[serde(rename = "appVersion")]` attribute at [`ChartYaml`]'s
1909 // `app_version` field. Same attribute-literal-only-grammar
1910 // constraint the peer
1911 // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
1912 // pin closes on the sibling per-Chart.yaml top-level YAML
1913 // axis-key applies here: a future refactor that dropped the
1914 // `#[serde(rename = "appVersion")]` attribute (or changed the
1915 // target key to `"AppVersion"` / `"applicationVersion"` /
1916 // `"appversion"`) would silently serialize the field under
1917 // Rust's default snake_case `app_version:` key, which Helm's
1918 // chart-schema parser silently drops from the parsed
1919 // chart-metadata shape, and every downstream Artifact Hub /
1920 // `helm search` per-chart index falls back to "no application
1921 // version" for the rendered chart. This pin closes the drift
1922 // by round-tripping a rendered `Chart.yaml` through
1923 // `serde_yaml::from_str::<serde_yaml::Value>` (rather than
1924 // through the [`ChartYaml`]-typed deserializer that would
1925 // silently absorb the rename drift). Peer to
1926 // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
1927 // on the sibling per-Chart.yaml top-level YAML axis-key
1928 // serialization pin surface — completes the per-Chart.yaml
1929 // top-level YAML axis-key production-emit pin pair at the
1930 // caixa-helm surface for the two serde-rename-literal-only
1931 // axes.
1932 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1933 let doc = parse_yaml_at_path(&dir.files, HELM_CHART_YAML_FILENAME);
1934 let mapping = doc.as_mapping().expect(
1935 "rendered Chart.yaml must be a top-level YAML mapping per \
1936 the Helm 3 chart-schema shape",
1937 );
1938 assert!(
1939 kube_has(mapping, HELM_CHART_KEY_APP_VERSION),
1940 "rendered Chart.yaml must carry a top-level \
1941 {HELM_CHART_KEY_APP_VERSION:?} axis-key — a drift on the \
1942 `#[serde(rename = {HELM_CHART_KEY_APP_VERSION:?})]` attribute at \
1943 ChartYaml's `app_version` field silently reroutes the per-chart \
1944 underlying-application-version axis through Rust's default \
1945 snake_case serialization (`app_version:`), which Helm's \
1946 chart-schema parser silently drops from the parsed chart-metadata \
1947 shape (every downstream Artifact Hub / `helm search` per-chart \
1948 index falls back to \"no application version\" for the rendered \
1949 chart with no process-log signal at the substrate-side emitter site)"
1950 );
1951 }
1952
1953 #[test]
1954 fn chart_yaml_serializes_api_version_axis_under_lifted_helm_chart_key_api_version() {
1955 // Fail-before-pass-after drift-detection pin on the
1956 // `#[serde(rename = "apiVersion")]` attribute at [`ChartYaml`]'s
1957 // `api_version` field. Same attribute-literal-only-grammar
1958 // constraint the peer
1959 // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
1960 // / [`chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`]
1961 // pins close on the sibling per-Chart.yaml top-level YAML
1962 // axis-keys applies here: a future refactor that dropped the
1963 // `#[serde(rename = "apiVersion")]` attribute (or changed the
1964 // target key to `"ApiVersion"` / `"apiversion"` /
1965 // `"schemaVersion"`) would silently serialize the field under
1966 // Rust's default snake_case `api_version:` key, which Helm's
1967 // chart-schema parser rejects at `helm lint` / `helm
1968 // dependency build` / `helm template` time with an "apiVersion
1969 // is required" error far from the drift site — every downstream
1970 // `lareira-<nome>` chart consumer drops with no field naming
1971 // the serde-rename-drift root cause. This pin closes the drift
1972 // by round-tripping a rendered `Chart.yaml` through
1973 // `serde_yaml::from_str::<serde_yaml::Value>` (rather than
1974 // through the [`ChartYaml`]-typed deserializer that would
1975 // silently absorb the rename drift via `#[serde(default)]`
1976 // fall-through at the struct-side). Peer to
1977 // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
1978 // / [`chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`]
1979 // on the sibling per-Chart.yaml top-level YAML axis-key
1980 // serialization pin surface — completes the per-Chart.yaml
1981 // top-level YAML axis-key production-emit pin trio at the
1982 // caixa-helm surface for the three serde-rename-literal-only
1983 // axes.
1984 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1985 let doc = parse_yaml_at_path(&dir.files, HELM_CHART_YAML_FILENAME);
1986 let mapping = doc.as_mapping().expect(
1987 "rendered Chart.yaml must be a top-level YAML mapping per \
1988 the Helm 3 chart-schema shape",
1989 );
1990 assert!(
1991 kube_has(mapping, HELM_CHART_KEY_API_VERSION),
1992 "rendered Chart.yaml must carry a top-level \
1993 {HELM_CHART_KEY_API_VERSION:?} axis-key — a drift on the \
1994 `#[serde(rename = {HELM_CHART_KEY_API_VERSION:?})]` attribute at \
1995 ChartYaml's `api_version` field silently reroutes the per-chart \
1996 chart-schema-apiVersion axis through Rust's default snake_case \
1997 serialization (`api_version:`), which Helm's chart-schema parser \
1998 rejects at `helm lint` / `helm template` time with an \"apiVersion \
1999 is required\" error far from the drift site"
2000 );
2001 }
2002
2003 #[test]
2004 fn chart_dependency_serializes_tetrad_under_lifted_helm_chart_dependency_keys() {
2005 // Fail-before-pass-after drift-detection pin on the per-
2006 // `dependencies[]`-entry sub-mapping serde field-name tetrad
2007 // at [`ChartDependency`]. The four fields are identity-mapped
2008 // to their target wire keys today (no `#[serde(rename)]` or
2009 // `#[serde(rename_all)]` attribute on the struct), so a drift
2010 // would surface as one of two shapes: a rename of the Rust
2011 // field (`pub name` → `pub nome`) that silently rebrands the
2012 // wire key, or a `#[serde(rename_all = "camelCase")]` attribute
2013 // addition that stays a no-op on the four lowercase-identity
2014 // fields today but silently activates on a future field
2015 // addition (e.g. an `import_values: Option<Vec<String>>` axis
2016 // matching Helm 3's per-dep `import-values` sub-key). Either
2017 // shape silently reroutes the per-dep sub-mapping through a
2018 // Helm-per-dep-resolver drop at `helm dependency build` time
2019 // far from the drift site (Helm silently drops the drifted
2020 // per-dep sub-mapping field and the per-dep resolver falls
2021 // back to the parsed-shape defaults). This pin closes the
2022 // drift by serializing a fully-populated [`ChartDependency`]
2023 // (`alias` set to a non-`None` value so the
2024 // `#[serde(skip_serializing_if = "Option::is_none")]`
2025 // attribute doesn't elide the axis from the emitted YAML)
2026 // through `serde_yaml::to_value` and asserting each of the
2027 // four per-dep sub-mapping wire keys resolves via
2028 // `Mapping::get(HELM_CHART_DEPENDENCY_KEY_*)`. Peer to
2029 // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
2030 // / [`chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`]
2031 // / [`chart_yaml_serializes_api_version_axis_under_lifted_helm_chart_key_api_version`]
2032 // on the sibling per-Chart.yaml top-level serde-rename-literal-
2033 // only axis-key drift-detection pin trio (cc44e4b / d29bc23) —
2034 // extends the drift-detection discipline from the per-Chart.yaml
2035 // top-level serde-rename-literal axes onto the per-
2036 // `dependencies[]`-entry sub-mapping serde-field-name tetrad.
2037 let dep = ChartDependency {
2038 name: "pleme-computeunit".into(),
2039 version: "~0.1.0".into(),
2040 repository: "file://../pleme-computeunit".into(),
2041 alias: Some("acme-alias".into()),
2042 };
2043 let doc = serde_yaml::to_value(&dep).unwrap();
2044 let mapping = doc.as_mapping().expect(
2045 "ChartDependency must serialize to a top-level YAML mapping per \
2046 the Helm 3 per-dep sub-mapping shape",
2047 );
2048 for key in [
2049 HELM_CHART_DEPENDENCY_KEY_NAME,
2050 HELM_CHART_DEPENDENCY_KEY_VERSION,
2051 HELM_CHART_DEPENDENCY_KEY_REPOSITORY,
2052 HELM_CHART_DEPENDENCY_KEY_ALIAS,
2053 ] {
2054 assert!(
2055 kube_has(mapping, key),
2056 "serialized ChartDependency must carry a top-level {key:?} \
2057 axis-key — a drift on the `ChartDependency` struct's serde \
2058 field-name (a Rust-side rename, an added \
2059 `#[serde(rename_all)]` attribute, an added `#[serde(rename)]` \
2060 per-field override) silently reroutes the per-dep sub-mapping \
2061 through a Helm-per-dep-resolver drop at `helm dependency \
2062 build` time far from the drift site (Helm silently drops the \
2063 drifted per-dep sub-mapping field and the per-dep resolver \
2064 falls back to the parsed-shape defaults); the emitted mapping \
2065 keys are {keys:?}",
2066 keys = mapping_string_keys(mapping)
2067 );
2068 }
2069 }
2070
2071 #[test]
2072 fn chart_yaml_serializes_dependencies_axis_under_lifted_helm_chart_key_dependencies() {
2073 // Fail-before-pass-after drift-detection pin on the top-level
2074 // per-chart dependency-list YAML axis-key at [`ChartYaml`]'s
2075 // `dependencies` field. The Rust field name and the emitted
2076 // wire key coincide by default today (no `#[serde(rename)]`
2077 // attribute on the field, no `#[serde(rename_all = "…")]`
2078 // attribute on the struct — so serde emits `dependencies:`
2079 // verbatim as the top-level list-container YAML key). A future
2080 // hostile refactor could silently rebrand the wire key in
2081 // three shapes:
2082 //
2083 // - a rename of the Rust field itself (`pub dependencies:
2084 // Vec<ChartDependency>` → `pub deps: Vec<ChartDependency>`
2085 // / `pub chart_dependencies: …`), which serde would then
2086 // serialize as `deps:` / `chart_dependencies:` verbatim;
2087 // - an added `#[serde(rename_all = "camelCase")]` /
2088 // `"snake_case"` / `"kebab-case"` attribute on the struct
2089 // itself — a no-op on the four identity-mapped top-level
2090 // lowercase keys (`name` / `description` / `version` /
2091 // `dependencies`) today but silently activates on a future
2092 // multi-word field addition (e.g. an `icon_url` axis
2093 // matching Helm 3's per-chart `icon:` future-schema slot);
2094 // - an added `#[serde(rename = "deps")]` per-field override
2095 // at the site of the `dependencies` field.
2096 //
2097 // Under any of the three shapes Helm's chart-schema parser
2098 // silently drops the entire per-chart dep list from the
2099 // parsed chart-metadata (unknown top-level YAML keys silently
2100 // ignored per the Helm 3 chart-schema fallthrough), `helm
2101 // dependency build` finds no chart to vendor, and every
2102 // rendered `lareira-<nome>` chart's install fails with
2103 // `template: no template ... associated with template ...`
2104 // far from the drift site with no field naming the top-level-
2105 // list-key-drift root cause. This pin closes the drift by
2106 // round-tripping a rendered `Chart.yaml` through
2107 // `serde_yaml::from_str::<serde_yaml::Value>` and asserting
2108 // the top-level `Mapping::get(HELM_CHART_KEY_DEPENDENCIES)`
2109 // resolves — rather than through the [`ChartYaml`]-typed
2110 // deserializer that would silently absorb any of the three
2111 // drift shapes via `#[serde(default)]` fall-through at the
2112 // struct-side. Peer to
2113 // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
2114 // / [`chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`]
2115 // / [`chart_yaml_serializes_api_version_axis_under_lifted_helm_chart_key_api_version`]
2116 // on the sibling per-Chart.yaml top-level YAML axis-key
2117 // serialization pin surface (d29bc23, cc44e4b) — extends the
2118 // per-Chart.yaml top-level YAML axis-key production-emit pin
2119 // trio those closed onto the fourth top-level axis-key, the
2120 // parent list-container the already-lifted per-
2121 // `dependencies[]`-entry sub-mapping tetrad
2122 // [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
2123 // [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
2124 // [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
2125 // [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] mounts one level down.
2126 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
2127 let doc = parse_yaml_at_path(&dir.files, HELM_CHART_YAML_FILENAME);
2128 let mapping = doc.as_mapping().expect(
2129 "rendered Chart.yaml must be a top-level YAML mapping per \
2130 the Helm 3 chart-schema shape",
2131 );
2132 assert!(
2133 kube_has(mapping, HELM_CHART_KEY_DEPENDENCIES),
2134 "rendered Chart.yaml must carry a top-level \
2135 {HELM_CHART_KEY_DEPENDENCIES:?} axis-key — a drift on the \
2136 `ChartYaml.dependencies` field's serde field-name (a Rust-side \
2137 rename to `deps` / `chart_dependencies`, an added \
2138 `#[serde(rename_all)]` attribute on the enclosing struct, an \
2139 added `#[serde(rename)]` per-field override) silently reroutes \
2140 the per-chart dependency-list axis through an unrecognized \
2141 top-level YAML key (`deps:` / `chartDependencies:` / \
2142 `chart_dependencies:`), which Helm's chart-schema parser \
2143 silently drops from the parsed chart-metadata shape (every \
2144 rendered `lareira-<nome>` chart's install fails with \
2145 `template: no template ... associated with template ...` at \
2146 `helm dependency build` / `helm template` time far from the \
2147 drift site with no field naming the top-level-list-key-drift \
2148 root cause); the emitted top-level mapping keys are {keys:?}",
2149 keys = mapping_string_keys(mapping)
2150 );
2151 }
2152
2153 #[test]
2154 fn helm_chart_key_dependencies_re_export_points_at_caixa_core_canonical() {
2155 // The renderer's [`HELM_CHART_KEY_DEPENDENCIES`] was lifted onto
2156 // a re-export of [`caixa_core::HELM_CHART_KEY_DEPENDENCIES`] so
2157 // the Helm 3 per-Chart.yaml top-level dependency-list-container
2158 // YAML axis-key — the parent whose per-`dependencies[]`-entry
2159 // sub-mapping tetrad [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
2160 // [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
2161 // [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
2162 // [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] mounts one level down —
2163 // lives in exactly one place across every caixa renderer. Pin
2164 // the equality + `&'static` static-data identity here so any
2165 // local re-introduction of a sibling `pub const
2166 // HELM_CHART_KEY_DEPENDENCIES: &str = "…"` at this crate — the
2167 // canonical drift footgun where a sibling local `pub const`
2168 // could happen to carry the same string at the source while
2169 // pointing at a different `&'static` allocation — is a build-
2170 // time test failure naming the offending drift, not a silent
2171 // per-Chart.yaml top-level-list-key reroute at `helm dependency
2172 // build` / `helm lint` / `helm template` time far from the
2173 // drift site. Peer to
2174 // [`helm_chart_yaml_filename_re_export_points_at_caixa_core_canonical`]
2175 // and every other `helm_chart_*_re_export_points_at_caixa_core_canonical`
2176 // pin on the sibling canonical-Helm-chart-schema-body-axis
2177 // re-export surface — extends the per-Chart.yaml top-level
2178 // YAML axis-key re-export identity discipline onto the fourth
2179 // top-level axis-key at the caixa-helm surface.
2180 caixa_core::assert_str_reexport_identity(
2181 "HELM_CHART_KEY_DEPENDENCIES",
2182 HELM_CHART_KEY_DEPENDENCIES,
2183 caixa_core::HELM_CHART_KEY_DEPENDENCIES,
2184 );
2185 }
2186
2187 #[test]
2188 fn render_opts_default_library_name_follows_lifted_constant() {
2189 // [`RenderOpts::default()`] sets `library_name` from
2190 // [`DEFAULT_LIBRARY_NAME`]; pin that the lift preserves the
2191 // default-knob value bit-for-bit. A future refactor that
2192 // detaches `RenderOpts::default()` from the lifted constant —
2193 // accidentally re-introducing an inline `"pleme-computeunit"`
2194 // literal in the impl — would silently break the shared-shape
2195 // contract with caixa-flux (which uses the same constant
2196 // directly for its `helmrelease.yaml` wrap key); this test
2197 // surfaces the regression at build time rather than at
2198 // apply time as a silent values-routing no-op.
2199 let opts = RenderOpts::default();
2200 assert_eq!(opts.library_name, caixa_core::DEFAULT_LIBRARY_NAME);
2201 assert_eq!(opts.library_name, "pleme-computeunit");
2202 }
2203
2204 #[test]
2205 fn helm_chart_yaml_filename_re_export_points_at_caixa_core_canonical() {
2206 // The renderer's `HELM_CHART_YAML_FILENAME` was lifted from the
2207 // seven production + test-side inline `"Chart.yaml"` /
2208 // `PathBuf::from("Chart.yaml")` / `chart_root.join("Chart.yaml")`
2209 // literals across [`render_chart_for_servico`]'s `ChartDir`
2210 // metadata-file `path` emit site + every test-side round-trip
2211 // navigator that reaches into the rendered `ChartDir` by the
2212 // metadata filename to a re-export of
2213 // [`caixa_core::HELM_CHART_YAML_FILENAME`] so the Helm 3
2214 // per-chart-directory metadata-file filename lives in exactly one
2215 // place across every caixa renderer. Pin the equality +
2216 // `&'static` static-data identity here so any local
2217 // re-introduction of a sibling `pub const
2218 // HELM_CHART_YAML_FILENAME: &str = "…"` at this crate — the
2219 // canonical drift footgun where a sibling local `pub const` could
2220 // happen to carry the same string at the source while pointing
2221 // at a different `&'static` allocation — is a build-time test
2222 // failure naming the offending drift, not a silent
2223 // Helm-chart-schema-parser "Chart.yaml file is missing" reroute
2224 // at `helm dependency build` / `helm lint` / `helm template` /
2225 // `helm install` time far from the drift site. Peer to
2226 // [`helm_chart_api_version_re_export_points_at_caixa_core_canonical`]
2227 // / [`helm_chart_type_application_re_export_points_at_caixa_core_canonical`]
2228 // on the sibling canonical-Helm-chart-schema-body-axis re-export
2229 // surfaces — completes the per-`lareira-<nome>`-chart-directory
2230 // `(filename, apiVersion, type)` canonical-scalar-axis re-export
2231 // triple every rendered chart declares at its top-level metadata
2232 // file.
2233 caixa_core::assert_str_reexport_identity(
2234 "HELM_CHART_YAML_FILENAME",
2235 HELM_CHART_YAML_FILENAME,
2236 caixa_core::HELM_CHART_YAML_FILENAME,
2237 );
2238 }
2239
2240 #[test]
2241 fn helm_values_yaml_filename_re_export_points_at_caixa_core_canonical() {
2242 // The renderer's `HELM_VALUES_YAML_FILENAME` was lifted from the
2243 // twelve production + test-side inline `"values.yaml"` /
2244 // `PathBuf::from("values.yaml")` / `chart_root.join("values.yaml")`
2245 // literals across [`render_chart_for_servico`]'s `ChartDir`
2246 // values-file `path` emit site + every test-side round-trip
2247 // navigator that reaches into the rendered `ChartDir` by the
2248 // values filename to a re-export of
2249 // [`caixa_core::HELM_VALUES_YAML_FILENAME`] so the Helm 3
2250 // per-chart-directory values-file filename lives in exactly one
2251 // place across every caixa renderer. Pin the equality +
2252 // `&'static` static-data identity here so any local
2253 // re-introduction of a sibling `pub const
2254 // HELM_VALUES_YAML_FILENAME: &str = "…"` at this crate — the
2255 // canonical drift footgun where a sibling local `pub const` could
2256 // happen to carry the same string at the source while pointing
2257 // at a different `&'static` allocation — is a build-time test
2258 // failure naming the offending drift, not a silent
2259 // Helm-per-chart-values-loader fall-through to the empty values
2260 // block at `helm template` / `helm install` time far from the
2261 // drift site (where the workload silently comes up under the
2262 // library chart's admission-time defaults with no per-Servico
2263 // M2 overlay applied). Peer to
2264 // [`helm_chart_yaml_filename_re_export_points_at_caixa_core_canonical`]
2265 // on the sibling canonical-Helm-per-chart-directory-metadata-file-
2266 // axis re-export surface — completes the
2267 // per-`lareira-<nome>`-chart-directory `(Chart.yaml, values.yaml)`
2268 // canonical-per-chart-directory-filename-axis re-export pair
2269 // every rendered chart declares as its two schema-load-bearing
2270 // `ChartDir::files` entries.
2271 caixa_core::assert_str_reexport_identity(
2272 "HELM_VALUES_YAML_FILENAME",
2273 HELM_VALUES_YAML_FILENAME,
2274 caixa_core::HELM_VALUES_YAML_FILENAME,
2275 );
2276 }
2277
2278 #[test]
2279 fn helm_chart_readme_filename_re_export_points_at_caixa_core_canonical() {
2280 // The renderer's `HELM_CHART_README_FILENAME` was lifted from the
2281 // three production + test-side inline `"README.md"` literals
2282 // across [`render_chart_for_servico`]'s `ChartDir` readme-file
2283 // `path` emit site + every test-side round-trip navigator that
2284 // reaches into the rendered `ChartDir` by the readme filename
2285 // (the [`renders_three_files`] files-vec-membership pin + the
2286 // [`ChartDir::write_to`] post-write existence pin) to a
2287 // re-export of [`caixa_core::HELM_CHART_README_FILENAME`] so the
2288 // per-`lareira-<nome>` chart-directory human-facing readme
2289 // filename lives in exactly one place across every caixa
2290 // renderer. Pin the equality + `&'static` static-data identity
2291 // here so any local re-introduction of a sibling `pub const
2292 // HELM_CHART_README_FILENAME: &str = "…"` at this crate — the
2293 // canonical drift footgun where a sibling local `pub const`
2294 // could happen to carry the same string at the source while
2295 // pointing at a different `&'static` allocation — is a build-
2296 // time test failure naming the offending drift, not a silent
2297 // GitHub / Artifact Hub / any per-chart README-surfacing UI
2298 // fall-through to "no README available" at chart-consumption
2299 // time far from the drift site. Peer to
2300 // [`helm_chart_yaml_filename_re_export_points_at_caixa_core_canonical`]
2301 // / [`helm_values_yaml_filename_re_export_points_at_caixa_core_canonical`]
2302 // on the sibling canonical-Helm-per-chart-directory-filename
2303 // axis re-export surfaces — completes the
2304 // per-`lareira-<nome>`-chart-directory `(Chart.yaml,
2305 // values.yaml, README.md)` canonical-per-chart-directory-
2306 // filename-axis re-export triple every rendered chart declares
2307 // as its three `ChartDir::files` entries.
2308 caixa_core::assert_str_reexport_identity(
2309 "HELM_CHART_README_FILENAME",
2310 HELM_CHART_README_FILENAME,
2311 caixa_core::HELM_CHART_README_FILENAME,
2312 );
2313 }
2314
2315 #[test]
2316 fn helm_values_key_enabled_re_export_points_at_caixa_core_canonical() {
2317 // The renderer's `HELM_VALUES_KEY_ENABLED` was lifted from the
2318 // production-code inline `"enabled".to_string()` literal at
2319 // [`build_values_yaml`]'s
2320 // `block.insert("enabled".to_string(), Value::Bool(…))` values-
2321 // block-toggle insert (formerly `caixa-helm/src/lib.rs:389`) plus
2322 // its two test-side round-trip navigators
2323 // (`values_yaml_wraps_under_pleme_computeunit_key`,
2324 // `values_yaml_wrap_key_follows_library_name_override`) to a
2325 // re-export of [`caixa_core::HELM_VALUES_KEY_ENABLED`] so the
2326 // canonical `pleme-computeunit` library-chart values-block
2327 // enable-toggle key lives in exactly one place across every
2328 // caixa renderer. Pin the equality + `&'static` static-data
2329 // identity here so any local re-introduction of a sibling
2330 // `pub const HELM_VALUES_KEY_ENABLED: &str = "…"` at this crate
2331 // — the canonical drift footgun where a sibling local
2332 // `pub const` could happen to carry the same string at the
2333 // source while pointing at a different `&'static` allocation —
2334 // is a build-time test failure naming the offending drift, not
2335 // a silent per-values enable-toggle reroute at `helm template` /
2336 // `helm install` time far from the drift site (where the
2337 // workload silently comes up with the library chart's
2338 // admission-time defaults instead of the per-cluster override
2339 // the operator set). Peer to
2340 // [`helm_chart_api_version_re_export_points_at_caixa_core_canonical`]
2341 // / [`kube_key_spec_re_export_points_at_caixa_core_canonical`] /
2342 // [`default_library_name_re_export_points_at_caixa_core_canonical`]
2343 // on the sibling re-export axes +
2344 // `caixa_flux::tests::helm_values_key_enabled_re_export_points_at_caixa_core_canonical`
2345 // on the peer bundle-path renderer crate.
2346 caixa_core::assert_str_reexport_identity(
2347 "HELM_VALUES_KEY_ENABLED",
2348 HELM_VALUES_KEY_ENABLED,
2349 caixa_core::HELM_VALUES_KEY_ENABLED,
2350 );
2351 }
2352
2353 #[test]
2354 fn values_yaml_enable_toggle_key_pins_lifted_helm_values_key_enabled() {
2355 // Fail-before-pass-after pin on the production-code substitution:
2356 // [`build_values_yaml`]'s `block.insert(…, Value::Bool(…))`
2357 // consults the lifted [`HELM_VALUES_KEY_ENABLED`] re-export at
2358 // its insert site, so the rendered `values.yaml`'s per-values
2359 // enable-toggle axis is byte-identical to the canonical constant
2360 // by construction. Before the lift the field carried an inline
2361 // `"enabled".to_string()` literal; a future refactor that
2362 // accidentally reverted the substitution — or any parallel per-
2363 // renderer variant that inlined a stale `"enable"` /
2364 // `"disabled"` literal — would silently emit a values block
2365 // whose per-values enable-toggle lands under one key while
2366 // [`caixa_flux::cluster_bundle`]'s `HelmRelease`
2367 // `spec.values.<library>.enabled` per-cluster override lands
2368 // under another, so this pin trips at caixa-helm build time.
2369 // Peer to `chart_yaml_uses_lifted_helm_chart_api_version` on the
2370 // sibling structural-cross-axis-invariant surface — both close
2371 // the drift between a rendered-value navigator's `.get(…)` /
2372 // struct-field read on the constant and the production-code
2373 // emit site that consumes the same constant.
2374 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
2375 let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
2376 let cu_block = parsed
2377 .get(DEFAULT_LIBRARY_NAME)
2378 .expect("must wrap under DEFAULT_LIBRARY_NAME");
2379 assert_eq!(
2380 cu_block.get(HELM_VALUES_KEY_ENABLED),
2381 Some(&serde_yaml::Value::Bool(false)),
2382 "rendered values.yaml `{DEFAULT_LIBRARY_NAME}.{HELM_VALUES_KEY_ENABLED}` must \
2383 equal the default-off toggle the lifted HELM_VALUES_KEY_ENABLED axis carries — \
2384 a drifted enable-toggle key silently splits the per-values enable-flip across \
2385 two sibling scalar names on the caixa-helm / caixa-flux consumer split"
2386 );
2387 }
2388
2389 #[test]
2390 fn computeunit_spec_key_module_re_export_points_at_caixa_core_canonical() {
2391 // The renderer's `COMPUTEUNIT_SPEC_KEY_MODULE` was lifted from
2392 // the two inline `"module"` test-side call sites in this crate
2393 // (`values_yaml_wraps_under_pleme_computeunit_key`'s per-values
2394 // module-block present-check + the peer navigator on the
2395 // `library_name`-override wrap-key axis
2396 // `values_yaml_wrap_key_follows_library_name_override`) — every
2397 // per-Servico ComputeUnit CRD `spec.module` sub-block readback
2398 // in this crate now navigates through the same `&'static str`
2399 // re-exported to a re-export of
2400 // [`caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE`] so the canonical
2401 // ComputeUnit-CRD per-`spec.*` wasm-module-reference axis lives
2402 // in exactly one place across every caixa renderer. Pin the
2403 // equality + static-data identity here so any local re-
2404 // introduction of a sibling `pub const COMPUTEUNIT_SPEC_KEY_MODULE:
2405 // &str = "…"` at this crate is a build-time test failure naming
2406 // the offending drift, not a silent per-Servico wasm-runtime-
2407 // binding drop at cluster-apply time. Peer to
2408 // [`helm_values_key_enabled_re_export_points_at_caixa_core_canonical`]
2409 // /
2410 // [`kube_key_spec_re_export_points_at_caixa_core_canonical`]
2411 // on the sibling canonical-Helm-load-bearing-string /
2412 // canonical-K8s-CR-body-key re-export axes +
2413 // `caixa_flux::tests::computeunit_spec_key_module_re_export_points_at_caixa_core_canonical`
2414 // on the peer per-Servico renderer crate.
2415 caixa_core::assert_str_reexport_identity(
2416 "COMPUTEUNIT_SPEC_KEY_MODULE",
2417 COMPUTEUNIT_SPEC_KEY_MODULE,
2418 caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE,
2419 );
2420 }
2421
2422 #[test]
2423 fn computeunit_spec_key_trigger_re_export_points_at_caixa_core_canonical() {
2424 // Peer to
2425 // [`computeunit_spec_key_module_re_export_points_at_caixa_core_canonical`]
2426 // on the same ComputeUnit-CRD per-`spec.*` sub-block re-export
2427 // surface — pins the per-Servico invocation-shape sub-block
2428 // key's identity on the same trajectory.
2429 caixa_core::assert_str_reexport_identity(
2430 "COMPUTEUNIT_SPEC_KEY_TRIGGER",
2431 COMPUTEUNIT_SPEC_KEY_TRIGGER,
2432 caixa_core::COMPUTEUNIT_SPEC_KEY_TRIGGER,
2433 );
2434 }
2435
2436 #[test]
2437 fn computeunit_spec_key_capabilities_re_export_points_at_caixa_core_canonical() {
2438 // Peer to
2439 // [`computeunit_spec_key_module_re_export_points_at_caixa_core_canonical`]
2440 // and
2441 // [`computeunit_spec_key_trigger_re_export_points_at_caixa_core_canonical`]
2442 // on the same ComputeUnit-CRD per-`spec.*` sub-block re-export
2443 // surface — completes the substrate-side ComputeUnit-CRD
2444 // per-`spec.*` sub-block re-export triple in this crate on the
2445 // WASI-capability-token-list axis.
2446 caixa_core::assert_str_reexport_identity(
2447 "COMPUTEUNIT_SPEC_KEY_CAPABILITIES",
2448 COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
2449 caixa_core::COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
2450 );
2451 }
2452
2453 #[test]
2454 fn chart_file_alias_resolves_to_caixa_core_rendered_file() {
2455 // Type-alias identity pin: the [`ChartFile`] alias at this
2456 // crate's boundary resolves to the canonical
2457 // [`caixa_core::RenderedFile`] the substrate-side "one rendered
2458 // leaf artifact" shape lives at. `let _: ChartFile = <a
2459 // RenderedFile>` type-checks *iff* [`ChartFile`] is the aliased
2460 // canonical (not a sibling pub-struct re-declaration that
2461 // happens to carry the same field pair — that would compile
2462 // past the struct-literal navigators below but fail this
2463 // assignment). A drifted local `pub struct ChartFile { pub
2464 // path: PathBuf, pub contents: String }` at this crate — the
2465 // canonical drift footgun that would carry the same field pair
2466 // at the source while pointing at a different struct
2467 // definition — trips this pin at caixa-helm build time rather
2468 // than surfacing as a downstream `caixa_core::RenderedFile`
2469 // consumer refusing a `ChartFile`-shaped value at type-check
2470 // time far from the drift commit. Peer to the sibling
2471 // [`caixa_flux::BundleFile`]-alias-identity pin on the same
2472 // per-target-renderer canonical [`caixa_core::RenderedFile`]
2473 // re-export surface — both crates' per-artifact leaf type now
2474 // resolves through the same canonical struct definition, so a
2475 // future rebrand on the record shape lands at one caixa-core
2476 // edit and reaches both consumers by construction.
2477 let canonical: caixa_core::RenderedFile = caixa_core::RenderedFile {
2478 path: PathBuf::from(HELM_CHART_YAML_FILENAME),
2479 contents: String::new(),
2480 };
2481 let aliased: ChartFile = canonical.clone();
2482 assert_eq!(aliased, canonical);
2483 // Struct-literal construction still resolves through the alias
2484 // — the pre-lift `ChartFile { path, contents }` shape at every
2485 // production emit site (three sites in
2486 // `render_chart_for_servico_with`'s `ChartDir::files` assembly)
2487 // continues to compile, and the derive tuple travels through
2488 // the alias so downstream `ChartDir::files.iter().find(|f|
2489 // f.path == PathBuf::from(HELM_VALUES_YAML_FILENAME))`
2490 // navigators keep matching by `PartialEq` on `PathBuf`.
2491 let via_alias = ChartFile {
2492 path: PathBuf::from(HELM_VALUES_YAML_FILENAME),
2493 contents: format!("{DEFAULT_LIBRARY_NAME}:\n enabled: false\n"),
2494 };
2495 assert_eq!(via_alias.path.to_string_lossy(), HELM_VALUES_YAML_FILENAME);
2496 }
2497
2498 #[test]
2499 fn chart_file_new_constructor_travels_through_alias_to_canonical() {
2500 // Inherent-method-through-alias pin: the canonical
2501 // [`caixa_core::RenderedFile::new`] `impl Into<PathBuf>` /
2502 // `impl Into<String>` constructor every per-artifact leaf in
2503 // [`render_chart_for_servico_with`] now routes through
2504 // resolves at `ChartFile::new(…)` — Rust inherent methods
2505 // travel through a `pub type ChartFile = caixa_core::RenderedFile`
2506 // alias to the aliased canonical at name resolution, so a
2507 // drifted local `pub struct ChartFile { pub path: PathBuf, pub
2508 // contents: String }` at this crate would carry the field
2509 // pair the sibling type-alias-identity pin above still
2510 // accepts (both records share `pub path` / `pub contents`
2511 // shape) while dropping the constructor — the six sweep sites
2512 // in [`render_chart_for_servico_with`] would stop compiling
2513 // and the failing calls would name `ChartFile` directly,
2514 // making the drift-source unambiguous. This test pins the
2515 // constructor's per-alias reachability + the byte-identical
2516 // record shape against a `HELM_CHART_YAML_FILENAME`-keyed
2517 // probe so the pin fires at caixa-helm build time.
2518 let via_alias_new: ChartFile = ChartFile::new(HELM_CHART_YAML_FILENAME, "apiVersion: v2\n");
2519 let via_canonical_new = caixa_core::RenderedFile::new(
2520 HELM_CHART_YAML_FILENAME,
2521 String::from("apiVersion: v2\n"),
2522 );
2523 assert_eq!(via_alias_new, via_canonical_new);
2524 assert_eq!(via_alias_new.path, PathBuf::from(HELM_CHART_YAML_FILENAME));
2525 assert_eq!(via_alias_new.contents, "apiVersion: v2\n");
2526 }
2527
2528 #[test]
2529 fn render_opts_default_library_version_follows_lifted_constant() {
2530 // Peer of [`render_opts_default_library_name_follows_lifted_constant`]
2531 // (which pins the same alignment on the sibling
2532 // [`RenderOpts::library_name`] / [`DEFAULT_LIBRARY_NAME`] axis). The
2533 // [`RenderOpts::default()`] impl sets `library_version` from
2534 // [`DEFAULT_LIBRARY_VERSION`]; a future refactor that detached the
2535 // default-knob from the lifted constant — accidentally re-inlining
2536 // `"~0.1.0"` in the impl body — would silently split the value the
2537 // default knob threads into every rendered `Chart.yaml`
2538 // `dependencies[0].version` axis from the const the const's callers
2539 // (and this crate's future per-`DEFAULT_LIBRARY_VERSION` drift pins)
2540 // read. The two `Chart.yaml`-dep `(name, version)` scalar-axes now
2541 // share the same "default-knob follows lifted constant, byte for
2542 // byte" pin discipline the peer library-name axis carries.
2543 let opts = RenderOpts::default();
2544 assert_eq!(opts.library_version, DEFAULT_LIBRARY_VERSION);
2545 assert_eq!(opts.library_version, "~0.1.0");
2546 }
2547
2548 #[test]
2549 fn render_opts_default_enabled_default_follows_lifted_constant() {
2550 // Peer of [`render_opts_default_library_name_follows_lifted_constant`]
2551 // /
2552 // [`render_opts_default_library_version_follows_lifted_constant`]
2553 // / [`render_opts_default_library_repo_follows_lifted_constant`] —
2554 // the fourth leg of the [`RenderOpts::default()`]-body
2555 // default-knob-follows-lifted-constant quartet. The
2556 // [`RenderOpts::default()`] impl seeds `enabled_default` from
2557 // [`STANDALONE_LAREIRA_ENABLED_DEFAULT`]; every rendered
2558 // `lareira-<nome>` chart's `values.yaml` under-`<library>.enabled`
2559 // scalar reads through this knob, so a future refactor that
2560 // detached the default-knob from the lifted constant —
2561 // accidentally re-inlining `false` in the impl body — would
2562 // silently split the `bool` the default seed writes from the
2563 // const the drift-detection pin
2564 // [`standalone_lareira_enabled_default_pins_canonical_value`]
2565 // (in caixa-core) reads. The rendered values block would then
2566 // carry one `bool` at the emit site while the const-consuming
2567 // sibling test-fixture navigators (this crate's future per-
2568 // `STANDALONE_LAREIRA_ENABLED_DEFAULT` drift pins) read another,
2569 // and the substrate's chosen mirror-symmetric standalone /
2570 // composition per-values-block child-chart-enablement-toggle-
2571 // scalar-value default pair would silently disagree on the
2572 // standalone-path half. Peer with the sibling
2573 // `caixa_flux::tests::cluster_bundle_lareira_enabled_default_re_export_matches_caixa_core_canonical_value`
2574 // pin on the composition-path half of the same
2575 // [`HELM_VALUES_KEY_ENABLED`] scalar-axis pair.
2576 let opts = RenderOpts::default();
2577 assert_eq!(opts.enabled_default, STANDALONE_LAREIRA_ENABLED_DEFAULT);
2578 assert!(!opts.enabled_default);
2579 }
2580
2581 #[test]
2582 fn standalone_lareira_enabled_default_re_export_matches_caixa_core_canonical_value() {
2583 // The renderer's `STANDALONE_LAREIRA_ENABLED_DEFAULT` was lifted
2584 // from the [`RenderOpts::default()`] impl-body inline `false`
2585 // scalar-value literal at `caixa-helm/src/lib.rs:700` to a
2586 // re-export of [`caixa_core::STANDALONE_LAREIRA_ENABLED_DEFAULT`]
2587 // so the substrate-side default the standalone per-chart path
2588 // seeds under the sibling [`HELM_VALUES_KEY_ENABLED`]
2589 // leaf-scalar-key lives in exactly one place across every caixa
2590 // renderer (this crate's standalone per-chart path + the peer
2591 // `caixa_flux::cluster_bundle`'s composition per-cluster-
2592 // `HelmRelease` values-overlay path, which reads through the
2593 // inverse [`caixa_flux::CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`]
2594 // re-export). Pin the equality here so any local re-introduction
2595 // of a sibling `pub const STANDALONE_LAREIRA_ENABLED_DEFAULT:
2596 // bool = …` at this crate (the canonical drift footgun the peer
2597 // `CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT` re-export identity
2598 // pin's rationale names as the recurring shape) is a build-time
2599 // test failure naming the offending drift, not a silent
2600 // apply-time toggle-mismatch routing the standalone per-chart
2601 // `values.<library>.enabled` seed onto one substrate-side
2602 // opt-out convention while the peer composition-path override
2603 // routes onto another. Peer to the sibling
2604 // `caixa_flux::tests::cluster_bundle_lareira_enabled_default_re_export_matches_caixa_core_canonical_value`
2605 // on the composition-path half of the same
2606 // [`HELM_VALUES_KEY_ENABLED`] scalar-axis pair — the two
2607 // per-path re-export identity pins together lock the two peer
2608 // scalar-value defaults' per-crate re-exports onto their shared
2609 // caixa-core canonical.
2610 assert_eq!(
2611 STANDALONE_LAREIRA_ENABLED_DEFAULT,
2612 caixa_core::STANDALONE_LAREIRA_ENABLED_DEFAULT,
2613 "STANDALONE_LAREIRA_ENABLED_DEFAULT re-export must remain the \
2614 same `bool` as its caixa-core canonical — a drifted local \
2615 `pub const STANDALONE_LAREIRA_ENABLED_DEFAULT: bool = …` at \
2616 caixa-helm would silently split the substrate's chosen \
2617 standalone per-chart opt-out seed from the peer \
2618 composition-path force-on inversion the caixa-core canonical \
2619 encodes."
2620 );
2621 assert!(
2622 !STANDALONE_LAREIRA_ENABLED_DEFAULT,
2623 "STANDALONE_LAREIRA_ENABLED_DEFAULT must remain `false` — the \
2624 standalone per-chart path is the substrate-side opt-out path \
2625 where cluster operators must opt each caixa in per-cluster, \
2626 inverse of the composition per-cluster-HelmRelease values-\
2627 overlay path's opt-in force-on."
2628 );
2629 }
2630
2631 #[test]
2632 fn render_opts_default_library_repo_follows_lifted_constant() {
2633 // Peer of [`render_opts_default_library_name_follows_lifted_constant`]
2634 // /
2635 // [`render_opts_default_library_version_follows_lifted_constant`] —
2636 // the third leg of the per-`Chart.yaml`-dep
2637 // `(repository, name, version)` default-knob triple. The
2638 // [`RenderOpts::default()`] impl seeds `library_repo` from
2639 // [`DEFAULT_LIBRARY_REPO`]; every rendered `lareira-<nome>` chart's
2640 // `Chart.yaml` `dependencies[0].repository` field reads through
2641 // this knob, so a future refactor that detached the default-knob
2642 // from the lifted constant — re-inlining
2643 // `"file://../pleme-computeunit"` in the impl body — would silently
2644 // split the URL the default seed writes from the const the
2645 // drift-detection pin below
2646 // ([`default_library_repo_ends_with_lifted_default_library_name`])
2647 // reads.
2648 let opts = RenderOpts::default();
2649 assert_eq!(opts.library_repo, DEFAULT_LIBRARY_REPO);
2650 assert_eq!(opts.library_repo, "file://../pleme-computeunit");
2651 }
2652
2653 #[test]
2654 fn default_library_version_parses_as_valid_semver_requirement() {
2655 // Structural pin: [`DEFAULT_LIBRARY_VERSION`] carries a Cargo-shaped
2656 // semver-requirement string that lands verbatim in every rendered
2657 // `lareira-<nome>` chart's `Chart.yaml` `dependencies[0].version`
2658 // field. Helm 3's chart-schema parser (`helm dependency build`,
2659 // `helm lint`, `helm template`, `helm install`) validates the
2660 // scalar against the same `semver::VersionReq` grammar
2661 // [`caixa_core::parse_requirement`] wraps, and rejects a malformed
2662 // shape (`"~0.1.,0"` — paste-from-typography stray comma;
2663 // `"v0.1.0"` — accidental Zig-style publish-tag prefix leaking back
2664 // from [`caixa_core::DEFAULT_PUBLISH_TAG_PREFIX`] into the
2665 // requirement axis; `"0.1"` with a trailing sigil dropped by a
2666 // fat-fingered edit) with the load-bearing `Error: found operator
2667 // …, expected version` diagnostic surfacing at chart-consumption
2668 // time — far from the constant-drift commit's source, with no
2669 // field naming the offending caixa or the drifted default. Routing
2670 // through [`caixa_core::parse_requirement`] here — the same
2671 // requirement-parser entry-point every peer typed `:versao`
2672 // requirement slot (`:deps`, `:deps-dev`, `:membros`, `:children`)
2673 // routes through via
2674 // [`caixa_core::require_valid_versao_requirement`] — closes the
2675 // drift structurally at caixa-helm build time and pins the const's
2676 // accepted set to exactly the set the peer author-facing
2677 // requirement axes accept: any shape a caixa author cannot write
2678 // in `:deps :versao` is a shape the substrate cannot seed as the
2679 // library-chart-dep default. Peer of the sibling
2680 // [`default_library_repo_ends_with_lifted_default_library_name`]
2681 // structural pin on the co-resident `(name, version)` per-Chart.yaml
2682 // dep-scalar pair.
2683 caixa_core::parse_requirement(DEFAULT_LIBRARY_VERSION).unwrap_or_else(|e| {
2684 panic!(
2685 "DEFAULT_LIBRARY_VERSION {DEFAULT_LIBRARY_VERSION:?} must parse as a valid \
2686 semver::VersionReq — every rendered lareira-<nome> chart's Chart.yaml \
2687 dependencies[0].version axis lands this scalar verbatim, and Helm 3's \
2688 chart-schema parser rejects a malformed shape at chart-consumption time \
2689 far from the constant-drift commit's source: {e}",
2690 )
2691 });
2692 }
2693
2694 #[test]
2695 fn default_library_repo_ends_with_lifted_default_library_name() {
2696 // Structural cross-const coherence pin: [`DEFAULT_LIBRARY_REPO`]
2697 // embeds the [`DEFAULT_LIBRARY_NAME`] byte-string verbatim as its
2698 // trailing directory-name component (the canonical
2699 // `file://../<library-chart-name>` shape every sibling
2700 // `lareira-<nome>` chart's `Chart.yaml` `dependencies[0]` entry
2701 // consults for a two-axis `(name, repository)` per-dep tuple that
2702 // Helm's per-chart-dep resolver `(chart-source-scheme + chart-name)`
2703 // navigator round-trips). The two axes must stay coupled: the
2704 // library-chart-directory on disk (the repo's trailing component)
2705 // and the library-chart's declared `name:` in its own
2706 // [`DEFAULT_LIBRARY_NAME`]-published `Chart.yaml` are the same
2707 // load-bearing chart-name identity. Prior to this pin the two
2708 // consts were independently authored — a future substrate-side
2709 // library-chart rebrand (`pleme-computeunit` → `pleme-cu` on a
2710 // shorter-form migration, `pleme-computeunit` →
2711 // `caixa-computeunit` on a substrate-alignment migration, a
2712 // per-edition library-chart fork the [`DEFAULT_LIBRARY_NAME`]
2713 // docstring names as a trajectory item) on the
2714 // [`caixa_core::DEFAULT_LIBRARY_NAME`] canonical without a
2715 // coordinated edit on this crate's [`DEFAULT_LIBRARY_REPO`] would
2716 // silently emit rendered `Chart.yaml` documents whose
2717 // `dependencies[0].name` names the new chart while
2718 // `dependencies[0].repository` points at the old directory —
2719 // `helm dependency build` would refuse to resolve the dep ("chart
2720 // <new-name> not found in file://../<old-name>") at chart-
2721 // consumption time, far from the constant-rebrand commit's source,
2722 // with no field naming the two-axis coherence drift root cause.
2723 // Pinning the structural `ends_with(DEFAULT_LIBRARY_NAME)` invariant
2724 // here surfaces the drift as a caixa-helm build-time test failure
2725 // and forces the coordinated `(REPO, NAME)` edit to move together.
2726 // Peer of the sibling
2727 // [`default_library_version_parses_as_valid_semver_requirement`]
2728 // structural pin on the co-resident `(name, version)` per-Chart.yaml
2729 // dep-scalar pair — completes the `(repository, name, version)`
2730 // per-Chart.yaml-dep default-triple's structural pin surface.
2731 assert!(
2732 DEFAULT_LIBRARY_REPO.ends_with(DEFAULT_LIBRARY_NAME),
2733 "DEFAULT_LIBRARY_REPO {DEFAULT_LIBRARY_REPO:?} must terminate with the lifted \
2734 DEFAULT_LIBRARY_NAME {DEFAULT_LIBRARY_NAME:?} — the two-axis (repository, name) \
2735 per-Chart.yaml-dep tuple must resolve to the same library-chart identity on \
2736 disk, so a rebrand on either axis must move both",
2737 );
2738 }
2739
2740 #[test]
2741 fn chart_yaml_version_routes_through_caixa_versao_accessor() {
2742 // Fail-before-pass-after pin: the emit-side per-`Chart.yaml`
2743 // top-level `version:` scalar the [`build_chart_yaml`] fn
2744 // writes must derive from the typed
2745 // [`caixa_core::Caixa::versao`] accessor byte-for-byte.
2746 // Before this converge the emit site carried a raw
2747 // `caixa.versao.clone()` field access at
2748 // [`build_chart_yaml`]'s per-`Chart.yaml` version-field
2749 // insert position — one of the two production-code
2750 // `String`-carry sites of `Caixa::versao` on this fn's
2751 // emit path — and a future extension of the accessor
2752 // (a build-metadata canonicalization pass the CAIXA-SDLC
2753 // §I SemVer-2 pin acknowledges, an OCI-tag normalization
2754 // the M4 registry-alignment slot lands, a per-edition
2755 // pre-release-tag overlay the sibling `Caixa::edicao`
2756 // universal-axis 4-digit-ASCII-decimal-year scalar
2757 // dispatches through) that landed on the accessor but
2758 // not on this emit site would silently split the
2759 // per-`Chart.yaml` `version:` axis (the discriminator
2760 // Helm's per-chart resolver keys per-release
2761 // reconciliation off, the paired `HelmRelease`
2762 // `spec.chart.spec.version` binds through, and every
2763 // `helm template <chart>` / `helm install <release>
2764 // <chart>` / `helm upgrade <release> <chart>
2765 // --version` invocation names through) from every peer
2766 // read-side consumer of `Caixa::versao` (the
2767 // README-body `v{versao}` scalar at
2768 // [`build_readme`]:958 the paired `feira chart`
2769 // Nord-themed emit round-trips through, every peer
2770 // per-axis navigator via `Caixa::versao()`, the
2771 // `caixa_flux::programs_yaml_entry` per-entry
2772 // `versao:` scalar at caixa-flux/src/lib.rs:2031, the
2773 // `caixa_feira::cmd::publish` per-tag `caixa {nome} v{versao}`
2774 // git-tag scalar at caixa-feira/src/cmd/publish.rs:72).
2775 // Byte-equal today (the accessor is `&self.versao`);
2776 // the pin catches any future accessor extension whose
2777 // emit-side write regresses to the raw field. Peer to
2778 // [`chart_yaml_app_version_routes_through_caixa_versao_accessor`]
2779 // on the sibling per-`Chart.yaml` `appVersion:` axis.
2780 let caixa = sample_caixa();
2781 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2782 let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
2783 assert_eq!(
2784 chart.version.as_str(),
2785 caixa.versao(),
2786 "Chart.yaml `version:` must derive from the typed \
2787 `caixa_core::Caixa::versao` accessor byte-for-byte — a regression \
2788 that re-inlines `caixa.versao.clone()` at the emit site silently \
2789 splits the per-`Chart.yaml` `version:` axis from every future \
2790 accessor extension (SemVer-2 build-metadata canonicalization, \
2791 OCI-tag normalization, per-edition pre-release-tag overlay) that \
2792 lands on the accessor",
2793 );
2794 }
2795
2796 #[test]
2797 fn chart_yaml_app_version_routes_through_caixa_versao_accessor() {
2798 // Fail-before-pass-after pin: the emit-side per-`Chart.yaml`
2799 // top-level `appVersion:` scalar the [`build_chart_yaml`] fn
2800 // writes must derive from the typed
2801 // [`caixa_core::Caixa::versao`] accessor byte-for-byte.
2802 // Same single-source `let versao = caixa.versao().to_string()`
2803 // binding as the peer `version:` sibling pin — this test
2804 // pins the derived `Chart.yaml` `appVersion:` axis (the
2805 // axis Helm chart-consumers key per-application-version
2806 // documentation / release-note / OCI-tag / operator-side
2807 // per-Caixa CR revision off). Peer to
2808 // [`chart_yaml_version_routes_through_caixa_versao_accessor`]
2809 // on the sibling per-`Chart.yaml` `version:` axis — the
2810 // two together pin every per-`Chart.yaml` version-carrier
2811 // field on the typed accessor.
2812 let caixa = sample_caixa();
2813 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2814 let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
2815 assert_eq!(
2816 chart.app_version.as_str(),
2817 caixa.versao(),
2818 "Chart.yaml `appVersion:` must derive from the typed \
2819 `caixa_core::Caixa::versao` accessor byte-for-byte — a regression \
2820 that re-inlines `caixa.versao.clone()` at the emit site silently \
2821 splits the per-`Chart.yaml` `appVersion:` axis from every future \
2822 accessor extension (SemVer-2 build-metadata canonicalization, \
2823 OCI-tag normalization, per-edition pre-release-tag overlay) that \
2824 lands on the accessor",
2825 );
2826 }
2827
2828 #[test]
2829 fn chart_yaml_name_routes_through_caixa_nome_accessor() {
2830 // Emit-path pin: the per-`Chart.yaml` top-level `name:`
2831 // scalar the [`build_chart_yaml`] fn writes must derive
2832 // from the typed [`caixa_core::Caixa::nome`] accessor
2833 // byte-for-byte through the substrate-canonical
2834 // [`caixa_core::lareira_chart_name`] identity composer.
2835 // Before this converge the outer `lareira_chart_name(&caixa.nome)`
2836 // call at [`render_chart_for_servico_with`] carried a raw
2837 // `&caixa.nome` borrow-then-deref of the underlying `String`
2838 // field, bypassing the typed accessor. Peer of the sibling
2839 // eb912de `caixa.versao().to_string()` converge on the
2840 // co-resident `Caixa::versao` `String`-carry axis in this
2841 // crate and the sibling 4a363bf / 54bf2f3 `caixa.nome().to_string()`
2842 // converges on the outer-Caixa `:nome` `String`-carry axis
2843 // in caixa-flux / caixa-mesh — extends the "one typed
2844 // dispatch on the substrate primitive, thin projections at
2845 // each consumer" discipline onto the non-`.clone()` raw-
2846 // field-access axis of `Caixa::nome` in caixa-helm. Byte-
2847 // equal today (the accessor is `&self.nome`); the pin
2848 // catches any future accessor extension (a per-cluster
2849 // alias overlay, an M4 CR-materializer name rewrite, a
2850 // future `:nome-suffix` slot) whose emit-side write
2851 // regresses to the raw `&caixa.nome` field access.
2852 let caixa = sample_caixa();
2853 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2854 let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
2855 assert_eq!(
2856 chart.name,
2857 caixa_core::lareira_chart_name(caixa.nome()),
2858 "Chart.yaml `name:` must derive from the typed \
2859 `caixa_core::Caixa::nome` accessor through \
2860 `caixa_core::lareira_chart_name` byte-for-byte — a regression \
2861 that re-inlines `lareira_chart_name(&caixa.nome)` at the emit \
2862 site silently splits the per-`Chart.yaml` `name:` axis from \
2863 every future accessor extension (per-cluster alias overlay, \
2864 M4 CR-materializer name rewrite, `:nome-suffix` slot) that \
2865 lands on the accessor",
2866 );
2867 }
2868
2869 #[test]
2870 fn chart_dir_name_routes_through_caixa_lareira_chart_name_accessor() {
2871 // Emit-path pin: the per-`ChartDir` top-level `name` axis the
2872 // [`render_chart_for_servico_with`] fn writes must derive from
2873 // the substrate-canonical
2874 // [`caixa_core::Caixa::lareira_chart_name`] resolved-chart-name
2875 // dispatch. Before this converge the outer `lareira_chart_name
2876 // (caixa.nome())` two-step compose at the emit site re-derived
2877 // the `LAREIRA_CHART_NAME_PREFIX + :nome` composition inline,
2878 // bypassing the substrate primitive's single-`&Caixa` dispatch.
2879 // Peer of the sibling [`Caixa::canonical_git_url`] (124f864) /
2880 // [`Caixa::publish_tag`] (07e05b8) resolved-composers'
2881 // paired-site convergence pins the sibling caixa-flux
2882 // [`cluster_bundle_opts_for_caixa_git_url_routes_through_canonical_git_url_accessor`]
2883 // + `_git_ref_routes_through_publish_tag_accessor` byte-parity
2884 // tests carry on the per-Servico-renderer emit surface. Byte-
2885 // equal today (the accessor is
2886 // `caixa_core::lareira_chart_name(self.nome())`); the pin
2887 // catches any future accessor extension (a per-cluster alias
2888 // overlay, an M4 CR-materializer name rewrite, a
2889 // `:nome-suffix` slot, the [`LAREIRA_CHART_NAME_PREFIX`]
2890 // rebrand once the chart-family scoping intent shifts) whose
2891 // emit-side write regresses to the two-step open-coded compose.
2892 let caixa = sample_caixa();
2893 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2894 assert_eq!(
2895 dir.name,
2896 caixa.lareira_chart_name(),
2897 "ChartDir.name must derive from the substrate-canonical \
2898 `caixa_core::Caixa::lareira_chart_name` accessor byte-for-\
2899 byte — a regression that re-inlines \
2900 `caixa_core::lareira_chart_name(caixa.nome())` at the emit \
2901 site silently splits the per-`ChartDir` `name` axis from \
2902 every future accessor extension (per-cluster alias overlay, \
2903 M4 CR-materializer name rewrite, `:nome-suffix` slot, \
2904 `LAREIRA_CHART_NAME_PREFIX` rebrand) that lands on the \
2905 accessor",
2906 );
2907 }
2908
2909 #[test]
2910 fn chart_yaml_description_fallback_routes_through_caixa_nome_accessor() {
2911 // Emit-path pin: on a `:descricao`-null caixa the
2912 // [`build_chart_yaml`] `description:` fallback substitutes
2913 // `format!("Generated chart for caixa Servico {}", caixa.nome())`,
2914 // which must derive its terminal identity byte-string from the
2915 // typed [`caixa_core::Caixa::nome`] accessor. Before this
2916 // converge the fallback carried a raw `caixa.nome` Display of
2917 // the underlying `String` field, bypassing the typed accessor.
2918 // Byte-equal today; the pin catches any future accessor
2919 // extension whose fallback emit regresses to the raw field.
2920 let caixa = Caixa {
2921 descricao: None,
2922 ..sample_caixa()
2923 };
2924 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2925 let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
2926 assert_eq!(
2927 chart.description,
2928 format!("Generated chart for caixa Servico {}", caixa.nome()),
2929 "Chart.yaml `description:` `:descricao`-null fallback must \
2930 derive from the typed `caixa_core::Caixa::nome` accessor \
2931 byte-for-byte — a regression that re-inlines \
2932 `format!(\"Generated chart for caixa Servico {{}}\", caixa.nome)` \
2933 at the emit site silently splits the per-`Chart.yaml` \
2934 `description:` axis from every future accessor extension \
2935 that lands on the accessor",
2936 );
2937 }
2938
2939 #[test]
2940 fn values_yaml_header_nome_routes_through_caixa_nome_accessor() {
2941 // Emit-path pin: the [`build_values_yaml`] `# Auto-generated
2942 // by caixa-helm from caixa.lisp + servicos/{nome}.computeunit.yaml.`
2943 // comment header carries the parent-caixa's `:nome` identity
2944 // byte-string verbatim through the typed
2945 // [`caixa_core::Caixa::nome`] accessor. Before this converge
2946 // the site carried a raw `nome = caixa.nome` Display of the
2947 // underlying `String` field, bypassing the typed accessor.
2948 // Byte-equal today; the pin catches any future accessor
2949 // extension whose header-emit regresses to the raw field.
2950 let caixa = sample_caixa();
2951 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2952 let values_file =
2953 find_file_by_path(&dir.files, HELM_VALUES_YAML_FILENAME).expect("values.yaml present");
2954 let expected = format!("servicos/{}.computeunit.yaml", caixa.nome());
2955 assert!(
2956 values_file.contents.contains(&expected),
2957 "values.yaml header comment must carry the typed \
2958 `caixa_core::Caixa::nome` accessor's byte-string \
2959 ({expected:?}) verbatim — a regression that re-inlines \
2960 `caixa.nome` in the header format silently splits the \
2961 values.yaml provenance-annotation axis from every future \
2962 accessor extension that lands on the accessor. \
2963 Full contents:\n{contents}",
2964 contents = values_file.contents,
2965 );
2966 }
2967
2968 #[test]
2969 fn readme_descricao_fallback_routes_through_caixa_nome_accessor() {
2970 // Emit-path pin: on a `:descricao`-null caixa the
2971 // [`build_readme`] descricao-line fallback substitutes
2972 // `format!("caixa Servico {}", caixa.nome())`, which must
2973 // derive its terminal identity byte-string from the typed
2974 // [`caixa_core::Caixa::nome`] accessor. Before this converge
2975 // the fallback carried a raw `caixa.nome` Display of the
2976 // underlying `String` field, bypassing the typed accessor.
2977 // Byte-equal today; the pin catches any future accessor
2978 // extension whose fallback emit regresses to the raw field.
2979 let caixa = Caixa {
2980 descricao: None,
2981 ..sample_caixa()
2982 };
2983 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2984 let readme_file =
2985 find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
2986 let expected = format!("caixa Servico {}", caixa.nome());
2987 assert!(
2988 readme_file.contents.contains(&expected),
2989 "README.md `:descricao`-null fallback must carry the typed \
2990 `caixa_core::Caixa::nome` accessor's byte-string ({expected:?}) \
2991 verbatim — a regression that re-inlines `format!(\"caixa \
2992 Servico {{}}\", caixa.nome)` at the emit site silently \
2993 splits the README fallback-descricao axis from every future \
2994 accessor extension. Full contents:\n{contents}",
2995 contents = readme_file.contents,
2996 );
2997 }
2998
2999 #[test]
3000 fn readme_body_version_routes_through_caixa_versao_accessor() {
3001 // Emit-path pin: the per-`README.md` `Origin` line the
3002 // [`build_readme`] fn writes carries the terminal
3003 // `v{versao}` scalar the `feira chart` Nord-themed emit
3004 // round-trips through — that scalar must derive from the
3005 // typed [`caixa_core::Caixa::versao`] accessor byte-for-byte.
3006 // Before this converge the emit site carried a raw
3007 // `caixa.versao` `Display` field-access, bypassing the
3008 // typed accessor. Sibling of the 162e2e2 (caixa-flux) /
3009 // 980c059 (caixa-mesh) / 22461ef (caixa-helm) `Caixa::nome`
3010 // Display-axis converges — this closes the co-resident
3011 // `Caixa::versao` Display-axis in caixa-helm the eb912de
3012 // `caixa.versao().to_string()` `String`-carry converge
3013 // left open on the read-only Display-borrow arm. Byte-equal
3014 // today (the accessor is `&self.versao`); the pin catches
3015 // any future accessor extension (SemVer-2 build-metadata
3016 // canonicalization, OCI-tag normalization, per-edition
3017 // pre-release-tag overlay) whose emit-side Display regresses
3018 // to the raw field.
3019 let caixa = sample_caixa();
3020 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3021 let readme_file =
3022 find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3023 let expected = format!("caixa.lisp` v{}.", caixa.versao());
3024 assert!(
3025 readme_file.contents.contains(&expected),
3026 "README.md `Origin`-line `v{{versao}}` scalar must derive from \
3027 the typed `caixa_core::Caixa::versao` accessor byte-for-byte \
3028 ({expected:?}) — a regression that re-inlines `caixa.versao` \
3029 in the format silently splits the README origin-line version \
3030 axis from every future accessor extension (SemVer-2 \
3031 build-metadata canonicalization, OCI-tag normalization, \
3032 per-edition pre-release-tag overlay) that lands on the \
3033 accessor. Full contents:\n{contents}",
3034 contents = readme_file.contents,
3035 );
3036 }
3037
3038 #[test]
3039 fn contains_key_bare_str_key_byte_equals_value_string_wrapped_form_across_swept_axis_keys() {
3040 // Per-crate fail-before-pass-after equivalence pin covering the
3041 // five test-side `assert!(mapping.contains_key(<KEY>))`
3042 // drift-detection sites that this commit swept off the
3043 // three-token `serde_yaml::Value::String(<KEY>.to_string())`
3044 // wrapped shape onto the shorter bare-`&str` form
3045 // ([`HELM_CHART_KEY_TYPE`] on the per-Chart.yaml top-level
3046 // chart-kind discriminator axis, [`HELM_CHART_KEY_APP_VERSION`]
3047 // on the underlying-application-version axis,
3048 // [`HELM_CHART_KEY_API_VERSION`] on the chart-schema-apiVersion
3049 // axis, the per-`dependencies[]`-entry tetrad iterator
3050 // ([`HELM_CHART_DEPENDENCY_KEY_NAME`] /
3051 // [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
3052 // [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
3053 // [`HELM_CHART_DEPENDENCY_KEY_ALIAS`]), and
3054 // [`HELM_CHART_KEY_DEPENDENCIES`] on the top-level
3055 // dependency-list container axis).
3056 //
3057 // The equivalence is a serde_yaml crate-level property (the
3058 // `impl Index for str` and `impl Index for Value` paths both
3059 // route through the same `HashLikeValue(&str)` bucket lookup)
3060 // pinned at the caixa-core-side sibling
3061 // `mapping_get_bare_str_key_byte_equals_value_string_wrapped_form`'s
3062 // trailing `contains_key` parity block for the generic axis
3063 // (0e84fb9). This pin lifts the same equivalence to the
3064 // caixa-helm-side lifted axis-key set every swept site keys
3065 // off, so a future `serde_yaml` upgrade whose `Index for str`
3066 // path diverges from `Index for Value` for the specific
3067 // key-string byte-shape at any of the seven swept keys is a
3068 // caixa-helm-build-time failure naming the offending axis-key,
3069 // not a silent regression that would let the swept
3070 // `assert!(_.contains_key(K))` shape probes drift past the
3071 // emitter's [`MappingExt::insert_str_key`]-promoted key.
3072 //
3073 // Peer to the caixa-core-side
3074 // `mapping_get_bare_str_key_byte_equals_value_string_wrapped_form`
3075 // pin (0e84fb9) on the generic `contains_key` parity axis —
3076 // the two together partition the equivalence-pin surface
3077 // exactly on the axis-key set specificity: caixa-core-side pin
3078 // covers the generic KUBE_KEY_KIND / KUBE_KEY_SPEC probe;
3079 // this caixa-helm-side pin covers the seven Chart.yaml-shape
3080 // axis-keys the swept sites key off.
3081 let mut m = serde_yaml::Mapping::new();
3082 m.insert_string(HELM_CHART_KEY_TYPE, "application");
3083 m.insert_string(HELM_CHART_KEY_APP_VERSION, "1.0.0");
3084 m.insert_string(HELM_CHART_KEY_API_VERSION, "v2");
3085 m.insert_string(HELM_CHART_KEY_DEPENDENCIES, "placeholder");
3086 m.insert_string(HELM_CHART_DEPENDENCY_KEY_NAME, "pleme-computeunit");
3087 m.insert_string(HELM_CHART_DEPENDENCY_KEY_VERSION, "0.1.0");
3088 m.insert_string(
3089 HELM_CHART_DEPENDENCY_KEY_REPOSITORY,
3090 "oci://ghcr.io/pleme-io",
3091 );
3092 m.insert_string(HELM_CHART_DEPENDENCY_KEY_ALIAS, "cu");
3093 for key in [
3094 HELM_CHART_KEY_TYPE,
3095 HELM_CHART_KEY_APP_VERSION,
3096 HELM_CHART_KEY_API_VERSION,
3097 HELM_CHART_KEY_DEPENDENCIES,
3098 HELM_CHART_DEPENDENCY_KEY_NAME,
3099 HELM_CHART_DEPENDENCY_KEY_VERSION,
3100 HELM_CHART_DEPENDENCY_KEY_REPOSITORY,
3101 HELM_CHART_DEPENDENCY_KEY_ALIAS,
3102 ] {
3103 // Present-key path: both forms find the same insertion.
3104 assert_eq!(
3105 m.contains_key(key),
3106 m.contains_key(serde_yaml::Value::String(key.to_string())),
3107 "present-key mapping.contains_key({key:?}) via bare-&str must \
3108 byte-equal mapping.contains_key(Value::String({key:?}.to_string())) \
3109 — otherwise the swept `assert!(_.contains_key({key:?}))` sites \
3110 in this file drift silently past the emitter's `insert_str_key` \
3111 promotion at every downstream drift-detection pin call site"
3112 );
3113 }
3114 // Absent-key path: both forms return false on a key that was
3115 // never inserted (mirroring the peer caixa-core-side
3116 // KUBE_KEY_SPEC absent-key parity assertion).
3117 let absent = "this_key_never_appears_in_any_chart_yaml_axis";
3118 assert_eq!(
3119 m.contains_key(absent),
3120 m.contains_key(serde_yaml::Value::String(absent.to_string())),
3121 "absent-key mapping.contains_key(<bare-&str>) must byte-equal \
3122 absent-key mapping.contains_key(Value::String(<key>.to_string())) \
3123 — otherwise a future swept drift-detection pin's absent-key arm \
3124 could silently disagree between the two forms"
3125 );
3126 }
3127
3128 #[test]
3129 fn readme_repositorio_null_fallback_routes_through_canonical_git_url_accessor() {
3130 // Emit-path pin: on a `:repositorio`-null caixa the
3131 // [`build_readme`] `## Origin` line's `{repo}` interpolation must
3132 // derive from the substrate-canonical
3133 // [`caixa_core::Caixa::canonical_git_url`] resolved-git-URL
3134 // composer's None-arm fallback (`https://github.com/{DEFAULT_PLEME_GIT_ORG}/<nome>`)
3135 // rather than the prior `caixa.repositorio().unwrap_or(caixa.nome())`
3136 // two-arm inline whose fallback folded to the bare `<nome>` scalar
3137 // and emitted the meaningless `Generated by `caixa-helm` from
3138 // `<nome>/caixa.lisp`` line the 124f864 commit body explicitly
3139 // called out as the live-behavior-correcting converge surface
3140 // waiting on this lift. Fail-before-pass-after: the pre-lift
3141 // shape carried the bare `hello-rio/caixa.lisp` substring and
3142 // this pin asserts the post-lift canonical pleme-org URL substring
3143 // in its place, so a regression that re-inlines the raw two-arm
3144 // fallback at the emit site surfaces at caixa-helm build time on
3145 // this test's failure rather than at a downstream README-consuming
3146 // UI's "where does this caixa live?" broken-URL trail.
3147 let caixa = Caixa {
3148 repositorio: None,
3149 ..sample_caixa()
3150 };
3151 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3152 let readme_file =
3153 find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3154 let expected = format!(
3155 "Generated by `caixa-helm` from `{}/caixa.lisp`",
3156 caixa.canonical_git_url()
3157 );
3158 assert!(
3159 readme_file.contents.contains(&expected),
3160 "README.md `:repositorio`-null fallback must derive its \
3161 `repo` interpolation from the typed \
3162 `caixa_core::Caixa::canonical_git_url` resolved-git-URL \
3163 composer byte-for-byte ({expected:?}) — a regression that \
3164 re-inlines the prior `caixa.repositorio().unwrap_or(caixa.nome())` \
3165 two-arm fallback at the emit site silently regresses the \
3166 README origin-line repo axis to the meaningless bare `<nome>` \
3167 prefix and splits the caixa-helm README axis from the sibling \
3168 [`caixa_flux::ClusterBundleOpts::for_caixa`] `git_url` seed \
3169 which already resolves through the canonical composer. \
3170 Full contents:\n{contents}",
3171 contents = readme_file.contents,
3172 );
3173 }
3174
3175 #[test]
3176 fn readme_repositorio_null_fallback_no_longer_emits_bare_nome_prefix() {
3177 // Fail-before-pass-after regression guard: pins the negative arm
3178 // the sibling
3179 // [`readme_repositorio_null_fallback_routes_through_canonical_git_url_accessor`]
3180 // pin's positive arm implies — the pre-lift bare `<nome>/caixa.lisp`
3181 // substring (the shape the prior `caixa.repositorio().unwrap_or(caixa.nome())`
3182 // inline two-arm fallback emitted on every `:repositorio`-null
3183 // caixa) must NOT appear in the rendered README's `## Origin` line.
3184 // A regression that re-inlines the two-arm fallback at
3185 // [`build_readme`]'s emit site would satisfy the positive
3186 // canonical-composer substring check on the `Some(<full-URL>)`
3187 // arm and drop the fallback arm back to `<nome>/caixa.lisp`
3188 // silently — so an explicit negative-substring pin closes the
3189 // remaining converge surface the sibling positive pin does not
3190 // catch on its own.
3191 let caixa = Caixa {
3192 repositorio: None,
3193 ..sample_caixa()
3194 };
3195 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3196 let readme_file =
3197 find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3198 let regressed = format!(
3199 "Generated by `caixa-helm` from `{}/caixa.lisp`",
3200 caixa.nome()
3201 );
3202 assert!(
3203 !readme_file.contents.contains(®ressed),
3204 "README.md `:repositorio`-null fallback must NOT emit the \
3205 pre-lift bare `<nome>/caixa.lisp` prefix ({regressed:?}) — \
3206 the `caixa.repositorio().unwrap_or(caixa.nome())` two-arm \
3207 inline fallback was retired in favor of \
3208 `caixa.canonical_git_url()` so the fallback arm now names \
3209 the substrate-canonical pleme-org URL; a regression that \
3210 re-inlines the two-arm fallback at the emit site would \
3211 silently split this caixa-helm README axis from the sibling \
3212 [`caixa_flux::ClusterBundleOpts::for_caixa`] `git_url` seed. \
3213 Full contents:\n{contents}",
3214 contents = readme_file.contents,
3215 );
3216 }
3217
3218 #[test]
3219 fn readme_repositorio_declared_arm_routes_through_canonical_git_url_accessor() {
3220 // Emit-path pin (peer of the `:repositorio`-null fallback sibling):
3221 // on a `:repositorio`-declared caixa the [`build_readme`] `## Origin`
3222 // line's `{repo}` interpolation must also derive from the substrate-
3223 // canonical [`caixa_core::Caixa::canonical_git_url`] resolved-git-URL
3224 // composer — the Some-arm return byte-string is the author-declared
3225 // `:repositorio` verbatim, so the substring the rendered README
3226 // carries matches the composer output on both arms and the pair
3227 // of pins jointly pins the entire two-arm dispatch through one
3228 // canonical composer. A regression that split the emit site into
3229 // a Some-arm-only bypass through the raw
3230 // [`caixa_core::Caixa::repositorio`] accessor while leaving the
3231 // fallback arm routed through the canonical composer would
3232 // silently drift the two-arm shape and be caught here (the raw
3233 // accessor path bypasses any future extension of the resolved-URL
3234 // composer, e.g. a `github:` → `https://github.com/` canonicalization
3235 // pass or the per-cluster repo-mirror overlay the M4 CR materializer
3236 // will apply).
3237 let caixa = sample_caixa();
3238 assert!(
3239 caixa.repositorio().is_some(),
3240 "sample_caixa() must carry a `Some(...)` `:repositorio` for \
3241 the Some-arm to exercise the canonical composer's non-fallback \
3242 path — a future sample_caixa() edit that drops the field must \
3243 re-establish it here or the pin regresses to a no-op",
3244 );
3245 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3246 let readme_file =
3247 find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3248 let expected = format!(
3249 "Generated by `caixa-helm` from `{}/caixa.lisp`",
3250 caixa.canonical_git_url()
3251 );
3252 assert!(
3253 readme_file.contents.contains(&expected),
3254 "README.md `:repositorio`-declared arm must derive its `repo` \
3255 interpolation from the typed `caixa_core::Caixa::canonical_git_url` \
3256 resolved-git-URL composer byte-for-byte ({expected:?}) so the \
3257 two-arm dispatch routes through one canonical composer end to \
3258 end. Full contents:\n{contents}",
3259 contents = readme_file.contents,
3260 );
3261 }
3262
3263 #[test]
3264 #[allow(clippy::cmp_owned)] // Deliberate: the pin reproduces the
3265 // prior owning-`PathBuf::from(...)` comparand byte-for-byte to
3266 // guarantee the lift's substitution is behavior-preserving on
3267 // exactly the shape the 26 converged caixa-helm callers previously
3268 // carried.
3269 fn find_file_by_path_matches_prior_inline_iter_find_pathbuf_from_shape() {
3270 // Per-crate byte-equivalence pin on the lifted
3271 // [`caixa_core::find_file_by_path`] navigator: for every leaf
3272 // the `render_chart_for_servico` `lareira-<nome>` chart-
3273 // directory emit writes ([`HELM_CHART_YAML_FILENAME`] /
3274 // [`HELM_VALUES_YAML_FILENAME`] /
3275 // [`HELM_CHART_README_FILENAME`]), the lifted navigator must
3276 // byte-equal the prior three-line inline
3277 // `dir.files.iter().find(|f| f.path ==
3278 // PathBuf::from(<FILENAME_CONST>))` combinator the 26 test-
3279 // side per-artifact readback sites previously carried. Mirrors
3280 // the sibling `caixa-flux`
3281 // `find_file_by_path_matches_prior_inline_iter_find_pathbuf_from_shape`
3282 // pin at the peer caller-crate altitude and the substrate-
3283 // level `find_file_by_path_matches_inline_iter_find_pathbuf_from_shape`
3284 // pin at the primitive definition — the three-arm closure
3285 // that closes the discipline the sibling `sequence_str_values`
3286 // / `kube_has` sweeps established.
3287 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
3288 for filename in [
3289 HELM_CHART_YAML_FILENAME,
3290 HELM_VALUES_YAML_FILENAME,
3291 HELM_CHART_README_FILENAME,
3292 ] {
3293 let via_helper = find_file_by_path(&dir.files, filename);
3294 let via_inline = dir.files.iter().find(|f| f.path == PathBuf::from(filename));
3295 assert_eq!(
3296 via_helper, via_inline,
3297 "find_file_by_path(&dir.files, {filename:?}) must \
3298 byte-equal the prior three-line `dir.files.iter()\
3299 .find(|f| f.path == PathBuf::from({filename:?}))` \
3300 combinator on every leaf the `lareira-<nome>` chart-\
3301 directory emit writes — otherwise the 26 routed per-\
3302 artifact readback sites regress silently on the \
3303 leaf-path axis",
3304 );
3305 }
3306 }
3307
3308 #[test]
3309 fn parse_yaml_at_path_matches_prior_inline_find_then_from_str_shape() {
3310 // Per-crate byte-equivalence pin on the lifted
3311 // [`caixa_core::parse_yaml_at_path`] composed navigator: for
3312 // every YAML leaf the `render_chart_for_servico` `lareira-
3313 // <nome>` chart-directory emit writes that the test-side
3314 // routed callers parse to a [`serde_yaml::Value`]
3315 // ([`HELM_CHART_YAML_FILENAME`] as `doc` /
3316 // [`HELM_VALUES_YAML_FILENAME`] as `parsed`), the lifted helper
3317 // must return a `Value` byte-equal to the prior two-step
3318 //
3319 // let f = find_file_by_path(&dir.files, <FILENAME>).unwrap();
3320 // let parsed: serde_yaml::Value =
3321 // serde_yaml::from_str(&f.contents).unwrap();
3322 //
3323 // the 12 test-side per-artifact YAML readback sites previously
3324 // carried. Mirrors the sibling `caixa-flux`
3325 // `parse_yaml_at_path_matches_prior_inline_find_then_from_str_shape`
3326 // pin at the peer caller-crate altitude and the substrate-
3327 // level `parse_yaml_at_path_matches_prior_inline_two_step_shape`
3328 // pin at the primitive definition — the three-arm closure that
3329 // closes the same discipline the sibling `find_file_by_path`
3330 // sweep established. [`HELM_CHART_README_FILENAME`] is
3331 // deliberately not swept: the routed callers only assert on its
3332 // raw-byte content, never parse it to YAML.
3333 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
3334 for filename in [HELM_CHART_YAML_FILENAME, HELM_VALUES_YAML_FILENAME] {
3335 let via_helper = parse_yaml_at_path(&dir.files, filename);
3336 let via_inline_file = find_file_by_path(&dir.files, filename).unwrap();
3337 let via_inline: serde_yaml::Value =
3338 serde_yaml::from_str(&via_inline_file.contents).unwrap();
3339 assert_eq!(
3340 via_helper, via_inline,
3341 "parse_yaml_at_path(&dir.files, {filename:?}) must \
3342 byte-equal the prior two-step `find_file_by_path(&dir.files, \
3343 {filename:?}).unwrap() + serde_yaml::from_str(&_.contents).unwrap()` \
3344 combinator on every YAML leaf the `lareira-<nome>` chart-\
3345 directory emit writes — otherwise the 12 routed per-\
3346 artifact YAML readback sites regress silently on the \
3347 parse-target axis",
3348 );
3349 }
3350 }
3351
3352 #[test]
3353 fn parse_yaml_at_path_as_matches_prior_inline_find_then_from_str_typed_shape() {
3354 // Per-crate byte-equivalence pin on the lifted
3355 // [`caixa_core::parse_yaml_at_path_as`] caller-typed composed
3356 // navigator: for the `Chart.yaml` YAML leaf the
3357 // `render_chart_for_servico` `lareira-<nome>` chart-directory
3358 // emit writes that the test-side routed callers parse to a
3359 // [`ChartYaml`]-typed body, the lifted helper must return a
3360 // `ChartYaml` byte-equal to the prior two-step
3361 //
3362 // let chart_file = find_file_by_path(&dir.files, HELM_CHART_YAML_FILENAME)
3363 // .unwrap(); // or .expect("Chart.yaml present")
3364 // let chart: ChartYaml =
3365 // serde_yaml::from_str(&chart_file.contents).unwrap();
3366 //
3367 // the 9 test-side per-`Chart.yaml`-typed-parse readback sites
3368 // previously carried. Mirrors the sibling
3369 // [`parse_yaml_at_path_matches_prior_inline_find_then_from_str_shape`]
3370 // untyped-Value-arity pin in this crate at the caller-typed-
3371 // arity peer, and the substrate-level
3372 // `parse_yaml_at_path_as_matches_prior_inline_typed_two_step_shape`
3373 // pin at the primitive definition — the three-arm closure
3374 // that closes the same discipline the sibling
3375 // [`parse_yaml_at_path`] sweep established. Only
3376 // [`HELM_CHART_YAML_FILENAME`] is swept: the routed
3377 // caller-typed-parse sites in this crate reach exclusively
3378 // through the `ChartYaml`-typed body at the `Chart.yaml`
3379 // leaf; [`HELM_VALUES_YAML_FILENAME`] carries no typed-mirror
3380 // struct in this crate (the routed callers navigate its
3381 // parsed [`serde_yaml::Value`] through `kube_*` / `mapping_*`
3382 // primitives), and [`HELM_CHART_README_FILENAME`] is not
3383 // parsed as YAML at all.
3384 let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
3385 let filename = HELM_CHART_YAML_FILENAME;
3386 let via_helper: ChartYaml = parse_yaml_at_path_as(&dir.files, filename);
3387 let via_inline_file = find_file_by_path(&dir.files, filename).unwrap();
3388 let via_inline: ChartYaml = serde_yaml::from_str(&via_inline_file.contents).unwrap();
3389 assert_eq!(
3390 via_helper, via_inline,
3391 "parse_yaml_at_path_as::<ChartYaml>(&dir.files, {filename:?}) \
3392 must byte-equal the prior two-step \
3393 `find_file_by_path(&dir.files, {filename:?}).unwrap() + \
3394 serde_yaml::from_str::<ChartYaml>(&_.contents).unwrap()` \
3395 combinator on the `Chart.yaml` YAML leaf — otherwise \
3396 the 9 routed per-artifact typed-parse readback sites \
3397 regress silently on the parse-target axis",
3398 );
3399 }
3400
3401 #[test]
3402 fn caixa_licenca_default_re_export_points_at_caixa_core_canonical() {
3403 // The renderer's `CAIXA_LICENCA_DEFAULT` was lifted from the
3404 // production-code inline `"MIT"` byte literal at [`build_readme`]'s
3405 // `caixa.licenca().unwrap_or("MIT")` `README.md` `## License`
3406 // fallback (formerly `caixa-helm/src/lib.rs:1018`) to a re-export
3407 // of [`caixa_core::CAIXA_LICENCA_DEFAULT`] so the substrate-side
3408 // per-`Caixa` author-omitted-`:licenca` SPDX-shaped license-
3409 // expression fallback lives in exactly one place across every
3410 // caixa renderer. Pin the equality + `&'static` static-data
3411 // identity here so any local re-introduction of a sibling `pub
3412 // const CAIXA_LICENCA_DEFAULT: &str = "…"` at this crate — the
3413 // canonical drift footgun where a sibling local `pub const` could
3414 // happen to carry the same byte at the source while pointing at a
3415 // different `&'static` allocation — is a build-time test failure
3416 // naming the offending drift, not a silent per-README license-line
3417 // fall-through to a stale SPDX identifier at chart-consumption
3418 // time far from the drift site. Peer to
3419 // [`helm_chart_readme_filename_re_export_points_at_caixa_core_canonical`]
3420 // / [`helm_values_key_enabled_re_export_points_at_caixa_core_canonical`]
3421 // on the sibling canonical-Helm-per-lifted-const re-export
3422 // surfaces.
3423 caixa_core::assert_str_reexport_identity(
3424 "CAIXA_LICENCA_DEFAULT",
3425 CAIXA_LICENCA_DEFAULT,
3426 caixa_core::CAIXA_LICENCA_DEFAULT,
3427 );
3428 }
3429
3430 #[test]
3431 fn build_readme_license_line_routes_through_lifted_caixa_licenca_default() {
3432 // Emit-path pin: on a `:licenca`-null caixa the [`build_readme`]
3433 // license-line fallback substitutes
3434 // `caixa.licenca().unwrap_or(caixa_core::CAIXA_LICENCA_DEFAULT)`,
3435 // which must derive its terminal SPDX byte-string from the lifted
3436 // [`caixa_core::CAIXA_LICENCA_DEFAULT`] `pub const` rather than the
3437 // pre-lift inline `"MIT"` byte literal at the emit site. Before
3438 // this converge the fallback carried the raw `"MIT"` scalar,
3439 // expressing no compile-time link back to the substrate-side
3440 // per-`Caixa` author-omitted-`:licenca` SPDX-shaped license-
3441 // expression fallback the sibling caixa-core
3442 // [`caixa_licenca_default_pins_canonical_mit_byte`] pinning test
3443 // anchors. Byte-equal today (the lifted const resolves to `"MIT"`);
3444 // the pin catches any future substrate-side license-fallback
3445 // rebrand whose emit-side regresses to the raw literal. Sibling of
3446 // the [`readme_repositorio_null_fallback_routes_through_canonical_git_url_accessor`]
3447 // (aaf1028) / [`readme_body_version_routes_through_caixa_versao_accessor`]
3448 // (eb912de) peer emit-path pins on the paired per-`README.md`
3449 // universal-axis fallback surfaces — completes the
3450 // per-`lareira-<nome>` chart-directory `README.md`
3451 // universal-axis author-omitted-slot fallback resolution's
3452 // substrate-primitive routing at the sole rendered leaf.
3453 let caixa = Caixa {
3454 licenca: None,
3455 ..sample_caixa()
3456 };
3457 let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3458 let readme_file =
3459 find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3460 let expected = format!(
3461 "## License\n\n{license}.\n",
3462 license = caixa_core::CAIXA_LICENCA_DEFAULT,
3463 );
3464 assert!(
3465 readme_file.contents.contains(&expected),
3466 "README.md `:licenca`-null fallback must carry the lifted \
3467 `caixa_core::CAIXA_LICENCA_DEFAULT` byte-string ({expected:?}) \
3468 verbatim — a regression that re-inlines `\"MIT\"` at the emit \
3469 site silently splits the README license-fallback axis from \
3470 every future substrate-side license-fallback rebrand. Full \
3471 contents:\n{contents}",
3472 contents = readme_file.contents,
3473 );
3474 }
3475}