Complete source 6 engineering workflows and preserve release validation evidence
This commit is contained in:
@@ -196,7 +196,7 @@ fn ready(f: &Fixture, reverse: bool) -> State {
|
||||
let mut s = State::new(&f.p, f.owners[usize::from(reverse)]).unwrap();
|
||||
s.stage_row_references(&f.facet).unwrap();
|
||||
s.references.insert(f.locals[0], f.locals[1]);
|
||||
s.update(&f.p, Event::Select(f.facet.clone()));
|
||||
s.update(&f.p, RowEvent::Select(f.facet.clone()));
|
||||
s
|
||||
}
|
||||
|
||||
@@ -216,27 +216,27 @@ fn independent_rows_keep_take_candidate_match_core_and_preserve_siblings_both_di
|
||||
&capture_facet(&f.p, f.owners[0], f.owners[1], &f.sibling).unwrap(),
|
||||
layer,
|
||||
);
|
||||
s.update(&f.p, Event::Decide(f.facet.clone(), Some(decision)));
|
||||
s.update(&f.p, RowEvent::Decide(f.facet.clone(), Some(decision)));
|
||||
if decision == SemanticDecision::Candidate {
|
||||
s.update(&f.p, Event::Presence(f.facet.clone(), !binding));
|
||||
s.update(&f.p, RowEvent::Presence(f.facet.clone(), !binding));
|
||||
if !binding {
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::RowExpression(f.facet.clone(), f.locals[0].to_string()),
|
||||
RowEvent::RowExpression(f.facet.clone(), f.locals[0].to_string()),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Force the GUI to compose the row alongside another candidate,
|
||||
// so Take/Keep also exercise adapter scope and mapped references.
|
||||
let header = FacetSelector::Header(HeaderFacet::Description);
|
||||
s.update(&f.p, Event::Select(header.clone()));
|
||||
s.update(&f.p, RowEvent::Select(header.clone()));
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(header, Some(SemanticDecision::Candidate)),
|
||||
RowEvent::Decide(header, Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Input("/description".into(), "Reviewed one row".into()),
|
||||
RowEvent::Input("/description".into(), "Reviewed one row".into()),
|
||||
);
|
||||
assert!(
|
||||
s.can_apply(),
|
||||
@@ -283,29 +283,29 @@ fn keyed_row_drafts_retain_expression_local_and_presence_across_decisions_and_st
|
||||
s.staged.insert(f.facet.clone());
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
if binding {
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::RowLocal(f.facet.clone(), view::choice(&f.p, f.locals[1])),
|
||||
RowEvent::RowLocal(f.facet.clone(), view::choice(&f.p, f.locals[1])),
|
||||
);
|
||||
} else {
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::RowExpression(f.facet.clone(), "unfinished (\n literal UUID text".into()),
|
||||
RowEvent::RowExpression(f.facet.clone(), "unfinished (\n literal UUID text".into()),
|
||||
);
|
||||
}
|
||||
s.update(&f.p, Event::Presence(f.facet.clone(), false));
|
||||
s.update(&f.p, RowEvent::Presence(f.facet.clone(), false));
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(f.facet.clone(), Some(SemanticDecision::KeepTarget)),
|
||||
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::KeepTarget)),
|
||||
);
|
||||
s.update(&f.p, Event::DropStaged(f.facet.clone()));
|
||||
s.update(&f.p, Event::Select(f.facet.clone()));
|
||||
s.update(&f.p, RowEvent::DropStaged(f.facet.clone()));
|
||||
s.update(&f.p, RowEvent::Select(f.facet.clone()));
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
assert!(!s.presence[&f.facet]);
|
||||
if binding {
|
||||
@@ -323,7 +323,7 @@ fn keyed_row_drafts_retain_expression_local_and_presence_across_decisions_and_st
|
||||
// A forged event for an unselected sibling cannot alter its row draft.
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::RowExpression(f.sibling.clone(), "wrong row".into()),
|
||||
RowEvent::RowExpression(f.sibling.clone(), "wrong row".into()),
|
||||
);
|
||||
assert!(s.error.as_ref().unwrap().contains("Select this row"));
|
||||
}
|
||||
@@ -333,21 +333,24 @@ fn keyed_row_drafts_retain_expression_local_and_presence_across_decisions_and_st
|
||||
fn annotation_address_browser_descends_qualified_fields_without_changing_rows() {
|
||||
let f = fixture(false);
|
||||
let mut s = ready(&f, false);
|
||||
s.update(&f.p, Event::MemberFamily(keyed::Family::Annotation));
|
||||
s.update(&f.p, RowEvent::MemberFamily(keyed::Family::Annotation));
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let roots = s.annotation_choices(&f.p, layer).unwrap();
|
||||
assert_eq!(roots.len(), 2);
|
||||
let key = roots[0].key.clone();
|
||||
s.update(&f.p, Event::AnnotationBrowse(layer, Some(key.clone()), 0));
|
||||
s.update(
|
||||
&f.p,
|
||||
RowEvent::AnnotationBrowse(layer, Some(key.clone()), 0),
|
||||
);
|
||||
let nested = s.annotation_choices(&f.p, layer).unwrap();
|
||||
assert_eq!(nested.len(), 1);
|
||||
assert_eq!(nested[0].key.path, vec![key.field]);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::AnnotationAddress(layer, Some(nested[0].key.clone())),
|
||||
RowEvent::AnnotationAddress(layer, Some(nested[0].key.clone())),
|
||||
);
|
||||
}
|
||||
s.update(&f.p, Event::StageMember);
|
||||
s.update(&f.p, RowEvent::StageMember);
|
||||
assert!(s.error.is_none(), "{}", errors(&s));
|
||||
assert_eq!(s.staged.len(), 1);
|
||||
assert!(s.decisions.is_empty());
|
||||
@@ -358,7 +361,7 @@ fn annotation_address_browser_descends_qualified_fields_without_changing_rows()
|
||||
fn binding_choices_separate_reused_keys_from_local_lifelines() {
|
||||
let f = fixture(true);
|
||||
let mut s = ready(&f, false);
|
||||
s.update(&f.p, Event::MemberFamily(keyed::Family::Binding));
|
||||
s.update(&f.p, RowEvent::MemberFamily(keyed::Family::Binding));
|
||||
for (index, layer) in [Layer::Oa, Layer::Sa].into_iter().enumerate() {
|
||||
assert_eq!(s.member_choices(&f.p, layer).len(), 2);
|
||||
assert!(
|
||||
@@ -379,14 +382,14 @@ fn binding_local_picker_follows_a_candidate_parent_before_the_review_is_valid()
|
||||
let local = add(&mut f.p, Kind::Lifeline, Layer::Sa, Some(destination));
|
||||
let mut s = ready(&f, false);
|
||||
let parent = FacetSelector::Header(HeaderFacet::Parent);
|
||||
s.update(&f.p, Event::Select(parent.clone()));
|
||||
s.update(&f.p, RowEvent::Select(parent.clone()));
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(parent, Some(SemanticDecision::Candidate)),
|
||||
RowEvent::Decide(parent, Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Shape("/parent".into(), serde_json::json!(destination)),
|
||||
RowEvent::Shape("/parent".into(), serde_json::json!(destination)),
|
||||
);
|
||||
assert_eq!(
|
||||
s.binding_choices(&f.p, Layer::Sa, false)
|
||||
@@ -416,14 +419,14 @@ fn row_review_apply_preserves_exact_source_and_semantic_history_through_undo_red
|
||||
app.document.apply_bundle(bundle).unwrap();
|
||||
let before = app.document.snapshot();
|
||||
start(&mut app, f.owners[0]);
|
||||
send(&mut app, Event::EditPair(f.facet.clone()));
|
||||
send(&mut app, Event::StageMember);
|
||||
send(&mut app, Event::Select(f.facet.clone()));
|
||||
send(&mut app, RowEvent::EditPair(f.facet.clone()));
|
||||
send(&mut app, RowEvent::StageMember);
|
||||
send(&mut app, RowEvent::Select(f.facet.clone()));
|
||||
send(
|
||||
&mut app,
|
||||
Event::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
send(&mut app, Event::Presence(f.facet.clone(), false));
|
||||
send(&mut app, RowEvent::Presence(f.facet.clone(), false));
|
||||
assert!(state(&app).can_apply(), "{}", errors(state(&app)));
|
||||
let expected = state(&app).proposed.as_ref().unwrap().clone();
|
||||
apply(&mut app);
|
||||
@@ -503,13 +506,13 @@ fn keyed_rows_distinguish_unpaired_source_from_explicit_absence_and_require_exac
|
||||
let mut s = ready(&f, false);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(f.facet.clone(), Some(SemanticDecision::KeepTarget)),
|
||||
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::KeepTarget)),
|
||||
);
|
||||
assert!(
|
||||
!s.can_apply(),
|
||||
"An old one-sided address cannot silently acquire a paired history"
|
||||
);
|
||||
s.update(&f.p, Event::Reset(source_only.clone(), true));
|
||||
s.update(&f.p, RowEvent::Reset(source_only.clone(), true));
|
||||
assert!(s.can_apply(), "{}", errors(&s));
|
||||
let next = s.proposed.as_ref().unwrap();
|
||||
let facts = &next.traces[&f.trace]
|
||||
@@ -541,17 +544,17 @@ fn keyed_rows_distinguish_unpaired_source_from_explicit_absence_and_require_exac
|
||||
let mut s = ready(&f, false);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(facet, Some(SemanticDecision::TakeSource)),
|
||||
RowEvent::Decide(facet, Some(SemanticDecision::TakeSource)),
|
||||
);
|
||||
let header = FacetSelector::Header(HeaderFacet::Description);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(header.clone(), Some(SemanticDecision::Candidate)),
|
||||
RowEvent::Decide(header.clone(), Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
s.update(&f.p, Event::Select(header));
|
||||
s.update(&f.p, RowEvent::Select(header));
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Input("/description".into(), "Compose explicit absence".into()),
|
||||
RowEvent::Input("/description".into(), "Compose explicit absence".into()),
|
||||
);
|
||||
assert!(s.can_apply(), "{}", errors(&s));
|
||||
let fact = capture_facet(
|
||||
@@ -576,18 +579,18 @@ fn binding_take_requires_local_correspondence_and_candidate_rejects_wrong_scenar
|
||||
s.references.remove(&f.locals[0]);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(f.facet.clone(), Some(SemanticDecision::TakeSource)),
|
||||
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::TakeSource)),
|
||||
);
|
||||
assert!(!s.can_apply());
|
||||
s.references.insert(f.locals[0], f.locals[1]);
|
||||
s.update(&f.p, Event::Preview);
|
||||
s.update(&f.p, RowEvent::Preview);
|
||||
assert!(s.can_apply(), "{}", errors(&s));
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
let wrong = s.binding_choices(&f.p, Layer::Sa, true)[0].clone();
|
||||
s.update(&f.p, Event::RowLocal(f.facet.clone(), wrong));
|
||||
s.update(&f.p, RowEvent::RowLocal(f.facet.clone(), wrong));
|
||||
assert!(!s.can_apply());
|
||||
assert!(
|
||||
s.row_drafts[&f.facet].local.is_some(),
|
||||
@@ -595,7 +598,7 @@ fn binding_take_requires_local_correspondence_and_candidate_rejects_wrong_scenar
|
||||
);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::RowLocal(f.facet.clone(), view::choice(&f.p, f.locals[1])),
|
||||
RowEvent::RowLocal(f.facet.clone(), view::choice(&f.p, f.locals[1])),
|
||||
);
|
||||
assert!(s.can_apply(), "{}", errors(&s));
|
||||
}
|
||||
@@ -635,16 +638,19 @@ fn keyed_recovery_retains_raw_rows_and_staged_pairs_both_directions_without_appl
|
||||
s.staged.insert(f.facet.clone());
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
if !binding {
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::RowExpression(f.facet.clone(), "unfinished (\n exact raw draft".into()),
|
||||
RowEvent::RowExpression(
|
||||
f.facet.clone(),
|
||||
"unfinished (\n exact raw draft".into(),
|
||||
),
|
||||
);
|
||||
}
|
||||
s.update(&f.p, Event::Presence(f.facet.clone(), false));
|
||||
s.update(&f.p, Event::EditPair(f.facet.clone()));
|
||||
s.update(&f.p, RowEvent::Presence(f.facet.clone(), false));
|
||||
s.update(&f.p, RowEvent::EditPair(f.facet.clone()));
|
||||
let saved = s.recovery_snapshot().unwrap();
|
||||
let restored = State::from_recovery(&f.p, saved).unwrap();
|
||||
assert_eq!(restored.direction, s.direction);
|
||||
@@ -698,23 +704,23 @@ fn multiple_new_rows_compose_in_empty_collections_using_destination_key_order()
|
||||
let object_count = f.p.elements.len();
|
||||
let mut s = ready(&f, reverse);
|
||||
for (facet, row) in facets.iter().zip(&original).rev() {
|
||||
s.update(&f.p, Event::EditPair(facet.clone()));
|
||||
s.update(&f.p, Event::StageMember);
|
||||
s.update(&f.p, RowEvent::EditPair(facet.clone()));
|
||||
s.update(&f.p, RowEvent::StageMember);
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::Decide(facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
RowEvent::Decide(facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
s.update(&f.p, Event::Presence(facet.clone(), true));
|
||||
s.update(&f.p, RowEvent::Presence(facet.clone(), true));
|
||||
if binding {
|
||||
let local = row["local"].as_str().unwrap().parse().unwrap();
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::RowLocal(facet.clone(), view::choice(&f.p, local)),
|
||||
RowEvent::RowLocal(facet.clone(), view::choice(&f.p, local)),
|
||||
);
|
||||
} else {
|
||||
s.update(
|
||||
&f.p,
|
||||
Event::RowExpression(facet.clone(), "new independent value".into()),
|
||||
RowEvent::RowExpression(facet.clone(), "new independent value".into()),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -734,3 +740,90 @@ fn multiple_new_rows_compose_in_empty_collections_using_destination_key_order()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_keyed_rows_compare_exact_target_slots_and_retain_independent_values() {
|
||||
for binding in [false, true] {
|
||||
let mut f = fixture(binding);
|
||||
let other = Id::new_v4();
|
||||
let mut source = f.p.elements[&f.owners[0]].clone();
|
||||
source.id = other;
|
||||
source.alias = format!("other_{}", other.simple());
|
||||
source.name = "Alternate operational rationale".into();
|
||||
let parent = source.parent.unwrap();
|
||||
f.p.elements.insert(other, source);
|
||||
f.p.elements
|
||||
.get_mut(&parent)
|
||||
.unwrap()
|
||||
.steps_mut()
|
||||
.unwrap()
|
||||
.push(other);
|
||||
let second = add_trace(&mut f.p, other, f.owners[1], "Alternate keyed rationale");
|
||||
validate_candidate(&f.p).unwrap();
|
||||
let mut state = ready(&f, false);
|
||||
state.update(
|
||||
&f.p,
|
||||
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
if binding {
|
||||
state.update(&f.p, RowEvent::Presence(f.facet.clone(), false));
|
||||
} else {
|
||||
state.update(
|
||||
&f.p,
|
||||
RowEvent::RowExpression(f.facet.clone(), f.locals[0].to_string()),
|
||||
);
|
||||
}
|
||||
assert!(state.can_apply(), "{}", errors(&state));
|
||||
let first_value = state.proposed.clone().unwrap();
|
||||
state.update(&f.p, Event::ChooseTrace(view::choice(&f.p, second)));
|
||||
state.update(&f.p, Event::OpenTrace);
|
||||
state.update(&f.p, RowEvent::Select(f.facet.clone()));
|
||||
state.update(
|
||||
&f.p,
|
||||
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
|
||||
);
|
||||
if !binding {
|
||||
state.update(
|
||||
&f.p,
|
||||
RowEvent::RowExpression(f.facet.clone(), "Another value".into()),
|
||||
);
|
||||
}
|
||||
assert!(!state.can_apply());
|
||||
assert!(errors(&state).contains("disagree"), "{}", errors(&state));
|
||||
let saved = state.recovery_snapshot().unwrap();
|
||||
let restored = State::from_recovery(&f.p, saved.clone()).unwrap();
|
||||
assert_eq!(restored.recovery_snapshot().unwrap(), saved);
|
||||
assert!(!restored.can_apply());
|
||||
if binding {
|
||||
state.update(&f.p, RowEvent::Presence(f.facet.clone(), false));
|
||||
} else {
|
||||
state.update(
|
||||
&f.p,
|
||||
RowEvent::RowExpression(f.facet.clone(), f.locals[0].to_string()),
|
||||
);
|
||||
}
|
||||
assert!(state.can_apply(), "{}", errors(&state));
|
||||
assert_eq!(state.plan.as_ref().unwrap().request.candidates.len(), 1);
|
||||
assert_eq!(
|
||||
state.proposed.as_ref().unwrap().elements[&f.owners[1]],
|
||||
first_value.elements[&f.owners[1]]
|
||||
);
|
||||
assert_eq!(
|
||||
scalar::value(
|
||||
&capture_facet(
|
||||
state.proposed.as_ref().unwrap(),
|
||||
f.owners[0],
|
||||
f.owners[1],
|
||||
&f.sibling
|
||||
)
|
||||
.unwrap(),
|
||||
Layer::Sa
|
||||
),
|
||||
scalar::value(
|
||||
&capture_facet(&f.p, f.owners[0], f.owners[1], &f.sibling).unwrap(),
|
||||
Layer::Sa
|
||||
)
|
||||
);
|
||||
let _ = state.view(&f.p);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user