#![allow(clippy::expect_used, clippy::indexing_slicing, clippy::unwrap_used)]
#[path = "support/mod.rs"]
mod support;
use support::*;
#[cfg(unix)]
fn set_mode(path: &std::path::Path, mode: u32) {
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(path).unwrap().permissions();
perms.set_mode(mode);
std::fs::set_permissions(path, perms).unwrap();
}
#[cfg(unix)]
fn mode_of(path: &std::path::Path) -> u32 {
use std::os::unix::fs::PermissionsExt;
std::fs::metadata(path).unwrap().permissions().mode() & 0o777
}
#[test]
fn replace_binary_materializes_byte_exact_across_sealed_generations() {
let repo = unique_repo("dc73-replace-binary-generations");
init(&repo);
generation(
&repo,
"heads/main",
"bin.dat",
&[0xff, 0x00, 0xfe, 0x01],
"genesis",
);
generation(
&repo,
"heads/main",
"bin.dat",
&[0xaa, 0xbb, 0xcc, 0xdd],
"generation 2",
);
generation(
&repo,
"heads/main",
"bin.dat",
&[0xff],
"generation 3 — shrink to one byte",
);
let materialize_root = rebuild_from_sealed_history(&repo, "dc73-replace-binary");
let rebuilt = std::fs::read(materialize_root.join("bin.dat")).unwrap();
assert_eq!(rebuilt, vec![0xff]);
}
#[test]
#[cfg(unix)]
fn change_perm_materializes_mode_exact_across_sealed_generations() {
let repo = unique_repo("dc73-change-perm-generations");
init(&repo);
std::fs::write(repo.join("a.txt"), b"content").unwrap();
ok(&commit(&repo, "heads/main", "genesis"), "genesis commit");
ok(&seal(&repo, "heads/main"), "genesis seal");
set_mode(&repo.join("a.txt"), 0o755);
ok(
&commit(&repo, "heads/main", "make executable"),
"commit change-perm",
);
ok(&seal(&repo, "heads/main"), "seal change-perm");
set_mode(&repo.join("a.txt"), 0o644);
ok(
&commit(&repo, "heads/main", "make non-executable again"),
"commit second change-perm",
);
ok(&seal(&repo, "heads/main"), "seal second change-perm");
let materialize_root = rebuild_from_sealed_history(&repo, "dc73-change-perm");
let rebuilt_path = materialize_root.join("a.txt");
assert_eq!(std::fs::read(&rebuilt_path).unwrap(), b"content");
assert_eq!(
mode_of(&rebuilt_path),
0o644,
"expected the final mode to survive materialization"
);
}
#[test]
#[cfg(unix)]
fn create_file_mode_survives_materialization_without_any_change_perm() {
let repo = unique_repo("dc73-createfile-mode-only");
init(&repo);
std::fs::write(repo.join("script.sh"), b"#!/bin/sh\necho hi\n").unwrap();
set_mode(&repo.join("script.sh"), 0o755);
ok(
&commit(&repo, "heads/main", "genesis with executable file"),
"genesis commit",
);
ok(&seal(&repo, "heads/main"), "genesis seal");
let materialize_root = rebuild_from_sealed_history(&repo, "dc73-createfile-mode");
let rebuilt_path = materialize_root.join("script.sh");
assert_eq!(mode_of(&rebuilt_path), 0o755);
}
#[test]
fn inverse_plan_succeeds_for_replace_binary_and_change_perm_history() {
let repo = unique_repo("dc73-inverse-plan");
init(&repo);
std::fs::write(repo.join("bin.dat"), [0xff, 0x00, 0xfe]).unwrap();
ok(&commit(&repo, "heads/main", "genesis"), "genesis commit");
ok(&seal(&repo, "heads/main"), "genesis seal");
std::fs::write(repo.join("bin.dat"), [0xff, 0xee, 0xdd]).unwrap();
ok(
&commit(&repo, "heads/main", "replace binary"),
"commit replace-binary",
);
ok(&seal(&repo, "heads/main"), "seal replace-binary");
#[cfg(unix)]
{
set_mode(&repo.join("bin.dat"), 0o755);
ok(
&commit(&repo, "heads/main", "change perm"),
"commit change-perm",
);
ok(&seal(&repo, "heads/main"), "seal change-perm");
}
let out = prikk(&repo).arg("inverse-plan").output().unwrap();
ok(&out, "inverse-plan");
let stdout = String::from_utf8_lossy(&out.stdout);
eprintln!("inverse-plan stdout: {stdout}");
assert!(
stdout.contains("replace-binary"),
"expected a replace-binary inverse: {stdout}"
);
#[cfg(unix)]
assert!(
stdout.contains("change-perm"),
"expected a change-perm inverse: {stdout}"
);
}