test: baseline test suite — 86 tests covering auth, CRUD, settings, chat, terraform, reports, terminal

This commit is contained in:
nogueiraguh
2026-04-03 17:31:39 -03:00
parent 856e3b6674
commit 2e74d316ae
13 changed files with 1126 additions and 0 deletions

223
backend/tests/test_auth.py Normal file
View File

@@ -0,0 +1,223 @@
"""
Tests for authentication, registration, password change, logout, OIDC, and MFA endpoints.
"""
import uuid
import pytest
pytestmark = pytest.mark.anyio
# ---------------------------------------------------------------------------
# Helper: create a disposable user, login, return (user_id, token, headers)
# ---------------------------------------------------------------------------
async def _make_temp_user(client, admin_headers, *, password="Test1234!"):
"""Register a unique user and log them in. Returns (uid, token, headers)."""
uname = f"tmp_{uuid.uuid4().hex[:10]}"
r = await client.post("/api/auth/register", json={
"first_name": "Tmp", "last_name": "User",
"username": uname, "email": f"{uname}@test.com",
"password": password, "role": "user",
}, headers=admin_headers)
assert r.status_code == 200, r.text
uid = r.json()["id"]
r2 = await client.post("/api/auth/login", json={"username": uname, "password": password})
assert r2.status_code == 200, r2.text
token = r2.json()["token"]
return uid, token, {"Authorization": f"Bearer {token}"}
# ── Health ─────────────────────────────────────────────────────────────────────
async def test_health_check(client):
r = await client.get("/api/health")
assert r.status_code == 200
body = r.json()
assert body["status"] == "ok"
assert body["db_ok"] is True
# ── Login ──────────────────────────────────────────────────────────────────────
async def test_login_success(client):
r = await client.post("/api/auth/login", json={"username": "admin", "password": "admin123"})
assert r.status_code == 200
body = r.json()
assert "token" in body
assert "user" in body
async def test_login_wrong_password(client):
r = await client.post("/api/auth/login", json={"username": "admin", "password": "wrong"})
assert r.status_code == 401
async def test_login_nonexistent_user(client):
r = await client.post("/api/auth/login", json={"username": "nobody", "password": "x"})
assert r.status_code == 401
async def test_login_returns_must_change_password(client):
r = await client.post("/api/auth/login", json={"username": "admin", "password": "admin123"})
assert r.status_code == 200
body = r.json()
assert "must_change_password" in body
# ── Register ───────────────────────────────────────────────────────────────────
async def test_register_user_as_admin(client, admin_headers):
uname = f"regtest_{uuid.uuid4().hex[:8]}"
r = await client.post("/api/auth/register", json={
"first_name": "Reg", "last_name": "Test",
"username": uname, "email": f"{uname}@test.com",
"password": "Reg12345!", "role": "user",
}, headers=admin_headers)
assert r.status_code == 200
body = r.json()
assert body["username"] == uname
assert body["role"] == "user"
async def test_register_duplicate_username(client, admin_headers):
uname = f"dup_{uuid.uuid4().hex[:8]}"
payload = {
"first_name": "Dup", "last_name": "Test",
"username": uname, "email": f"{uname}@test.com",
"password": "Dup12345!", "role": "user",
}
r1 = await client.post("/api/auth/register", json=payload, headers=admin_headers)
assert r1.status_code == 200
r2 = await client.post("/api/auth/register", json=payload, headers=admin_headers)
assert r2.status_code == 400
async def test_register_as_non_admin(client, user_headers):
r = await client.post("/api/auth/register", json={
"first_name": "No", "last_name": "Way",
"username": "nope", "email": "nope@test.com",
"password": "Nope1234!", "role": "user",
}, headers=user_headers)
assert r.status_code == 403
async def test_register_invalid_role(client, admin_headers):
r = await client.post("/api/auth/register", json={
"first_name": "Bad", "last_name": "Role",
"username": f"badrole_{uuid.uuid4().hex[:8]}",
"email": "bad@test.com",
"password": "Bad12345!", "role": "superadmin",
}, headers=admin_headers)
assert r.status_code == 400
# ── Password change ────────────────────────────────────────────────────────────
async def test_change_password_success(client, admin_headers):
_, _, hdrs = await _make_temp_user(client, admin_headers)
r = await client.post("/api/auth/change-password", json={
"current_password": "Test1234!", "new_password": "NewPass1!",
}, headers=hdrs)
assert r.status_code == 200
async def test_change_password_wrong_current(client, admin_headers):
_, _, hdrs = await _make_temp_user(client, admin_headers)
r = await client.post("/api/auth/change-password", json={
"current_password": "WrongOld!", "new_password": "NewPass1!",
}, headers=hdrs)
assert r.status_code == 400
async def test_change_password_too_short(client, admin_headers):
_, _, hdrs = await _make_temp_user(client, admin_headers)
r = await client.post("/api/auth/change-password", json={
"current_password": "Test1234!", "new_password": "Ab1!",
}, headers=hdrs)
assert r.status_code == 400
async def test_change_password_no_uppercase(client, admin_headers):
_, _, hdrs = await _make_temp_user(client, admin_headers)
r = await client.post("/api/auth/change-password", json={
"current_password": "Test1234!", "new_password": "abcdefg1!",
}, headers=hdrs)
assert r.status_code == 400
async def test_change_password_no_lowercase(client, admin_headers):
_, _, hdrs = await _make_temp_user(client, admin_headers)
r = await client.post("/api/auth/change-password", json={
"current_password": "Test1234!", "new_password": "ABCDEFG1!",
}, headers=hdrs)
assert r.status_code == 400
async def test_change_password_no_number(client, admin_headers):
_, _, hdrs = await _make_temp_user(client, admin_headers)
r = await client.post("/api/auth/change-password", json={
"current_password": "Test1234!", "new_password": "Abcdefgh!",
}, headers=hdrs)
assert r.status_code == 400
async def test_change_password_no_special(client, admin_headers):
_, _, hdrs = await _make_temp_user(client, admin_headers)
r = await client.post("/api/auth/change-password", json={
"current_password": "Test1234!", "new_password": "Abcdefg1",
}, headers=hdrs)
assert r.status_code == 400
async def test_change_password_same_as_old(client, admin_headers):
_, _, hdrs = await _make_temp_user(client, admin_headers)
r = await client.post("/api/auth/change-password", json={
"current_password": "Test1234!", "new_password": "Test1234!",
}, headers=hdrs)
assert r.status_code == 400
# ── Logout ─────────────────────────────────────────────────────────────────────
async def test_logout(client, admin_headers):
_, _, hdrs = await _make_temp_user(client, admin_headers)
r = await client.post("/api/auth/logout", headers=hdrs)
assert r.status_code == 200
assert r.json().get("ok") is True
async def test_request_after_logout(client, admin_headers):
_, token, hdrs = await _make_temp_user(client, admin_headers)
await client.post("/api/auth/logout", headers=hdrs)
r = await client.get("/api/users/me", headers={"Authorization": f"Bearer {token}"})
assert r.status_code == 401
# ── OIDC ───────────────────────────────────────────────────────────────────────
async def test_oidc_config_public(client):
r = await client.get("/api/auth/oidc/config")
assert r.status_code == 200
assert "auth_mode" in r.json()
async def test_oidc_login_disabled(client):
r = await client.get("/api/auth/oidc/login", follow_redirects=False)
assert r.status_code == 400
# ── MFA ────────────────────────────────────────────────────────────────────────
async def test_mfa_setup(client, admin_headers):
_, _, hdrs = await _make_temp_user(client, admin_headers)
r = await client.post("/api/mfa/setup", headers=hdrs)
assert r.status_code == 200
body = r.json()
assert "secret" in body
assert "uri" in body
async def test_mfa_disable(client, admin_headers):
uid, _, _ = await _make_temp_user(client, admin_headers)
r = await client.post(f"/api/mfa/disable/{uid}", headers=admin_headers)
assert r.status_code == 200
assert r.json().get("ok") is True