Files
codewhale/crates/protocol/tests/parity_protocol.rs
T

161 lines
5.8 KiB
Rust

use codewhale_protocol::{
EventFrame, ThreadListParams, ThreadRequest, ThreadResumeParams,
runtime::{RUNTIME_EVENT_ENVELOPE_SCHEMA_VERSION, RuntimeEventEnvelope},
};
use serde_json::{Value, json};
#[test]
fn thread_resume_params_round_trip() {
let request = ThreadRequest::Resume(ThreadResumeParams {
thread_id: "thread-123".to_string(),
history: None,
path: None,
model: Some("deepseek-v4-pro".to_string()),
model_provider: Some("deepseek".to_string()),
cwd: None,
approval_policy: Some("on-request".to_string()),
sandbox: Some("workspace-write".to_string()),
config: None,
base_instructions: Some("base".to_string()),
developer_instructions: Some("dev".to_string()),
personality: Some("default".to_string()),
persist_extended_history: true,
});
let encoded = serde_json::to_string(&request).expect("serialize request");
let decoded: ThreadRequest = serde_json::from_str(&encoded).expect("deserialize request");
match decoded {
ThreadRequest::Resume(params) => {
assert_eq!(params.thread_id, "thread-123");
assert_eq!(params.model.as_deref(), Some("deepseek-v4-pro"));
assert!(params.persist_extended_history);
}
other => panic!("unexpected request: {other:?}"),
}
}
#[test]
fn thread_list_params_defaults_are_serializable() {
let request = ThreadRequest::List(ThreadListParams {
include_archived: false,
limit: Some(20),
});
let encoded = serde_json::to_string_pretty(&request).expect("serialize list request");
assert!(encoded.contains("include_archived"));
}
#[test]
fn event_frame_serialization_contains_expected_tag() {
let frame = EventFrame::TurnComplete {
turn_id: "turn-1".to_string(),
};
let encoded = serde_json::to_string(&frame).expect("serialize frame");
assert!(encoded.contains("turn_complete"));
}
#[test]
fn runtime_event_envelope_roundtrip() {
let input = json!({
"schema_version": 1,
"seq": 12,
"event": "item.delta",
"kind": "item.delta",
"thread_id": "thr_123",
"turn_id": "turn_456",
"item_id": "item_789",
"timestamp": "2026-02-11T20:18:49.123Z",
"created_at": "2026-02-11T20:18:49.123Z",
"payload": { "delta": "ok", "kind": "agent_message" },
});
let envelope: RuntimeEventEnvelope =
serde_json::from_value(input).expect("deserialize runtime event envelope");
assert_eq!(envelope.schema_version, 1);
assert_eq!(envelope.seq, 12);
assert_eq!(envelope.event, "item.delta");
assert_eq!(envelope.kind, "item.delta");
assert_eq!(envelope.thread_id, "thr_123");
let encoded = serde_json::to_value(&envelope).expect("serialize runtime event envelope");
assert_eq!(encoded["event"], encoded["kind"]);
assert_eq!(encoded["schema_version"], 1);
assert_eq!(encoded["seq"], 12);
assert_eq!(encoded["thread_id"], "thr_123");
assert_eq!(encoded["turn_id"], "turn_456");
assert_eq!(encoded["item_id"], "item_789");
assert_eq!(encoded["timestamp"], "2026-02-11T20:18:49.123Z");
assert_eq!(encoded["created_at"], "2026-02-11T20:18:49.123Z");
assert_eq!(
encoded["payload"],
json!({ "delta": "ok", "kind": "agent_message" })
);
}
#[test]
fn runtime_event_envelope_defaults_to_api_schema_version() {
let input = json!({
"seq": 15,
"event": "thread.started",
"kind": "thread.started",
"thread_id": "thr_default_version",
"timestamp": "2026-02-11T20:18:49.123Z",
"payload": {},
});
let envelope: RuntimeEventEnvelope = serde_json::from_value(input)
.expect("deserialize runtime event envelope without schema version");
assert_eq!(
envelope.schema_version,
RUNTIME_EVENT_ENVELOPE_SCHEMA_VERSION
);
}
#[test]
fn runtime_event_envelope_thread_level_keeps_turn_and_item_ids() {
let input = json!({
"schema_version": 1,
"seq": 14,
"event": "thread.started",
"kind": "thread.started",
"thread_id": "thr_thread",
"timestamp": "2026-02-11T20:18:49.123Z",
"payload": { "thread": { "id": "thr_thread" } },
});
let envelope: RuntimeEventEnvelope = serde_json::from_value(input)
.expect("deserialize runtime event envelope without thread-level turn/item ids");
assert!(envelope.turn_id.is_none());
assert!(envelope.item_id.is_none());
let encoded = serde_json::to_value(envelope).expect("serialize runtime event envelope");
assert!(encoded.get("turn_id").is_some());
assert!(encoded.get("item_id").is_some());
assert!(encoded["turn_id"].is_null());
assert!(encoded["item_id"].is_null());
}
#[test]
fn runtime_event_envelope_preserves_unknown_fields() {
let input: Value = json!({
"schema_version": 1,
"seq": 13,
"event": "turn.completed",
"kind": "turn.completed",
"thread_id": "thr_unknown",
"timestamp": "2026-02-11T20:18:49.123Z",
"payload": {},
"forward_compatibility_hint": "v2-ready",
});
let envelope: RuntimeEventEnvelope = serde_json::from_value(input.clone())
.expect("deserialize runtime event envelope with unknown field");
assert!(envelope.extra.contains_key("forward_compatibility_hint"));
let encoded = serde_json::to_value(envelope).expect("serialize runtime event envelope");
assert_eq!(encoded["forward_compatibility_hint"], "v2-ready");
assert_eq!(encoded["schema_version"], 1);
assert_eq!(encoded["seq"], 13);
assert_eq!(encoded["event"], "turn.completed");
assert_eq!(encoded["kind"], "turn.completed");
assert_eq!(encoded["thread_id"], "thr_unknown");
assert!(encoded["turn_id"].is_null());
assert!(encoded["item_id"].is_null());
}