use std::collections::BTreeMap;
use lifeloop::router::{AdapterRegistry, BuiltinAdapterRegistry, RouteError, RoutingPlan, route};
use lifeloop::{
AdapterManifest, AdapterRole, CallbackRequest, ConformanceLevel, FrameClass, FrameContext,
IntegrationMode, LifecycleEventKind, ManifestContextPressure, ManifestPlacementClass,
ManifestPlacementSupport, ManifestReceipts, PayloadRef, RegisteredAdapter, SCHEMA_VERSION,
SupportState,
};
const FAKE_ADAPTER_ID: &str = "fake-adapter";
const FAKE_ADAPTER_VERSION: &str = "0.0.1";
const FAKE_ADAPTER_OTHER_VERSION: &str = "9.9.9";
fn fake_manifest() -> AdapterManifest {
AdapterManifest {
contract_version: SCHEMA_VERSION.to_string(),
adapter_id: FAKE_ADAPTER_ID.into(),
adapter_version: FAKE_ADAPTER_VERSION.into(),
display_name: "Fake".into(),
role: AdapterRole::PrimaryWorker,
integration_modes: vec![IntegrationMode::NativeHook],
lifecycle_events: BTreeMap::new(),
placement: BTreeMap::from([(
ManifestPlacementClass::PreSession,
ManifestPlacementSupport {
support: SupportState::Native,
max_bytes: None,
},
)]),
context_pressure: ManifestContextPressure {
support: SupportState::Unavailable,
evidence: None,
},
receipts: ManifestReceipts {
native: false,
lifeloop_synthesized: true,
receipt_ledger: SupportState::Unavailable,
},
session_identity: None,
session_rename: None,
renewal: None,
approval_surface: None,
failure_modes: Vec::new(),
telemetry_sources: Vec::new(),
known_degradations: Vec::new(),
}
}
struct FakeRegistry {
manifest: AdapterManifest,
}
impl FakeRegistry {
fn new() -> Self {
Self {
manifest: fake_manifest(),
}
}
}
impl AdapterRegistry for FakeRegistry {
fn resolve(
&self,
adapter_id: &str,
adapter_version: &str,
) -> lifeloop::router::AdapterResolution {
use lifeloop::router::AdapterResolution;
if adapter_id != self.manifest.adapter_id {
return AdapterResolution::UnknownId;
}
if adapter_version != self.manifest.adapter_version {
return AdapterResolution::VersionMismatch {
registered_version: self.manifest.adapter_version.clone(),
};
}
AdapterResolution::Found(RegisteredAdapter {
manifest: self.manifest.clone(),
conformance: ConformanceLevel::PreConformance,
})
}
}
fn valid_request() -> CallbackRequest {
CallbackRequest {
schema_version: SCHEMA_VERSION.to_string(),
event: LifecycleEventKind::SessionStarted,
event_id: "evt-1".into(),
adapter_id: FAKE_ADAPTER_ID.into(),
adapter_version: FAKE_ADAPTER_VERSION.into(),
integration_mode: IntegrationMode::NativeHook,
invocation_id: "inv-1".into(),
harness_session_id: Some("sess-1".into()),
harness_run_id: None,
harness_task_id: None,
frame_context: None,
capability_snapshot_ref: None,
payload_refs: Vec::new(),
sequence: None,
idempotency_key: None,
metadata: serde_json::Map::new(),
}
}
#[test]
fn route_produces_plan_for_valid_request_against_fake_registry() {
let reg = FakeRegistry::new();
let req = valid_request();
let plan: RoutingPlan = route(&req, ®).expect("route");
assert_eq!(plan.event, LifecycleEventKind::SessionStarted);
assert_eq!(plan.event_id, "evt-1");
assert_eq!(plan.invocation_id, "inv-1");
assert_eq!(plan.adapter.adapter_id, FAKE_ADAPTER_ID);
assert_eq!(plan.adapter.adapter_version, FAKE_ADAPTER_VERSION);
assert_eq!(plan.integration_mode, IntegrationMode::NativeHook);
}
#[test]
fn route_preserves_opaque_payload_refs_unchanged() {
let reg = FakeRegistry::new();
let mut req = valid_request();
let refs = vec![
PayloadRef {
payload_id: "p-1".into(),
payload_kind: "ccd.frame_card".into(),
content_digest: Some("sha256:abc".into()),
byte_size: Some(42),
},
PayloadRef {
payload_id: "p-2".into(),
payload_kind: "client.opaque".into(),
content_digest: None,
byte_size: None,
},
];
req.payload_refs = refs.clone();
let plan = route(&req, ®).expect("route");
assert_eq!(plan.payload_refs, refs);
}
#[test]
fn route_against_builtin_registry_resolves_codex() {
let req = CallbackRequest {
adapter_id: "codex".into(),
adapter_version: "0.1.0".into(),
..valid_request()
};
let plan = route(&req, &BuiltinAdapterRegistry).expect("route");
assert_eq!(plan.adapter.adapter_id, "codex");
}
#[test]
fn route_rejects_schema_version_mismatch() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.schema_version = "lifeloop.v0.0".into();
let err = route(&req, ®).unwrap_err();
assert!(
matches!(err, RouteError::SchemaVersionMismatch { .. }),
"got {err:?}"
);
}
#[test]
fn route_rejects_empty_event_id() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.event_id = String::new();
match route(&req, ®).unwrap_err() {
RouteError::EmptySentinel { field } => assert_eq!(field, "request.event_id"),
other => panic!("expected EmptySentinel, got {other:?}"),
}
}
#[test]
fn route_rejects_empty_adapter_id() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.adapter_id = String::new();
assert!(matches!(
route(&req, ®).unwrap_err(),
RouteError::EmptySentinel {
field: "request.adapter_id"
}
));
}
#[test]
fn route_rejects_empty_adapter_version() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.adapter_version = String::new();
assert!(matches!(
route(&req, ®).unwrap_err(),
RouteError::EmptySentinel {
field: "request.adapter_version"
}
));
}
#[test]
fn route_rejects_empty_invocation_id() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.invocation_id = String::new();
assert!(matches!(
route(&req, ®).unwrap_err(),
RouteError::EmptySentinel {
field: "request.invocation_id"
}
));
}
#[test]
fn route_rejects_empty_optional_when_present() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.harness_session_id = Some(String::new());
assert!(matches!(
route(&req, ®).unwrap_err(),
RouteError::EmptySentinel {
field: "request.harness_session_id"
}
));
}
#[test]
fn unknown_event_wire_name_is_rejected_at_deserialize_boundary() {
let raw = serde_json::json!({
"schema_version": SCHEMA_VERSION,
"event": "session.bogus",
"event_id": "e",
"adapter_id": FAKE_ADAPTER_ID,
"adapter_version": FAKE_ADAPTER_VERSION,
"integration_mode": "native_hook",
"invocation_id": "i",
});
let r: Result<CallbackRequest, _> = serde_json::from_value(raw);
assert!(
r.is_err(),
"deserialize should reject unknown event wire name"
);
}
#[test]
fn unknown_integration_mode_wire_name_is_rejected_at_deserialize_boundary() {
let raw = serde_json::json!({
"schema_version": SCHEMA_VERSION,
"event": "session.started",
"event_id": "e",
"adapter_id": FAKE_ADAPTER_ID,
"adapter_version": FAKE_ADAPTER_VERSION,
"integration_mode": "warp_drive",
"invocation_id": "i",
});
let r: Result<CallbackRequest, _> = serde_json::from_value(raw);
assert!(
r.is_err(),
"deserialize should reject unknown integration_mode wire name"
);
}
#[test]
fn route_error_unknown_event_name_variant_constructs_and_displays() {
let err = RouteError::UnknownEventName {
received: "session.bogus".into(),
};
let s = format!("{err}");
assert!(s.contains("session.bogus"), "{s}");
}
#[test]
fn route_rejects_subcall_without_parent_frame_id() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.event = LifecycleEventKind::FrameOpened;
req.frame_context = Some(FrameContext {
frame_id: "f-1".into(),
parent_frame_id: None,
frame_class: FrameClass::Subcall,
});
assert!(matches!(
route(&req, ®).unwrap_err(),
RouteError::InvalidFrameContext { .. }
));
}
#[test]
fn route_rejects_top_level_with_parent_frame_id() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.event = LifecycleEventKind::FrameOpened;
req.frame_context = Some(FrameContext {
frame_id: "f-1".into(),
parent_frame_id: Some("f-0".into()),
frame_class: FrameClass::TopLevel,
});
assert!(matches!(
route(&req, ®).unwrap_err(),
RouteError::InvalidFrameContext { .. }
));
}
#[test]
fn route_rejects_empty_frame_id() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.event = LifecycleEventKind::FrameOpened;
req.frame_context = Some(FrameContext {
frame_id: String::new(),
parent_frame_id: None,
frame_class: FrameClass::TopLevel,
});
assert!(matches!(
route(&req, ®).unwrap_err(),
RouteError::InvalidFrameContext { .. }
));
}
#[test]
fn route_rejects_frame_event_missing_frame_context() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.event = LifecycleEventKind::FrameOpened;
req.frame_context = None;
assert!(matches!(
route(&req, ®).unwrap_err(),
RouteError::InvalidFrameContext { .. }
));
}
#[test]
fn route_rejects_receipt_emitted_with_idempotency_key() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.event = LifecycleEventKind::ReceiptEmitted;
req.idempotency_key = Some("idem-1".into());
assert!(matches!(
route(&req, ®).unwrap_err(),
RouteError::InvalidEventEnvelope { .. }
));
}
#[test]
fn route_rejects_payload_ref_with_empty_payload_id() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.payload_refs = vec![PayloadRef {
payload_id: String::new(),
payload_kind: "k".into(),
content_digest: None,
byte_size: None,
}];
match route(&req, ®).unwrap_err() {
RouteError::InvalidPayloadRef { index, .. } => assert_eq!(index, 0),
other => panic!("expected InvalidPayloadRef, got {other:?}"),
}
}
#[test]
fn route_rejects_payload_ref_with_empty_payload_kind_at_correct_index() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.payload_refs = vec![
PayloadRef {
payload_id: "ok".into(),
payload_kind: "k".into(),
content_digest: None,
byte_size: None,
},
PayloadRef {
payload_id: "p2".into(),
payload_kind: String::new(),
content_digest: None,
byte_size: None,
},
];
match route(&req, ®).unwrap_err() {
RouteError::InvalidPayloadRef { index, .. } => assert_eq!(index, 1),
other => panic!("expected InvalidPayloadRef, got {other:?}"),
}
}
#[test]
fn route_rejects_unknown_adapter_id() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.adapter_id = "ghost".into();
match route(&req, ®).unwrap_err() {
RouteError::AdapterIdNotFound { adapter_id } => assert_eq!(adapter_id, "ghost"),
other => panic!("expected AdapterIdNotFound, got {other:?}"),
}
}
#[test]
fn route_distinguishes_adapter_version_mismatch_from_unknown_id() {
let reg = FakeRegistry::new();
let mut req = valid_request();
req.adapter_version = FAKE_ADAPTER_OTHER_VERSION.into();
match route(&req, ®).unwrap_err() {
RouteError::AdapterVersionMismatch {
adapter_id,
requested,
registered,
} => {
assert_eq!(adapter_id, FAKE_ADAPTER_ID);
assert_eq!(requested, FAKE_ADAPTER_OTHER_VERSION);
assert_eq!(registered, FAKE_ADAPTER_VERSION);
}
other => panic!("expected AdapterVersionMismatch, got {other:?}"),
}
}