mirror of
https://github.com/Joulenap/joulenap.git
synced 2026-08-11 13:21:43 +02:00
106 lines
3.7 KiB
Python
106 lines
3.7 KiB
Python
"""Auth flow: first-run setup, login/logout, session-guarded routes, redaction."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.config import load_config
|
|
from app.main import create_app
|
|
|
|
|
|
@pytest.fixture
|
|
def client(temp_config, temp_db):
|
|
# temp_config routes JOULENAP_CONFIG/DATA_DIR at a tmp path; create_app loads it.
|
|
app = create_app()
|
|
with TestClient(app) as c:
|
|
yield c
|
|
|
|
|
|
def test_secret_key_is_generated(temp_config, temp_db):
|
|
# Example ships secret_key: CHANGE_ME; startup should replace + persist it.
|
|
create_app()
|
|
cfg = load_config(temp_config)
|
|
assert cfg.app.secret_key not in ("", "CHANGE_ME")
|
|
assert len(cfg.app.secret_key) >= 32
|
|
|
|
|
|
def test_status_reports_setup_needed(client):
|
|
r = client.get("/api/auth/status")
|
|
assert r.status_code == 200
|
|
body = r.json()
|
|
assert body["setup_needed"] is True
|
|
assert body["authenticated"] is False
|
|
|
|
|
|
def test_me_requires_auth(client):
|
|
assert client.get("/api/auth/me").status_code == 401
|
|
|
|
|
|
def test_setup_then_authenticated(client):
|
|
r = client.post("/api/auth/setup", json={"username": "admin", "password": "secret12"})
|
|
assert r.status_code == 201
|
|
assert r.json() == {"username": "admin"}
|
|
# Session established by setup.
|
|
me = client.get("/api/auth/me")
|
|
assert me.status_code == 200 and me.json()["username"] == "admin"
|
|
# Status now reflects configured + authenticated.
|
|
st = client.get("/api/auth/status").json()
|
|
assert st["setup_needed"] is False and st["authenticated"] is True
|
|
|
|
|
|
def test_setup_rejected_when_account_exists(client):
|
|
client.post("/api/auth/setup", json={"username": "admin", "password": "secret12"})
|
|
r = client.post("/api/auth/setup", json={"username": "other", "password": "yyyyyyyy"})
|
|
assert r.status_code == 409
|
|
|
|
|
|
def test_setup_persists_timezone(client):
|
|
# The first-run screen sends the browser-detected timezone; it must land in app.timezone.
|
|
r = client.post(
|
|
"/api/auth/setup",
|
|
json={"username": "admin", "password": "secret12", "timezone": "America/New_York"},
|
|
)
|
|
assert r.status_code == 201
|
|
cfg = client.get("/api/config").json()
|
|
assert cfg["app"]["timezone"] == "America/New_York"
|
|
|
|
|
|
def test_setup_without_timezone_keeps_default(client):
|
|
r = client.post("/api/auth/setup", json={"username": "admin", "password": "secret12"})
|
|
assert r.status_code == 201
|
|
cfg = client.get("/api/config").json()
|
|
assert cfg["app"]["timezone"] == ""
|
|
|
|
|
|
def test_setup_validation(client):
|
|
short_user = client.post("/api/auth/setup", json={"username": "ab", "password": "secret12"})
|
|
assert short_user.status_code == 422
|
|
short_pass = client.post("/api/auth/setup", json={"username": "abc", "password": "no"})
|
|
assert short_pass.status_code == 422
|
|
|
|
|
|
def test_login_logout_cycle(client):
|
|
client.post("/api/auth/setup", json={"username": "admin", "password": "secret12"})
|
|
client.post("/api/logout")
|
|
assert client.get("/api/auth/me").status_code == 401
|
|
|
|
bad = client.post("/api/login", json={"username": "admin", "password": "wrong"})
|
|
assert bad.status_code == 401
|
|
|
|
ok = client.post("/api/login", json={"username": "admin", "password": "secret12"})
|
|
assert ok.status_code == 200
|
|
assert client.get("/api/auth/me").json()["username"] == "admin"
|
|
|
|
|
|
def test_password_is_hashed_not_plaintext(client, temp_config):
|
|
client.post("/api/auth/setup", json={"username": "admin", "password": "secret12"})
|
|
cfg = load_config(temp_config)
|
|
assert cfg.app.auth.password_hash.startswith("$2")
|
|
assert "secret12" not in cfg.app.auth.password_hash
|
|
|
|
|
|
def test_setup_rejects_short_password(client):
|
|
r = client.post("/api/auth/setup", json={"username": "admin", "password": "1234"})
|
|
assert r.status_code == 422
|