mod common;
use common::{TestRuntime, TestValue};
const _: fn() = || {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<graphrefly_core::Core>();
assert_sync::<graphrefly_core::Core>();
assert_send::<graphrefly_core::SubgraphId>();
assert_sync::<graphrefly_core::SubgraphId>();
};
#[test]
fn singleton_registration_creates_partition() {
let rt = TestRuntime::new();
let _s = rt.state(Some(TestValue::Int(0)));
assert_eq!(rt.core.partition_count(), 1);
}
#[test]
fn derived_unions_with_dep() {
let rt = TestRuntime::new();
let s = rt.state(Some(TestValue::Int(0)));
let d = rt.derived(&[s.id], |_| Some(TestValue::Int(0)));
assert_eq!(rt.core.partition_count(), 1);
assert_eq!(rt.core.partition_of(s.id), rt.core.partition_of(d));
}
#[test]
fn disjoint_state_nodes_have_distinct_partitions() {
let rt = TestRuntime::new();
let s1 = rt.state(Some(TestValue::Int(1)));
let s2 = rt.state(Some(TestValue::Int(2)));
assert_eq!(rt.core.partition_count(), 2);
assert_ne!(rt.core.partition_of(s1.id), rt.core.partition_of(s2.id));
}
#[test]
fn diamond_topology_collapses_to_one_partition() {
let rt = TestRuntime::new();
let s1 = rt.state(Some(TestValue::Int(1)));
let s2 = rt.state(Some(TestValue::Int(2)));
let _d = rt.derived(&[s1.id, s2.id], |_| Some(TestValue::Int(0)));
assert_eq!(rt.core.partition_count(), 1);
assert_eq!(
rt.core.partition_of(s1.id),
rt.core.partition_of(s2.id),
"s1 and s2 connected via shared derived consumer"
);
}
#[test]
fn set_deps_add_edge_unions_partitions() {
let rt = TestRuntime::new();
let s1 = rt.state(Some(TestValue::Int(1)));
let s2 = rt.state(Some(TestValue::Int(2)));
let d = rt.dynamic(&[s1.id], |_| (Some(TestValue::Int(0)), None));
assert_eq!(rt.core.partition_count(), 2);
assert_eq!(rt.core.partition_of(d), rt.core.partition_of(s1.id));
assert_ne!(rt.core.partition_of(d), rt.core.partition_of(s2.id));
rt.core.set_deps(d, &[s1.id, s2.id]).expect("set_deps");
assert_eq!(rt.core.partition_count(), 1);
assert_eq!(
rt.core.partition_of(d),
rt.core.partition_of(s2.id),
"s2 unioned in via set_deps add-edge"
);
}
#[test]
fn set_deps_remove_edge_keeps_partition_x5_monotonic() {
let rt = TestRuntime::new();
let s1 = rt.state(Some(TestValue::Int(1)));
let s2 = rt.state(Some(TestValue::Int(2)));
let d = rt.dynamic(&[s1.id, s2.id], |_| (Some(TestValue::Int(0)), None));
assert_eq!(rt.core.partition_count(), 1);
rt.core.set_deps(d, &[s1.id]).expect("set_deps");
assert_eq!(
rt.core.partition_count(),
1,
"X5 monotonic-merge: edge removal does not split"
);
}
#[test]
fn set_deps_mixed_add_and_remove_in_one_call() {
let rt = TestRuntime::new();
let s1 = rt.state(Some(TestValue::Int(1)));
let s2 = rt.state(Some(TestValue::Int(2)));
let s3 = rt.state(Some(TestValue::Int(3)));
let d = rt.dynamic(&[s1.id, s2.id], |_| (Some(TestValue::Int(0)), None));
assert_eq!(rt.core.partition_count(), 2); assert_ne!(rt.core.partition_of(d), rt.core.partition_of(s3.id));
rt.core.set_deps(d, &[s1.id, s3.id]).expect("set_deps");
assert_eq!(
rt.core.partition_count(),
1,
"all four nodes share a single partition after mixed set_deps \
(s2 stays via X5 monotonic-merge; s3 unioned in via add-edge)"
);
assert_eq!(rt.core.partition_of(d), rt.core.partition_of(s1.id));
assert_eq!(rt.core.partition_of(d), rt.core.partition_of(s2.id));
assert_eq!(rt.core.partition_of(d), rt.core.partition_of(s3.id));
}
#[test]
fn unrelated_subgraphs_stay_disjoint_through_topology_mutation() {
let rt = TestRuntime::new();
let s1 = rt.state(Some(TestValue::Int(1)));
let d1 = rt.derived(&[s1.id], |_| Some(TestValue::Int(0)));
let s2 = rt.state(Some(TestValue::Int(2)));
let d2 = rt.derived(&[s2.id], |_| Some(TestValue::Int(0)));
assert_eq!(rt.core.partition_count(), 2);
let p_a = rt.core.partition_of(d1).expect("registered");
let p_b = rt.core.partition_of(d2).expect("registered");
assert_ne!(p_a, p_b);
assert_eq!(rt.core.partition_of(s1.id), Some(p_a));
assert_eq!(rt.core.partition_of(s2.id), Some(p_b));
}