gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Definiciones

⎇ main crates/takana-cli/tests/bootstrap_cli.rs Rust · 76 líneas · 2.8 KB Historia Quién tocó qué crudo

takana etapa 5d: comentarios de crates, CLAUDE.md y el skill de granja b818f5249 · Sergio ·

1//! E2E del CLI `takana bootstrap stage0` (track posterior, SDD 11). Construye un tarball de
2//! semilla falso, lo ingiere vía `file://` (sin red) y verifica el camino completo:
3//! sella + imprime el hash, es idempotente, y un sha256 erróneo falla sin sellar.
4
5use std::path::Path;
6use std::process::Command;
7
8const BIN: &str = env!("CARGO_BIN_EXE_hammer");
9
10fn sha256_hex(bytes: &[u8]) -> String {
11    use sha2::{Digest, Sha256};
12    let mut h = Sha256::new();
13    h.update(bytes);
14    hex::encode(h.finalize())
15}
16
17/// Crea un tarball con `bin/zig` dentro y devuelve `(url file://, sha256 hex)`.
18fn make_seed_tarball(dir: &Path) -> (String, String) {
19    let src = dir.join("src");
20    std::fs::create_dir_all(src.join("bin")).unwrap();
21    std::fs::write(src.join("bin/zig"), b"#!/bin/sh\necho fake zig\n").unwrap();
22    let tarball = dir.join("seed.tar.gz");
23    let st = Command::new("tar")
24        .arg("-czf")
25        .arg(&tarball)
26        .arg("-C")
27        .arg(&src)
28        .arg(".")
29        .status()
30        .unwrap();
31    assert!(st.success());
32    let sha = sha256_hex(&std::fs::read(&tarball).unwrap());
33    (format!("file://{}", tarball.display()), sha)
34}
35
36fn stage0(store: &Path, url: &str, sha256: &str, version: &str) -> std::process::Output {
37    Command::new(BIN)
38        .arg("--store")
39        .arg(store)
40        .args(["bootstrap", "stage0", "--url"])
41        .arg(url)
42        .args(["--sha256", sha256, "--version", version])
43        .output()
44        .unwrap()
45}
46
47#[test]
48fn stage0_seals_idempotent_and_rejects_bad_sha() {
49    let tmp = tempfile::tempdir().unwrap();
50    let (url, sha) = make_seed_tarball(tmp.path());
51    let store = tmp.path().join("store");
52
53    // --- primera ingestión: sella e imprime el hash ---
54    let out = stage0(&store, &url, &sha, "0.13.0");
55    assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
56    let hash = String::from_utf8(out.stdout).unwrap().trim().to_string();
57    assert!(hash.starts_with("b3:"), "hash inesperado: {hash}");
58
59    // El artefacto está en el store con el contenido extraído.
60    let zig = store.join(format!("{}-seed-zig", hash.trim_start_matches("b3:"))).join("bin/zig");
61    assert!(zig.is_file(), "bin/zig debe existir en el artefacto sellado");
62
63    // --- idempotente: mismo hash, sin re-sellar ---
64    let out2 = stage0(&store, &url, &sha, "0.13.0");
65    assert!(out2.status.success());
66    assert_eq!(String::from_utf8(out2.stdout).unwrap().trim(), hash);
67
68    // --- sha erróneo: falla y no sella un artefacto nuevo ---
69    let bad = stage0(&store, &url, &"0".repeat(64), "bad");
70    assert!(!bad.status.success(), "un sha erróneo debe fallar");
71    assert!(
72        String::from_utf8_lossy(&bad.stderr).contains("sha256 mismatch"),
73        "stderr: {}",
74        String::from_utf8_lossy(&bad.stderr)
75    );
76}