Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .ddd/seams/seam-rust-ledger-cli-tests-common-mod-rs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,51 @@ bindings:
after: sha256:4950bad589fd7178075bd548a44829ce5427af2c8f9efb20d2cfb88bf1460db8
base_revision: 70395dfe317cb9a00665433813766ca763f3c58a
hash: sha256:8debaac1a6019c0a4e89fd05aebdc38472dc853fd33d1e9eef714237ff482817
- symbol: copy_fixture_into
file: ledger-cli/tests/common/mod.rs
before: sha256:4950bad589fd7178075bd548a44829ce5427af2c8f9efb20d2cfb88bf1460db8
after: sha256:d38812b1be3063fc0a3b6246abceb1ce7867035b787e9c5dfaca58026242aa7d
base_revision: 0fafebcd753944b5931fffd3d4fe841b703a6b8b
hash: sha256:2e68b054fa86d714cbb63cf75496c44896ca04bcdc7ab0e4e9b6ad00f71a3f5e
- symbol: copy_store_into
file: ledger-cli/tests/common/mod.rs
before: sha256:4950bad589fd7178075bd548a44829ce5427af2c8f9efb20d2cfb88bf1460db8
after: sha256:d38812b1be3063fc0a3b6246abceb1ce7867035b787e9c5dfaca58026242aa7d
base_revision: 0fafebcd753944b5931fffd3d4fe841b703a6b8b
hash: sha256:6e6f8f489f38de5fe96392bc425ddbfe26da43e61732baadb8b8dcfd44769a86
- symbol: fixture_copy
file: ledger-cli/tests/common/mod.rs
before: sha256:4950bad589fd7178075bd548a44829ce5427af2c8f9efb20d2cfb88bf1460db8
after: sha256:d38812b1be3063fc0a3b6246abceb1ce7867035b787e9c5dfaca58026242aa7d
base_revision: 0fafebcd753944b5931fffd3d4fe841b703a6b8b
hash: sha256:500db47caf9fd8712a2281e9408e902f8b2214147d0a54e389e2a4c16df1c5b8
- symbol: workspace_copy
file: ledger-cli/tests/common/mod.rs
before: sha256:4950bad589fd7178075bd548a44829ce5427af2c8f9efb20d2cfb88bf1460db8
after: sha256:d38812b1be3063fc0a3b6246abceb1ce7867035b787e9c5dfaca58026242aa7d
base_revision: 0fafebcd753944b5931fffd3d4fe841b703a6b8b
hash: sha256:6e2265df119ec47cc2c0b2262a75b56575b9d0bfa19a41748015ea4e7ee31ca5
- symbol: stage_fixture
file: ledger-cli/tests/common/mod.rs
before: sha256:4950bad589fd7178075bd548a44829ce5427af2c8f9efb20d2cfb88bf1460db8
after: sha256:d38812b1be3063fc0a3b6246abceb1ce7867035b787e9c5dfaca58026242aa7d
base_revision: 0fafebcd753944b5931fffd3d4fe841b703a6b8b
hash: sha256:95ee587ed7f5d0e93e31548cbc105c211d55ca70ce8eaef6fd9469c5c98a596c
- symbol: stage_fixture_into
file: ledger-cli/tests/common/mod.rs
before: sha256:4950bad589fd7178075bd548a44829ce5427af2c8f9efb20d2cfb88bf1460db8
after: sha256:d38812b1be3063fc0a3b6246abceb1ce7867035b787e9c5dfaca58026242aa7d
base_revision: 0fafebcd753944b5931fffd3d4fe841b703a6b8b
hash: sha256:2a7a9bba87a4d4faa005ae07dde38f88074c2497e6c9126effa6e3dd292ae559
- symbol: stage_store_into
file: ledger-cli/tests/common/mod.rs
before: sha256:4950bad589fd7178075bd548a44829ce5427af2c8f9efb20d2cfb88bf1460db8
after: sha256:d38812b1be3063fc0a3b6246abceb1ce7867035b787e9c5dfaca58026242aa7d
base_revision: 0fafebcd753944b5931fffd3d4fe841b703a6b8b
hash: sha256:46c21d64d31e336403e2299122c3285bf997a177c6d474522a6dc542cc2e9688
- symbol: stage_workspace
file: ledger-cli/tests/common/mod.rs
before: sha256:4950bad589fd7178075bd548a44829ce5427af2c8f9efb20d2cfb88bf1460db8
after: sha256:d38812b1be3063fc0a3b6246abceb1ce7867035b787e9c5dfaca58026242aa7d
base_revision: 0fafebcd753944b5931fffd3d4fe841b703a6b8b
hash: sha256:d2d52238f232a4fdbcb5d5bb897771d777f21866a29653124f560e8d142cf8ed
90 changes: 23 additions & 67 deletions ledger-cli/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,87 +314,43 @@ pub fn both(out: &Output) -> String {
format!("{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr))
}

/// A scratch copy of a committed fixture store, laid out under
/// `.decisions/ns/<namespace>/` (LP-3.34). The fixtures under
/// `tests/fixtures/` are still in the flat layout until issue 9 re-lays
/// them out; until then this stages each one the way the loader reads.
pub fn stage_fixture(name: &str) -> tempfile::TempDir {
/// A scratch copy of a committed fixture store under `tests/fixtures/`,
/// laid out as the loader reads it, `.decisions/ns/<namespace>/` (LP-3.34).
pub fn fixture_copy(name: &str) -> tempfile::TempDir {
let dir = tempfile::tempdir().expect("tempdir");
stage_fixture_into(name, dir.path());
copy_fixture_into(name, dir.path());
dir
}

/// Stage the fixture `name` into `root`'s store; returns its one namespace.
pub fn stage_fixture_into(name: &str, root: &Path) -> String {
/// Copy the fixture `name` into `root`'s store, byte for byte; returns its
/// one namespace.
pub fn copy_fixture_into(name: &str, root: &Path) -> String {
let from = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures").join(name).join(".decisions");
let namespaces = stage_store_into(&from, root);
let namespaces = copy_store_into(&from, root);
assert_eq!(namespaces.len(), 1, "a fixture holds one namespace: {namespaces:?}");
namespaces.into_iter().next().expect("one namespace")
}

/// A scratch copy of this repository's own store, staged the same way: the
/// store is flat until issue 10 re-lays it out, and the loader reads only
/// `ns/<namespace>/`.
pub fn stage_workspace() -> tempfile::TempDir {
/// A scratch copy of this repository's own store.
pub fn workspace_copy() -> tempfile::TempDir {
let dir = tempfile::tempdir().expect("tempdir");
let from = Path::new(env!("CARGO_MANIFEST_DIR")).join("..").join(".decisions");
stage_store_into(&from, dir.path());
copy_store_into(&from, dir.path());
dir
}

/// Stage the store at `from` (a `.decisions/` directory, flat or per
/// namespace) into `root`'s store under `ns/<namespace>/`: a change-set
/// goes to the namespace its decisions name, a set to the namespace whose
/// versions name it. Bytes are copied, never rewritten. Returns the
/// namespaces staged.
pub fn stage_store_into(from: &Path, root: &Path) -> Vec<String> {
if from.join("ns").is_dir() {
let mut out = Vec::new();
for ns in std::fs::read_dir(from.join("ns")).expect("ns").flatten().filter(|e| e.path().is_dir()) {
let name = ns.file_name().to_string_lossy().into_owned();
copy_tree(&ns.path(), &ledger_core::layout::namespace_dir(root, &name));
out.push(name);
}
out.sort();
return out;
}
let mut namespaces: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
let mut set_homes: std::collections::BTreeMap<String, String> = std::collections::BTreeMap::new();
let mut logs: Vec<(std::path::PathBuf, String)> = Vec::new();
for entry in std::fs::read_dir(from.join("log")).into_iter().flatten().flatten() {
let text = std::fs::read_to_string(entry.path()).expect("read");
let cs: ledger_core::changeset::ChangeSet = serde_yaml::from_str(&text).expect("a change-set");
let ns = cs
.decisions
.iter()
.map(|d| d.id.namespace().to_string())
.chain(cs.versions.iter().map(|v| v.decision.namespace().to_string()))
.chain(cs.acceptances.iter().map(|a| a.decision.namespace().to_string()))
.next()
.unwrap_or_else(|| panic!("{} names no namespace", entry.path().display()));
for v in &cs.versions {
set_homes.entry(v.set.clone()).or_insert_with(|| ns.clone());
}
namespaces.insert(ns.clone());
logs.push((entry.path(), ns));
}
for (path, ns) in logs {
let target = ledger_core::layout::log_dir(root, &ns);
std::fs::create_dir_all(&target).expect("mkdir");
std::fs::copy(&path, target.join(path.file_name().expect("name"))).expect("copy");
}
for entry in std::fs::read_dir(from.join("sets")).into_iter().flatten().flatten() {
let stem = entry.path().file_stem().expect("stem").to_string_lossy().into_owned();
let ns = set_homes
.get(&stem)
.cloned()
.or_else(|| (namespaces.len() == 1).then(|| namespaces.iter().next().cloned()).flatten())
.unwrap_or_else(|| panic!("set `{stem}` is named by no version, and the store speaks several namespaces"));
let target = ledger_core::layout::sets_dir(root, &ns);
std::fs::create_dir_all(&target).expect("mkdir");
std::fs::copy(entry.path(), target.join(entry.file_name())).expect("copy");
}
namespaces.into_iter().collect()
/// Copy the store at `from` (a `.decisions/` directory) into `root`'s
/// store, namespace by namespace. Bytes are copied, never rewritten.
/// Returns the namespaces copied, sorted.
pub fn copy_store_into(from: &Path, root: &Path) -> Vec<String> {
let mut out = Vec::new();
for ns in std::fs::read_dir(from.join("ns")).expect("a store under ns/").flatten().filter(|e| e.path().is_dir()) {
let name = ns.file_name().to_string_lossy().into_owned();
copy_tree(&ns.path(), &ledger_core::layout::namespace_dir(root, &name));
out.push(name);
}
out.sort();
out
}

fn copy_tree(from: &Path, to: &Path) {
Expand Down
12 changes: 3 additions & 9 deletions ledger-cli/tests/digests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,13 @@ fn workspace() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("..")
}

/// Every log directory of a store: `.decisions/ns/<ns>/log` (LP-3.34), or
/// the flat `.decisions/log` of a store not yet re-laid out.
/// Every log directory of a store: `.decisions/ns/<ns>/log` (LP-3.34).
fn log_dirs_of(root: &Path) -> Vec<PathBuf> {
let mut out: Vec<PathBuf> = ledger_core::layout::namespaces(root)
ledger_core::layout::namespaces(root)
.into_iter()
.map(|ns| ledger_core::layout::log_dir(root, &ns))
.filter(|d| d.is_dir())
.collect();
let flat = root.join(".decisions/log");
if flat.is_dir() {
out.push(flat);
}
out
.collect()
}

fn log_dirs() -> Vec<PathBuf> {
Expand Down
2 changes: 1 addition & 1 deletion ledger-cli/tests/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn text(out: &Output) -> String {

/// A scratch copy of the conformant fixture, so tests can write exports.
fn scratch() -> tempfile::TempDir {
common::stage_fixture("pass")
common::fixture_copy("pass")
}

fn namespace_of_fixture(dir: &Path) -> String {
Expand Down
8 changes: 4 additions & 4 deletions ledger-cli/tests/export_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ fn bytes_rebuilt_from_the_export_equal_the_signed_bytes_over_every_fixture() {
let fixtures = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures");
let mut names: Vec<String> = std::fs::read_dir(&fixtures).expect("fixtures").flatten().map(|e| e.file_name().to_string_lossy().into_owned()).collect();
names.sort();
// Each fixture staged under `ns/<namespace>/`, the signed store, and
// this repository's own store: 91 acceptances signed by nobody yet,
// Each fixture, the signed store, and this repository's own store: 91
// acceptances signed by nobody yet,
// whose bytes the export must still rebuild exactly.
let mut staged: Vec<tempfile::TempDir> = names.iter().map(|n| common::stage_fixture(n)).collect();
staged.push(common::stage_workspace());
let mut staged: Vec<tempfile::TempDir> = names.iter().map(|n| common::fixture_copy(n)).collect();
staged.push(common::workspace_copy());
let mut roots: Vec<std::path::PathBuf> = staged.iter().map(|d| d.path().to_path_buf()).collect();
roots.push(signed.path().to_path_buf());
let mut checked = 0;
Expand Down
2 changes: 1 addition & 1 deletion ledger-cli/tests/format_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const CS: &str = "01K2C4YQJ3F8M0PT5W7NZ9RDXW";
/// The `pass` fixture's store (its change-set needs format 1), landed
/// declaring `format: N`. Returns the log file.
fn landed_at(repo: &Repo, n: u32) -> PathBuf {
common::stage_fixture_into("pass", repo.path());
common::copy_fixture_into("pass", repo.path());
let file = repo.log_dir("fixture.ledger").join(format!("{CS}.yml"));
redeclare(&file, 1, n);
hand::commit(repo, &format!("landed at format {n}"));
Expand Down
21 changes: 11 additions & 10 deletions ledger-cli/tests/gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ fn fixtures() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures")
}

/// Run the gate over a fixture store, staged under `.decisions/ns/<ns>/`
/// (`common::stage_fixture`). Blame is off: these stores live inside
/// Run the gate over a scratch copy of a fixture store
/// (`common::fixture_copy`). Blame is off: these stores live inside
/// this repository, so their history attributes to whoever committed them.
fn gate(fixture: &str, extra: &[&str]) -> Output {
let staged = common::stage_fixture(fixture);
let staged = common::fixture_copy(fixture);
let mut cmd = Command::cargo_bin("ledger").expect("binary");
cmd.arg("--root")
.arg(staged.path())
Expand Down Expand Up @@ -201,7 +201,7 @@ mod blame {
}

fn copy_pass_fixture(&self) {
common::stage_fixture_into("pass", self.path());
common::copy_fixture_into("pass", self.path());
}

fn git(&self, args: &[&str]) {
Expand Down Expand Up @@ -284,13 +284,14 @@ fn fixture_hashes_are_current() {
);
}

/// The log files of one fixture store: `.decisions/ns/<ns>/log/*` (LP-3.34)
/// or, until issue 9 re-lays the fixtures out, the flat `.decisions/log/*`.
/// The log files of one fixture store: `.decisions/ns/<ns>/log/*` (LP-3.34).
fn fixture_log_files(root: &Path) -> Vec<std::fs::DirEntry> {
let mut dirs: Vec<PathBuf> =
ledger_core::layout::namespaces(root).iter().map(|ns| ledger_core::layout::log_dir(root, ns)).collect();
dirs.push(root.join(".decisions/log"));
dirs.into_iter().filter(|d| d.is_dir()).flat_map(|d| std::fs::read_dir(d).expect("log").flatten()).collect()
ledger_core::layout::namespaces(root)
.iter()
.map(|ns| ledger_core::layout::log_dir(root, ns))
.filter(|d| d.is_dir())
.flat_map(|d| std::fs::read_dir(d).expect("log").flatten())
.collect()
}

/// Rewrite the stored hashes in one log file. Returns whether it was stale.
Expand Down
12 changes: 6 additions & 6 deletions ledger-cli/tests/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn stdout(out: &Output) -> String {

/// A scratch copy of the coverage fixture, so tests can write an index.
fn scratch_coverage() -> tempfile::TempDir {
common::stage_fixture("coverage")
common::fixture_copy("coverage")
}

#[test]
Expand All @@ -46,7 +46,7 @@ fn the_rebuild_is_byte_identical_after_deleting_the_index() {

#[test]
fn coverage_distinguishes_all_seven_states_on_the_fixture() {
let staged = common::stage_fixture("coverage");
let staged = common::fixture_copy("coverage");
let root = staged.path().to_path_buf();
let out = ledger(&root, &["coverage", "--today", TODAY]);
assert_eq!(out.status.code(), Some(0), "{}", String::from_utf8_lossy(&out.stderr));
Expand All @@ -70,7 +70,7 @@ fn coverage_distinguishes_all_seven_states_on_the_fixture() {

#[test]
fn coverage_json_carries_the_states_and_the_chain_machine_readably() {
let staged = common::stage_fixture("coverage");
let staged = common::fixture_copy("coverage");
let root = staged.path().to_path_buf();
let out = ledger(&root, &["coverage", "--today", TODAY, "--json"]);
let parsed: serde_json::Value = serde_json::from_str(&stdout(&out)).expect("json");
Expand All @@ -84,7 +84,7 @@ fn coverage_json_carries_the_states_and_the_chain_machine_readably() {
fn a_superseded_versions_acceptances_are_historical_in_blame_and_status() {
// d7 (the chain root) is superseded; the fixture's `decided` decision
// d3 stays live. Status renders the supersession terminally.
let staged = common::stage_fixture("coverage");
let staged = common::fixture_copy("coverage");
let root = staged.path().to_path_buf();
let out = ledger(&root, &["status", "--today", TODAY]);
let text = stdout(&out);
Expand Down Expand Up @@ -128,7 +128,7 @@ fn latest_follows_the_parent_dag_when_ulid_order_contradicts_it() {
// a writer whose clock runs ahead. ULID order would call the root
// latest and the signed revision historical; the parent DAG knows the
// revision is the tip, so the store is conformant and decided.
let staged = common::stage_fixture("two-clocks");
let staged = common::fixture_copy("two-clocks");
let root = staged.path().to_path_buf();
let out = ledger(&root, &["verify", "--today", "2026-08-10", "--no-blame"]);
assert_eq!(out.status.code(), Some(0), "{}", String::from_utf8_lossy(&out.stderr));
Expand All @@ -150,7 +150,7 @@ fn g004_a_forked_chain_fails_verify_and_names_both_tips() {
// produces: one parent, two divergent revisions. No file is malformed;
// the graph stage names the fork and the store is non-conformant until
// a human arbitrates.
let staged = common::stage_fixture("forked");
let staged = common::fixture_copy("forked");
let root = staged.path().to_path_buf();
let out = ledger(&root, &["verify", "--today", "2026-08-10", "--no-blame"]);
assert_eq!(out.status.code(), Some(1), "{}", String::from_utf8_lossy(&out.stderr));
Expand Down
2 changes: 1 addition & 1 deletion ledger-cli/tests/landed_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const CS: &str = "01K2C4YQJ3F8M0PT5W7NZ9RDXW";
/// The `pass` fixture's store, committed.
fn landed() -> Repo {
let repo = Repo::human();
common::stage_fixture_into("pass", repo.path());
common::copy_fixture_into("pass", repo.path());
hand::commit(&repo, "landed");
repo
}
Expand Down
4 changes: 3 additions & 1 deletion ledger-cli/tests/legacy_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ const RELAYER: &str = "relayout@example";
/// commit's id.
fn flat_history() -> (Repo, String) {
let repo = Repo::with_identity(ACCEPTOR);
let from = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/pass/.decisions");
// The fixture is committed in the v1.9 layout; its files are laid flat
// here, byte for byte, as a v1.8 writer filed them.
let from = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/pass/.decisions/ns").join(NS);
for sub in ["sets", "log"] {
let to = repo.path().join(".decisions").join(sub);
std::fs::create_dir_all(&to).expect("mkdir");
Expand Down
2 changes: 1 addition & 1 deletion ledger-cli/tests/revisit_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ fn the_corrected_files_declare_exactly_what_they_need() {
/// acceptance of their own hash, and the gate finds nothing about them.
#[test]
fn the_four_acceptances_still_verify() {
let staged = common::stage_workspace();
let staged = common::workspace_copy();
let store = ledger_core::store::load(staged.path());
let view = View::build(&store);
let report = verify::verify(&store, &Options::offline(chrono::Utc::now().date_naive()));
Expand Down
2 changes: 1 addition & 1 deletion ledger-cli/tests/scalars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const CS: &str = "01K2C4YQJ3F8M0PT5W7NZ9RDXW";
/// the loader computes, committed beside the `pass` fixture's set.
fn committed(scalar: &str) -> (i32, String) {
let repo = Repo::human();
let sets = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/pass/.decisions/sets");
let sets = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/pass/.decisions/ns/fixture.ledger/sets");
std::fs::create_dir_all(repo.sets_dir("fixture.ledger")).expect("sets dir");
for entry in std::fs::read_dir(sets).expect("fixture").flatten() {
std::fs::copy(entry.path(), repo.sets_dir("fixture.ledger").join(entry.file_name())).expect("copy");
Expand Down
Loading
Loading