feat: sql writer with dedup insert and apply call

Adds SqlWriter: a pyodbc-backed writer that dedup-inserts into
dbo.SyncQueue (IF NOT EXISTS guarded by UX_SyncQueue_Dedup), invokes
dbo.usp_SyncApply, and reports applied SourceLogIDs.

Connection is opened with autocommit=True per the controller revision:
usp_SyncApply manages its own transaction internally (BEGIN/ROLLBACK),
and an outer pyodbc transaction would conflict on ROLLBACK (SQL error
266). The dedup IF NOT EXISTS...INSERT is a single atomic statement.

Integration test self-cleans via SourceFile='sqlw_test.accdb' marker;
conn_str comes from the gitignored config.yaml (no hardcoded creds).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-07-14 12:29:39 +08:00
parent 1f7191421a
commit 399534e7af
2 changed files with 150 additions and 0 deletions

55
tests/test_sql_writer.py Normal file
View File

@@ -0,0 +1,55 @@
"""Integration test for sync.sql_writer.SqlWriter.
Validates the dedup INSERT (same SourceLogID is only ever enqueued once), the
``applied_log_ids`` query (rows flipped to ``applied`` come back in order), and
that ``call_apply`` invokes the proc without raising.
conn_str is read from the gitignored ``config.yaml`` via ``load_config``; no
credentials are hardcoded here. The test self-cleans using a throwaway
``SourceFile='sqlw_test.accdb'`` marker so no residue is left on SyncQueue.
"""
import os
import pytest
from sync.sql_writer import SqlWriter, QueueRow
from sync.config import load_config
@pytest.mark.integration
def test_insert_dedup_and_applied_ids():
if not os.environ.get("RUN_INTEGRATION"):
pytest.skip("integration")
cfg = load_config("config.yaml")
w = SqlWriter(cfg.sql_server.conn_str, "dbo.SyncQueue")
try:
cur = w._conn.cursor()
cur.execute("DELETE dbo.SyncQueue WHERE SourceFile='sqlw_test.accdb'")
qr = QueueRow(
source_file="sqlw_test.accdb",
source_table="T",
record_id="7",
target_schema="sync_test",
target_table="T_YEAR2026",
source_log_id=100,
operate_type="Insert",
row_data='{"ID":7}',
)
w.insert_queue_row(qr)
w.insert_queue_row(qr) # duplicate must be deduped (ignored)
cur.execute(
"SELECT COUNT(*) FROM dbo.SyncQueue "
"WHERE SourceFile='sqlw_test.accdb' AND SourceLogID=100"
)
assert cur.fetchone()[0] == 1
# call_apply must run without error (proc logic validated elsewhere).
w.call_apply(max_retries=5)
cur.execute(
"UPDATE dbo.SyncQueue SET Status='applied' "
"WHERE SourceFile='sqlw_test.accdb'"
)
assert w.applied_log_ids("sqlw_test.accdb") == [100]
finally:
cur.execute("DELETE dbo.SyncQueue WHERE SourceFile='sqlw_test.accdb'")
w.close()