# -*- coding: utf-8 -*- """auth_system 异步栈的读写并发验证:读不持锁、写仍串行(WAL)。""" import asyncio import sqlite3 import threading import pytest import auth_system @pytest.fixture() def temp_site(tmp_path, monkeypatch): monkeypatch.setattr(auth_system.db, 'DATA_DIR', tmp_path) monkeypatch.setattr(auth_system.db, 'DB_PATH', tmp_path / 'site.db') monkeypatch.setattr(auth_system.db, 'BACKUP_DIR', tmp_path / 'backups') monkeypatch.setattr(auth_system.db, 'SECRET_KEY_PATH', tmp_path / 'secret.key') asyncio.run(auth_system.init_db()) return tmp_path / 'site.db' def test_q_does_not_hold_lock(temp_site): """_q 读不应获取写锁(否则读会被写挡住)。""" async def _main(): # 已在写锁内:读仍应立即返回(读路径不取写锁) async with auth_system.db._write_lock(): rows = await auth_system._q('SELECT COUNT(*) AS n FROM users') assert rows is not None assert rows[0]['n'] >= 0 asyncio.run(_main()) def test_concurrent_reads_and_writes(temp_site): """多读线程 + 一个写线程并发:无异常、结果一致。""" errors = [] stop = threading.Event() def writer(): try: for i in range(50): asyncio.run(auth_system.set_setting(f'k{i}', str(i))) except Exception as e: errors.append(('w', e)) finally: stop.set() def reader(): while not stop.is_set(): try: asyncio.run(auth_system.get_setting('k0')) except Exception as e: errors.append(('r', e)) ws = threading.Thread(target=writer, daemon=True) rs = [threading.Thread(target=reader, daemon=True) for _ in range(4)] ws.start() for r in rs: r.start() ws.join(20) for r in rs: r.join(2) assert not errors, f'并发读写异常: {errors[:3]}' assert asyncio.run(auth_system.get_setting('k49')) == '49' def test_wal_enabled(temp_site): conn = sqlite3.connect(str(temp_site)) try: mode = conn.execute('PRAGMA journal_mode').fetchone()[0] finally: conn.close() assert str(mode).lower() == 'wal'