Audit findings 6.1-6.4 + the audit's remediation plan itself (docs/superpowers/plans/2026-07-20-full-audit-remediation-plan.md). - ruff: 34 → 0 (unused imports/f-strings auto-fixed; E702 semicolon lines split in test files; one unused local dropped). Suite still 819 green. - CLAUDE.md + PROJECT_STATE.md no longer claim "no database / settings in settings.json": the harness selects filesystem OR PostgreSQL session storage (repository.py, server mode), and settings flow through harness preferences with the JSON file as fallback only. - tools/replay: stub /me (SPA boot was parsing the SPA's own HTML as JSON) and /runtime/prewarm. - failSession best-effort persistence now logs its failure server-side instead of vanishing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
57 lines
1.8 KiB
Python
57 lines
1.8 KiB
Python
"""L0: db/connection read-only enforcement against real Postgres (testcontainers).
|
|
|
|
The ported read-only contract: the psd_ro role can SELECT but not write, and
|
|
can_create_in_schema / writable_tables reflect that. This is where 'ported code
|
|
is not assumed reliable' gains real teeth for the data layer.
|
|
"""
|
|
import pytest
|
|
from sqlalchemy import create_engine, text
|
|
|
|
from tht.db.connection import can_create_in_schema, ping, writable_tables
|
|
|
|
pytestmark = [pytest.mark.l0]
|
|
|
|
|
|
def test_ping_succeeds_on_read_only_role(ro_url):
|
|
engine = create_engine(ro_url)
|
|
try:
|
|
ping(engine) # SELECT 1 — must not raise
|
|
finally:
|
|
engine.dispose()
|
|
|
|
|
|
def test_read_only_role_cannot_create_in_schema(ro_url):
|
|
engine = create_engine(ro_url)
|
|
try:
|
|
# psd_ro has USAGE + SELECT only, not CREATE on the dw schema.
|
|
assert can_create_in_schema(engine, "dw") is False
|
|
finally:
|
|
engine.dispose()
|
|
|
|
|
|
def test_writable_tables_empty_for_read_only_role(ro_url):
|
|
engine = create_engine(ro_url)
|
|
try:
|
|
tables = writable_tables(engine, "dw")
|
|
assert tables == [] # read-only role has no INSERT/UPDATE/DELETE grants
|
|
finally:
|
|
engine.dispose()
|
|
|
|
|
|
def test_read_only_role_cannot_insert(ro_url):
|
|
"""The hard guarantee: a write attempt raises (enforced by Postgres, surfaced
|
|
by our engine)."""
|
|
engine = create_engine(ro_url)
|
|
try:
|
|
with pytest.raises(Exception):
|
|
with engine.begin() as conn:
|
|
conn.execute(text('INSERT INTO dw.dim_pazienti VALUES (999, %s, %s)'),
|
|
("test", "test"))
|
|
finally:
|
|
engine.dispose()
|
|
|
|
|
|
def test_admin_engine_can_create_in_schema(admin_engine):
|
|
# Sanity: the admin (table owner) CAN create — confirms the test harness itself.
|
|
assert can_create_in_schema(admin_engine, "dw") is True
|