///|
fn followup_once(definition : Value, facts : Value) -> Value raise {
let input = obj(definition).copy()
if optional_str(get(definition, "operation"), "decide") != "decide" {
raise input_error("E3002", "replay_versions accepts decide definitions")
}
input["facts"] = facts
arr(get(run(Object(input)), "results"))[0]
}
///|
fn followup_ids(outcome : Value) -> Value raise {
let matched = get(get(outcome, "result"), "matched")
if matched == Null {
return Array([])
}
let ids = arr(matched).map(item => str(get(item, "id")))
ids.sort_by(stable_string_compare)
strings(ids)
}
///|
fn replay_versions_request(request : Value) -> Value raise {
let cases = arr(get(request, "cases"))
if cases.length() > 10000 {
raise input_error("E3007", "too many replay cases")
}
let seen : Map[String, Bool] = Map([])
let output : Array[Value] = []
let mut changed = 0
for case in cases {
let id = str(get(case, "id"))
if id == "" || seen.contains(id) {
raise input_error("E4001", "duplicate or empty replay identity")
}
seen[id] = true
let facts = get(case, "facts")
ignore(obj(facts))
let before = followup_once(get(request, "old"), facts)
let after = followup_once(get(request, "new"), facts)
let a = get(before, "result")
let b = get(after, "result")
let decision = get(a, "decision") != get(b, "decision")
let conflict = get(a, "conflict") != get(b, "conflict")
let error = get(before, "error") != get(after, "error")
let matched = followup_ids(before) != followup_ids(after)
if decision || conflict || error || matched {
changed += 1
}
output.push(
record([
("id", String(id)),
("before", before),
("after", after),
("decision_changed", Bool(decision)),
("conflict_changed", Bool(conflict)),
("error_changed", Bool(error)),
("matched_changed", Bool(matched)),
]),
)
}
record([
("cases", Array(output)),
("sample_count", Number(cases.length().to_double())),
("changed_count", Number(changed.to_double())),
("equivalent_on_samples", Bool(changed == 0)),
("global_equivalence_proven", Bool(false)),
("ok", Bool(changed == 0)),
])
}