Complete source 6 engineering workflows and preserve release validation evidence
This commit is contained in:
@@ -172,8 +172,8 @@ fn ready(f: &Fixture, reverse: bool) -> State {
|
||||
state
|
||||
}
|
||||
fn decide(state: &mut State, p: &Project, facet: FacetSelector, decision: SemanticDecision) {
|
||||
state.update(p, Event::Select(facet.clone()));
|
||||
state.update(p, Event::Decide(facet, Some(decision)));
|
||||
state.update(p, RowEvent::Select(facet.clone()));
|
||||
state.update(p, RowEvent::Decide(facet, Some(decision)));
|
||||
}
|
||||
fn errors(s: &State) -> String {
|
||||
s.error
|
||||
@@ -231,7 +231,7 @@ fn ordered_take_uses_unique_anchors_and_matches_core_with_unrelated_candidate_bo
|
||||
decide(&mut s, &f.p, header, SemanticDecision::Candidate);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Input("/description".into(), "Reviewed startup insertion".into()),
|
||||
RowEvent::Input("/description".into(), "Reviewed startup insertion".into()),
|
||||
);
|
||||
let ti = 1 - usize::from(reverse);
|
||||
let mut expected = f.p.clone();
|
||||
@@ -268,7 +268,7 @@ fn ambiguous_order_preserves_local_member_and_requires_explicit_candidate_positi
|
||||
s.order_drafts[&f.which],
|
||||
vec![f.rows[ti][0], f.rows[ti][3], f.rows[ti][1], f.rows[ti][2]]
|
||||
);
|
||||
s.update(&f.p, Event::OrderMove(f.which, 3, -1));
|
||||
s.update(&f.p, RowEvent::OrderMove(f.which, 3, -1));
|
||||
let mut expected = f.p.clone();
|
||||
set_order(
|
||||
&mut expected,
|
||||
@@ -297,7 +297,7 @@ fn ordered_membership_choices_stage_exact_pairs_in_all_owner_shapes() {
|
||||
let mut s = ready(&f, false);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::MemberFamily(keyed::Family::Member(ordered::collection(f.which))),
|
||||
RowEvent::MemberFamily(keyed::Family::Member(ordered::collection(f.which))),
|
||||
);
|
||||
for (i, layer) in [(0, Layer::Oa), (1, Layer::Sa)] {
|
||||
let choices = s.member_choices(&f.p, layer);
|
||||
@@ -311,13 +311,13 @@ fn ordered_membership_choices_stage_exact_pairs_in_all_owner_shapes() {
|
||||
}
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::MemberOa(Some(view::choice(&f.p, f.rows[0][2]))),
|
||||
RowEvent::MemberOa(Some(view::choice(&f.p, f.rows[0][2]))),
|
||||
);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::MemberSa(Some(view::choice(&f.p, f.rows[1][2]))),
|
||||
RowEvent::MemberSa(Some(view::choice(&f.p, f.rows[1][2]))),
|
||||
);
|
||||
s.update(&f.p, Event::StageMember);
|
||||
s.update(&f.p, RowEvent::StageMember);
|
||||
assert!(s.staged.contains(&member(&f, 2)));
|
||||
assert_eq!(s.references[&f.rows[0][2]], f.rows[1][2]);
|
||||
}
|
||||
@@ -330,20 +330,20 @@ fn raw_order_survives_membership_changes_decisions_and_recovery_until_explicit_r
|
||||
let mut s = ready(&f, reverse);
|
||||
let order = FacetSelector::Order(f.which);
|
||||
decide(&mut s, &f.p, order.clone(), SemanticDecision::Candidate);
|
||||
s.update(&f.p, Event::OrderMove(f.which, 1, -1));
|
||||
s.update(&f.p, RowEvent::OrderMove(f.which, 1, -1));
|
||||
let authored = s.order_drafts[&f.which].clone();
|
||||
decide(&mut s, &f.p, member(&f, 2), SemanticDecision::Candidate);
|
||||
s.update(&f.p, Event::Presence(member(&f, 2), true));
|
||||
s.update(&f.p, RowEvent::Presence(member(&f, 2), true));
|
||||
assert!(!s.can_apply());
|
||||
assert_eq!(s.order_drafts[&f.which], authored);
|
||||
s.update(&f.p, Event::Decide(order.clone(), None));
|
||||
s.update(&f.p, RowEvent::Decide(order.clone(), None));
|
||||
decide(&mut s, &f.p, order.clone(), SemanticDecision::Candidate);
|
||||
let saved = s.recovery_snapshot().unwrap();
|
||||
let mut restored = State::from_recovery(&f.p, saved.clone()).unwrap();
|
||||
assert_eq!(restored.order_drafts[&f.which], authored);
|
||||
assert!(!restored.can_apply());
|
||||
assert_eq!(restored.recovery_snapshot().unwrap(), saved);
|
||||
restored.update(&f.p, Event::OrderReconcile(f.which));
|
||||
restored.update(&f.p, RowEvent::OrderReconcile(f.which));
|
||||
let ti = 1 - usize::from(reverse);
|
||||
let mut expected = f.p.clone();
|
||||
let mut final_order = authored;
|
||||
@@ -385,7 +385,7 @@ fn order_take_preserves_destination_only_positions_and_order_alone_cannot_add_me
|
||||
s.refresh(&f.p);
|
||||
assert!(!s.can_apply());
|
||||
assert!(errors(&s).contains("surviving"), "{}", errors(&s));
|
||||
s.update(&f.p, Event::OrderReconcile(f.which));
|
||||
s.update(&f.p, RowEvent::OrderReconcile(f.which));
|
||||
assert!(s.can_apply(), "{}", errors(&s));
|
||||
}
|
||||
}
|
||||
@@ -406,28 +406,28 @@ fn ordered_review_apply_is_exact_source_history_transaction_with_reopen_and_undo
|
||||
for i in 0..3 {
|
||||
send(
|
||||
&mut app,
|
||||
Event::ReferenceOa(view::choice(&f.p, f.rows[0][i])),
|
||||
RowEvent::ReferenceOa(view::choice(&f.p, f.rows[0][i])),
|
||||
);
|
||||
send(
|
||||
&mut app,
|
||||
Event::ReferenceSa(view::choice(&f.p, f.rows[1][i])),
|
||||
RowEvent::ReferenceSa(view::choice(&f.p, f.rows[1][i])),
|
||||
);
|
||||
send(&mut app, Event::AddReference);
|
||||
send(&mut app, RowEvent::AddReference);
|
||||
}
|
||||
send(&mut app, Event::Select(member(&f, 2)));
|
||||
send(&mut app, RowEvent::Select(member(&f, 2)));
|
||||
send(
|
||||
&mut app,
|
||||
Event::Decide(member(&f, 2), Some(SemanticDecision::TakeSource)),
|
||||
RowEvent::Decide(member(&f, 2), Some(SemanticDecision::TakeSource)),
|
||||
);
|
||||
send(&mut app, Event::Select(FacetSelector::Order(f.which)));
|
||||
send(&mut app, RowEvent::Select(FacetSelector::Order(f.which)));
|
||||
send(
|
||||
&mut app,
|
||||
Event::Decide(
|
||||
RowEvent::Decide(
|
||||
FacetSelector::Order(f.which),
|
||||
Some(SemanticDecision::Candidate),
|
||||
),
|
||||
);
|
||||
send(&mut app, Event::OrderMove(f.which, 3, -1));
|
||||
send(&mut app, RowEvent::OrderMove(f.which, 3, -1));
|
||||
assert!(state(&app).can_apply(), "{}", errors(state(&app)));
|
||||
let expected = state(&app).proposed.clone().unwrap();
|
||||
apply(&mut app);
|
||||
@@ -479,7 +479,7 @@ fn several_incoming_members_follow_source_order_despite_independent_identity_sor
|
||||
);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Input("/description".into(), "Multiple incoming steps".into()),
|
||||
RowEvent::Input("/description".into(), "Multiple incoming steps".into()),
|
||||
);
|
||||
let mut expected = f.p.clone();
|
||||
set_order(
|
||||
@@ -513,7 +513,7 @@ fn ordered_presence_keep_take_candidate_remove_only_selected_entry_and_order_tak
|
||||
let mut s = ready(&f, reverse);
|
||||
decide(&mut s, &f.p, member(&f, 0), decision);
|
||||
if decision == SemanticDecision::Candidate {
|
||||
s.update(&f.p, Event::Presence(member(&f, 0), false));
|
||||
s.update(&f.p, RowEvent::Presence(member(&f, 0), false));
|
||||
}
|
||||
decide(
|
||||
&mut s,
|
||||
@@ -523,7 +523,7 @@ fn ordered_presence_keep_take_candidate_remove_only_selected_entry_and_order_tak
|
||||
);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Input("/description".into(), "Only selected membership".into()),
|
||||
RowEvent::Input("/description".into(), "Only selected membership".into()),
|
||||
);
|
||||
let mut expected = f.p.clone();
|
||||
if decision != SemanticDecision::KeepTarget {
|
||||
@@ -565,3 +565,58 @@ fn ordered_presence_keep_take_candidate_remove_only_selected_entry_and_order_tak
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn historical_pair_restore_uses_exact_member_keys_and_never_zips_order_observations() {
|
||||
let f = fixture(0, false, true);
|
||||
let mut state = ready(&f, false);
|
||||
decide(
|
||||
&mut state,
|
||||
&f.p,
|
||||
member(&f, 2),
|
||||
SemanticDecision::TakeSource,
|
||||
);
|
||||
decide(
|
||||
&mut state,
|
||||
&f.p,
|
||||
FacetSelector::Order(f.which),
|
||||
SemanticDecision::Candidate,
|
||||
);
|
||||
state.update(&f.p, RowEvent::OrderMove(f.which, 3, -1));
|
||||
assert!(state.can_apply(), "{}", errors(&state));
|
||||
let project = state.proposed.clone().unwrap();
|
||||
let mut reopened = State::new(&project, f.trace).unwrap();
|
||||
let exact = history::reviewed_pairs(&reopened, &project).unwrap();
|
||||
assert_eq!(exact, BTreeMap::from([(f.rows[0][2], f.rows[1][2])]));
|
||||
assert_eq!(
|
||||
project.traces[&f.trace]
|
||||
.baseline
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.semantic
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.facets
|
||||
.len(),
|
||||
2
|
||||
);
|
||||
reopened.update(&project, RowEvent::RestoreReviewedReferences);
|
||||
assert_eq!(reopened.references, exact);
|
||||
assert!(reopened.decisions.is_empty());
|
||||
assert_eq!(reopened.proposed.as_ref(), Some(&project));
|
||||
reopened.references.insert(f.rows[0][2], f.rows[1][0]);
|
||||
let prior = reopened.references.clone();
|
||||
reopened.update(&project, RowEvent::RestoreReviewedReferences);
|
||||
assert!(reopened.error.as_ref().unwrap().contains("conflicts"));
|
||||
assert_eq!(
|
||||
reopened.references, prior,
|
||||
"Restore is atomic on disagreement"
|
||||
);
|
||||
reopened.references.clear();
|
||||
reopened.update(&project, RowEvent::Reset(member(&f, 2), true));
|
||||
assert!(
|
||||
history::reviewed_pairs(&reopened, &project)
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user