# -*- coding: utf-8 -*- """merge_increment.py 计数契约: inserted 只计本连接写入(total_changes)。 2026-08-25 实锤: 03:00 合并窗口 bs_5m 并发写主库, after-before 把 5m 插的行 记成自己的 inserted → skipped(dup) 被挤成负数(-4881)。total_changes 只计本 连接 INSERT 生效行, 对并发写者天然免疫; skipped=inc-inserted 恢复真实含义。 """ import importlib.util import sqlite3 import sys from pathlib import Path SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "nas_sync" / "merge_increment.py" SCHEMA = ( "CREATE TABLE dbbardata(id INTEGER PRIMARY KEY AUTOINCREMENT, " "symbol TEXT, exchange TEXT, datetime TEXT, interval TEXT, " "volume REAL, turnover REAL, open_interest REAL, " "open_price REAL, high_price REAL, low_price REAL, close_price REAL)") def _run(monkeypatch, capsys, main_db, inc_db): monkeypatch.setattr(sys, "argv", [ "merge_increment.py", "--db", str(main_db), "--inc", str(inc_db)]) spec = importlib.util.spec_from_file_location("merge_increment_mod", SCRIPT) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) mod.main() return capsys.readouterr().out def _mk_inc(path, rows): c = sqlite3.connect(path) c.execute(SCHEMA) c.executemany( "INSERT INTO dbbardata(symbol,exchange,datetime,interval,close_price) " "VALUES(?,?,?,?,?)", rows) c.commit() c.close() def test_merge_accounting_new_then_dup(tmp_path, monkeypatch, capsys): main_db = tmp_path / "main.db" inc1 = tmp_path / "inc1.db" _mk_inc(inc1, [("600000", "SSE", "2026-08-21", "d", 10.0), ("600000", "SSE", "2026-08-22", "d", 11.0)]) out = _run(monkeypatch, capsys, main_db, inc1) assert "inc=2 inserted=2 skipped(dup)=0" in out # 同增量重跑: 全 dup, inserted=0(OR IGNORE 计数路径) out = _run(monkeypatch, capsys, main_db, inc1) assert "inc=2 inserted=0 skipped(dup)=2" in out def test_merge_accounting_mixed_and_other_writer_rows(tmp_path, monkeypatch, capsys): """部分 dup 精确计数; 主库他源行(=bs_5m 并发写)不进 inserted/skipped。""" main_db = tmp_path / "main.db" inc1 = tmp_path / "inc1.db" _mk_inc(inc1, [("600000", "SSE", "2026-08-21", "d", 10.0)]) _run(monkeypatch, capsys, main_db, inc1) # 建 schema + 首灌 # 模拟并发写者(另一连接)直接向主库插 3 行 —— 任何时刻都不该被算进 merge 计数 c = sqlite3.connect(main_db) c.executemany( "INSERT INTO dbbardata(symbol,exchange,datetime,interval,close_price) " "VALUES(?,?,?,?,?)", [("000001", "SZSE", "2026-08-22", "5m", 1.0), ("000001", "SZSE", "2026-08-22 09:35:00", "5m", 1.0), ("000002", "SZSE", "2026-08-22", "5m", 1.0)]) c.commit() c.close() inc2 = tmp_path / "inc2.db" _mk_inc(inc2, [("600000", "SSE", "2026-08-21", "d", 10.0), # dup ("600000", "SSE", "2026-08-22", "d", 11.0)]) # 新 out = _run(monkeypatch, capsys, main_db, inc2) assert "inc=2 inserted=1 skipped(dup)=1" in out