"""Unit tests for the cleanup bookkeeping added to SqlWriter / AccessReader. These mock pyodbc so no database is required. They pin the contract that ``mark_cleaned`` / ``purge_cleaned`` issue the right statements, and that ``delete_log_ids`` returns the actual number of rows deleted (not ``None``). """ from unittest.mock import MagicMock, patch from sync.sql_writer import SqlWriter from sync.access_reader import AccessReader def _writer(): """Build a SqlWriter whose pyodbc connection is a MagicMock.""" with patch("sync.sql_writer.pyodbc.connect") as mconnect: conn = MagicMock() mconnect.return_value = conn w = SqlWriter("conn_str", "dbo.SyncQueue") return w, conn def test_mark_cleaned_updates_only_applied_rows_for_file(): w, conn = _writer() cur = conn.cursor.return_value w.mark_cleaned("a.accdb", [1, 2, 3]) assert cur.execute.call_count == 1 sql = cur.execute.call_args.args[0] assert "UPDATE dbo.SyncQueue SET Status='cleaned'" in sql assert "CleanedAt=sysdatetime()" in sql assert "SourceFile=?" in sql assert "Status='applied'" in sql # first param is the file, the rest are the SourceLogIDs assert cur.execute.call_args.args[1] == "a.accdb" assert list(cur.execute.call_args.args[2:]) == [1, 2, 3] def test_mark_cleaned_no_ids_is_noop(): w, conn = _writer() cur = conn.cursor.return_value w.mark_cleaned("a.accdb", []) cur.execute.assert_not_called() def test_purge_cleaned_deletes_old_cleaned_rows(): w, conn = _writer() cur = conn.cursor.return_value cur.rowcount = 42 n = w.purge_cleaned(24) assert n == 42 sql = cur.execute.call_args.args[0] assert "DELETE FROM dbo.SyncQueue" in sql assert "Status='cleaned'" in sql assert "CleanedAt < DATEADD(hour, -?, GETDATE())" in sql assert cur.execute.call_args.args[1] == 24 def test_delete_log_ids_returns_rowcount_and_chunks(): r = AccessReader("x.accdb", "drv") fake_conn = MagicMock() fake_cur = MagicMock() fake_cur.rowcount = 7 # per-chunk affected rows fake_conn.cursor.return_value = fake_cur with patch.object(r, "_connect", return_value=fake_conn): # 5 ids, batch size 2 -> 3 chunks, 3 DELETEs, total 21 rows n = r.delete_log_ids([1, 2, 3, 4, 5], 2, 3) assert n == 21 assert fake_cur.execute.call_count == 3