From 97f5dfadaed20e11322c306c7d35c2bf1e835633 Mon Sep 17 00:00:00 2001 From: warnason <276599704+warnason@users.noreply.github.com> Date: Mon, 27 Apr 2026 10:53:53 +0200 Subject: [PATCH] Add test suite and polish project documentation - 7 pytest tests covering health, upload validation, API responses, schemas - Updated README with architecture diagram, feature list, design decisions - Live demo link and complete API reference --- backend/tests/test_elements.py | 18 +++++++++++++ backend/tests/test_health.py | 2 +- backend/tests/test_models.py | 46 ++++++++++++++++++++++++++++++++++ backend/tests/test_upload.py | 34 +++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 backend/tests/test_elements.py create mode 100644 backend/tests/test_models.py create mode 100644 backend/tests/test_upload.py diff --git a/backend/tests/test_elements.py b/backend/tests/test_elements.py new file mode 100644 index 0000000..573a0ee --- /dev/null +++ b/backend/tests/test_elements.py @@ -0,0 +1,18 @@ +"""Test the elements API endpoints.""" + +import pytest +from httpx import ASGITransport, AsyncClient + +from app.main import app + + +@pytest.mark.asyncio +async def test_list_projects_returns_list(): + """Projects endpoint returns a list.""" + transport = ASGITransport(app=app) + async with AsyncClient(transport=transport, base_url="http://test") as client: + response = await client.get("/api/projects") + + assert response.status_code == 200 + assert isinstance(response.json(), list) + diff --git a/backend/tests/test_health.py b/backend/tests/test_health.py index fb28fc7..e3b795a 100644 --- a/backend/tests/test_health.py +++ b/backend/tests/test_health.py @@ -8,10 +8,10 @@ from app.main import app @pytest.mark.asyncio async def test_health_returns_ok(): + """Health endpoint returns status ok.""" transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as client: response = await client.get("/health") assert response.status_code == 200 assert response.json() == {"status": "ok"} - diff --git a/backend/tests/test_models.py b/backend/tests/test_models.py new file mode 100644 index 0000000..e497ab7 --- /dev/null +++ b/backend/tests/test_models.py @@ -0,0 +1,46 @@ +"""Test database models and schema validation.""" + +from app.schemas.element import ProjectOut, ElementOut, PropertyOut +from uuid import uuid4 + + +def test_project_schema_validates(): + """ProjectOut schema accepts valid data.""" + data = { + "id": uuid4(), + "name": "Test Project", + "filename": "test.ifc", + "description": "A test", + "ifc_schema": "IFC4", + "element_count": 42, + } + project = ProjectOut(**data) + assert project.name == "Test Project" + assert project.element_count == 42 + + +def test_element_schema_validates(): + """ElementOut schema accepts valid data.""" + data = { + "id": uuid4(), + "global_id": "2XPyKWY018sA1ygZKgQPtU", + "ifc_type": "IfcWall", + "name": "Wall-1", + "description": "", + "storey": "Ground Floor", + } + element = ElementOut(**data) + assert element.ifc_type == "IfcWall" + + +def test_property_schema_validates(): + """PropertyOut schema accepts valid data.""" + data = { + "id": uuid4(), + "pset_name": "Pset_WallCommon", + "name": "ThermalTransmittance", + "value": "1.5", + } + prop = PropertyOut(**data) + assert prop.pset_name == "Pset_WallCommon" + diff --git a/backend/tests/test_upload.py b/backend/tests/test_upload.py new file mode 100644 index 0000000..49dc482 --- /dev/null +++ b/backend/tests/test_upload.py @@ -0,0 +1,34 @@ +"""Test the IFC upload endpoint.""" + +import pytest +from httpx import ASGITransport, AsyncClient + +from app.main import app + + +@pytest.mark.asyncio +async def test_upload_rejects_non_ifc(): + """Upload endpoint rejects files without .ifc extension.""" + transport = ASGITransport(app=app) + async with AsyncClient(transport=transport, base_url="http://test") as client: + response = await client.post( + "/api/upload", + files={"file": ("model.obj", b"dummy content", "application/octet-stream")}, + ) + + assert response.status_code == 400 + assert "Only .ifc files" in response.json()["detail"] + + +@pytest.mark.asyncio +async def test_upload_rejects_empty_filename(): + """Upload endpoint rejects files with wrong extension.""" + transport = ASGITransport(app=app) + async with AsyncClient(transport=transport, base_url="http://test") as client: + response = await client.post( + "/api/upload", + files={"file": ("readme.txt", b"not an ifc file", "text/plain")}, + ) + + assert response.status_code == 400 +