feat: add Meet Kevin pydantic schemas (analysis + API shapes)

This commit is contained in:
Viktor Barzin 2026-05-21 19:06:04 +00:00
parent 8f616e6487
commit 75534de71b
2 changed files with 512 additions and 0 deletions

View file

@ -584,3 +584,197 @@ class TestTokenResponse:
)
restored = TokenResponse.model_validate_json(t.model_dump_json())
assert restored == t
# ---------------------------------------------------------------------------
# --- Meet Kevin schemas ---
# ---------------------------------------------------------------------------
class TestMeetKevinTickerMention:
def test_valid_ticker_mention(self) -> None:
from shared.schemas.meet_kevin import MeetKevinTickerMention
mention = MeetKevinTickerMention(
symbol="AAPL",
action="buy",
conviction=0.85,
time_horizon="months",
rationale_quote="Strong earnings growth expected",
video_timestamp_seconds=120,
)
assert mention.symbol == "AAPL"
assert mention.conviction == 0.85
def test_symbol_auto_uppercases(self) -> None:
from shared.schemas.meet_kevin import MeetKevinTickerMention
mention = MeetKevinTickerMention(
symbol="tsla",
action="hold",
conviction=0.5,
time_horizon="weeks",
rationale_quote="Neutral outlook",
)
assert mention.symbol == "TSLA"
def test_conviction_out_of_range_high(self) -> None:
from shared.schemas.meet_kevin import MeetKevinTickerMention
with pytest.raises(ValidationError):
MeetKevinTickerMention(
symbol="AAPL",
action="buy",
conviction=1.5,
time_horizon="months",
rationale_quote="Too confident",
)
def test_conviction_out_of_range_low(self) -> None:
from shared.schemas.meet_kevin import MeetKevinTickerMention
with pytest.raises(ValidationError):
MeetKevinTickerMention(
symbol="AAPL",
action="sell",
conviction=-0.1,
time_horizon="days",
rationale_quote="Negative conviction",
)
def test_conviction_edge_cases(self) -> None:
from shared.schemas.meet_kevin import MeetKevinTickerMention
# Test 0.0
m1 = MeetKevinTickerMention(
symbol="GOOG",
action="avoid",
conviction=0.0,
time_horizon="unspecified",
rationale_quote="No confidence",
)
assert m1.conviction == 0.0
# Test 1.0
m2 = MeetKevinTickerMention(
symbol="MSFT",
action="buy",
conviction=1.0,
time_horizon="long_term",
rationale_quote="Maximum confidence",
)
assert m2.conviction == 1.0
def test_timestamp_optional(self) -> None:
from shared.schemas.meet_kevin import MeetKevinTickerMention
mention = MeetKevinTickerMention(
symbol="NVDA",
action="watch",
conviction=0.6,
time_horizon="intraday",
rationale_quote="Monitoring closely",
)
assert mention.video_timestamp_seconds is None
class TestMeetKevinAnalysis:
def test_valid_analysis(self) -> None:
from shared.schemas.meet_kevin import (
MeetKevinAnalysis,
MeetKevinTickerMention,
)
analysis = MeetKevinAnalysis(
market_outlook_direction="bullish",
market_outlook_reasoning="Strong macro tailwinds",
macro_themes=["inflation_easing", "ai_acceleration"],
key_risks=["geopolitical_uncertainty", "rate_volatility"],
summary="Overall positive outlook for tech sector",
tickers=[
MeetKevinTickerMention(
symbol="AAPL",
action="buy",
conviction=0.85,
time_horizon="months",
rationale_quote="Strong earnings expected",
)
],
)
assert analysis.market_outlook_direction == "bullish"
assert len(analysis.tickers) == 1
assert analysis.tickers[0].symbol == "AAPL"
def test_multiple_tickers(self) -> None:
from shared.schemas.meet_kevin import (
MeetKevinAnalysis,
MeetKevinTickerMention,
)
analysis = MeetKevinAnalysis(
market_outlook_direction="neutral",
market_outlook_reasoning="Mixed signals",
macro_themes=["earnings_season"],
key_risks=["fed_decisions"],
summary="Cautious outlook",
tickers=[
MeetKevinTickerMention(
symbol="TSLA",
action="buy",
conviction=0.7,
time_horizon="weeks",
rationale_quote="Breakout expected",
),
MeetKevinTickerMention(
symbol="GOOG",
action="hold",
conviction=0.5,
time_horizon="months",
rationale_quote="Wait for clarity",
),
],
)
assert len(analysis.tickers) == 2
assert analysis.tickers[0].symbol == "TSLA"
assert analysis.tickers[1].symbol == "GOOG"
def test_empty_tickers_list(self) -> None:
from shared.schemas.meet_kevin import MeetKevinAnalysis
analysis = MeetKevinAnalysis(
market_outlook_direction="bearish",
market_outlook_reasoning="Recession risk",
macro_themes=["inflation"],
key_risks=["unemployment"],
summary="Negative outlook",
tickers=[],
)
assert len(analysis.tickers) == 0
def test_json_round_trip(self) -> None:
from shared.schemas.meet_kevin import (
MeetKevinAnalysis,
MeetKevinTickerMention,
)
analysis = MeetKevinAnalysis(
market_outlook_direction="mixed",
market_outlook_reasoning="Divergent sector performance",
macro_themes=["rate_peak", "ai_growth"],
key_risks=["credit_stress"],
summary="Selective opportunities",
tickers=[
MeetKevinTickerMention(
symbol="NVIDIA",
action="buy",
conviction=0.95,
time_horizon="long_term",
rationale_quote="AI leader",
video_timestamp_seconds=300,
)
],
)
json_str = analysis.model_dump_json()
restored = MeetKevinAnalysis.model_validate_json(json_str)
assert restored == analysis
assert restored.tickers[0].symbol == "NVIDIA"