All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Pipeline #46 surfaced two pre-existing CI bugs once fakeredis was installed and tests could collect: 1. test_models.py:389 asserted "DISCOVERED" in status_col.type.enums, but the model defines KevinVideoStatus with values_callable so .enums returns the lowercase string values, not member names. Asserting "discovered" instead. 2. Four test files use the db_session fixture which requires a real Postgres on localhost:5432. CI has no Postgres, so 10 tests failed with Connect call failed (errno 111). These genuinely need a DB — mirroring tests/integration/* which already use @pytest.mark.integration. Adding module-level pytestmark = pytest.mark.integration to: - tests/shared/models/test_meet_kevin_trading.py - tests/services/kevin_signal_bridge/test_aggregator.py - tests/services/kevin_signal_bridge/test_audit.py - tests/services/kevin_signal_bridge/test_exit_scanner.py CI runs with -m "not integration" so they're now deselected. Local pytest still picks them up by default (no marker filter).
123 lines
3.3 KiB
Python
123 lines
3.3 KiB
Python
"""Tests for the kevin trading SA models (audit + backtest)."""
|
|
|
|
import uuid
|
|
from datetime import datetime, timezone
|
|
from decimal import Decimal
|
|
|
|
import pytest
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
pytestmark = pytest.mark.integration
|
|
|
|
from shared.models.meet_kevin import (
|
|
KevinAnalysis,
|
|
KevinChannel,
|
|
KevinMarketOutlook,
|
|
KevinStockMention,
|
|
KevinTickerAction,
|
|
KevinTimeHorizon,
|
|
KevinVideo,
|
|
KevinVideoStatus,
|
|
)
|
|
from shared.models.meet_kevin_trading import (
|
|
BridgeStatus,
|
|
KevinBacktestRun,
|
|
KevinBacktestRunStatus,
|
|
KevinBacktestTrade,
|
|
KevinSignalBridgeState,
|
|
TriggerSource,
|
|
)
|
|
|
|
|
|
async def _seed_mention(db_session: AsyncSession) -> KevinStockMention:
|
|
channel = KevinChannel(youtube_channel_id="UCtest", title="t")
|
|
db_session.add(channel)
|
|
await db_session.flush()
|
|
video = KevinVideo(
|
|
channel_id=channel.id,
|
|
youtube_video_id="v1",
|
|
title="t",
|
|
published_at=datetime.now(timezone.utc),
|
|
status=KevinVideoStatus.ANALYZED,
|
|
)
|
|
db_session.add(video)
|
|
await db_session.flush()
|
|
analysis = KevinAnalysis(
|
|
video_id=video.id,
|
|
model="claude-sonnet",
|
|
prompt_version="v1",
|
|
market_outlook_direction=KevinMarketOutlook.NEUTRAL,
|
|
market_outlook_reasoning="x",
|
|
summary="x",
|
|
prompt_tokens=10,
|
|
completion_tokens=10,
|
|
cost_usd=Decimal("0.01"),
|
|
)
|
|
db_session.add(analysis)
|
|
await db_session.flush()
|
|
mention = KevinStockMention(
|
|
video_id=video.id,
|
|
analysis_id=analysis.id,
|
|
symbol="NVDA",
|
|
action=KevinTickerAction.BUY,
|
|
conviction=Decimal("0.7"),
|
|
time_horizon=KevinTimeHorizon.WEEKS,
|
|
rationale_quote="x",
|
|
)
|
|
db_session.add(mention)
|
|
await db_session.flush()
|
|
return mention
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_bridge_state_inserts_and_loads(db_session: AsyncSession):
|
|
mention = await _seed_mention(db_session)
|
|
|
|
state = KevinSignalBridgeState(
|
|
mention_id=mention.id,
|
|
bridge_status=BridgeStatus.EMITTED,
|
|
effective_conviction=Decimal("0.75"),
|
|
notes="boosted +0.05",
|
|
)
|
|
db_session.add(state)
|
|
await db_session.flush()
|
|
|
|
row = (
|
|
await db_session.execute(
|
|
select(KevinSignalBridgeState).where(
|
|
KevinSignalBridgeState.mention_id == mention.id
|
|
)
|
|
)
|
|
).scalar_one()
|
|
assert row.bridge_status == BridgeStatus.EMITTED
|
|
assert row.effective_conviction == Decimal("0.750")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_backtest_run_and_trade_cascade(db_session: AsyncSession):
|
|
run = KevinBacktestRun(
|
|
run_uuid=uuid.uuid4(),
|
|
status=KevinBacktestRunStatus.COMPLETED,
|
|
trigger_source=TriggerSource.MANUAL,
|
|
params_json={"holding_days": 10},
|
|
metrics_json={"total_return_pct": 3.4},
|
|
)
|
|
db_session.add(run)
|
|
await db_session.flush()
|
|
|
|
trade = KevinBacktestTrade(
|
|
run_id=run.id,
|
|
symbol="NVDA",
|
|
entry_at=datetime.now(timezone.utc),
|
|
entry_price=Decimal("180.00"),
|
|
qty=Decimal("11"),
|
|
)
|
|
db_session.add(trade)
|
|
await db_session.flush()
|
|
|
|
# Cascade: delete run -> trade gone
|
|
await db_session.delete(run)
|
|
await db_session.flush()
|
|
trades = (await db_session.execute(select(KevinBacktestTrade))).scalars().all()
|
|
assert trades == []
|