import os import uuid from datetime import date import pytest from sqlalchemy import create_engine from sqlalchemy.exc import DBAPIError from sqlalchemy.orm import Session from app.db.session import set_space_context from app.imports.blob import get_blob_store from app.imports.models import HoldResolution, ParseRun, SourceRecord, StatementDocument from app.ledger.models import Account from app.spaces.models import FinancialSpace @pytest.mark.skipif(not os.getenv("RLS_DATABASE_URL"), reason="requires restricted PostgreSQL role") def test_postgres_rls_requires_matching_transaction_context() -> None: engine = create_engine(os.environ["RLS_DATABASE_URL"]) with Session(engine) as db: space = FinancialSpace(name="RLS integration") db.add(space) db.commit() space_id = space.id db.add(Account(space_id=space_id, name="Blocked", kind="cash", opened_on=date.today())) with pytest.raises(DBAPIError): db.commit() db.rollback() set_space_context(db, space_id) account = Account(space_id=space_id, name="Allowed", kind="cash", opened_on=date.today()) db.add(account) db.flush() document = StatementDocument( space_id=space_id, account_id=account.id, original_filename="synthetic.xlsx", size_bytes=1, content_sha256=uuid.uuid4().hex + uuid.uuid4().hex, object_key=uuid.uuid4().hex, status="confirmed", ) db.add(document) db.flush() run = ParseRun( space_id=space_id, document_id=document.id, adapter_id="alfa_debit_xlsx", status="succeeded", ) db.add(run) db.flush() document.active_parse_run_id = run.id hold = SourceRecord( space_id=space_id, document_id=document.id, parse_run_id=run.id, occurred_on=date.today(), amount_kopecks=100, direction="out", bank_status="hold", row_ref="sheet0:row1", record_fp=uuid.uuid4().hex, ) db.add(hold) db.flush() db.add( HoldResolution( space_id=space_id, hold_source_record_id=hold.id, resolution="discarded_as_cancelled", reason="synthetic test resolution", ) ) db.commit() set_space_context(db, uuid.uuid4()) assert db.query(Account).count() == 0 assert db.query(HoldResolution).count() == 0 db.rollback() set_space_context(db, space_id) persisted_space = db.get(FinancialSpace, space_id) assert persisted_space is not None db.delete(persisted_space) db.commit() engine.dispose() @pytest.mark.skipif(not os.getenv("S3_ENDPOINT_URL"), reason="requires S3-compatible storage") def test_s3_blob_round_trip() -> None: store = get_blob_store() key = uuid.uuid4().hex payload = b"synthetic statement payload" store.put(key, payload) assert store.get(key) == payload store.delete(key)