Files
ARCADIA-Studio/crates/oa-desktop/src/semantic_review/keyed_rows_tests.rs
T
Codex e2ecfea69b
Rust checks / check (ubuntu-latest) (push) Canceled after 0s
Rust checks / check (windows-latest) (push) Canceled after 0s
Checkpoint source 7 workflows and in-progress diagram authoring redesign
Preserve current core, source, REST, desktop, recovery, documentation and validation work, including AGENTS.md and the development summary. This is a work-in-progress checkpoint: the new compact palette has outstanding compilation errors and native acceptance; earlier passing gates retain their documented scope.
2026-09-10 10:06:55 -07:00

831 lines
30 KiB
Rust

use super::{tests::*, *};
use crate::{app::App, document::Document};
struct Fixture {
p: Project,
owners: [Id; 2],
trace: Id,
facet: FacetSelector,
sibling: FacetSelector,
locals: [Id; 2],
}
fn errors(s: &State) -> String {
s.error
.iter()
.chain(s.plan.iter().flat_map(|p| &p.conflicts))
.cloned()
.collect::<Vec<_>>()
.join("; ")
}
fn add(p: &mut Project, kind: Kind, layer: Layer, parent: Option<Id>) -> Id {
p.add_element(kind, layer, kind.label(), parent).unwrap()
}
fn fixture(binding: bool) -> Fixture {
let mut p = Project::empty();
let mut owners = vec![];
let mut keys = vec![];
let mut sibling_keys = vec![];
let mut locals = vec![];
for layer in [Layer::Oa, Layer::Sa] {
let scenario = add(&mut p, Kind::Scenario, layer, None);
let owner = add(&mut p, Kind::Occurrence, layer, Some(scenario));
p.elements
.get_mut(&scenario)
.unwrap()
.steps_mut()
.unwrap()
.push(owner);
let body = if binding {
let reused = add(&mut p, Kind::Scenario, layer, None);
let referenced = add(&mut p, Kind::Lifeline, layer, Some(reused));
let sibling = add(&mut p, Kind::Lifeline, layer, Some(reused));
let local = add(&mut p, Kind::Lifeline, layer, Some(scenario));
let sibling_local = add(&mut p, Kind::Lifeline, layer, Some(scenario));
keys.push(AnnotationKey {
path: vec![],
field: referenced,
});
sibling_keys.push(AnnotationKey {
path: vec![],
field: sibling,
});
locals.push(local);
OccurrenceBody::ScenarioUse {
scenario: Some(reused),
bindings: vec![
LifelineBinding { referenced, local },
LifelineBinding {
referenced: sibling,
local: sibling_local,
},
],
}
} else {
let work = if layer == Layer::Oa {
Kind::Activity
} else {
Kind::Function
};
let from = add(&mut p, work, layer, None);
let to = add(&mut p, work, layer, None);
let item = add(&mut p, Kind::ExchangeItem, layer, None);
let record = add(&mut p, Kind::DataType, layer, None);
*p.elements.get_mut(&record).unwrap().shared_mut().unwrap() = SharedBody::DataType {
definition: DataTypeDefinition::Record { supertype: None },
default: None,
unit: String::new(),
};
let first = add(&mut p, Kind::DataField, layer, Some(item));
let second = add(&mut p, Kind::DataField, layer, Some(item));
for field in [first, second] {
if let Some(SharedBody::DataField { type_ref, .. }) =
p.elements.get_mut(&field).unwrap().shared_mut()
{
*type_ref = TypeRef::Named(record);
}
}
let leaf = add(&mut p, Kind::DataField, layer, Some(record));
let exchange = p.relate(
if layer == Layer::Oa {
RelationKind::Interaction
} else {
RelationKind::FunctionalExchange
},
"Carrier",
from,
to,
);
let ports = match &mut p.relations.get_mut(&exchange).unwrap().body {
RelationBody::Interaction { items, .. } => {
items.push(item);
None
}
RelationBody::FunctionalExchange {
items,
source_port,
target_port,
..
} => {
items.push(item);
Some((*source_port, *target_port))
}
_ => unreachable!(),
};
if let Some((output, input)) = ports {
if let Some(SharedBody::Port {
contract: PortContract::FunctionOutput { outgoing_items },
..
}) = p.elements.get_mut(&output).unwrap().shared_mut()
{
outgoing_items.push(item);
}
if let Some(SharedBody::Port {
contract: PortContract::FunctionInput { incoming_items },
..
}) = p.elements.get_mut(&input).unwrap().shared_mut()
{
incoming_items.push(item);
}
}
keys.push(AnnotationKey {
path: vec![first],
field: leaf,
});
sibling_keys.push(AnnotationKey {
path: vec![second],
field: leaf,
});
locals.push(leaf);
OccurrenceBody::Exchange {
operation: None,
exchange: Some(exchange),
sender: None,
receiver: None,
message_kind: MessageKind::Asynchronous,
reply_to: None,
values: vec![
MessageValue {
path: vec![first],
field: leaf,
expression: if layer == Layer::Oa {
"operational"
} else {
"system"
}
.into(),
},
MessageValue {
path: vec![second],
field: leaf,
expression: "sibling stays".into(),
},
],
}
};
*p.elements.get_mut(&owner).unwrap().shared_mut().unwrap() = SharedBody::Occurrence {
body,
precondition: "Retain precondition".into(),
postcondition: "Retain postcondition".into(),
};
owners.push(owner);
}
let trace = add_trace(&mut p, owners[0], owners[1], "Individual row review");
let facet = |keys: &[AnnotationKey]| {
if binding {
FacetSelector::Binding(KeyPair {
oa: Some(keys[0].field),
sa: Some(keys[1].field),
})
} else {
FacetSelector::Annotation(KeyPair {
oa: Some(keys[0].clone()),
sa: Some(keys[1].clone()),
})
}
};
validate_candidate(&p).unwrap();
Fixture {
p,
owners: [owners[0], owners[1]],
trace,
facet: facet(&keys),
sibling: facet(&sibling_keys),
locals: [locals[0], locals[1]],
}
}
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, RowEvent::Select(f.facet.clone()));
s
}
#[test]
fn independent_rows_keep_take_candidate_match_core_and_preserve_siblings_both_directions() {
for binding in [false, true] {
for reverse in [false, true] {
for decision in [
SemanticDecision::KeepTarget,
SemanticDecision::TakeSource,
SemanticDecision::Candidate,
] {
let f = fixture(binding);
let mut s = ready(&f, reverse);
let layer = s.direction.target_layer();
let before_sibling = scalar::value(
&capture_facet(&f.p, f.owners[0], f.owners[1], &f.sibling).unwrap(),
layer,
);
s.update(&f.p, RowEvent::Decide(f.facet.clone(), Some(decision)));
if decision == SemanticDecision::Candidate {
s.update(&f.p, RowEvent::Presence(f.facet.clone(), !binding));
if !binding {
s.update(
&f.p,
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, RowEvent::Select(header.clone()));
s.update(
&f.p,
RowEvent::Decide(header, Some(SemanticDecision::Candidate)),
);
s.update(
&f.p,
RowEvent::Input("/description".into(), "Reviewed one row".into()),
);
assert!(
s.can_apply(),
"{binding}/{reverse}/{decision:?}: {}",
errors(&s)
);
let proposed = s.proposed.as_ref().unwrap().clone();
assert_eq!(
scalar::value(
&capture_facet(&proposed, f.owners[0], f.owners[1], &f.sibling).unwrap(),
layer
),
before_sibling
);
let current = scalar::value(
&capture_facet(&proposed, f.owners[0], f.owners[1], &f.facet).unwrap(),
layer,
)
.unwrap();
if decision == SemanticDecision::Candidate {
if binding {
assert!(current.is_null());
} else {
assert_eq!(current["expression"], f.locals[0].to_string());
}
}
assert_eq!(
prepare_command(&f.p, &s.command(&f.p).unwrap())
.unwrap()
.project,
proposed
);
let _ = s.view(&f.p);
}
}
}
}
#[test]
fn keyed_row_drafts_retain_expression_local_and_presence_across_decisions_and_staging() {
for binding in [false, true] {
let f = fixture(binding);
let mut s = ready(&f, false);
s.staged.insert(f.facet.clone());
s.update(
&f.p,
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
);
if binding {
s.update(
&f.p,
RowEvent::RowLocal(f.facet.clone(), view::choice(&f.p, f.locals[1])),
);
} else {
s.update(
&f.p,
RowEvent::RowExpression(f.facet.clone(), "unfinished (\n literal UUID text".into()),
);
}
s.update(&f.p, RowEvent::Presence(f.facet.clone(), false));
s.update(
&f.p,
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::KeepTarget)),
);
s.update(&f.p, RowEvent::DropStaged(f.facet.clone()));
s.update(&f.p, RowEvent::Select(f.facet.clone()));
s.update(
&f.p,
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
);
assert!(!s.presence[&f.facet]);
if binding {
assert_eq!(
s.row_drafts[&f.facet].local.as_ref().unwrap().id,
f.locals[1]
);
} else {
assert_eq!(
s.row_drafts[&f.facet].expression,
"unfinished (\n literal UUID text"
);
}
assert!(s.can_apply(), "{}", errors(&s));
// A forged event for an unselected sibling cannot alter its row draft.
s.update(
&f.p,
RowEvent::RowExpression(f.sibling.clone(), "wrong row".into()),
);
assert!(s.error.as_ref().unwrap().contains("Select this row"));
}
}
#[test]
fn annotation_address_browser_descends_qualified_fields_without_changing_rows() {
let f = fixture(false);
let mut s = ready(&f, false);
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,
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,
RowEvent::AnnotationAddress(layer, Some(nested[0].key.clone())),
);
}
s.update(&f.p, RowEvent::StageMember);
assert!(s.error.is_none(), "{}", errors(&s));
assert_eq!(s.staged.len(), 1);
assert!(s.decisions.is_empty());
let _ = s.view(&f.p);
}
#[test]
fn binding_choices_separate_reused_keys_from_local_lifelines() {
let f = fixture(true);
let mut s = ready(&f, false);
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!(
!s.member_choices(&f.p, layer)
.iter()
.any(|c| c.id == f.locals[index])
);
let local_choices = s.binding_choices(&f.p, layer, false);
assert_eq!(local_choices.len(), 2);
assert!(local_choices.iter().any(|c| c.id == f.locals[index]));
}
}
#[test]
fn binding_local_picker_follows_a_candidate_parent_before_the_review_is_valid() {
let mut f = fixture(true);
let destination = add(&mut f.p, Kind::Scenario, Layer::Sa, None);
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, RowEvent::Select(parent.clone()));
s.update(
&f.p,
RowEvent::Decide(parent, Some(SemanticDecision::Candidate)),
);
s.update(
&f.p,
RowEvent::Shape("/parent".into(), serde_json::json!(destination)),
);
assert_eq!(
s.binding_choices(&f.p, Layer::Sa, false)
.iter()
.map(|c| c.id)
.collect::<Vec<_>>(),
vec![local]
);
assert!(
!s.binding_choices(&f.p, Layer::Sa, false)
.iter()
.any(|c| c.id == f.locals[1])
);
}
#[test]
fn row_review_apply_preserves_exact_source_and_semantic_history_through_undo_redo() {
for binding in [false, true] {
let f = fixture(binding);
let mut app = App::with_project(f.p);
let mut bundle = app.document.bundle().clone();
bundle
.files
.get_mut(&bundle.entry.clone())
.unwrap()
.insert_str(0, "// Exact row rationale\r\n");
app.document.apply_bundle(bundle).unwrap();
let before = app.document.snapshot();
start(&mut app, f.owners[0]);
send(&mut app, RowEvent::EditPair(f.facet.clone()));
send(&mut app, RowEvent::StageMember);
send(&mut app, RowEvent::Select(f.facet.clone()));
send(
&mut app,
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
);
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);
assert_eq!(app.project(), &expected);
assert!(
app.project().traces[&f.trace]
.baseline
.as_ref()
.unwrap()
.semantic
.as_ref()
.unwrap()
.facets
.iter()
.any(|r| r.selector() == f.facet)
);
let after = app.document.snapshot();
assert!(
app.document.bundle().files[&app.document.bundle().entry]
.starts_with("// Exact row rationale\r\n")
);
let _ = app.update(Message::Undo);
assert_eq!(app.document.bundle(), &before.bundle);
assert_eq!(app.project(), &before.decoded.project);
let _ = app.update(Message::Redo);
assert_eq!(app.document.bundle(), &after.bundle);
assert_eq!(app.project(), &after.decoded.project);
assert_eq!(
Document::from_bundle(
after.bundle.clone(),
source_project::ProjectLimits::default()
)
.unwrap()
.project(),
&expected
);
}
}
#[test]
fn keyed_rows_distinguish_unpaired_source_from_explicit_absence_and_require_exact_reset() {
for binding in [false, true] {
let mut f = fixture(binding);
let (source_only, target_only) = match &f.facet {
FacetSelector::Annotation(keys) => (
FacetSelector::Annotation(KeyPair {
oa: keys.oa.clone(),
sa: None,
}),
FacetSelector::Annotation(KeyPair {
oa: None,
sa: keys.sa.clone(),
}),
),
FacetSelector::Binding(keys) => (
FacetSelector::Binding(KeyPair {
oa: keys.oa,
sa: None,
}),
FacetSelector::Binding(KeyPair {
oa: None,
sa: keys.sa,
}),
),
_ => unreachable!(),
};
assert!(keyed::overlap(&source_only, &f.facet));
assert!(!keyed::overlap(&f.sibling, &f.facet));
let prior = capture_facet(&f.p, f.owners[0], f.owners[1], &source_only).unwrap();
f.p.traces.get_mut(&f.trace).unwrap().baseline = Some(TraceBaseline {
oa: f.p.properties(f.owners[0]).unwrap(),
sa: f.p.properties(f.owners[1]).unwrap(),
semantic: Some(SemanticBaseline {
facets: vec![prior],
}),
});
let mut s = ready(&f, false);
s.update(
&f.p,
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, 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]
.baseline
.as_ref()
.unwrap()
.semantic
.as_ref()
.unwrap()
.facets;
assert!(
facts
.iter()
.any(|f| f.selector() == s.selected.clone().unwrap())
);
assert!(!facts.iter().any(|f| f.selector() == source_only));
f.p.traces.get_mut(&f.trace).unwrap().baseline = None;
if let Some(SharedBody::Occurrence { body, .. }) =
f.p.elements.get_mut(&f.owners[0]).unwrap().shared_mut()
{
match body {
OccurrenceBody::Exchange { values, .. } => values.clear(),
OccurrenceBody::ScenarioUse { bindings, .. } => bindings.clear(),
_ => unreachable!(),
}
}
for (facet, expect_absent) in [(target_only, false), (f.facet.clone(), true)] {
let mut s = ready(&f, false);
s.update(
&f.p,
RowEvent::Decide(facet, Some(SemanticDecision::TakeSource)),
);
let header = FacetSelector::Header(HeaderFacet::Description);
s.update(
&f.p,
RowEvent::Decide(header.clone(), Some(SemanticDecision::Candidate)),
);
s.update(&f.p, RowEvent::Select(header));
s.update(
&f.p,
RowEvent::Input("/description".into(), "Compose explicit absence".into()),
);
assert!(s.can_apply(), "{}", errors(&s));
let fact = capture_facet(
s.proposed.as_ref().unwrap(),
f.owners[0],
f.owners[1],
&f.facet,
)
.unwrap();
assert_eq!(
scalar::value(&fact, Layer::Sa).unwrap().is_null(),
expect_absent
);
}
}
}
#[test]
fn binding_take_requires_local_correspondence_and_candidate_rejects_wrong_scenario() {
let f = fixture(true);
let mut s = ready(&f, false);
s.references.remove(&f.locals[0]);
s.update(
&f.p,
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, RowEvent::Preview);
assert!(s.can_apply(), "{}", errors(&s));
s.update(
&f.p,
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
);
let wrong = s.binding_choices(&f.p, Layer::Sa, true)[0].clone();
s.update(&f.p, RowEvent::RowLocal(f.facet.clone(), wrong));
assert!(!s.can_apply());
assert!(
s.row_drafts[&f.facet].local.is_some(),
"Invalid typed selection remains reviewable"
);
s.update(
&f.p,
RowEvent::RowLocal(f.facet.clone(), view::choice(&f.p, f.locals[1])),
);
assert!(s.can_apply(), "{}", errors(&s));
}
#[test]
fn annotation_observations_keep_uuid_shaped_expression_text_in_both_sides_and_history() {
let mut f = fixture(false);
for owner in f.owners {
if let Some(SharedBody::Occurrence {
body: OccurrenceBody::Exchange { values, .. },
..
}) = f.p.elements.get_mut(&owner).unwrap().shared_mut()
{
values[0].expression = values[0].field.to_string();
}
}
let historical = capture_facet(&f.p, f.owners[0], f.owners[1], &f.facet).unwrap();
for layer in [Layer::Oa, Layer::Sa] {
let expression = scalar::value(&historical, layer).unwrap()["expression"]
.as_str()
.unwrap()
.to_owned();
let display = view::observed(&f.p, Some(&historical), layer);
assert!(
display.contains(&format!("Expression: {expression:?}")),
"{display}"
);
}
}
#[test]
fn keyed_recovery_retains_raw_rows_and_staged_pairs_both_directions_without_applying() {
for binding in [false, true] {
for reverse in [false, true] {
let f = fixture(binding);
let mut s = ready(&f, reverse);
s.staged.insert(f.facet.clone());
s.update(
&f.p,
RowEvent::Decide(f.facet.clone(), Some(SemanticDecision::Candidate)),
);
if !binding {
s.update(
&f.p,
RowEvent::RowExpression(
f.facet.clone(),
"unfinished (\n exact raw draft".into(),
),
);
}
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);
assert_eq!(restored.decisions, s.decisions);
assert_eq!(restored.references, s.references);
assert_eq!(restored.presence, s.presence);
assert_eq!(restored.staged, s.staged);
assert_eq!(
restored.row_drafts[&f.facet].expression,
s.row_drafts[&f.facet].expression
);
assert_eq!(
restored.row_drafts[&f.facet].local,
s.row_drafts[&f.facet].local
);
assert_eq!(restored.proposed, s.proposed);
assert_eq!(restored.annotation_oa.selected, s.annotation_oa.selected);
assert_eq!(restored.annotation_sa.selected, s.annotation_sa.selected);
assert!(restored.can_apply(), "{}", errors(&restored));
}
}
}
#[test]
fn multiple_new_rows_compose_in_empty_collections_using_destination_key_order() {
for binding in [false, true] {
for reverse in [false, true] {
let mut f = fixture(binding);
let target_layer = if reverse { Layer::Oa } else { Layer::Sa };
let target = f.owners[usize::from(!reverse)];
let facets = [f.facet.clone(), f.sibling.clone()];
let original: Vec<_> = facets
.iter()
.map(|facet| {
scalar::value(
&capture_facet(&f.p, f.owners[0], f.owners[1], facet).unwrap(),
target_layer,
)
.unwrap()
})
.collect();
if let Some(SharedBody::Occurrence { body, .. }) =
f.p.elements.get_mut(&target).unwrap().shared_mut()
{
match body {
OccurrenceBody::Exchange { values, .. } => values.clear(),
OccurrenceBody::ScenarioUse { bindings, .. } => bindings.clear(),
_ => unreachable!(),
}
}
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, RowEvent::EditPair(facet.clone()));
s.update(&f.p, RowEvent::StageMember);
s.update(
&f.p,
RowEvent::Decide(facet.clone(), Some(SemanticDecision::Candidate)),
);
s.update(&f.p, RowEvent::Presence(facet.clone(), true));
if binding {
let local = row["local"].as_str().unwrap().parse().unwrap();
s.update(
&f.p,
RowEvent::RowLocal(facet.clone(), view::choice(&f.p, local)),
);
} else {
s.update(
&f.p,
RowEvent::RowExpression(facet.clone(), "new independent value".into()),
);
}
}
assert!(s.can_apply(), "{binding}/{reverse}: {}", errors(&s));
let proposed = s.proposed.as_ref().unwrap();
assert_eq!(proposed.elements.len(), object_count);
for facet in facets {
assert!(
!scalar::value(
&capture_facet(proposed, f.owners[0], f.owners[1], &facet).unwrap(),
target_layer
)
.unwrap()
.is_null()
);
}
}
}
}
#[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);
}
}