backup
This commit is contained in:
@@ -12,6 +12,7 @@ from httpx import ASGITransport, AsyncClient
|
||||
from app.config import Settings
|
||||
from app.db import init_db
|
||||
from app.main import _lifespan, create_app
|
||||
from app.models.server import ServerStatus
|
||||
from app.services import setup_service
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -42,10 +43,16 @@ async def app_and_client(tmp_path: Path) -> tuple[object, AsyncClient]: # type:
|
||||
Yields:
|
||||
A tuple of ``(FastAPI app instance, AsyncClient)``.
|
||||
"""
|
||||
config_dir = tmp_path / "fail2ban"
|
||||
(config_dir / "jail.d").mkdir(parents=True)
|
||||
(config_dir / "filter.d").mkdir(parents=True)
|
||||
(config_dir / "action.d").mkdir(parents=True)
|
||||
|
||||
settings = Settings(
|
||||
database_path=str(tmp_path / "setup_cache_test.db"),
|
||||
fail2ban_socket="/tmp/fake_fail2ban.sock",
|
||||
session_secret="test-setup-cache-secret",
|
||||
fail2ban_config_dir=str(config_dir),
|
||||
session_secret="test-setup-cache-secret-that-is-long-enough",
|
||||
session_duration_minutes=60,
|
||||
timezone="UTC",
|
||||
log_level="debug",
|
||||
@@ -115,7 +122,7 @@ class TestPostSetup:
|
||||
"/api/v1/setup",
|
||||
json={"master_password": "short"},
|
||||
)
|
||||
assert response.status_code == 422
|
||||
assert response.status_code == 400
|
||||
|
||||
async def test_rejects_missing_uppercase_password(self, client: AsyncClient) -> None:
|
||||
"""Setup endpoint rejects passwords missing an uppercase character."""
|
||||
@@ -123,11 +130,9 @@ class TestPostSetup:
|
||||
"/api/v1/setup",
|
||||
json={"master_password": "lowercase1!"},
|
||||
)
|
||||
assert response.status_code == 422
|
||||
assert any(
|
||||
"uppercase" in error["msg"].lower()
|
||||
for error in response.json()["detail"]
|
||||
)
|
||||
assert response.status_code == 400
|
||||
body = response.json()
|
||||
assert body["code"] == "invalid_input"
|
||||
|
||||
async def test_rejects_missing_number_password(self, client: AsyncClient) -> None:
|
||||
"""Setup endpoint rejects passwords missing a numeric character."""
|
||||
@@ -135,11 +140,9 @@ class TestPostSetup:
|
||||
"/api/v1/setup",
|
||||
json={"master_password": "NoNumbers!"},
|
||||
)
|
||||
assert response.status_code == 422
|
||||
assert any(
|
||||
"number" in error["msg"].lower()
|
||||
for error in response.json()["detail"]
|
||||
)
|
||||
assert response.status_code == 400
|
||||
body = response.json()
|
||||
assert body["code"] == "invalid_input"
|
||||
|
||||
async def test_rejects_missing_special_character_password(
|
||||
self, client: AsyncClient
|
||||
@@ -149,11 +152,9 @@ class TestPostSetup:
|
||||
"/api/v1/setup",
|
||||
json={"master_password": "NoSpecial1"},
|
||||
)
|
||||
assert response.status_code == 422
|
||||
assert any(
|
||||
"special character" in error["msg"].lower()
|
||||
for error in response.json()["detail"]
|
||||
)
|
||||
assert response.status_code == 400
|
||||
body = response.json()
|
||||
assert body["code"] == "invalid_input"
|
||||
|
||||
async def test_rejects_second_call(self, client: AsyncClient) -> None:
|
||||
"""Setup endpoint returns 409 if setup has already been completed."""
|
||||
@@ -354,10 +355,20 @@ class TestLifespanDatabaseDirectoryCreation:
|
||||
nested_db = tmp_path / "deep" / "nested" / "bangui.db"
|
||||
assert not nested_db.parent.exists()
|
||||
|
||||
# Settings requires the database parent to exist at construction time,
|
||||
# so create the immediate parent. The intermediate directory
|
||||
# (nested_db.parent.parent) is deliberately left non-existent to verify
|
||||
# lifespan creates it.
|
||||
nested_db.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
config_dir = tmp_path / "fail2ban"
|
||||
config_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
settings = Settings(
|
||||
database_path=str(nested_db),
|
||||
fail2ban_socket="/tmp/fake.sock",
|
||||
session_secret="test-lifespan-mkdir-secret",
|
||||
fail2ban_config_dir=str(config_dir),
|
||||
session_secret="test-lifespan-mkdir-secret-that-is-long-enough",
|
||||
session_duration_minutes=60,
|
||||
timezone="UTC",
|
||||
log_level="debug",
|
||||
@@ -398,10 +409,14 @@ class TestLifespanDatabaseDirectoryCreation:
|
||||
db_path = tmp_path / "bangui.db"
|
||||
# tmp_path already exists — this simulates a pre-existing volume.
|
||||
|
||||
config_dir = tmp_path / "fail2ban"
|
||||
config_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
settings = Settings(
|
||||
database_path=str(db_path),
|
||||
fail2ban_socket="/tmp/fake.sock",
|
||||
session_secret="test-lifespan-exist-ok-secret",
|
||||
fail2ban_config_dir=str(config_dir),
|
||||
session_secret="test-lifespan-exist-ok-secret-that-is-long-enough",
|
||||
session_duration_minutes=60,
|
||||
timezone="UTC",
|
||||
log_level="debug",
|
||||
@@ -436,10 +451,16 @@ class TestLifespanSetupCache:
|
||||
|
||||
async def test_startup_caches_setup_completion(self, tmp_path: Path) -> None:
|
||||
"""Lifespan should populate ``setup_complete_cached`` based on the DB."""
|
||||
config_dir = tmp_path / "fail2ban"
|
||||
(config_dir / "jail.d").mkdir(parents=True)
|
||||
(config_dir / "filter.d").mkdir(parents=True)
|
||||
(config_dir / "action.d").mkdir(parents=True)
|
||||
|
||||
settings = Settings(
|
||||
database_path=str(tmp_path / "bangui.db"),
|
||||
fail2ban_socket="/tmp/fake.sock",
|
||||
session_secret="test-lifespan-setup-cache-secret",
|
||||
fail2ban_config_dir=str(config_dir),
|
||||
session_secret="test-lifespan-setup-cache-secret-that-is-long",
|
||||
session_duration_minutes=60,
|
||||
timezone="UTC",
|
||||
log_level="debug",
|
||||
@@ -495,10 +516,16 @@ class TestSetupRedirectMiddlewareDbNone:
|
||||
Simulates the race window where a request arrives before the lifespan
|
||||
has finished initialising the database connection.
|
||||
"""
|
||||
config_dir = tmp_path / "fail2ban"
|
||||
(config_dir / "jail.d").mkdir(parents=True)
|
||||
(config_dir / "filter.d").mkdir(parents=True)
|
||||
(config_dir / "action.d").mkdir(parents=True)
|
||||
|
||||
settings = Settings(
|
||||
database_path=str(tmp_path / "bangui.db"),
|
||||
fail2ban_socket="/tmp/fake_fail2ban.sock",
|
||||
session_secret="test-db-none-secret",
|
||||
fail2ban_config_dir=str(config_dir),
|
||||
session_secret="test-db-none-secret-that-is-long-enough",
|
||||
session_duration_minutes=60,
|
||||
timezone="UTC",
|
||||
log_level="debug",
|
||||
@@ -517,15 +544,22 @@ class TestSetupRedirectMiddlewareDbNone:
|
||||
|
||||
async def test_health_reachable_when_db_not_set(self, tmp_path: Path) -> None:
|
||||
"""Health endpoint is always reachable even when db is not initialised."""
|
||||
config_dir = tmp_path / "fail2ban"
|
||||
(config_dir / "jail.d").mkdir(parents=True)
|
||||
(config_dir / "filter.d").mkdir(parents=True)
|
||||
(config_dir / "action.d").mkdir(parents=True)
|
||||
|
||||
settings = Settings(
|
||||
database_path=str(tmp_path / "bangui.db"),
|
||||
fail2ban_socket="/tmp/fake_fail2ban.sock",
|
||||
session_secret="test-db-none-health-secret",
|
||||
fail2ban_config_dir=str(config_dir),
|
||||
session_secret="test-db-none-health-secret-that-is-long",
|
||||
session_duration_minutes=60,
|
||||
timezone="UTC",
|
||||
log_level="debug",
|
||||
)
|
||||
app = create_app(settings=settings)
|
||||
app.state.server_status = ServerStatus(online=True)
|
||||
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(
|
||||
|
||||
Reference in New Issue
Block a user