Multi-tenant factory inspection system (SpiFox, Enkid, Alpet): - FastAPI backend with JWT auth, PostgreSQL (asyncpg) - Next.js 16 frontend with App Router, SWR data fetching - Machines CRUD with equipment parts management - Part lifecycle tracking (hours/count/date) with counters - Partial unique index for soft-delete support - 24 pytest tests passing, E2E verified Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
109 lines
3.1 KiB
Python
109 lines
3.1 KiB
Python
import pytest
|
|
from httpx import AsyncClient
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_health_check(client: AsyncClient):
|
|
response = await client.get("/api/health")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == "ok"
|
|
assert data["version"] == "2.0.0"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_login_invalid_credentials(client: AsyncClient):
|
|
response = await client.post(
|
|
"/api/auth/login",
|
|
json={"email": "nonexistent@test.com", "password": "wrongpass"},
|
|
)
|
|
assert response.status_code == 401
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_me_unauthenticated(client: AsyncClient):
|
|
response = await client.get("/api/auth/me")
|
|
assert response.status_code == 401
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_admin_users_unauthenticated(client: AsyncClient):
|
|
response = await client.get("/api/admin/users")
|
|
assert response.status_code == 401
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_user_and_login(client: AsyncClient, db_session):
|
|
from src.auth.password import hash_password
|
|
from src.database.models import User, Tenant
|
|
import uuid
|
|
|
|
db_session.add(Tenant(id="test-tenant", name="Test Tenant"))
|
|
await db_session.commit()
|
|
|
|
db_session.add(
|
|
User(
|
|
id=uuid.uuid4(),
|
|
email="admin@test.com",
|
|
password_hash=hash_password("password123"),
|
|
name="Admin",
|
|
role="superadmin",
|
|
tenant_id=None,
|
|
)
|
|
)
|
|
await db_session.commit()
|
|
|
|
login_resp = await client.post(
|
|
"/api/auth/login",
|
|
json={"email": "admin@test.com", "password": "password123"},
|
|
)
|
|
assert login_resp.status_code == 200
|
|
token_data = login_resp.json()
|
|
assert "access_token" in token_data
|
|
assert token_data["user"]["email"] == "admin@test.com"
|
|
assert token_data["user"]["role"] == "superadmin"
|
|
|
|
token = token_data["access_token"]
|
|
|
|
me_resp = await client.get(
|
|
"/api/auth/me", headers={"Authorization": f"Bearer {token}"}
|
|
)
|
|
assert me_resp.status_code == 200
|
|
assert me_resp.json()["email"] == "admin@test.com"
|
|
|
|
create_resp = await client.post(
|
|
"/api/admin/users",
|
|
json={
|
|
"email": "user1@test.com",
|
|
"password": "userpass",
|
|
"name": "User 1",
|
|
"role": "user",
|
|
"tenant_id": "test-tenant",
|
|
},
|
|
headers={"Authorization": f"Bearer {token}"},
|
|
)
|
|
assert create_resp.status_code == 200
|
|
created = create_resp.json()
|
|
assert created["email"] == "user1@test.com"
|
|
assert created["tenant_id"] == "test-tenant"
|
|
|
|
users_resp = await client.get(
|
|
"/api/admin/users", headers={"Authorization": f"Bearer {token}"}
|
|
)
|
|
assert users_resp.status_code == 200
|
|
users = users_resp.json()
|
|
assert len(users) == 2
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_logout(client: AsyncClient):
|
|
response = await client.post("/api/auth/logout")
|
|
assert response.status_code == 200
|
|
assert response.json()["status"] == "success"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_tenants_unauthenticated(client: AsyncClient):
|
|
response = await client.get("/api/tenants")
|
|
assert response.status_code == 401
|