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.
This commit is contained in:
parent
335ad5e0ee
commit
a59ad5ce3f
2 changed files with 25 additions and 14 deletions
|
|
@ -148,8 +148,7 @@ fn outcome_of(result: Result<(), String>) -> Outcome {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a declared `job` into the shared graph and record its per-node
|
/// Insert a declared `job` into the shared graph, returning the inserted ids.
|
||||||
/// `node_rt`, returning the inserted ids.
|
|
||||||
///
|
///
|
||||||
/// A node that declared no parent hangs under `group_parent` — the DAG
|
/// A node that declared no parent hangs under `group_parent` — the DAG
|
||||||
/// container for a template, the emitting node for a runtime-appended
|
/// container for a template, the emitting node for a runtime-appended
|
||||||
|
|
|
||||||
|
|
@ -1690,18 +1690,30 @@ fn history_evicts_oldest_terminals_past_flat_cap() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn error_is_truncated() {
|
fn error_truncation_cuts_on_a_char_boundary() {
|
||||||
let q = JobQueue::new(1);
|
// `truncate_error` is a pure `&str -> String`. This used to submit a DAG,
|
||||||
let id = submit(&q, rebuild("agent-a", "r"));
|
// claim its head, fail it with a long string and read the error back out of
|
||||||
let c = claim_one(&q);
|
// a snapshot — four moving parts to observe one transform, and the DAG
|
||||||
q.complete_node(c.node_id, Err("x".repeat(5000)));
|
// round-trip is covered by its own tests either way.
|
||||||
let snap = q.snapshot();
|
//
|
||||||
let err = snap.iter().find(|d| d.id == id).expect("dag").nodes[0]
|
// Testing it directly also reaches the case the round-trip never did: the
|
||||||
.error
|
// cap is a **byte** length, so a multibyte char straddling it would panic
|
||||||
.clone()
|
// the slice. That boundary scan is the only non-obvious line in the fn and
|
||||||
.expect("error stored");
|
// it had no coverage at all.
|
||||||
assert!(err.chars().count() <= 2001, "truncated + ellipsis");
|
assert_eq!(
|
||||||
assert!(err.ends_with('…'));
|
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 ----
|
// ---- template shapes ----
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue