// Acceptance for P2.2 — the content-addressed artifact store. // // Drives a fresh store rooted under `.sx-tmp/` (never /tmp) and asserts // the four Slice-3 invariants: // 1. put → object lands at `objects/` and its bytes round-trip; // the storage key equals std.hash, an independent `shasum -a 256`, // and the pinned SHA-256("abc") vector. // 2. dedup — identical bytes are not stored twice and an existing // object is never rewritten. // 3. atomicity — a staged-but-unpublished write is invisible at the // final path, and a publish that fails before/at the rename leaves // no object. // 4. put_file — a file source produces the same key and bytes. // Exits 0 only if every assertion holds (process.assert aborts otherwise). #import "modules/std.sx"; fs :: #import "modules/fs.sx"; hash :: #import "modules/std/hash.sx"; process :: #import "modules/process.sx"; #import "../src/store/store.sx"; // SHA-256("abc"), the FIPS 180-4 one-block known-answer vector. ABC_SHA256 :: "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; // std.hash digest of `s` as a heap string key (independent of the store). stdhash_key :: (s: string) -> string { d := hash.sha256_hex(s); view := string.{ ptr = @d[0], len = 64 }; return substr(view, 0, 64); } // First 64 hex chars of `shasum -a 256` over `bytes`, via the shell. // `bytes` must be shell-safe (the fixtures here are plain ASCII). shasum_key :: (bytes: string) -> string { cmd := concat("printf '%s' ", concat(bytes, " | shasum -a 256")); r := process.run(cmd); process.assert(r != null, "shasum -a 256 must run"); res := r!; process.assert(res.exit_code == 0, "shasum -a 256 must exit 0"); return substr(res.stdout, 0, 64); } // Number of directory entries under `dir`, parsed from `ls -1 | wc -l`. entry_count :: (dir: string) -> string { cmd := concat("ls -1 ", concat(dir, " | wc -l | tr -dc '0-9'")); r := process.run(cmd); process.assert(r != null, "ls/wc must run"); res := r!; return res.stdout; } main :: () -> s32 { root := ".sx-tmp/store-cas"; process.run(concat("rm -rf ", root)); // fresh root, even after a crashed prior run st := Store.init(root); // ── 1. put + content addressing ───────────────────────────────────── fixture := "abc"; key, e := st.put_bytes(fixture); process.assert(!e, "put_bytes(abc) must succeed"); process.assert(key == ABC_SHA256, "key must equal pinned SHA-256(abc) vector"); process.assert(key == stdhash_key(fixture), "store key must equal std.hash digest"); process.assert(key == shasum_key(fixture), "store key must equal shasum -a 256"); print(" store == std.hash == shasum == vector: {}\n", key); process.assert(st.has(key), "object must exist at objects/"); stored := fs.read_file(st.object_path(key)); process.assert(stored != null, "stored object must be readable"); process.assert(stored! == fixture, "stored bytes must equal the input"); // ── 2. dedup: same bytes, one object, never rewritten ─────────────── // Overwrite the object on disk; a deduped re-put must NOT touch it. process.assert(fs.write_file(st.object_path(key), "TAMPERED"), "tamper write must succeed"); key2, e2 := st.put_bytes(fixture); process.assert(!e2, "second put_bytes must succeed"); process.assert(key2 == key, "dedup: identical bytes yield the same key"); after := fs.read_file(st.object_path(key)); process.assert(after! == "TAMPERED", "dedup: existing object must not be rewritten"); process.assert(entry_count(st.objects_dir()) == "1", "dedup: exactly one object stored"); // Restore the real bytes so the store is left consistent. process.assert(fs.write_file(st.object_path(key), fixture), "restore write must succeed"); print(" dedup: one object, copy skipped on re-put\n"); // ── 3. atomicity: staged write is invisible until publish ─────────── pending := "interrupted-upload-bytes"; pkey := stdhash_key(pending); process.assert(!st.has(pkey), "fresh store: pending object must be absent"); sp, se := st.stage_write(pkey, pending); process.assert(!se, "stage_write must succeed"); process.assert(fs.exists(sp), "staged file must exist after stage_write"); process.assert(!st.has(pkey), "atomicity: object must NOT exist before the rename"); // A publish whose staging source is missing fails and creates nothing. missing := "1111111111111111111111111111111111111111111111111111111111111111"; process.assert(!st.has(missing), "precondition: no object for the missing key"); failed := false; st.publish(st.staging_path(missing), missing) catch { failed = true; }; process.assert(failed, "publish of a missing staging file must fail"); process.assert(!st.has(missing), "failed publish must leave no object"); print(" atomicity: staged write invisible; failed publish leaves no object\n"); // ── 4. put_file: file source, same key + bytes ────────────────────── src := ".sx-tmp/store-cas-src.bin"; file_bytes := "the quick brown fox\n"; process.assert(fs.write_file(src, file_bytes), "fixture source file must be written"); fkey, fe := st.put_file(src); process.assert(!fe, "put_file must succeed"); process.assert(fkey == stdhash_key(file_bytes), "put_file key must equal std.hash of the file bytes"); process.assert(st.has(fkey), "put_file object must be published"); fstored := fs.read_file(st.object_path(fkey)); process.assert(fstored! == file_bytes, "put_file stored bytes must equal the file"); print(" put_file: key {} published\n", fkey); // ── cleanup ───────────────────────────────────────────────────────── process.run(concat("rm -rf ", root)); fs.delete_file(src); print("store_content_addressed: ALL CASES PASS\n"); return 0; }