Files
BanGUI/backend/tests/test_repositories/test_db_init.py
Lukas 7392c930d6 feat: Stage 1 — backend and frontend scaffolding
Backend (tasks 1.1, 1.5–1.8):
- pyproject.toml with FastAPI, Pydantic v2, aiosqlite, APScheduler 3.x,
  structlog, bcrypt; ruff + mypy strict configured
- Pydantic Settings (BANGUI_ prefix env vars, fail-fast validation)
- SQLite schema: settings, sessions, blocklist_sources, import_log;
  WAL mode + foreign keys; idempotent init_db()
- FastAPI app factory with lifespan (DB, aiohttp session, scheduler),
  CORS, unhandled-exception handler, GET /api/health
- Fail2BanClient: async Unix-socket wrapper using run_in_executor,
  custom error types, async context manager
- Utility modules: ip_utils, time_utils, constants
- 47 tests; ruff 0 errors; mypy --strict 0 errors

Frontend (tasks 1.2–1.4):
- Vite + React 18 + TypeScript strict; Fluent UI v9; ESLint + Prettier
- Custom brand theme (#0F6CBD, WCAG AA contrast) with light/dark variants
- Typed fetch API client (ApiError, get/post/put/del) + endpoints constants
- tsc --noEmit 0 errors
2026-02-28 21:15:01 +01:00

70 lines
2.3 KiB
Python

"""Tests for app.db — database schema initialisation."""
from pathlib import Path
import aiosqlite
import pytest
from app.db import init_db
@pytest.mark.asyncio
async def test_init_db_creates_settings_table(tmp_path: Path) -> None:
"""``init_db`` must create the ``settings`` table."""
db_path = str(tmp_path / "test.db")
async with aiosqlite.connect(db_path) as db:
await init_db(db)
async with db.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name='settings';"
) as cursor:
row = await cursor.fetchone()
assert row is not None
@pytest.mark.asyncio
async def test_init_db_creates_sessions_table(tmp_path: Path) -> None:
"""``init_db`` must create the ``sessions`` table."""
db_path = str(tmp_path / "test.db")
async with aiosqlite.connect(db_path) as db:
await init_db(db)
async with db.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name='sessions';"
) as cursor:
row = await cursor.fetchone()
assert row is not None
@pytest.mark.asyncio
async def test_init_db_creates_blocklist_sources_table(tmp_path: Path) -> None:
"""``init_db`` must create the ``blocklist_sources`` table."""
db_path = str(tmp_path / "test.db")
async with aiosqlite.connect(db_path) as db:
await init_db(db)
async with db.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name='blocklist_sources';"
) as cursor:
row = await cursor.fetchone()
assert row is not None
@pytest.mark.asyncio
async def test_init_db_creates_import_log_table(tmp_path: Path) -> None:
"""``init_db`` must create the ``import_log`` table."""
db_path = str(tmp_path / "test.db")
async with aiosqlite.connect(db_path) as db:
await init_db(db)
async with db.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name='import_log';"
) as cursor:
row = await cursor.fetchone()
assert row is not None
@pytest.mark.asyncio
async def test_init_db_is_idempotent(tmp_path: Path) -> None:
"""Calling ``init_db`` twice on the same database must not raise."""
db_path = str(tmp_path / "test.db")
async with aiosqlite.connect(db_path) as db:
await init_db(db)
await init_db(db) # Second call must be a no-op.