# -*- coding: utf-8 -*- """异步 SQLite 层测试:读写并发、WAL、事务原子性。""" import asyncio import pytest import core.db as db @pytest.fixture() def temp_db(tmp_path, monkeypatch): """把 DB 指向临时目录,独立于生产 data/site.db。""" monkeypatch.setattr(db, 'DATA_DIR', tmp_path) monkeypatch.setattr(db, 'DB_PATH', tmp_path / 'site.db') return tmp_path / 'site.db' async def test_write_then_read(temp_db): await db.execute('CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)') rid = await db.execute('INSERT INTO t (v) VALUES (?)', ('hello',)) assert rid == 1 row = await db.fetch_one('SELECT v FROM t WHERE id=?', (1,)) assert row['v'] == 'hello' assert await db.fetch_val('SELECT COUNT(*) FROM t') == 1 async def test_wal_mode_enabled(temp_db): await db.execute('CREATE TABLE t (id INTEGER PRIMARY KEY)') mode = await db.fetch_val('PRAGMA journal_mode') assert str(mode).lower() == 'wal' async def test_concurrent_read_write(temp_db): """核心诉求:写的同时读不被阻塞、且读到一致快照(WAL 快照隔离)。""" await db.execute('CREATE TABLE nums (id INTEGER PRIMARY KEY, v INTEGER)') stop = asyncio.Event() read_errors = [] write_count = 0 async def writer(): nonlocal write_count for i in range(200): await db.execute('INSERT INTO nums (v) VALUES (?)', (i,)) write_count += 1 stop.set() async def reader(): # 写入过程中持续读,不应抛 SQLITE_BUSY / 不应被阻塞致死 while not stop.is_set(): try: n = await db.fetch_val('SELECT COUNT(*) FROM nums') assert isinstance(n, int) except Exception as e: read_errors.append(e) await asyncio.sleep(0) await asyncio.gather(writer(), *[reader() for _ in range(4)]) assert not read_errors, f'读期间出现异常: {read_errors[:3]}' assert write_count == 200 assert await db.fetch_val('SELECT COUNT(*) FROM nums') == 200 async def test_write_transaction_commit_and_rollback(temp_db): await db.execute('CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)') async with db.write_transaction() as conn: await conn.execute('INSERT INTO t (v) VALUES (?)', ('a',)) await conn.execute('INSERT INTO t (v) VALUES (?)', ('b',)) assert await db.fetch_val('SELECT COUNT(*) FROM t') == 2 with pytest.raises(RuntimeError): async with db.write_transaction() as conn: await conn.execute('INSERT INTO t (v) VALUES (?)', ('c',)) raise RuntimeError('boom') # 回滚:c 不应落库 assert await db.fetch_val('SELECT COUNT(*) FROM t') == 2