diff --git a/crates/worker-runtime/src/worker_backend/startup_binding_tests.rs b/crates/worker-runtime/src/worker_backend/startup_binding_tests.rs index 1a532fc26..0053e596c 100644 --- a/crates/worker-runtime/src/worker_backend/startup_binding_tests.rs +++ b/crates/worker-runtime/src/worker_backend/startup_binding_tests.rs @@ -3,6 +3,9 @@ mod startup_binding_tests { //! real aggregate/Session stores and reopen. Only the Workspace response is //! synthetic; this suite does not test domain authorization or network transport. use super::*; + use protocol::session_failure::{ + SessionPrincipal, SessionRegistrationCause, session_registration_attachment, + }; use protocol::startup_failure::{RuntimeStartupCause, StartupPersistenceOutcome}; use session_store::worker_metadata::StartupFailureRead; use worker::{WorkspaceClientError, WorkspaceRequest, WorkspaceResponse}; @@ -67,13 +70,27 @@ mod startup_binding_tests { _ => self.store.read_by_name(&self.name).unwrap().unwrap(), }; if matches!(self.response, Response::Lost) { - return Err(WorkspaceClientError::Unavailable( + return Err(WorkspaceClientError::Request( "response deliberately lost".into(), )); } Ok(WorkspaceResponse { status: 403, - body: "synthetic rejection with private token /internal/path".into(), + body: serde_json::to_string( + &server_api::RepositoryApiError::new( + 403, + "PermissionDenied", + "synthetic rejection with private token /internal/path", + vec![], + ) + .with_failure( + session_registration_attachment(SessionRegistrationCause::denied( + SessionPrincipal::Subject, + )) + .unwrap(), + ), + ) + .unwrap(), }) } } @@ -138,8 +155,25 @@ mod startup_binding_tests { .err() .expect("failure must remain a failure"); let result = failure.execution_result(WorkerExecutionOperation::Spawn); - let primary = result.startup_failure::().unwrap(); - assert!(matches!(primary, error_contract::DecodeFailure::Known(_))); + if matches!(response, Response::BeforeSession | Response::BeforeOwner) { + let primary = result.startup_failure::().unwrap(); + assert!(matches!(primary, error_contract::DecodeFailure::Known(_))); + } else { + let error_contract::DecodeFailure::Known(primary) = result + .startup_failure::() + .unwrap() + else { + panic!("a Session decision must not be relabeled as a generic Runtime failure"); + }; + assert_eq!( + *primary.facts().cause(), + if matches!(response, Response::Lost) { + SessionRegistrationCause::outcome_unknown() + } else { + SessionRegistrationCause::denied(SessionPrincipal::Subject) + } + ); + } drop(worker); drop(store); let reopened = WorkerAggregateStore::new(root.path(), name).unwrap(); @@ -151,13 +185,17 @@ mod startup_binding_tests { Some(StartupPersistenceOutcome::Persisted) ); let StartupFailureRead::Present(error_contract::DecodeFailure::Known(cause)) = - metadata.decode_startup_failure::() + metadata.decode_startup_failure::() else { panic!("same-Session failure must actually save a typed cause") }; assert_eq!( - cause.facts().cause().reason, - protocol::startup_failure::RuntimeStartupReason::AttributionFailed + *cause.facts().cause(), + if matches!(response, Response::Lost) { + SessionRegistrationCause::outcome_unknown() + } else { + SessionRegistrationCause::denied(SessionPrincipal::Subject) + } ); assert_eq!(metadata.active.unwrap().session_id, origin); let saved = serde_json::to_string(&metadata.startup_failure).unwrap();