fix(audit): bind jsonb through asyncpg
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m45s
CD Pipeline / build-and-deploy (push) Successful in 17m47s
CD Pipeline / post-deploy-checks (push) Successful in 2m49s
AI 技術雷達監控 / ai-technology-watch (push) Successful in 35s

This commit is contained in:
ogt
2026-07-16 19:21:00 +08:00
parent ab798c1328
commit 7577ca6dfd
3 changed files with 91 additions and 2 deletions

View File

@@ -0,0 +1,89 @@
from __future__ import annotations
import json
import os
from contextlib import asynccontextmanager
from typing import Any
import pytest
os.environ.setdefault(
"DATABASE_URL",
"postgresql+asyncpg://test:test@localhost/test",
)
from src.db import base as db_base # noqa: E402
from src.services import audit_sink, contract_service # noqa: E402
class _CaptureDb:
def __init__(self) -> None:
self.calls: list[tuple[Any, dict[str, Any]]] = []
async def execute(
self,
statement: Any,
params: dict[str, Any],
) -> None:
self.calls.append((statement, params))
def _assert_jsonb_bind_contract(db: _CaptureDb) -> None:
assert len(db.calls) == 1
statement, params = db.calls[0]
normalized_sql = " ".join(str(statement).split())
assert ":details::jsonb" not in normalized_sql
assert "CAST(:details AS JSONB)" in normalized_sql
assert "details" in statement._bindparams
assert "detail" not in statement._bindparams
assert json.loads(params["details"])["result"] == "verified"
@pytest.mark.asyncio
async def test_audit_sink_uses_asyncpg_safe_jsonb_bind(
monkeypatch: pytest.MonkeyPatch,
) -> None:
db = _CaptureDb()
@asynccontextmanager
async def fake_db_context(project_id: str):
assert project_id == "awoooi"
yield db
monkeypatch.setattr(db_base, "get_db_context", fake_db_context)
await audit_sink._write_audit_impl(
project_id="awoooi",
action="run.approval.approve",
resource_type="run",
resource_id="run-1",
details={"result": "verified"},
run_id="run-1",
trace_id="trace-1",
)
_assert_jsonb_bind_contract(db)
@pytest.mark.asyncio
async def test_contract_service_uses_asyncpg_safe_jsonb_bind(
monkeypatch: pytest.MonkeyPatch,
) -> None:
db = _CaptureDb()
@asynccontextmanager
async def fake_db_context(project_id: str):
assert project_id == "awoooi"
yield db
monkeypatch.setattr(db_base, "get_db_context", fake_db_context)
await contract_service._write_audit(
project_id="awoooi",
action="contract.revision.activated",
resource_type="contract",
resource_id="contract-1",
details={"result": "verified"},
)
_assert_jsonb_bind_contract(db)