F3.3 tests: zombie fixture + fallback (15/15 pass)
This commit is contained in:
parent
ee021bd266
commit
b5fe12a626
|
|
@ -123,6 +123,59 @@ def test_f32_zombie_broadcast():
|
|||
f"F3.2 zombie fail: dialogue={dialogue.reciprocity_f32:.4f} <= broadcast={broadcast.reciprocity_f32:.4f}"
|
||||
|
||||
|
||||
def test_f33_zombie_broadcast_concepts():
|
||||
"""F3.3 unique-concept-count must crush spam even harder than F3.2.
|
||||
100 pings with 2 concepts vs 5 messages with 5 concepts.
|
||||
Idea from 小風 (ClawdChat): replace count with unique topic count."""
|
||||
msgs = []
|
||||
# 5-message dialogue, high reciprocity (3+2), 5 unique concepts
|
||||
concepts_rich = [["graph", "reciprocity"], ["decay", "metrics"], ["shadow"],
|
||||
["echo", "idf"], ["influence"]]
|
||||
for i in range(3):
|
||||
msgs.append({"from_id": "alice", "to_id": "bob",
|
||||
"timestamp": now - 1*day + i*100,
|
||||
"concepts": concepts_rich[i]})
|
||||
for i in range(2):
|
||||
msgs.append({"from_id": "bob", "to_id": "alice",
|
||||
"timestamp": now - 0.5*day + i*100,
|
||||
"concepts": concepts_rich[3+i]})
|
||||
# 100 pings, low reciprocity (95+5), only 2 unique concepts (repeated)
|
||||
for i in range(95):
|
||||
msgs.append({"from_id": "carol", "to_id": "dave",
|
||||
"timestamp": now - 2*day + i*60,
|
||||
"concepts": ["hello", "ping"]})
|
||||
for i in range(5):
|
||||
msgs.append({"from_id": "dave", "to_id": "carol",
|
||||
"timestamp": now - 1.5*day + i*600,
|
||||
"concepts": ["hello"]})
|
||||
|
||||
edges = score_reciprocity(msgs, now=now)
|
||||
dialogue = [e for e in edges.values() if "alice" in (e.source, e.target)][0]
|
||||
broadcast = [e for e in edges.values() if "carol" in (e.source, e.target)][0]
|
||||
|
||||
# F3.3 must separate them
|
||||
assert dialogue.reciprocity_f33 > broadcast.reciprocity_f33, \
|
||||
(f"F3.3 zombie fail: dialogue={dialogue.reciprocity_f33:.4f} "
|
||||
f"<= broadcast={broadcast.reciprocity_f33:.4f}")
|
||||
# F3.3 spread should be wider than F3.2
|
||||
f33_ratio = dialogue.reciprocity_f33 / max(broadcast.reciprocity_f33, 0.001)
|
||||
f32_ratio = dialogue.reciprocity_f32 / max(broadcast.reciprocity_f32, 0.001)
|
||||
assert f33_ratio > f32_ratio, \
|
||||
f"F3.3 should separate more than F3.2: f33_ratio={f33_ratio:.1f} <= f32_ratio={f32_ratio:.1f}"
|
||||
|
||||
|
||||
def test_f33_fallback_no_concepts():
|
||||
"""F3.3 falls back to F3.2 when messages have no concepts."""
|
||||
msgs = [
|
||||
{"from_id": "a", "to_id": "b", "timestamp": now - 1*day},
|
||||
{"from_id": "b", "to_id": "a", "timestamp": now - 0.5*day},
|
||||
]
|
||||
edges = score_reciprocity(msgs, now=now)
|
||||
edge = list(edges.values())[0]
|
||||
assert edge.reciprocity_f33 == edge.reciprocity_f32, \
|
||||
f"F3.3 should equal F3.2 with no concepts: f33={edge.reciprocity_f33}, f32={edge.reciprocity_f32}"
|
||||
|
||||
|
||||
def test_f32_idf_echo_combined():
|
||||
"""F3.2 and IDF echo work together in full analyze()."""
|
||||
msgs = [
|
||||
|
|
|
|||
Loading…
Reference in New Issue