From a59ad5ce3ff3923306d5c34209e9661336546960 Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 2 Aug 2026 19:24:52 +0200 Subject: [PATCH] job_queue: test error truncation as the pure fn it is error_is_truncated submitted a DAG, claimed its head, failed it with a long string and read the error back out of a snapshot -- four moving parts to observe one `&str -> String`. The DAG round-trip it depended on is covered by its own tests either way. Testing truncate_error directly also reaches the case the round-trip never could: the cap is a byte length, so a multibyte char straddling it would panic the slice. That boundary scan is the only non-obvious line in the function and it had no coverage at all -- the old test used a repeated ASCII 'x', where byte and char offsets coincide. Also drops a stale claim from insert_job's doc: it has not recorded a per-node node_rt since that map was deleted. --- hive-c0re/src/job_queue/mod.rs | 3 +-- hive-c0re/src/job_queue/tests.rs | 36 +++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/hive-c0re/src/job_queue/mod.rs b/hive-c0re/src/job_queue/mod.rs index f4ed5cc0..12522f18 100644 --- a/hive-c0re/src/job_queue/mod.rs +++ b/hive-c0re/src/job_queue/mod.rs @@ -148,8 +148,7 @@ fn outcome_of(result: Result<(), String>) -> Outcome { } } -/// Insert a declared `job` into the shared graph and record its per-node -/// `node_rt`, returning the inserted ids. +/// Insert a declared `job` into the shared graph, returning the inserted ids. /// /// A node that declared no parent hangs under `group_parent` — the DAG /// container for a template, the emitting node for a runtime-appended diff --git a/hive-c0re/src/job_queue/tests.rs b/hive-c0re/src/job_queue/tests.rs index 1cfbdc35..019651f5 100644 --- a/hive-c0re/src/job_queue/tests.rs +++ b/hive-c0re/src/job_queue/tests.rs @@ -1690,18 +1690,30 @@ fn history_evicts_oldest_terminals_past_flat_cap() { } #[test] -fn error_is_truncated() { - let q = JobQueue::new(1); - let id = submit(&q, rebuild("agent-a", "r")); - let c = claim_one(&q); - q.complete_node(c.node_id, Err("x".repeat(5000))); - let snap = q.snapshot(); - let err = snap.iter().find(|d| d.id == id).expect("dag").nodes[0] - .error - .clone() - .expect("error stored"); - assert!(err.chars().count() <= 2001, "truncated + ellipsis"); - assert!(err.ends_with('…')); +fn error_truncation_cuts_on_a_char_boundary() { + // `truncate_error` is a pure `&str -> String`. This used to submit a DAG, + // claim its head, fail it with a long string and read the error back out of + // a snapshot — four moving parts to observe one transform, and the DAG + // round-trip is covered by its own tests either way. + // + // Testing it directly also reaches the case the round-trip never did: the + // cap is a **byte** length, so a multibyte char straddling it would panic + // the slice. That boundary scan is the only non-obvious line in the fn and + // it had no coverage at all. + assert_eq!( + truncate_error("short"), + "short", + "under the cap is untouched" + ); + + let ascii = truncate_error(&"x".repeat(5000)); + assert!(ascii.ends_with('…')); + assert!(ascii.len() <= MAX_ERROR_LEN + '…'.len_utf8()); + + // 'é' is 2 bytes, so the 2000-byte cap lands mid-char. + let multibyte = truncate_error(&"é".repeat(5000)); + assert!(multibyte.ends_with('…')); + assert!(multibyte.len() <= MAX_ERROR_LEN + '…'.len_utf8()); } // ---- template shapes ----