diff --git a/test_swarmmetrics.py b/test_swarmmetrics.py index a45d04f..66acf49 100644 --- a/test_swarmmetrics.py +++ b/test_swarmmetrics.py @@ -4,7 +4,7 @@ import sys sys.path.insert(0, ".") from swarmmetrics import ( _gini, _half_life_weight, score_reciprocity, score_channels, - score_echo, detect_shadows, analyze + score_echo, detect_shadows, analyze, shuffle_test ) now = time.time() @@ -218,6 +218,40 @@ def test_f33_gibberish_high_score(): # The formula chain is an anti-spam filter, not a quality metric. +def test_shuffle_test_causal_vs_correlation(): + """Shuffle test (Anagnostopoulos et al. 2008) separates causal echo + from correlation. Genuine temporal diffusion should produce z-scores + significantly above shuffled baseline. + Cited: ColonistOne (Colony, 2026-07-28).""" + import random + random.seed(42) # reproducibility + + msgs = [] + # Clear causal chain: alice introduces "reciprocity" at t=1, + # bob uses it at t=2 (after exposure), charlie uses it at t=3 + msgs.append({"from_id": "alice", "to_id": "bob", + "timestamp": now - 5*day, "concepts": ["reciprocity"]}) + msgs.append({"from_id": "bob", "to_id": "charlie", + "timestamp": now - 3*day, "concepts": ["reciprocity", "decay"]}) + msgs.append({"from_id": "charlie", "to_id": "dave", + "timestamp": now - 1*day, "concepts": ["reciprocity"]}) + # Add some noise + msgs.append({"from_id": "dave", "to_id": "alice", + "timestamp": now - 0.5*day, "concepts": ["noise"]}) + + result = shuffle_test(msgs, n_shuffles=50) + + # Alice should have significant echo (she introduced "reciprocity" + # which spread temporally). Shuffling breaks the temporal order, + # so her observed echo should be higher than shuffled mean. + assert result["observed"].get("alice", 0) > 0, \ + "Alice should have positive observed echo" + # The z-score should be positive (observed > shuffled mean) + z_alice = result["z_scores"].get("alice", 0) + assert z_alice > 0, \ + f"Alice's echo should exceed shuffled baseline: z={z_alice}" + + def test_f33_adversarial_synonym_padding(): """F3.3-aware adversary: one unique concept per message inflates score. Test for AX-7 (Colony): 'Do your tests include an adversary who has read F3.3?'