huskies: merge 913
This commit is contained in:
@@ -750,4 +750,82 @@ fn merge_failure_transition_emits_event_with_full_reason() {
|
||||
);
|
||||
}
|
||||
|
||||
// ── Story 913: MergeFailure + MergeFailed self-loop ────────────────
|
||||
|
||||
/// AC1: `MergeFailure + MergeFailed` is a valid self-transition — no error logged.
|
||||
#[test]
|
||||
fn merge_failure_plus_merge_failed_is_self_loop() {
|
||||
let s = Stage::MergeFailure {
|
||||
reason: "initial failure".into(),
|
||||
};
|
||||
let result = transition(
|
||||
s,
|
||||
PipelineEvent::MergeFailed {
|
||||
reason: "second failure".into(),
|
||||
},
|
||||
);
|
||||
assert!(
|
||||
matches!(result, Ok(Stage::MergeFailure { .. })),
|
||||
"MergeFailure + MergeFailed should self-loop to MergeFailure, got: {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
/// AC2 + AC3: applying `MergeFailed` to a story already in `MergeFailure` succeeds and
|
||||
/// the `TransitionFired::before` is `MergeFailure`, allowing callers to suppress the
|
||||
/// duplicate notification.
|
||||
#[test]
|
||||
fn repeated_merge_failure_apply_transition_no_error_no_duplicate_notification() {
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
|
||||
let story_id = "99913_story_merge_failure_selfloop";
|
||||
crate::db::write_item_with_content(
|
||||
story_id,
|
||||
"4_merge_failure",
|
||||
"---\nname: MergeFailure Self-loop Test\n---\n# Story\n",
|
||||
crate::db::ItemMeta::from_yaml("---\nname: MergeFailure Self-loop Test\n---\n# Story\n"),
|
||||
);
|
||||
|
||||
// Apply a second MergeFailed to a story already in MergeFailure.
|
||||
let fired = super::apply::apply_transition(
|
||||
story_id,
|
||||
PipelineEvent::MergeFailed {
|
||||
reason: "duplicate failure".into(),
|
||||
},
|
||||
None,
|
||||
)
|
||||
.expect("MergeFailed on already-failed story should succeed without error");
|
||||
|
||||
// The before-stage was MergeFailure: this was a self-loop.
|
||||
// Callers check this to decide whether to suppress the chat notification.
|
||||
assert!(
|
||||
matches!(fired.before, Stage::MergeFailure { .. }),
|
||||
"fired.before should be MergeFailure (self-loop): {:?}",
|
||||
fired.before
|
||||
);
|
||||
assert!(
|
||||
matches!(fired.after, Stage::MergeFailure { .. }),
|
||||
"fired.after should remain MergeFailure: {:?}",
|
||||
fired.after
|
||||
);
|
||||
|
||||
// Verify the CRDT stage is still 4_merge_failure.
|
||||
let item = read_typed(story_id)
|
||||
.expect("CRDT read should succeed")
|
||||
.expect("item should still exist");
|
||||
assert_eq!(
|
||||
item.stage.dir_name(),
|
||||
"4_merge_failure",
|
||||
"CRDT stage should remain 4_merge_failure after self-loop"
|
||||
);
|
||||
|
||||
// Simulate the caller's de-dup logic: since fired.before is already MergeFailure,
|
||||
// no notification should be dispatched.
|
||||
let should_notify = !matches!(fired.before, Stage::MergeFailure { .. });
|
||||
assert!(
|
||||
!should_notify,
|
||||
"should_notify must be false for a self-loop to prevent duplicate notification"
|
||||
);
|
||||
}
|
||||
|
||||
// ── ProjectionError Display ─────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user