"""Testes do app: fallback de data, autenticação e CRUD de documentos.""" import os import tempfile import unittest from datetime import date from pathlib import Path class DateFallbackTests(unittest.TestCase): def test_iso_date_is_kept(self): from app.dates import resolve_competencia self.assertEqual(resolve_competencia("2026-06-09"), (6, 2026)) def test_br_date_is_normalized(self): from app.dates import resolve_competencia self.assertEqual(resolve_competencia("09/06/2026"), (6, 2026)) def test_illegible_date_falls_back_to_current_month(self): from app.dates import resolve_competencia ref = date(2026, 7, 24) self.assertEqual(resolve_competencia("ilegível", reference=ref), (7, 2026)) self.assertEqual(resolve_competencia(None, reference=ref), (7, 2026)) self.assertEqual(resolve_competencia("", reference=ref), (7, 2026)) def test_invalid_calendar_date_falls_back(self): from app.dates import resolve_competencia ref = date(2026, 7, 24) self.assertEqual(resolve_competencia("2026-13-40", reference=ref), (7, 2026)) class PasswordTests(unittest.TestCase): def test_hash_and_verify(self): from app import auth h = auth.hash_password("segredo-forte") self.assertTrue(auth.verify_password("segredo-forte", h)) self.assertFalse(auth.verify_password("errada", h)) class CrudTests(unittest.TestCase): def setUp(self): self._tmp = tempfile.TemporaryDirectory() os.environ["DB_PATH"] = str(Path(self._tmp.name) / "crud.sqlite3") # zera o cache de settings para pegar o DB_PATH novo from app.config import get_settings get_settings.cache_clear() def tearDown(self): os.environ.pop("DB_PATH", None) from app.config import get_settings get_settings.cache_clear() self._tmp.cleanup() def test_create_update_delete_list(self): from app import database as db with db.session() as conn: doc_id = db.create_fiscal( conn, mes=7, ano=2026, supplier_name="Padaria X", total_paid=10.0 ) self.assertEqual(db.overall_totals(conn)["count"], 1) db.update_fiscal( conn, doc_id, mes=7, ano=2026, supplier_name="Padaria Y", total_paid=12.5 ) row = db.get_fiscal(conn, doc_id) self.assertEqual(row["supplier_name"], "Padaria Y") self.assertEqual(row["total_paid"], 12.5) rows = db.list_fiscal(conn, supplier="Padaria") self.assertEqual(len(rows), 1) db.delete_fiscal(conn, doc_id) self.assertEqual(db.overall_totals(conn)["count"], 0) class StagingGateTests(unittest.TestCase): def setUp(self): self._tmp = tempfile.TemporaryDirectory() os.environ["DB_PATH"] = str(Path(self._tmp.name) / "stage.sqlite3") from app.config import get_settings get_settings.cache_clear() def tearDown(self): os.environ.pop("DB_PATH", None) from app.config import get_settings get_settings.cache_clear() self._tmp.cleanup() def _stage(self, conn, batch_id, upload_id, **over): base = dict( upload_id=upload_id, batch_id=batch_id, source_file_name="f.pdf", source_page=None, source_location="f.pdf", raw_text="", mes=7, ano=2026, supplier_name="Loja", total_paid=10.0, confidence="high", field_confidence={}, legible=True, uncertain_fields=[], extractor="local", ) base.update(over) from app import database as db return db.insert_detected(conn, **base) def test_incomplete_row_counts_as_pending_and_is_not_imported(self): from app import database as db with db.session() as conn: batch = db.create_batch(conn) up = db.insert_upload(conn, batch, "f.pdf", Path("f.pdf"), "application/pdf", 1) self._stage(conn, batch, up) # completo # legível mas SEM fornecedor -> incompleto, deve contar como pendente self._stage(conn, batch, up, supplier_name=None, legible=True) summary = db.batch_summary(conn, batch) self.assertEqual(summary["count"], 2) self.assertEqual(summary["incompletos"], 1) self.assertEqual(summary["pendentes"], 1) def test_missing_competencia_counts_as_pending_and_blocks_confirmation(self): from app import database as db with db.session() as conn: batch = db.create_batch(conn) up = db.insert_upload(conn, batch, "f.pdf", Path("f.pdf"), "application/pdf", 1) self._stage(conn, batch, up) # completo # legível, fornecedor e valor ok, mas SEM competência -> pendente self._stage(conn, batch, up, mes=None, ano=None) summary = db.batch_summary(conn, batch) self.assertEqual(summary["pendentes"], 1) inserted = db.confirm_batch(conn, batch) self.assertEqual(inserted, 1) def test_confirm_only_promotes_complete_rows(self): from app import database as db with db.session() as conn: batch = db.create_batch(conn) up = db.insert_upload(conn, batch, "f.pdf", Path("f.pdf"), "application/pdf", 1) self._stage(conn, batch, up, supplier_name="Loja A", total_paid=10.0) self._stage(conn, batch, up, supplier_name="Loja B", total_paid=20.0) inserted = db.confirm_batch(conn, batch) self.assertEqual(inserted, 2) self.assertEqual(db.overall_totals(conn)["total"], 30.0) if __name__ == "__main__": unittest.main()