From 81ec58cbfd3bcb66fd579f5bd47300b8b1cf71da Mon Sep 17 00:00:00 2001 From: Olav Groenaas Gjerde Date: Thu, 17 Sep 2026 15:58:13 +0200 Subject: [PATCH] fix(tests): two live tests read the problem the api now sends The api's errors series changed two refusals these tests still pinned to their old shape, and both fail against current datahub-platform main: - A delete that would strand a node is a 409 would-strand (api commit 7fbbb565), not a 400. AGENTS.md already says so. The test now checks the status and the problem type, and finds the stranded node in blockedBy instead of in the message. - Datapoints sent to a series that does not exist get a 404 not-found problem that lists the skipped series in `missing` (api #98). The old wording, "Could not find following timeseries", is gone. The test checks the problem type and that `missing` names the series. Both now read the problem's type and members rather than its prose, as problem_integration does, so rewording a detail cannot break them. The binary-path test is unchanged: the SDK raises that 404 itself. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Olav Groenaas Gjerde --- src/relations/tests.rs | 21 ++++++++++++--------- src/timeseries/test.rs | 18 ++++++++++++------ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/relations/tests.rs b/src/relations/tests.rs index a174475..18cc2d4 100644 --- a/src/relations/tests.rs +++ b/src/relations/tests.rs @@ -457,20 +457,23 @@ mod live { .expect_err("deleting c's last route to the root should be refused"); assert_eq!( refused.get_status().as_u16(), - 400, - "a stranding delete is a client error, not a server fault: {refused:?}" + 409, + "a stranding delete conflicts with the graph as it stands: {refused:?}" + ); + assert_eq!( + refused.problem_slug().as_deref(), + Some("would-strand"), + "the refusal should say what it is refusing: {refused:?}" ); // With `a` declared root, the victim is unambiguous: `c` is the node that loses its last // route. Without a declared root this named a different node between runs, because the // projection picked the anchor itself. - let message = refused.get_message(); + let blocked_by = refused.problem().map(|p| p.blocked_by()).unwrap_or_default(); assert!( - message.contains("disconnect"), - "the refusal should say what it is refusing: {message}" - ); - assert!( - message.contains(c), - "the refusal should name {c} as the stranded resource: {message}" + blocked_by + .iter() + .any(|b| b.get("externalId").and_then(serde_json::Value::as_str) == Some(c)), + "the refusal should name {c} as the stranded resource: {blocked_by:?}" ); // Tear down in one request. Deleting these nodes individually is perfectly legal — diff --git a/src/timeseries/test.rs b/src/timeseries/test.rs index 4512a75..b0072d5 100644 --- a/src/timeseries/test.rs +++ b/src/timeseries/test.rs @@ -1531,18 +1531,24 @@ mod tests { Ok(_) => panic!("Expected 404 Not Found for non-existent timeseries"), Err(e) => { assert_eq!(e.get_status(), StatusCode::NOT_FOUND); - let msg = e.get_message(); - assert!( - msg.contains("Could not find following timeseries"), - "unexpected error body: {msg}" - ); + let problem = e.problem().unwrap_or_else(|| panic!("not a problem document: {}", e.get_message())); + assert_eq!(problem.slug(), Some("not-found"), "{problem:?}"); + // `missing` names each series whose data-points were skipped. + let named = problem + .extensions + .get("missing") + .and_then(serde_json::Value::as_array) + .is_some_and(|missing| { + missing.iter().any(|m| m.get("externalId").and_then(serde_json::Value::as_str) == Some(missing_ext_id.as_str())) + }); + assert!(named, "the refusal should name {missing_ext_id}: {problem:?}"); } } Ok(()) } /// The binary path resolves every series before it builds a frame, so a missing one is - /// refused here, with the JSON path's wording, and no frame is ever sent. + /// refused here, by the SDK itself, and no frame is ever sent. #[tokio::test] async fn test_insert_datapoints_binary_missing_timeseries_returns_not_found() -> Result<(), Box> { let api_service = create_api_service();