myco-bonding-curve/tests/test_crdt_flow.py

109 lines
4.0 KiB
Python

"""Tests for CRDT flow dampening."""
import numpy as np
from src.crdt.flow_dampening import (
CRDTFlowSystem,
merge_flow_states,
peer_penalty,
simulate_drain_attack,
update_peer_flow,
)
class TestPeerFlowTracking:
def test_new_peer_no_penalty(self):
system = CRDTFlowSystem(default_total_value_ref=1000.0)
assert peer_penalty(system, "alice") == 1.0
def test_update_records_flow(self):
system = CRDTFlowSystem(default_total_value_ref=1000.0)
system = update_peer_flow(system, "alice", 50.0, 0.0)
assert "alice" in system.peers
assert system.peers["alice"].tracker.current_flow == 50.0
def test_multiple_peers_independent(self):
system = CRDTFlowSystem(default_total_value_ref=1000.0)
system = update_peer_flow(system, "alice", 50.0, 0.0)
system = update_peer_flow(system, "bob", 200.0, 0.0)
assert system.peers["alice"].tracker.current_flow == 50.0
assert system.peers["bob"].tracker.current_flow == 200.0
def test_high_flow_triggers_penalty(self):
system = CRDTFlowSystem(
default_total_value_ref=1000.0, default_threshold=0.1,
)
system = update_peer_flow(system, "alice", 200.0, 0.0)
penalty = peer_penalty(system, "alice")
assert penalty < 1.0
def test_pure_functional_no_mutation(self):
system = CRDTFlowSystem(default_total_value_ref=1000.0)
updated = update_peer_flow(system, "alice", 50.0, 0.0)
assert "alice" not in system.peers
assert "alice" in updated.peers
class TestFlowMerge:
def test_merge_commutativity(self):
a = CRDTFlowSystem(default_total_value_ref=1000.0)
a = update_peer_flow(a, "alice", 100.0, 0.0)
b = CRDTFlowSystem(default_total_value_ref=1000.0)
b = update_peer_flow(b, "alice", 50.0, 1.0)
ab = merge_flow_states(a, b)
ba = merge_flow_states(b, a)
assert ab.peers["alice"].tracker.current_flow == ba.peers["alice"].tracker.current_flow
assert ab.peers["alice"].tracker.last_update_time == ba.peers["alice"].tracker.last_update_time
def test_merge_idempotency(self):
a = CRDTFlowSystem(default_total_value_ref=1000.0)
a = update_peer_flow(a, "alice", 100.0, 0.0)
aa = merge_flow_states(a, a)
assert aa.peers["alice"].tracker.current_flow == a.peers["alice"].tracker.current_flow
def test_merge_takes_max_flow(self):
a = CRDTFlowSystem(default_total_value_ref=1000.0)
a = update_peer_flow(a, "alice", 100.0, 0.0)
b = CRDTFlowSystem(default_total_value_ref=1000.0)
b = update_peer_flow(b, "alice", 200.0, 0.0)
merged = merge_flow_states(a, b)
assert merged.peers["alice"].tracker.current_flow == 200.0
def test_merge_disjoint_peers(self):
a = CRDTFlowSystem(default_total_value_ref=1000.0)
a = update_peer_flow(a, "alice", 100.0, 0.0)
b = CRDTFlowSystem(default_total_value_ref=1000.0)
b = update_peer_flow(b, "bob", 50.0, 0.0)
merged = merge_flow_states(a, b)
assert "alice" in merged.peers
assert "bob" in merged.peers
class TestDrainSimulation:
def test_simulation_returns_arrays(self):
system = CRDTFlowSystem(
default_total_value_ref=10000.0, default_threshold=0.1,
)
result = simulate_drain_attack(
system, "attacker",
amounts=[100.0] * 10,
times=[float(i) for i in range(10)],
)
assert len(result["times"]) == 10
assert len(result["flows"]) == 10
assert len(result["penalties"]) == 10
def test_penalty_increases_with_drain(self):
system = CRDTFlowSystem(
default_total_value_ref=1000.0, default_threshold=0.1,
)
result = simulate_drain_attack(
system, "attacker",
amounts=[200.0] * 5,
times=[0.0, 0.1, 0.2, 0.3, 0.4],
)
# Penalties should decrease (get worse) as flow accumulates
assert result["penalties"][-1] <= result["penalties"][0]