mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-12 23:02:41 +00:00
The manifest builder's best-effort pre-run push converted every result into a PreRunPushOutcome that was serialized into GitContext, expanded into five OpenAPI union arms, and generated into API clients — but no production path ever read it; every field read was a test. Delete the concept while preserving the behavior: - Drop the PreRunPushOutcome enum and GitContext.push_outcome from fabro-types; GitContext keeps origin_url, branch, optional sha, and dirty, which remain real execution inputs and provenance. - Rename the manifest outcome builder to push_manifest_branch_best_effort, a side-effect-only helper with the same decision rules: skip without an origin, skip on configured-repository mismatch, skip when the branch is already synced, otherwise push noninteractively and discard the result without failing manifest creation or logging raw Git stderr. - Prove the push through repository state instead of the deleted enum: a branch ahead of a local bare origin is pushed during manifest build, a mismatched configured repository is not, and a failing remote helper still cannot fail manifest creation. - Remove push_outcome from GitContext in OpenAPI, delete the five-arm union schemas, and drop the fabro-api type replacement and re-export. - Keep one regression proving historical run.created events with a nested push_outcome still deserialize through ordinary unknown-field tolerance and reserialize to the reduced shape. No migration or event rewrite. Old JSON carrying the removed field stays readable. Newly generated clients omit a field older servers required, so new-client-to-old-server compatibility is intentionally not promised for this pre-1.0 contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
97 lines
2.7 KiB
Rust
97 lines
2.7 KiB
Rust
use std::any::{TypeId, type_name};
|
|
|
|
use fabro_api::types::{DirtyStatus as ApiDirtyStatus, GitContext as ApiGitContext};
|
|
use fabro_types::{DirtyStatus, GitContext};
|
|
use serde_json::json;
|
|
|
|
#[test]
|
|
fn git_context_reuses_canonical_types() {
|
|
assert_same_type::<ApiGitContext, GitContext>();
|
|
assert_same_type::<ApiDirtyStatus, DirtyStatus>();
|
|
}
|
|
|
|
#[test]
|
|
fn dirty_status_serializes_with_snake_case_strings() {
|
|
assert_eq!(
|
|
serde_json::to_value(DirtyStatus::Clean).unwrap(),
|
|
json!("clean")
|
|
);
|
|
assert_eq!(
|
|
serde_json::to_value(DirtyStatus::Dirty).unwrap(),
|
|
json!("dirty")
|
|
);
|
|
assert_eq!(
|
|
serde_json::to_value(DirtyStatus::Unknown).unwrap(),
|
|
json!("unknown")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn git_context_with_known_sha_round_trips() {
|
|
let ctx = GitContext {
|
|
origin_url: "https://github.com/acme/widgets".to_string(),
|
|
branch: "main".to_string(),
|
|
sha: Some("abc123".to_string()),
|
|
dirty: DirtyStatus::Clean,
|
|
};
|
|
let json = serde_json::to_value(&ctx).unwrap();
|
|
assert_eq!(
|
|
json,
|
|
json!({
|
|
"origin_url": "https://github.com/acme/widgets",
|
|
"branch": "main",
|
|
"sha": "abc123",
|
|
"dirty": "clean",
|
|
})
|
|
);
|
|
let round_trip: GitContext = serde_json::from_value(json).unwrap();
|
|
assert_eq!(round_trip, ctx);
|
|
}
|
|
|
|
#[test]
|
|
fn git_context_omits_absent_sha_on_serialize() {
|
|
let ctx = GitContext {
|
|
origin_url: "https://github.com/acme/widgets".to_string(),
|
|
branch: "feature/foo".to_string(),
|
|
sha: None,
|
|
dirty: DirtyStatus::Unknown,
|
|
};
|
|
let json = serde_json::to_value(&ctx).unwrap();
|
|
assert!(json.get("sha").is_none());
|
|
assert_eq!(json["dirty"], "unknown");
|
|
}
|
|
|
|
#[test]
|
|
fn git_context_deserializes_when_sha_is_absent() {
|
|
let ctx: GitContext = serde_json::from_value(json!({
|
|
"origin_url": "https://github.com/acme/widgets",
|
|
"branch": "main",
|
|
"dirty": "dirty",
|
|
}))
|
|
.unwrap();
|
|
assert_eq!(ctx.sha, None);
|
|
assert_eq!(ctx.dirty, DirtyStatus::Dirty);
|
|
}
|
|
|
|
#[test]
|
|
fn git_context_tolerates_legacy_push_outcome_field() {
|
|
let ctx: GitContext = serde_json::from_value(json!({
|
|
"origin_url": "https://github.com/acme/widgets",
|
|
"branch": "main",
|
|
"dirty": "clean",
|
|
"push_outcome": { "type": "not_attempted" },
|
|
}))
|
|
.unwrap();
|
|
assert_eq!(ctx.origin_url, "https://github.com/acme/widgets");
|
|
assert_eq!(ctx.dirty, DirtyStatus::Clean);
|
|
}
|
|
|
|
fn assert_same_type<T: 'static, U: 'static>() {
|
|
assert_eq!(
|
|
TypeId::of::<T>(),
|
|
TypeId::of::<U>(),
|
|
"{} should be the same type as {}",
|
|
type_name::<T>(),
|
|
type_name::<U>()
|
|
);
|
|
}
|