Files
awoooi/apps/api/tests/integration/test_aider_event_repository.py
Your Name d0591c54b0
Some checks failed
CD Pipeline / build-and-deploy (push) Failing after 35s
fix(security): 體健修復 — 7項 Critical/Major 安全問題全修
## Critical 修復 (C1-C5)
- C1: git rm --cached 03-secrets.yaml(CHANGE_ME 模板不再追蹤)
- C2: git rm --cached awoooi.db + .gitignore 加 *.db(SQLite HARD_RULES 違規)
- C3: sentry-tunnel SENTRY_HOST 改為 process.env fallback
- C4: config.py DATABASE_URL 移除 changeme default,改為必填
- C5: run_migration.py 改為 os.environ["DATABASE_URL"]

## Major 修復 (M1-M4)
- M1: auto_repair /execute 加 CSRF 保護 + AutoRepairPanel.tsx 同步
- M2: drift /rollback /adopt 加 CSRF 保護(/internal/scan 保持無 CSRF)
- M3: terminal /intent 加 CSRF 保護 + terminal.store.ts 同步
- M4: live-dashboard HOST_IPS + host-grid VIP 改為 env var

## 其他
- 新增 apps/web/.env.example(6 個 env var 說明)
- K8s deployment-web 補入 3 個新 env var
- 整合測試:新增 aider_event_repository + ai_router_feedback 真實 DB 測試
- test_terminal.py CSRF dependency override 修復

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 01:27:39 +08:00

166 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# tests/integration/test_aider_event_repository.py | 2026-04-22 @ Asia/Taipei
"""AiderEventRepository 整合測試 — 使用真實 awoooi_dev PostgreSQL
替換 tests/test_aider_event_processor.py 中違反 feedback_no_mock_testing.md 的
FakeRepo / FakeSession mock。
原測試 (test_aider_event_processor.py) 驗證的是 AiderEventProcessor._process_one()
的整體流程parse → incident → DB write → ACK其中
- FakeRepo / FakeSession → 此整合測試改用真實 DB 驗證 insert 行為
- fake_r.xack (Redis) → Redis 屬外部 broker仍可在 unit test 中 mock符合「外部 API」例外
- fake_engine (IncidentEngine) → AI 推斷服務,屬外部呼叫,仍可 mock
此檔案: 只測「DB 層」— AiderEventRepository.insert() + model_stats_since()
其餘路由邏輯已在 test_aider_event_processor.py 的 parser/ACK 部份覆蓋。
規則: 每個測試後 rollback由 integration/conftest.py db_session fixture 保證)
禁止 Mock — 直接使用真實 DB 連線。
"""
from __future__ import annotations
from datetime import datetime, timezone, timedelta
import pytest
from src.repositories.aider_event_repository import AiderEventRepository
TAIPEI = timezone(timedelta(hours=8))
def _ts() -> datetime:
return datetime.now(TAIPEI)
# =============================================================================
# insert() 基本寫入
# =============================================================================
class TestAiderEventRepositoryInsert:
"""驗證 insert() 正確寫入 aider_events 表。"""
@pytest.mark.asyncio
async def test_insert_error_event_returns_id(self, db_session):
"""error event 插入後應回傳有效 BIGSERIAL id。"""
repo = AiderEventRepository(db_session)
row_id = await repo.insert(
session_id="s-test-001",
ts=_ts(),
type_="error",
host="ogt-mac",
payload={"cwd": "/r", "model": "elephant-alpha",
"kind": "api_rate_limit", "message": "429",
"context_50chars": ""},
)
assert isinstance(row_id, int)
assert row_id > 0
@pytest.mark.asyncio
async def test_insert_session_start_with_incident_id(self, db_session):
"""insert 可附帶 incident_idnullable FK"""
repo = AiderEventRepository(db_session)
row_id = await repo.insert(
session_id="s-test-002",
ts=_ts(),
type_="session_start",
host="ogt-mac",
payload={"cwd": "/r", "model": "elephant-alpha",
"aider_args": [], "aider_pid": 1, "cli_version": "0.86"},
incident_id="INC-20260422-0001",
)
assert row_id > 0
@pytest.mark.asyncio
async def test_insert_without_incident_id(self, db_session):
"""incident_id 可為 None常見情境"""
repo = AiderEventRepository(db_session)
row_id = await repo.insert(
session_id="s-test-003",
ts=_ts(),
type_="commit",
host="ogt-mac",
payload={"cwd": "/r", "model": "gemini-pro",
"commit_hash": "abc123", "message": "fix: something"},
)
assert row_id > 0
# =============================================================================
# count_by_session()
# =============================================================================
class TestAiderEventRepositoryCount:
@pytest.mark.asyncio
async def test_count_returns_correct_value(self, db_session):
"""插入 3 筆相同 session_idcount 應回傳 3。"""
repo = AiderEventRepository(db_session)
sid = "s-count-test-001"
for i in range(3):
await repo.insert(
session_id=sid,
ts=_ts(),
type_="error",
host="ogt-mac",
payload={"kind": "test", "message": f"err-{i}",
"model": "m", "cwd": "/r", "context_50chars": ""},
)
count = await repo.count_by_session(sid)
assert count == 3
@pytest.mark.asyncio
async def test_count_unknown_session_is_zero(self, db_session):
"""不存在的 session_id 應回傳 0。"""
repo = AiderEventRepository(db_session)
count = await repo.count_by_session("nonexistent-session-xyz")
assert count == 0
# =============================================================================
# model_stats_since() — AI Router feedback 聚合查詢
# =============================================================================
class TestAiderEventRepositoryModelStats:
@pytest.mark.asyncio
async def test_model_stats_returns_list(self, db_session):
"""model_stats_since() 應回傳 list即使空"""
repo = AiderEventRepository(db_session)
result = await repo.model_stats_since(days=1)
assert isinstance(result, list)
@pytest.mark.asyncio
async def test_model_stats_aggregates_correctly(self, db_session):
"""插入 session_start + errorstats 應正確統計 error_rate。
注意model_stats_since 聚合邏輯依賴 session_start/session_end payload.model 欄位,
且需要多筆 session 才能統計。此測試驗證:不崩潰、回傳正確型別。
"""
repo = AiderEventRepository(db_session)
ts_now = _ts()
# 插入一個 session 含 session_start提供 model 資訊)+ 一筆 error
sid = "s-stats-test-001"
await repo.insert(
session_id=sid, ts=ts_now,
type_="session_start", host="ogt-mac",
payload={"cwd": "/awoooi", "model": "elephant-alpha",
"aider_args": [], "aider_pid": 1, "cli_version": "0.86"},
)
await repo.insert(
session_id=sid, ts=ts_now,
type_="error", host="ogt-mac",
payload={"cwd": "/awoooi", "model": "elephant-alpha",
"kind": "api_rate_limit", "message": "429",
"context_50chars": ""},
)
await db_session.flush() # flush 使 SQL CTE 能看到資料(不 commit
result = await repo.model_stats_since(days=1)
assert isinstance(result, list)
# 若有回傳結果,驗證欄位格式正確
for row in result:
assert "model" in row
assert "total" in row
assert "errors" in row
assert "success_rate" in row