"""Tests for organisation and user CRUD endpoints and schemas.""" import uuid import pytest from pydantic import ValidationError from src.schemas.organisation import OrganisationCreate, OrganisationResponse, OrganisationUpdate from src.schemas.user import UserCreate, UserResponse, UserRole, UserUpdate class TestOrganisationSchemas: def test_create_valid(self): org = OrganisationCreate(name="Acme Corp", slug="acme-corp") assert org.name == "Acme Corp" assert org.slug == "acme-corp" assert org.billing_plan == "free" def test_create_invalid_slug(self): with pytest.raises(ValidationError): OrganisationCreate(name="Acme", slug="INVALID SLUG!") def test_create_empty_name_rejected(self): with pytest.raises(ValidationError): OrganisationCreate(name="", slug="valid-slug") def test_update_partial(self): update = OrganisationUpdate(name="New Name") data = update.model_dump(exclude_unset=True) assert data == {"name": "New Name"} assert "contact_email" not in data def test_response_from_attributes(self): now = "2026-01-01T00:00:00Z" resp = OrganisationResponse( id=uuid.uuid4(), name="Test", slug="test", contact_email=None, billing_plan="free", created_at=now, updated_at=now, ) assert resp.name == "Test" class TestUserSchemas: def test_create_valid(self): user = UserCreate( email="test@example.com", password="securepass123", full_name="Test User", ) assert user.role == UserRole.VIEWER def test_create_short_password_rejected(self): with pytest.raises(ValidationError): UserCreate(email="a@b.com", password="short", full_name="Test") def test_create_invalid_email_rejected(self): with pytest.raises(ValidationError): UserCreate(email="not-an-email", password="securepass123", full_name="Test") def test_create_with_role(self): user = UserCreate( email="admin@example.com", password="securepass123", full_name="Admin", role=UserRole.ADMIN, ) assert user.role == UserRole.ADMIN def test_update_partial(self): update = UserUpdate(role=UserRole.EDITOR) data = update.model_dump(exclude_unset=True) assert data == {"role": "editor"} def test_response_from_attributes(self): now = "2026-01-01T00:00:00Z" resp = UserResponse( id=uuid.uuid4(), organisation_id=uuid.uuid4(), email="a@b.com", full_name="Test", role="viewer", created_at=now, updated_at=now, ) assert resp.role == "viewer" class TestUserRole: def test_role_values(self): assert UserRole.OWNER == "owner" assert UserRole.ADMIN == "admin" assert UserRole.EDITOR == "editor" assert UserRole.VIEWER == "viewer" def test_invalid_role_rejected(self): with pytest.raises(ValidationError): UserCreate( email="a@b.com", password="securepass123", full_name="Test", role="superadmin", ) @pytest.mark.asyncio class TestRoutesRegistered: async def test_org_routes(self, client): response = await client.get("/openapi.json") paths = response.json()["paths"] assert "/api/v1/organisations/" in paths assert "/api/v1/organisations/me" in paths async def test_user_routes(self, client): response = await client.get("/openapi.json") paths = response.json()["paths"] assert "/api/v1/users/" in paths assert "/api/v1/users/{user_id}" in paths async def test_org_endpoints_require_auth(self, client): response = await client.get("/api/v1/organisations/me") assert response.status_code == 401 async def test_user_endpoints_require_auth(self, client): response = await client.get("/api/v1/users/") assert response.status_code == 401 async def test_create_org_rejects_invalid_body(self, client, monkeypatch): """Create org endpoint validates the request body schema. We need to enable the bootstrap token first so the request reaches the body-validation stage (the token guard otherwise fires before Pydantic validation and we'd see 403 instead). """ from src.config.settings import get_settings monkeypatch.setattr(get_settings(), "admin_bootstrap_token", "test-token") response = await client.post( "/api/v1/organisations/", json={"name": "", "slug": "INVALID SLUG!"}, headers={"X-Admin-Bootstrap-Token": "test-token"}, ) assert response.status_code == 422