from __future__ import annotations import os import unittest from uuid import uuid4 from alembic import command from alembic.config import Config from sqlalchemy import select, update from sqlalchemy.exc import DBAPIError from services.control_api.persistence.db import ( create_engine_from_url, session_factory, ) from services.control_api.persistence.models import AuditEventRow, WorkspaceRow from services.control_api.persistence.seed import seed_database @unittest.skipUnless( os.getenv("WP030_DATABASE_URL"), "set WP030_DATABASE_URL to run the disposable PostgreSQL acceptance test", ) class PostgresAcceptanceTests(unittest.TestCase): def test_migrate_seed_and_audit_append_only(self) -> None: url = os.environ["WP030_DATABASE_URL"] config = Config("infrastructure/database/alembic.ini") config.set_main_option("sqlalchemy.url", url) command.upgrade(config, "head") engine = create_engine_from_url(url) factory = session_factory(engine) workspace_id = uuid4() try: with factory() as session: result = seed_database(session) self.assertIsNotNone(result.workspace_id) workspace = WorkspaceRow( id=workspace_id, slug=f"pg-{workspace_id.hex[:12]}", name="PostgreSQL acceptance workspace", status="active", ) session.add(workspace) session.flush() event = AuditEventRow( workspace_id=workspace_id, project_id=None, trace_id="wp-030-test", event_type="acceptance.test", outcome="allowed", sanitized_arguments={"safe": True}, ) session.add(event) session.commit() event_id = event.id with factory() as session: with self.assertRaises(DBAPIError): session.execute( update(AuditEventRow) .where(AuditEventRow.id == event_id) .values(outcome="tampered") ) session.rollback() with factory() as session: stored = session.scalar(select(AuditEventRow).where(AuditEventRow.id == event_id)) self.assertIsNotNone(stored) self.assertEqual(stored.outcome, "allowed") finally: engine.dispose() if __name__ == "__main__": unittest.main()