Files
Shadowbroker/backend/services/infonet/tests/test_lifecycle_invalid_ordering.py
T

155 lines
5.3 KiB
Python

"""Regression coverage for malformed market lifecycle ordering metadata."""
from typing import Any
import pytest
from services.infonet.config import CONFIG
from services.infonet.markets import MarketStatus, compute_market_status, should_advance_phase
from services.infonet.markets.event_selection import events_for_market
from services.infonet.markets.resolution import resolve_market
from services.infonet.markets.snapshot import InvalidAuthoritativeSnapshot, find_snapshot
def _create(trigger_date: Any = 200.0) -> dict[str, Any]:
return {
"event_type": "prediction_create",
"node_id": "creator",
"timestamp": 100.0,
"sequence": 1,
"payload": {"market_id": "m1", "trigger_date": trigger_date},
}
def _snapshot(
timestamp: Any = 200.0, frozen_at: Any = 200.0, sequence: Any = 2
) -> dict[str, Any]:
return {
"event_type": "market_snapshot",
"node_id": "creator",
"timestamp": timestamp,
"sequence": sequence,
"payload": {
"market_id": "m1",
"frozen_at": frozen_at,
"frozen_predictor_ids": ["predictor"],
},
}
def _finalize(timestamp: Any = 300.0, sequence: Any = 3) -> dict[str, Any]:
return {
"event_type": "resolution_finalize",
"node_id": "creator",
"timestamp": timestamp,
"sequence": sequence,
"payload": {"market_id": "m1", "outcome": "yes"},
}
def test_malformed_non_authoritative_metadata_does_not_break_projection() -> None:
chain = [
_create(),
_snapshot(),
{
"event_type": "prediction_place",
"node_id": "peer",
"timestamp": "not-a-timestamp",
"sequence": "not-a-sequence",
"payload": {"market_id": "m1"},
},
]
assert compute_market_status("m1", chain, now=201.0) == MarketStatus.EVIDENCE
def test_market_events_preserve_canonical_hashchain_order() -> None:
first = _snapshot(timestamp=300.0, sequence=5)
second = _snapshot(timestamp=200.0, sequence=2)
events = events_for_market("m1", [_create(), first, second])
assert events[1:] == [first, second]
def test_lifecycle_and_find_snapshot_share_authoritative_append_order() -> None:
evidence_window = float(CONFIG["evidence_window_hours"]) * 3600.0
first_timestamp = 10.0 * evidence_window
first = _snapshot(timestamp=first_timestamp, frozen_at=first_timestamp, sequence=5)
second = _snapshot(timestamp=1.0, frozen_at=1.0, sequence=2)
chain = [_create(), first, second]
# Lifecycle must use the first appended snapshot. If snapshot.py re-sorted
# by timestamp/sequence, the much older second event would be selected and
# this same `now` would project RESOLVING instead of EVIDENCE.
now = first_timestamp + evidence_window / 2.0
assert compute_market_status("m1", chain, now=now) == MarketStatus.EVIDENCE
assert find_snapshot("m1", chain) == first["payload"]
def test_invalid_first_snapshot_fails_closed_without_replacement() -> None:
chain = [
_create(),
_snapshot(timestamp="bad", frozen_at=200.0, sequence="bad"),
_snapshot(timestamp=200.0, frozen_at=200.0, sequence=3),
]
assert compute_market_status("m1", chain, now=10_000.0) == MarketStatus.PREDICTING
assert should_advance_phase("m1", chain, now=10_000.0) is None
with pytest.raises(InvalidAuthoritativeSnapshot):
find_snapshot("m1", chain)
def test_invalid_first_snapshot_halts_direct_resolution() -> None:
chain = [
_create(),
_snapshot(timestamp="bad", frozen_at=200.0, sequence="bad"),
_snapshot(timestamp=200.0, frozen_at=200.0, sequence=3),
{
"event_type": "resolution_stake",
"node_id": "resolver",
"timestamp": 400.0,
"sequence": 4,
"payload": {
"market_id": "m1",
"side": "yes",
"amount": 10.0,
"rep_type": "oracle",
},
},
]
# A malformed authoritative commitment is distinct from no snapshot.
# Resolution must halt instead of treating it as an empty exclusion set.
with pytest.raises(InvalidAuthoritativeSnapshot):
resolve_market("m1", chain)
def test_nonfinite_snapshot_timestamp_does_not_fall_back_to_frozen_at() -> None:
chain = [_create(), _snapshot(timestamp=float("nan"), frozen_at=200.0)]
assert compute_market_status("m1", chain, now=201.0) == MarketStatus.PREDICTING
assert should_advance_phase("m1", chain, now=10_000.0) is None
with pytest.raises(InvalidAuthoritativeSnapshot):
find_snapshot("m1", chain)
def test_invalid_first_finalize_cannot_be_replaced_by_later_finalize() -> None:
chain = [
_create(),
_snapshot(),
_finalize(timestamp="bad", sequence="bad"),
_finalize(timestamp=300.0, sequence=4),
]
# The malformed finalize has no terminal authority, so projection stays
# in the prior snapshot-backed phase. The later finalize cannot replace it.
assert compute_market_status("m1", chain, now=201.0) == MarketStatus.EVIDENCE
assert should_advance_phase("m1", chain, now=10_000.0) is None
def test_invalid_trigger_date_cannot_authorize_phase_advance() -> None:
chain = [_create(trigger_date="not-a-date")]
assert should_advance_phase("m1", chain, now=10_000.0) is None