Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016DvdRZkyCy9NJpHYDdPqmS
74 lines
2.2 KiB
Python
74 lines
2.2 KiB
Python
"""Phase-0 gate: the output contract validates and rejects garbage."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from agents.schemas import AgentOutput, AgentStatus, Direction, Pick, RunManifest
|
|
|
|
|
|
def _pick(**kw) -> Pick:
|
|
base = dict(ticker="nvda", conviction=0.8, thesis="momentum", requested_weight_pct=0.05)
|
|
base.update(kw)
|
|
return Pick(**base)
|
|
|
|
|
|
def test_pick_valid_and_ticker_uppercased():
|
|
p = _pick()
|
|
assert p.ticker == "NVDA"
|
|
assert p.direction == Direction.LONG
|
|
|
|
|
|
def test_conviction_out_of_range_rejected():
|
|
with pytest.raises(ValidationError):
|
|
_pick(conviction=1.5)
|
|
|
|
|
|
def test_weight_must_be_positive_fraction():
|
|
with pytest.raises(ValidationError):
|
|
_pick(requested_weight_pct=0.0)
|
|
with pytest.raises(ValidationError):
|
|
_pick(requested_weight_pct=1.5)
|
|
|
|
|
|
def test_stop_take_pct_must_be_fraction():
|
|
with pytest.raises(ValidationError):
|
|
_pick(stop_loss_pct=7.0) # 700% — clearly a mistake
|
|
assert _pick(stop_loss_pct=0.07).stop_loss_pct == 0.07
|
|
|
|
|
|
def test_long_absolute_levels_ordering_enforced():
|
|
with pytest.raises(ValidationError):
|
|
_pick(entry_type="limit", limit_price=100.0, stop_price=105.0) # stop above entry
|
|
with pytest.raises(ValidationError):
|
|
_pick(entry_type="limit", limit_price=100.0, take_price=95.0) # take below entry
|
|
ok = _pick(entry_type="limit", limit_price=100.0, stop_price=93.0, take_price=120.0)
|
|
assert ok.limit_price == 100.0
|
|
|
|
|
|
def test_agent_output_roundtrip():
|
|
out = AgentOutput(
|
|
agent_name="Nova",
|
|
persona_id="nova",
|
|
started_at=datetime.now(timezone.utc),
|
|
status=AgentStatus.COMPLETE,
|
|
picks=[_pick(), _pick(ticker="amd", conviction=0.6)],
|
|
)
|
|
restored = AgentOutput.model_validate_json(out.model_dump_json())
|
|
assert restored.agent_name == "Nova"
|
|
assert [p.ticker for p in restored.picks] == ["NVDA", "AMD"]
|
|
|
|
|
|
def test_run_manifest_minimal():
|
|
m = RunManifest(
|
|
run_id="r1",
|
|
trade_date="2026-07-01",
|
|
model="gpt-oss-20b",
|
|
started_at=datetime.now(timezone.utc),
|
|
)
|
|
assert m.status == "running"
|
|
assert m.agents == []
|