"""Tests for setup_service and settings_repo.""" from __future__ import annotations from pathlib import Path import aiosqlite import pytest from app.db import init_db from app.repositories import settings_repo from app.services import setup_service @pytest.fixture async def db(tmp_path: Path) -> aiosqlite.Connection: # type: ignore[misc] """Provide an initialised aiosqlite connection for service-level tests.""" conn: aiosqlite.Connection = await aiosqlite.connect(str(tmp_path / "test.db")) conn.row_factory = aiosqlite.Row await init_db(conn) yield conn await conn.close() class TestIsSetupComplete: async def test_returns_false_on_fresh_db( self, db: aiosqlite.Connection ) -> None: """Setup is not complete on a fresh database.""" assert await setup_service.is_setup_complete(db) is False async def test_returns_true_after_run_setup( self, db: aiosqlite.Connection ) -> None: """Setup is marked complete after run_setup() succeeds.""" await setup_service.run_setup( db, master_password="mypassword1", database_path="bangui.db", fail2ban_socket="/var/run/fail2ban/fail2ban.sock", timezone="UTC", session_duration_minutes=60, ) assert await setup_service.is_setup_complete(db) is True class TestRunSetup: async def test_persists_all_settings(self, db: aiosqlite.Connection) -> None: """run_setup() stores every provided setting.""" await setup_service.run_setup( db, master_password="mypassword1", database_path="/data/bangui.db", fail2ban_socket="/tmp/f2b.sock", timezone="Europe/Berlin", session_duration_minutes=120, ) all_settings = await settings_repo.get_all_settings(db) assert all_settings["database_path"] == "/data/bangui.db" assert all_settings["fail2ban_socket"] == "/tmp/f2b.sock" assert all_settings["timezone"] == "Europe/Berlin" assert all_settings["session_duration_minutes"] == "120" async def test_password_stored_as_bcrypt_hash( self, db: aiosqlite.Connection ) -> None: """The master password is stored as a bcrypt hash, not plain text.""" import bcrypt await setup_service.run_setup( db, master_password="mypassword1", database_path="bangui.db", fail2ban_socket="/var/run/fail2ban/fail2ban.sock", timezone="UTC", session_duration_minutes=60, ) stored = await setup_service.get_password_hash(db) assert stored is not None assert stored != "mypassword1" # Verify it is a valid bcrypt hash. assert bcrypt.checkpw(b"mypassword1", stored.encode()) async def test_raises_if_setup_already_complete( self, db: aiosqlite.Connection ) -> None: """run_setup() raises RuntimeError if called a second time.""" kwargs = { "master_password": "mypassword1", "database_path": "bangui.db", "fail2ban_socket": "/var/run/fail2ban/fail2ban.sock", "timezone": "UTC", "session_duration_minutes": 60, } await setup_service.run_setup(db, **kwargs) # type: ignore[arg-type] with pytest.raises(RuntimeError, match="already been completed"): await setup_service.run_setup(db, **kwargs) # type: ignore[arg-type]