mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-07-17 09:17:28 +02:00
96 lines
3.3 KiB
Python
96 lines
3.3 KiB
Python
"""Sprint 2 — InfonetReputationAdapter end-to-end coverage.
|
||
|
||
The adapter is the integration boundary every later sprint will extend.
|
||
Sprint 2 wires it to the pure functions in ``services/infonet/reputation/``.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from services.infonet.adapters.reputation_adapter import InfonetReputationAdapter
|
||
from services.infonet.tests._chain_factory import make_event, make_market_chain
|
||
|
||
|
||
def test_adapter_zero_rep_for_unknown_node():
|
||
a = InfonetReputationAdapter(lambda: [])
|
||
assert a.oracle_rep("nobody") == 0.0
|
||
assert a.common_rep("nobody") == 0.0
|
||
assert a.oracle_rep_lifetime("nobody") == 0.0
|
||
|
||
|
||
def test_adapter_oracle_rep_breakdown_components():
|
||
base = 1_000_000.0
|
||
chain = make_market_chain(
|
||
"m1", "creator", outcome="yes",
|
||
predictions=[
|
||
{"node_id": "alice", "side": "yes", "stake_amount": 10.0},
|
||
{"node_id": "bob", "side": "no", "stake_amount": 10.0},
|
||
],
|
||
base_ts=base,
|
||
participants=5, total_stake=20.0,
|
||
)
|
||
a = InfonetReputationAdapter(lambda: chain)
|
||
bd = a.oracle_rep_breakdown("alice")
|
||
assert bd.staked_prediction_returns == 20.0
|
||
assert bd.staked_prediction_losses == 0.0
|
||
assert bd.total == 20.0
|
||
|
||
|
||
def test_adapter_uses_chain_majority_time_for_decay():
|
||
base = 1_000_000.0
|
||
chain = make_market_chain(
|
||
"m1", "creator", outcome="yes",
|
||
predictions=[{"node_id": "alice", "side": "yes", "probability_at_bet": 30.0}],
|
||
base_ts=base,
|
||
participants=5, total_stake=10.0,
|
||
)
|
||
# Add many recent events from distinct nodes to drive chain_majority_time
|
||
# well beyond the decay window.
|
||
later = base + 86400.0 * 200 # 200 days past finalize
|
||
for i in range(11):
|
||
chain.append(make_event(
|
||
"uprep", f"chatter{i}",
|
||
{"target_node_id": "alice", "target_event_id": f"e{i}"},
|
||
timestamp=later + i, sequence=1,
|
||
))
|
||
a = InfonetReputationAdapter(lambda: chain)
|
||
base_balance = a.oracle_rep("alice")
|
||
active = a.oracle_rep_active("alice")
|
||
# Within 0–90 days: full weight. After 90: factor 0.5. After 180: 0.25.
|
||
# 200 days → 2 periods → 0.25.
|
||
assert active < base_balance
|
||
|
||
|
||
def test_adapter_decay_factor_helper_exposes_zero_for_unknown_node():
|
||
a = InfonetReputationAdapter(lambda: [])
|
||
assert a.decay_factor("nobody") == 0.0
|
||
|
||
|
||
def test_adapter_last_successful_prediction_ts():
|
||
base = 1_000_000.0
|
||
chain = make_market_chain(
|
||
"m1", "creator", outcome="yes",
|
||
predictions=[{"node_id": "alice", "side": "yes", "probability_at_bet": 30.0}],
|
||
base_ts=base,
|
||
participants=5, total_stake=10.0,
|
||
)
|
||
a = InfonetReputationAdapter(lambda: chain)
|
||
ts = a.last_successful_prediction_ts("alice")
|
||
assert ts is not None and ts >= base
|
||
|
||
|
||
def test_adapter_callable_chain_provider_is_invoked_per_call():
|
||
"""Adapter must NOT cache the chain — fresh evaluation each call so
|
||
new events show up. Caching at the adapter is a Sprint 3+ concern."""
|
||
snapshot_calls = {"n": 0}
|
||
chain: list[dict] = []
|
||
|
||
def provider():
|
||
snapshot_calls["n"] += 1
|
||
return list(chain)
|
||
|
||
a = InfonetReputationAdapter(provider)
|
||
a.oracle_rep("x")
|
||
a.common_rep("x")
|
||
a.oracle_rep_lifetime("x")
|
||
assert snapshot_calls["n"] == 3
|