use cuttlefish_abi::{
error_codes, Command, Envelope, Event, JobError, JobStatus, TokenAction, Usage,
};
#[test]
fn infer_command_serializes_to_the_expected_json() {
let cmd = Command::Infer {
prompt: "hi".into(),
max_tokens: 32,
};
assert_eq!(
serde_json::to_string(&cmd).unwrap(),
r#"{"cmd":"infer","prompt":"hi","max_tokens":32}"#
);
}
#[test]
fn every_command_round_trips() {
let cases = vec![
Command::Infer {
prompt: "p".into(),
max_tokens: 1,
},
Command::Open {
path: "/a/b".into(),
},
Command::Slice {
handle: 7,
offset: 4096,
len: 1024,
},
Command::Emit {
progress: serde_json::json!({"done": 2}),
},
Command::Done {
result: serde_json::json!({"summary": "s"}),
},
Command::Fail {
code: "bad".into(),
message: "why".into(),
},
];
for cmd in cases {
let json = serde_json::to_string(&cmd).unwrap();
assert_eq!(
serde_json::from_str::<Command>(&json).unwrap(),
cmd,
"round trip failed for {json}"
);
}
}
#[test]
fn every_event_round_trips() {
let cases = vec![
Event::InferDone {
text: "yo".into(),
tokens_out: 2,
},
Event::Opened {
handle: 3,
len: 900,
},
Event::Sliced {
text: "abc".into(),
next_offset: 3,
},
Event::Emitted,
];
for ev in cases {
let json = serde_json::to_string(&ev).unwrap();
assert_eq!(
serde_json::from_str::<Event>(&json).unwrap(),
ev,
"round trip failed for {json}"
);
}
}
#[test]
fn slice_offsets_survive_beyond_u32() {
let cmd = Command::Slice {
handle: 1,
offset: u64::from(u32::MAX) + 1,
len: 8,
};
let back: Command = serde_json::from_str(&serde_json::to_string(&cmd).unwrap()).unwrap();
assert_eq!(back, cmd);
}
#[test]
fn envelope_omits_absent_optional_fields() {
let env = Envelope {
status: JobStatus::Completed,
result: Some(serde_json::json!({"summary": "s"})),
error: None,
usage: Usage::default(),
};
let json = serde_json::to_string(&env).unwrap();
assert!(
!json.contains("error"),
"an absent error must not serialize at all: {json}"
);
}
#[test]
fn failed_envelope_carries_a_code_and_no_result() {
let env = Envelope {
status: JobStatus::Failed,
result: None,
error: Some(JobError {
code: error_codes::CAPABILITY_DENIED.into(),
message: "read not permitted".into(),
}),
usage: Usage::default(),
};
let json = serde_json::to_string(&env).unwrap();
assert!(json.contains(r#""status":"failed""#), "{json}");
assert!(json.contains(r#""code":"capability_denied""#), "{json}");
assert!(
!json.contains(r#""result""#),
"a failed job must never carry a partial result: {json}"
);
}
#[test]
fn token_action_maps_to_the_abi_integers() {
assert_eq!(TokenAction::Continue.as_i32(), 0);
assert_eq!(TokenAction::Stop.as_i32(), 1);
assert_eq!(TokenAction::from_i32(0), TokenAction::Continue);
assert_eq!(TokenAction::from_i32(1), TokenAction::Stop);
}
#[test]
fn unknown_token_action_values_stop_rather_than_continue() {
assert_eq!(TokenAction::from_i32(42), TokenAction::Stop);
assert_eq!(TokenAction::from_i32(-1), TokenAction::Stop);
}
#[test]
fn terminal_statuses_are_exactly_the_finished_ones() {
assert!(JobStatus::Completed.is_terminal());
assert!(JobStatus::Failed.is_terminal());
assert!(JobStatus::Cancelled.is_terminal());
assert!(!JobStatus::Queued.is_terminal());
assert!(!JobStatus::Running.is_terminal());
}
#[test]
fn job_status_serializes_as_snake_case() {
let json = serde_json::to_string(&JobStatus::Cancelled).unwrap();
assert_eq!(json, r#""cancelled""#);
}