use super::*;
type UniqueClaims = Vec<(String, usize)>;
pub(super) type UniqueFingerprint = Vec<(String, Vec<String>, UniqueClaims)>;
pub(super) fn unique_fingerprint(graph: &DirGraph) -> UniqueFingerprint {
let mut out: Vec<_> = graph
.unique_indices
.iter()
.map(|((node_type, properties), occupants)| {
let mut claims: UniqueClaims = occupants
.iter()
.map(|(value, idx)| (format!("{value:?}"), idx.index()))
.collect();
claims.sort();
(node_type.clone(), properties.clone(), claims)
})
.collect();
out.sort();
out
}
pub(super) fn seeded_with_unique_name() -> DirGraph {
let mut graph = seeded();
run(
&mut graph,
"CREATE CONSTRAINT FOR (i:Item) REQUIRE i.name IS UNIQUE",
);
assert_eq!(
graph.unique_indices.len(),
1,
"the constraint must be declared and enforcing"
);
assert!(
graph.property_indices.is_empty()
&& graph.composite_indices.is_empty()
&& graph.range_indices.is_empty(),
"a unique constraint must not create a user index, or these tests \
would exercise the clone checkpoint instead of the journal"
);
graph
}
fn seeded_columnar_with_unique_qty() -> DirGraph {
let mut graph = seeded();
run(
&mut graph,
"CREATE CONSTRAINT FOR (i:Item) REQUIRE i.qty IS UNIQUE",
);
graph.enable_columnar();
assert_eq!(
graph.unique_indices.len(),
1,
"the constraint must be declared and enforcing"
);
assert!(
graph.column_store_count() > 0,
"the graph must be saved, or this is the plain unique fixture again"
);
graph
}
#[test]
fn rollback_restores_claims_moved_by_a_columnar_property_overwrite() {
let mut graph = seeded_columnar_with_unique_qty();
let before = unique_fingerprint(&graph);
assert!(
!before.is_empty() && !before[0].2.is_empty(),
"the constraint must hold claims, or this test is vacuous"
);
let error = expect_failure(
&mut graph,
"MATCH (i:Item {id: 1}) SET i.qty = 999 \
WITH i MATCH (j:Item {id: 2}) SET j.bad = duration({months: 2147483648})",
None,
);
assert_eq!(
unique_fingerprint(&graph),
before,
"a claim moved through the master column store must move back.\
\nerror: {error}"
);
expect_failure(&mut graph, "CREATE (:Item {id: 40, qty: 10})", None);
run(&mut graph, "CREATE (:Item {id: 41, qty: 999})");
}
#[test]
fn rollback_releases_a_claim_the_failed_statement_added() {
let mut graph = seeded_with_unique_name();
let before = unique_fingerprint(&graph);
let error = expect_failure(
&mut graph,
"CREATE (:Item {id: 10, name: 'zeta'}), (:Item {id: 11, name: 'b'})",
None,
);
assert_eq!(
unique_fingerprint(&graph),
before,
"the rolled-back claim must be gone.\nerror: {error}"
);
run(&mut graph, "CREATE (:Item {id: 13, name: 'zeta'})");
}
#[test]
fn rollback_restores_a_claim_the_failed_statement_released() {
let mut graph = seeded_with_unique_name();
let before = unique_fingerprint(&graph);
let error = expect_failure(
&mut graph,
"MATCH (i:Item {id: 1}) DETACH DELETE i CREATE (:Item {id: 20, name: 'b'})",
None,
);
assert_eq!(
unique_fingerprint(&graph),
before,
"the released claim must be restored, pointing at the restored slot.\
\nerror: {error}"
);
expect_failure(&mut graph, "CREATE (:Item {id: 21, name: 'a'})", None);
}
#[test]
fn rollback_restores_claims_moved_by_a_property_overwrite() {
let mut graph = seeded_with_unique_name();
let before = unique_fingerprint(&graph);
let error = expect_failure(
&mut graph,
"MATCH (i:Item {id: 1}) SET i.name = 'renamed' \
WITH i MATCH (j:Item {id: 2}) SET j.bad = duration({months: 2147483648})",
None,
);
assert_eq!(
unique_fingerprint(&graph),
before,
"an overwritten claim must move back.\nerror: {error}"
);
expect_failure(&mut graph, "CREATE (:Item {id: 30, name: 'a'})", None);
run(&mut graph, "CREATE (:Item {id: 31, name: 'renamed'})");
}