mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-09 03:20:56 +00:00
118 lines
4 KiB
Rust
118 lines
4 KiB
Rust
use std::any::{TypeId, type_name};
|
|
|
|
use fabro_api::types::{
|
|
Conclusion as ApiConclusion, ExecOutputTail as ApiExecOutputTail,
|
|
FailureCategory as ApiFailureCategory, FailureDetail as ApiFailureDetail,
|
|
FailureSignature as ApiFailureSignature, RunFailure as ApiRunFailure,
|
|
};
|
|
use fabro_types::{
|
|
Conclusion, ExecOutputTail, FailureCategory, FailureDetail, FailureReason, FailureSignature,
|
|
RunFailure, StageOutcome,
|
|
};
|
|
use serde::Serialize;
|
|
use serde_json::{Value, json};
|
|
|
|
#[test]
|
|
fn run_failure_family_reuses_domain_types() {
|
|
assert_same_type::<ApiConclusion, Conclusion>();
|
|
assert_same_type::<ApiRunFailure, RunFailure>();
|
|
assert_same_type::<ApiFailureDetail, FailureDetail>();
|
|
assert_same_type::<ApiFailureCategory, FailureCategory>();
|
|
assert_same_type::<ApiFailureSignature, FailureSignature>();
|
|
assert_same_type::<ApiExecOutputTail, ExecOutputTail>();
|
|
}
|
|
|
|
#[test]
|
|
fn run_failure_json_matches_openapi_shape() {
|
|
assert_json(
|
|
RunFailure {
|
|
reason: FailureReason::SandboxInitFailed,
|
|
detail: {
|
|
let mut detail = FailureDetail::new(
|
|
"Failed to initialize sandbox",
|
|
FailureCategory::TransientInfra,
|
|
);
|
|
detail.causes = vec!["connection refused".to_string()];
|
|
detail.signature =
|
|
Some(FailureSignature("init|transient_infra|docker".to_string()));
|
|
detail.exec_output_tail = Some(ExecOutputTail {
|
|
stdout: None,
|
|
stderr: Some("last stderr line".to_string()),
|
|
stdout_truncated: false,
|
|
stderr_truncated: true,
|
|
});
|
|
detail
|
|
},
|
|
},
|
|
json!({
|
|
"reason": "sandbox_init_failed",
|
|
"detail": {
|
|
"message": "Failed to initialize sandbox",
|
|
"causes": ["connection refused"],
|
|
"category": "transient_infra",
|
|
"signature": "init|transient_infra|docker",
|
|
"exec_output_tail": {
|
|
"stderr": "last stderr line",
|
|
"stderr_truncated": true
|
|
}
|
|
}
|
|
}),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn conclusion_json_uses_failure_object() {
|
|
assert_json(
|
|
Conclusion {
|
|
timestamp: chrono::DateTime::parse_from_rfc3339("2026-05-13T12:00:00Z")
|
|
.unwrap()
|
|
.with_timezone(&chrono::Utc),
|
|
status: StageOutcome::Failed {
|
|
retry_requested: false,
|
|
},
|
|
timing: fabro_types::RunTiming::wall_only(42),
|
|
failure: Some(RunFailure {
|
|
reason: FailureReason::WorkflowError,
|
|
detail: FailureDetail::new("boom", FailureCategory::Deterministic),
|
|
}),
|
|
final_git_commit_sha: None,
|
|
stages: Vec::new(),
|
|
billing: None,
|
|
total_retries: 0,
|
|
diff: Default::default(),
|
|
},
|
|
json!({
|
|
"timestamp": "2026-05-13T12:00:00Z",
|
|
"status": "failed",
|
|
"timing": {
|
|
"wall_time_ms": 42,
|
|
"inference_time_ms": 0,
|
|
"tool_time_ms": 0,
|
|
"active_time_ms": 0
|
|
},
|
|
"failure": {
|
|
"reason": "workflow_error",
|
|
"detail": {
|
|
"message": "boom",
|
|
"category": "deterministic"
|
|
}
|
|
},
|
|
"total_retries": 0,
|
|
"diff": {}
|
|
}),
|
|
);
|
|
}
|
|
|
|
fn assert_same_type<T: 'static, U: 'static>() {
|
|
assert_eq!(
|
|
TypeId::of::<T>(),
|
|
TypeId::of::<U>(),
|
|
"{} should reuse {}",
|
|
type_name::<T>(),
|
|
type_name::<U>()
|
|
);
|
|
}
|
|
|
|
fn assert_json<T: Serialize>(value: T, expected: Value) {
|
|
assert_eq!(serde_json::to_value(value).unwrap(), expected);
|
|
}
|