use bestool_canopy::schema::{
BackupPurpose, BackupTarget, BeginArgs, BeginResponse, CompleteArgs, CompleteResponse,
CredentialProcessOutput, Issue, NewEvent, ReportArgs, RunOutcome, Severity,
};
#[test]
fn backup_target_response_roundtrips() {
let json = serde_json::json!({
"storage": "s3",
"bucket": "backups",
"prefix": "",
"region": "ap-southeast-2",
"repo_password": "hunter2",
});
let target: BackupTarget = serde_json::from_value(json.clone()).unwrap();
assert_eq!(target.storage, "s3");
assert_eq!(target.bucket, "backups");
assert_eq!(target.region, "ap-southeast-2");
assert_eq!(serde_json::to_value(&target).unwrap(), json);
}
#[test]
fn report_args_request_roundtrips() {
let json = serde_json::json!({
"run_id": "6f8a2b1c-0000-4000-8000-000000000000",
"type": "postgres",
"purpose": "backup",
"outcome": "success",
"bytes_uploaded": 1234,
});
let report: ReportArgs = serde_json::from_value(json).unwrap();
assert_eq!(report.type_, "postgres");
assert_eq!(report.purpose, BackupPurpose::Backup);
assert_eq!(report.outcome, RunOutcome::Success);
assert_eq!(report.bytes_uploaded, Some(1234));
let back = serde_json::to_value(&report).unwrap();
assert_eq!(back["type"], "postgres");
assert_eq!(back["outcome"], "success");
}
#[test]
fn register_begin_types_roundtrip() {
let args_json = serde_json::json!({
"server_id": "6f8a2b1c-0000-4000-8000-000000000000",
"token": "enrol-token",
});
let args: BeginArgs = serde_json::from_value(args_json).unwrap();
assert_eq!(args.token, "enrol-token");
assert!(args.spki.is_none());
let resp: BeginResponse = serde_json::from_value(serde_json::json!({
"nonce": "YmFzZTY0",
"channel_binding_required": true,
}))
.unwrap();
assert!(resp.channel_binding_required);
assert_eq!(resp.nonce, "YmFzZTY0");
}
#[test]
fn register_complete_types_roundtrip() {
let args: CompleteArgs = serde_json::from_value(serde_json::json!({
"server_id": "6f8a2b1c-0000-4000-8000-000000000000",
"nonce": "YmFzZTY0",
"signature": "c2ln",
}))
.unwrap();
assert_eq!(args.signature, "c2ln");
let resp: CompleteResponse = serde_json::from_value(serde_json::json!({
"server_id": "6f8a2b1c-0000-4000-8000-000000000000",
"device_id": "1111aaaa-0000-4000-8000-000000000000",
}))
.unwrap();
assert_eq!(
resp.device_id.to_string(),
"1111aaaa-0000-4000-8000-000000000000"
);
}
#[test]
fn credential_secrets_are_redacted_and_expiry_is_a_timestamp() {
let wire = serde_json::json!({
"Version": 1,
"AccessKeyId": "AKIA",
"SecretAccessKey": "super-secret-key",
"SessionToken": "super-secret-token",
"Expiration": "2026-05-21T13:00:00Z",
});
let creds: CredentialProcessOutput = serde_json::from_value(wire.clone()).unwrap();
assert_eq!(&*creds.secret_access_key, "super-secret-key");
assert_eq!(&*creds.session_token, "super-secret-token");
let debug = format!("{creds:?}");
assert!(!debug.contains("super-secret-key"), "{debug}");
assert!(!debug.contains("super-secret-token"), "{debug}");
assert!(debug.contains("<redacted>"), "{debug}");
let _: jiff::Timestamp = creds.expiration;
assert_eq!(serde_json::to_value(&creds).unwrap(), wire);
}
#[test]
fn backup_target_repo_password_is_redacted() {
let target: BackupTarget = serde_json::from_value(serde_json::json!({
"storage": "s3",
"bucket": "backups",
"prefix": "",
"region": "ap-southeast-2",
"repo_password": "hunter2",
}))
.unwrap();
assert_eq!(&*target.repo_password, "hunter2");
let debug = format!("{target:?}");
assert!(!debug.contains("hunter2"), "{debug}");
assert!(debug.contains("<redacted>"), "{debug}");
}
#[test]
fn new_event_omits_optional_fields() {
let event = NewEvent {
source: "src".to_owned(),
ref_: "host/alert:tgt".to_owned(),
message: "msg".to_owned(),
description: None,
severity: None,
occurred_at: None,
active: None,
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("\"source\":\"src\""));
assert!(json.contains("\"ref\":\"host/alert:tgt\""));
assert!(json.contains("\"message\":\"msg\""));
assert!(!json.contains("description"));
assert!(!json.contains("severity"));
assert!(!json.contains("occurredAt"));
assert!(!json.contains("active"));
}
#[test]
fn new_event_serialises_occurred_at_camel_case_and_severity_lowercase() {
let event = NewEvent {
source: "src".to_owned(),
ref_: "ref".to_owned(),
message: "msg".to_owned(),
description: Some("desc".to_owned()),
severity: Some(Severity::Warning),
occurred_at: Some("2025-01-01T00:00:00Z".parse().unwrap()),
active: Some(true),
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("\"occurredAt\":"));
assert!(json.contains("\"severity\":\"warning\""));
assert!(json.contains("\"active\":true"));
}
#[test]
fn datetime_fields_deserialise_into_jiff_timestamp() {
let issue: Issue = serde_json::from_value(serde_json::json!({
"active": true,
"created_at": "2026-01-02T03:04:05Z",
"first_seen": "2026-01-02T03:04:05Z",
"last_seen": "2026-01-02T03:04:05Z",
"id": "6f8a2b1c-0000-4000-8000-000000000000",
"message": "disk full",
"ref": "host/alert:disk",
"severity": "warning",
"source": "alertd",
"updated_at": "2026-01-02T03:04:05Z",
}))
.unwrap();
let created: jiff::Timestamp = issue.created_at;
assert_eq!(created, "2026-01-02T03:04:05Z".parse().unwrap());
}