refactor: rename soft-delete marker from .deleted to .closed

Internal rename: DELETE_MARKER -> CLOSE_MARKER, DeleteMarker ->
CloseMarker, is_deleted -> is_closed. The batch UUID is dropped;
undo now groups cases by their exact closed_at timestamp, which
bulk-close writes identically across its selection while per-case
close writes a unique stamp.

Behavior unchanged. No migration (project is pre-production);
legacy .deleted files in tmpdata were removed manually.
This commit is contained in:
2026-04-21 10:36:55 +02:00
parent a8389a89db
commit 9410d6daaa
13 changed files with 131 additions and 145 deletions
+20 -20
View File
@@ -628,14 +628,13 @@ async fn delete_writes_marker_and_redirects() {
.unwrap(),
"/web/cases"
);
let marker = case_dir.join(".deleted");
assert!(marker.exists(), ".deleted marker missing");
let marker = case_dir.join(".closed");
assert!(marker.exists(), ".closed marker missing");
let raw = std::fs::read_to_string(&marker).unwrap();
let parsed: Value = serde_json::from_str(&raw).unwrap();
assert!(parsed["batch"].is_string(), "batch must be a UUID string");
assert!(
parsed["deleted_at"].is_string(),
"deleted_at must be a string"
parsed["closed_at"].is_string(),
"closed_at must be a string"
);
}
@@ -670,7 +669,7 @@ async fn deleted_case_returns_404_on_detail() {
}
#[tokio::test]
async fn undo_delete_restores_latest_batch_only() {
async fn undo_delete_restores_latest_group_only() {
let config = config_with_llm(unique_tmp("undo-1"), "http://unused".into());
let case_a = "11111111-1111-1111-1111-111111111111";
let case_b = "22222222-2222-2222-2222-222222222222";
@@ -682,15 +681,16 @@ async fn undo_delete_restores_latest_batch_only() {
let app = doctate_server::create_router(config);
let cookie = login(app.clone(), "dr_a").await;
// Two separate delete clicks → two distinct batches.
// Two separate close clicks → two distinct close-groups (distinct
// `closed_at` timestamps).
let _ = app
.clone()
.oneshot(delete_request(case_a, &cookie))
.await
.unwrap();
// Batch IDs derive from `now_rfc3339()`, which rounds to whole
// seconds — so the sleep must cross a second boundary for the two
// deletes to land in distinct batches.
// `closed_at` is `now_rfc3339()` with whole-second granularity — the
// sleep must cross a second boundary so the two closes land in
// distinct groups.
tokio::time::sleep(Duration::from_millis(1100)).await;
let _ = app
.clone()
@@ -712,16 +712,16 @@ async fn undo_delete_restores_latest_batch_only() {
.unwrap();
assert_eq!(undo.status(), StatusCode::SEE_OTHER);
// case_b (latest delete) restored, case_a still deleted.
// case_b (latest close) restored, case_a still closed.
assert!(
!dir_b.join(".deleted").exists(),
!dir_b.join(".closed").exists(),
"case_b marker should be gone"
);
assert!(dir_a.join(".deleted").exists(), "case_a marker must remain");
assert!(dir_a.join(".closed").exists(), "case_a marker must remain");
}
#[tokio::test]
async fn bulk_delete_shares_one_batch_uuid() {
async fn bulk_delete_shares_one_closed_at_timestamp() {
let config = config_with_llm(unique_tmp("bulk-d"), "http://unused".into());
let case_a = "11111111-1111-1111-1111-111111111111";
let case_b = "22222222-2222-2222-2222-222222222222";
@@ -749,12 +749,12 @@ async fn bulk_delete_shares_one_batch_uuid() {
assert_eq!(resp.status(), StatusCode::SEE_OTHER);
let m_a: Value =
serde_json::from_str(&std::fs::read_to_string(dir_a.join(".deleted")).unwrap()).unwrap();
serde_json::from_str(&std::fs::read_to_string(dir_a.join(".closed")).unwrap()).unwrap();
let m_b: Value =
serde_json::from_str(&std::fs::read_to_string(dir_b.join(".deleted")).unwrap()).unwrap();
serde_json::from_str(&std::fs::read_to_string(dir_b.join(".closed")).unwrap()).unwrap();
assert_eq!(
m_a["batch"], m_b["batch"],
"bulk-delete must share batch UUID"
m_a["closed_at"], m_b["closed_at"],
"bulk-delete must share one closed_at timestamp so undo can group them"
);
}
@@ -797,8 +797,8 @@ async fn recovery_skips_deleted_cases() {
std::fs::create_dir_all(&case_dir).unwrap();
std::fs::write(case_dir.join("analysis_input.json"), "{}").unwrap();
std::fs::write(
case_dir.join(".deleted"),
r#"{"batch":"00000000-0000-0000-0000-000000000000","deleted_at":"2026-04-15T10:00:00Z"}"#,
case_dir.join(".closed"),
r#"{"closed_at":"2026-04-15T10:00:00Z"}"#,
)
.unwrap();