huskies: merge 1007

This commit is contained in:
dave
2026-05-14 08:36:46 +00:00
parent 13ab97a615
commit 761b6934f1
7 changed files with 215 additions and 7 deletions
+73
View File
@@ -507,3 +507,76 @@ fn list_refactor_files_returns_ok() {
let result = list_refactor_files(tmp.path());
assert!(result.is_ok());
}
/// Regression for bug 1001: 8 rapid sequential `create_refactor` calls with
/// `depends_on` chains must all land cleanly in BOTH the content store AND
/// the CRDT. Before the fix, the allocator could return a tombstoned ID on
/// a tight loop, producing a half-written item invisible to `list_refactors`
/// and `update_story`.
#[test]
fn rapid_sequential_creates_all_land_in_crdt() {
crate::crdt_state::init_for_test();
crate::db::ensure_content_store();
let tmp = tempfile::tempdir().unwrap();
let mut ids: Vec<String> = Vec::new();
for i in 0..8u32 {
// Build a depends_on chain: item i depends on item i-1.
let depends_on: Option<Vec<u32>> = if ids.is_empty() {
None
} else {
let last_num: u32 = ids.last().unwrap().parse().unwrap_or(0);
Some(vec![last_num])
};
let id = create_refactor_file(
tmp.path(),
&format!("Rapid Refactor {i}"),
None,
&[format!("AC for rapid refactor {i}")],
depends_on.as_deref(),
)
.unwrap_or_else(|e| panic!("create_refactor_file {i} failed: {e}"));
// Immediately verify it landed in CRDT (the core transactional guarantee).
assert!(
crate::crdt_state::read_item(&id).is_some(),
"refactor {i} (id={id}) must be in CRDT immediately after create"
);
ids.push(id);
}
assert_eq!(ids.len(), 8, "all 8 creates must succeed");
assert_eq!(
ids.iter().collect::<std::collections::HashSet<_>>().len(),
8,
"all 8 IDs must be distinct"
);
// list_refactor_files must return all 8 (reads from CRDT).
let listed = list_refactor_files(tmp.path()).expect("list_refactor_files must succeed");
let listed_ids: std::collections::HashSet<&str> =
listed.iter().map(|(id, _)| id.as_str()).collect();
for id in &ids {
assert!(
listed_ids.contains(id.as_str()),
"refactor '{id}' must appear in list_refactor_files: {listed_ids:?}"
);
}
// set_name (the update_story --name path) must succeed for all 8.
for (i, id) in ids.iter().enumerate() {
let new_name = format!("Renamed Rapid Refactor {i}");
let success = crate::crdt_state::set_name(id, Some(&new_name));
assert!(success, "set_name for refactor {i} (id={id}) must succeed");
let view = crate::crdt_state::read_item(id)
.unwrap_or_else(|| panic!("item {i} (id={id}) must still be in CRDT after rename"));
assert_eq!(
view.name(),
new_name,
"CRDT must reflect the new name for refactor {i}"
);
}
}