P2: tests mutation found, three bugs fixed; plan P12 and P13
Mutation testing pauses until P12 (D-33, owner 2026-10-20), which reruns
the whole max-testing pass over everything since P1. Before the pause,
pass 1 on slices 3-6 and its triage gave tests in ledger-store,
ledger-package and ledger-mcp, and three real bugs, fixed:
- the command line took an admin answer longer than four MiB, or cut off
before its newline, whenever the bytes that came still parsed;
- The CPA package put a twin's number on the last dot of the whole path,
so in a folder with a dot the second file landed in another folder;
- a document or category named of dots alone ("..") could climb out of
its folder when the CPA package is unpacked.
The plan gains P13, bookspec (D-34): a formal, sourced accounting
specification first (rules with symbolic forms and worked examples), then
its own formal validation and a conformance harness any engine can be
tested against, seeded by P3's regression tests. docs/testing.md records
slices 3-8 and what P12 must cover; plan/STATUS.md says where P2 stands.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
1 parent
3cf359a0cd
commit
d5c9522ad1
24 files changed
+2937
-20
No files matched your search
@@ -441,6 +441,64 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_log_the_disk_wont_read_is_refused_not_taken_for_empty() {
|
||||
// A folder where the log should be: reading it fails, and an empty
|
||||
// log is never made of that.
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = dir.path().join("log.jsonl");
|
||||
std::fs::create_dir(&path).unwrap();
|
||||
assert!(matches!(read(&path, G), Err(ChainError::Io(_))));
|
||||
assert!(matches!(ChainLog::open(&path, G), Err(ChainError::Io(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_log_shows_its_head_but_never_its_path() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = three(dir.path());
|
||||
let (log, _) = ChainLog::open(&path, G).unwrap();
|
||||
let shown = format!("{log:?}");
|
||||
assert!(shown.starts_with("ChainLog"), "{shown}");
|
||||
assert!(shown.contains("seq: 3"), "{shown}");
|
||||
assert!(shown.contains(&log.head().hash), "{shown}");
|
||||
assert!(shown.contains("broken: false"), "{shown}");
|
||||
assert!(
|
||||
!shown.contains(&dir.path().display().to_string()),
|
||||
"{shown}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn only_a_new_log_syncs_its_folder_and_only_others_bits_are_taken_away() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = three(dir.path());
|
||||
// The owner's own bits are the owner's: a log 0700 stays 0700.
|
||||
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o700)).unwrap();
|
||||
drop(ChainLog::open(&path, G).unwrap());
|
||||
let mode = std::fs::metadata(&path).unwrap().permissions().mode();
|
||||
assert_eq!(mode & 0o777, 0o700);
|
||||
// A folder that can be entered but not listed: a log already in it
|
||||
// opens (its folder's entry for it was synced when it was made);
|
||||
// a new one can't be made there, as its entry can't be synced.
|
||||
let shut = dir.path().join("shut");
|
||||
std::fs::create_dir(&shut).unwrap();
|
||||
let (mut log, _) = ChainLog::open(&shut.join("old.jsonl"), G).unwrap();
|
||||
log.append(fields(1)).unwrap();
|
||||
drop(log);
|
||||
std::fs::set_permissions(&shut, std::fs::Permissions::from_mode(0o300)).unwrap();
|
||||
if File::open(&shut).is_err() {
|
||||
let (_, records) = ChainLog::open(&shut.join("old.jsonl"), G).unwrap();
|
||||
assert_eq!(records.len(), 1);
|
||||
assert_eq!(
|
||||
ChainLog::open(&shut.join("new.jsonl"), G).unwrap_err(),
|
||||
ChainError::Io(std::io::ErrorKind::PermissionDenied)
|
||||
);
|
||||
}
|
||||
std::fs::set_permissions(&shut, std::fs::Permissions::from_mode(0o700)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_unfinished_last_line_is_cut_away_on_open() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
@@ -484,6 +542,23 @@ mod tests {
|
||||
] {
|
||||
assert!(!e.to_string().contains('/'), "{e}");
|
||||
}
|
||||
// Each says what went wrong, and where in the log.
|
||||
assert_eq!(
|
||||
ChainError::Damaged {
|
||||
line: 3,
|
||||
why: "its number is out of order".into(),
|
||||
}
|
||||
.to_string(),
|
||||
"record 3 is damaged: its number is out of order"
|
||||
);
|
||||
assert_eq!(
|
||||
ChainError::Io(std::io::ErrorKind::PermissionDenied).to_string(),
|
||||
"the disk refused: permission denied"
|
||||
);
|
||||
assert_eq!(
|
||||
ChainError::Broken.to_string(),
|
||||
"an earlier record couldn't be written"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
|
||||
Reference in new issue
Block a user