feat(test): B5 整合測試框架 — 真實 DB, 5/5 通過
Some checks failed
CD Pipeline / build-and-deploy (push) Failing after 2m34s
Some checks failed
CD Pipeline / build-and-deploy (push) Failing after 2m34s
新增: - docker-compose.test.yml: CI 用臨時 pgvector PostgreSQL (port 15432) - tests/factories.py: Incident/Approval/Knowledge/RAG 測試資料工廠 - tests/integration/test_b5_core_flows.py: 5 個 E2E 整合測試 (5/5 PASSED 1.03s) - tests/integration/setup_test_schema.sql: CI schema 初始化 SQL - cd.yaml: 新增 Integration Tests B5 step - scripts/sync_dev_db.py: dev DB 同步工具 修正: - .env.test: DATABASE_URL 指向 awoooi_dev (本機設定, gitignore 不入庫) 禁止 Mock 鐵律: 所有 DB 測試使用真實 PostgreSQL, 無 SQLite/MagicMock Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
109
scripts/sync_dev_db.py
Normal file
109
scripts/sync_dev_db.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
"""同步 dev DB — 補齊 prod 有但 dev 沒有的表"""
|
||||
import asyncio
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlalchemy import text
|
||||
|
||||
DEV_URL = "postgresql+asyncpg://awoooi:awoooi_prod_2026@192.168.0.188:5432/awoooi_dev"
|
||||
|
||||
MIGRATIONS = [
|
||||
("auto_repair_executions", """
|
||||
CREATE TABLE IF NOT EXISTS auto_repair_executions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
incident_id UUID,
|
||||
playbook_name TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
started_at TIMESTAMPTZ,
|
||||
finished_at TIMESTAMPTZ,
|
||||
result JSONB,
|
||||
error_message TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
"""),
|
||||
("alert_operation_log", """
|
||||
CREATE TABLE IF NOT EXISTS alert_operation_log (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
event_type TEXT NOT NULL,
|
||||
incident_id UUID,
|
||||
approval_id UUID,
|
||||
actor TEXT,
|
||||
action_detail TEXT,
|
||||
success BOOLEAN DEFAULT true,
|
||||
context JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
"""),
|
||||
("playbooks", """
|
||||
CREATE TABLE IF NOT EXISTS playbooks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
steps JSONB NOT NULL DEFAULT '[]',
|
||||
tags TEXT[] DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
"""),
|
||||
("drift_reports", """
|
||||
CREATE TABLE IF NOT EXISTS drift_reports (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
host TEXT NOT NULL,
|
||||
report_type TEXT NOT NULL DEFAULT 'config',
|
||||
items JSONB NOT NULL DEFAULT '[]',
|
||||
summary TEXT,
|
||||
severity TEXT DEFAULT 'low',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
"""),
|
||||
("pr_reviews", """
|
||||
CREATE TABLE IF NOT EXISTS pr_reviews (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
repo TEXT NOT NULL,
|
||||
pr_id TEXT NOT NULL,
|
||||
review_text TEXT,
|
||||
model TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
"""),
|
||||
("vector_extension", "CREATE EXTENSION IF NOT EXISTS vector"),
|
||||
("rag_chunks", """
|
||||
CREATE TABLE IF NOT EXISTS rag_chunks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
source TEXT NOT NULL,
|
||||
source_id TEXT NOT NULL,
|
||||
title TEXT,
|
||||
chunk_text TEXT NOT NULL,
|
||||
embedding vector(768),
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
"""),
|
||||
]
|
||||
|
||||
|
||||
async def main():
|
||||
engine = create_async_engine(DEV_URL, echo=False)
|
||||
async with engine.begin() as conn:
|
||||
r = await conn.execute(text(
|
||||
"SELECT table_name FROM information_schema.tables WHERE table_schema='public' ORDER BY table_name"
|
||||
))
|
||||
existing = {row[0] for row in r}
|
||||
print(f"dev 現有: {sorted(existing)}")
|
||||
|
||||
for name, sql in MIGRATIONS:
|
||||
try:
|
||||
await conn.execute(text(sql))
|
||||
print(f" ✓ {name}")
|
||||
except Exception as e:
|
||||
print(f" ⚠ {name}: {e}")
|
||||
|
||||
r2 = await conn.execute(text(
|
||||
"SELECT table_name FROM information_schema.tables WHERE table_schema='public' ORDER BY table_name"
|
||||
))
|
||||
final = [row[0] for row in r2]
|
||||
print(f"\ndev 最終: {final}")
|
||||
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user