landvex: Fixar och tester klara för alla komponenter

- Datafabrik: Dockerfile fix, agentorkestrering fungerar
- Vision: Identify-modell, FAISS, OCR alla testade
- API: Alla 7 integrationstester passerade
- Upplösare: Entitetsupplösning verifierad
This commit is contained in:
Bernt
2026-07-05 06:41:32 +00:00
parent f4f853d94b
commit aee0f09db8
19583 changed files with 1450867 additions and 1153 deletions
@@ -0,0 +1 @@
# Tests package
+97
View File
@@ -0,0 +1,97 @@
"""Pytest fixtures."""
import asyncio
import uuid
from datetime import datetime
import pytest_asyncio
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from app.main import app
from app.database import get_db, Base
from app.models import User, Tenant, UserRole, UserStatus
from app.security import get_password_hash
TEST_DATABASE_URL = "postgresql+asyncpg://postgres:postgres@localhost:5432/landvex_test"
engine = create_async_engine(TEST_DATABASE_URL, echo=False, future=True)
AsyncTestingSessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async def override_get_db():
async with AsyncTestingSessionLocal() as session:
yield session
app.dependency_overrides[get_db] = override_get_db
@pytest_asyncio.fixture(scope="session")
def event_loop():
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
@pytest_asyncio.fixture(scope="session", autouse=True)
async def setup_database():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await conn.run_sync(Base.metadata.create_all)
yield
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await engine.dispose()
@pytest_asyncio.fixture
async def db_session() -> AsyncSession:
async with AsyncTestingSessionLocal() as session:
yield session
@pytest_asyncio.fixture
async def client() -> AsyncClient:
async with AsyncClient(app=app, base_url="http://test") as ac:
yield ac
@pytest_asyncio.fixture
async def test_tenant(db_session: AsyncSession) -> Tenant:
tenant = Tenant(
id=uuid.uuid4(),
name="Test Kommun",
slug="test-kommun",
)
db_session.add(tenant)
await db_session.commit()
await db_session.refresh(tenant)
return tenant
@pytest_asyncio.fixture
async def test_user(db_session: AsyncSession, test_tenant: Tenant) -> User:
user = User(
id=uuid.uuid4(),
email="admin@landvex.se",
first_name="Admin",
last_name="User",
hashed_password=get_password_hash("password123"),
role=UserRole.ADMIN,
status=UserStatus.ACTIVE,
tenant_id=test_tenant.id,
)
db_session.add(user)
await db_session.commit()
await db_session.refresh(user)
return user
@pytest_asyncio.fixture
async def admin_token(client: AsyncClient, test_user: User) -> str:
response = await client.post("/api/v1/auth/login", json={
"email": test_user.email,
"password": "password123",
})
data = response.json()
return data["access_token"]
@@ -0,0 +1,51 @@
"""Tester för auth-endpoints."""
import pytest
from httpx import AsyncClient
from app.models import User
@pytest.mark.asyncio
async def test_login_success(client: AsyncClient, test_user: User):
response = await client.post("/api/v1/auth/login", json={
"email": test_user.email,
"password": "password123",
})
assert response.status_code == 200
data = response.json()
assert "access_token" in data
assert "refresh_token" in data
assert data["token_type"] == "bearer"
@pytest.mark.asyncio
async def test_login_wrong_password(client: AsyncClient, test_user: User):
response = await client.post("/api/v1/auth/login", json={
"email": test_user.email,
"password": "wrongpassword",
})
assert response.status_code == 401
@pytest.mark.asyncio
async def test_me_endpoint(client: AsyncClient, admin_token: str, test_user: User):
response = await client.get("/api/v1/auth/me", headers={"Authorization": f"Bearer {admin_token}"})
assert response.status_code == 200
data = response.json()
assert data["email"] == test_user.email
assert data["role"] == "admin"
@pytest.mark.asyncio
async def test_refresh_token(client: AsyncClient, admin_token: str):
# Hämta refresh token via login
response = await client.post("/api/v1/auth/login", json={
"email": "admin@landvex.se",
"password": "password123",
})
refresh = response.json()["refresh_token"]
response = await client.post("/api/v1/auth/refresh", json={"refresh_token": refresh})
assert response.status_code == 200
data = response.json()
assert "access_token" in data
@@ -0,0 +1,65 @@
"""Tester för invitation-flödet."""
import pytest
from httpx import AsyncClient
from app.models import User
@pytest.mark.asyncio
async def test_create_invitation(client: AsyncClient, admin_token: str):
response = await client.post("/api/v1/invitations", headers={"Authorization": f"Bearer {admin_token}"}, json={
"email": "invited@landvex.se",
"role": "viewer",
})
assert response.status_code == 201
data = response.json()
assert data["email"] == "invited@landvex.se"
assert data["role"] == "viewer"
assert "token" in data
@pytest.mark.asyncio
async def test_validate_invitation(client: AsyncClient, admin_token: str):
# Skapa inbjudan
response = await client.post("/api/v1/invitations", headers={"Authorization": f"Bearer {admin_token}"}, json={
"email": "validate@landvex.se",
"role": "analyst",
})
token = response.json()["token"]
response = await client.get(f"/api/v1/invitations/validate/{token}")
assert response.status_code == 200
data = response.json()
assert data["valid"] is True
assert data["email"] == "validate@landvex.se"
@pytest.mark.asyncio
async def test_accept_invitation(client: AsyncClient, admin_token: str):
# Skapa inbjudan
response = await client.post("/api/v1/invitations", headers={"Authorization": f"Bearer {admin_token}"}, json={
"email": "accept@landvex.se",
"role": "manager",
})
token = response.json()["token"]
response = await client.post("/api/v1/invitations/accept", json={
"token": token,
"first_name": "Accepted",
"last_name": "User",
"password": "newpassword123",
})
assert response.status_code == 200
data = response.json()
assert data["email"] == "accept@landvex.se"
assert data["role"] == "manager"
assert data["status"] == "active"
# Försök acceptera igen — ska misslyckas
response = await client.post("/api/v1/invitations/accept", json={
"token": token,
"first_name": "Accepted",
"last_name": "User",
"password": "newpassword123",
})
assert response.status_code == 400
@@ -0,0 +1,73 @@
"""Tester för user CRUD."""
import pytest
from httpx import AsyncClient
from app.models import User, Tenant
@pytest.mark.asyncio
async def test_create_user(client: AsyncClient, admin_token: str, test_tenant: Tenant):
response = await client.post("/api/v1/users", headers={"Authorization": f"Bearer {admin_token}"}, json={
"email": "newuser@landvex.se",
"first_name": "New",
"last_name": "User",
"password": "securepass123",
"role": "analyst",
"tenant_id": str(test_tenant.id),
})
assert response.status_code == 201
data = response.json()
assert data["email"] == "newuser@landvex.se"
assert data["role"] == "analyst"
assert data["status"] == "active"
@pytest.mark.asyncio
async def test_list_users(client: AsyncClient, admin_token: str):
response = await client.get("/api/v1/users", headers={"Authorization": f"Bearer {admin_token}"})
assert response.status_code == 200
data = response.json()
assert "items" in data
assert "total" in data
assert len(data["items"]) >= 1
@pytest.mark.asyncio
async def test_get_user(client: AsyncClient, admin_token: str, test_user: User):
response = await client.get(f"/api/v1/users/{test_user.id}", headers={"Authorization": f"Bearer {admin_token}"})
assert response.status_code == 200
data = response.json()
assert data["email"] == test_user.email
@pytest.mark.asyncio
async def test_update_user(client: AsyncClient, admin_token: str, test_user: User):
response = await client.patch(f"/api/v1/users/{test_user.id}", headers={"Authorization": f"Bearer {admin_token}"}, json={
"first_name": "Updated",
})
assert response.status_code == 200
data = response.json()
assert data["first_name"] == "Updated"
@pytest.mark.asyncio
async def test_delete_user(client: AsyncClient, admin_token: str, test_tenant: Tenant, db_session):
# Skapa en användare att ta bort
from app.models import User, UserRole, UserStatus
from app.security import get_password_hash
import uuid
user = User(
id=uuid.uuid4(),
email="delete-me@landvex.se",
first_name="Delete",
last_name="Me",
hashed_password=get_password_hash("password123"),
role=UserRole.VIEWER,
status=UserStatus.ACTIVE,
tenant_id=test_tenant.id,
)
db_session.add(user)
await db_session.commit()
response = await client.delete(f"/api/v1/users/{user.id}", headers={"Authorization": f"Bearer {admin_token}"})
assert response.status_code == 204