|
@@ -227,6 +227,156 @@ class TestArchivesAPI:
|
|
|
assert "successful_prints" in result
|
|
assert "successful_prints" in result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+class TestArchivesSlimAPI:
|
|
|
|
|
+ """Integration tests for /api/v1/archives/slim endpoint."""
|
|
|
|
|
+
|
|
|
|
|
+ @pytest.mark.asyncio
|
|
|
|
|
+ @pytest.mark.integration
|
|
|
|
|
+ async def test_slim_empty(self, async_client: AsyncClient):
|
|
|
|
|
+ """Verify empty list when no archives exist."""
|
|
|
|
|
+ response = await async_client.get("/api/v1/archives/slim")
|
|
|
|
|
+
|
|
|
|
|
+ assert response.status_code == 200
|
|
|
|
|
+ assert response.json() == []
|
|
|
|
|
+
|
|
|
|
|
+ @pytest.mark.asyncio
|
|
|
|
|
+ @pytest.mark.integration
|
|
|
|
|
+ async def test_slim_returns_only_expected_fields(
|
|
|
|
|
+ self, async_client: AsyncClient, archive_factory, printer_factory, db_session
|
|
|
|
|
+ ):
|
|
|
|
|
+ """Verify response contains only slim fields, not full archive data."""
|
|
|
|
|
+ printer = await printer_factory()
|
|
|
|
|
+ await archive_factory(
|
|
|
|
|
+ printer.id,
|
|
|
|
|
+ print_name="Slim Test",
|
|
|
|
|
+ status="completed",
|
|
|
|
|
+ filament_type="PLA",
|
|
|
|
|
+ filament_color="#FF0000",
|
|
|
|
|
+ filament_used_grams=50.0,
|
|
|
|
|
+ print_time_seconds=3600,
|
|
|
|
|
+ cost=1.50,
|
|
|
|
|
+ quantity=2,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ response = await async_client.get("/api/v1/archives/slim")
|
|
|
|
|
+
|
|
|
|
|
+ assert response.status_code == 200
|
|
|
|
|
+ data = response.json()
|
|
|
|
|
+ assert len(data) == 1
|
|
|
|
|
+ item = data[0]
|
|
|
|
|
+
|
|
|
|
|
+ # Expected fields present
|
|
|
|
|
+ assert item["printer_id"] == printer.id
|
|
|
|
|
+ assert item["print_name"] == "Slim Test"
|
|
|
|
|
+ assert item["status"] == "completed"
|
|
|
|
|
+ assert item["filament_type"] == "PLA"
|
|
|
|
|
+ assert item["filament_color"] == "#FF0000"
|
|
|
|
|
+ assert item["filament_used_grams"] == 50.0
|
|
|
|
|
+ assert item["print_time_seconds"] == 3600
|
|
|
|
|
+ assert item["cost"] == 1.50
|
|
|
|
|
+ assert item["quantity"] == 2
|
|
|
|
|
+ assert "created_at" in item
|
|
|
|
|
+
|
|
|
|
|
+ # Full archive fields must NOT be present
|
|
|
|
|
+ assert "id" not in item
|
|
|
|
|
+ assert "filename" not in item
|
|
|
|
|
+ assert "file_path" not in item
|
|
|
|
|
+ assert "file_size" not in item
|
|
|
|
|
+ assert "extra_data" not in item
|
|
|
|
|
+ assert "notes" not in item
|
|
|
|
|
+ assert "tags" not in item
|
|
|
|
|
+ assert "photos" not in item
|
|
|
|
|
+ assert "thumbnail_path" not in item
|
|
|
|
|
+ assert "content_hash" not in item
|
|
|
|
|
+ assert "duplicates" not in item
|
|
|
|
|
+ assert "duplicate_count" not in item
|
|
|
|
|
+
|
|
|
|
|
+ @pytest.mark.asyncio
|
|
|
|
|
+ @pytest.mark.integration
|
|
|
|
|
+ async def test_slim_computes_actual_time(
|
|
|
|
|
+ self, async_client: AsyncClient, archive_factory, printer_factory, db_session
|
|
|
|
|
+ ):
|
|
|
|
|
+ """Verify actual_time_seconds is computed from started_at/completed_at."""
|
|
|
|
|
+ from datetime import datetime, timezone
|
|
|
|
|
+
|
|
|
|
|
+ printer = await printer_factory()
|
|
|
|
|
+ started = datetime(2024, 1, 1, 10, 0, 0, tzinfo=timezone.utc)
|
|
|
|
|
+ completed = datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc) # 2 hours = 7200s
|
|
|
|
|
+ await archive_factory(
|
|
|
|
|
+ printer.id,
|
|
|
|
|
+ status="completed",
|
|
|
|
|
+ started_at=started,
|
|
|
|
|
+ completed_at=completed,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ response = await async_client.get("/api/v1/archives/slim")
|
|
|
|
|
+
|
|
|
|
|
+ assert response.status_code == 200
|
|
|
|
|
+ item = response.json()[0]
|
|
|
|
|
+ assert item["actual_time_seconds"] == 7200
|
|
|
|
|
+
|
|
|
|
|
+ @pytest.mark.asyncio
|
|
|
|
|
+ @pytest.mark.integration
|
|
|
|
|
+ async def test_slim_actual_time_null_for_failed(
|
|
|
|
|
+ self, async_client: AsyncClient, archive_factory, printer_factory, db_session
|
|
|
|
|
+ ):
|
|
|
|
|
+ """Verify actual_time_seconds is null for non-completed prints."""
|
|
|
|
|
+ from datetime import datetime, timezone
|
|
|
|
|
+
|
|
|
|
|
+ printer = await printer_factory()
|
|
|
|
|
+ await archive_factory(
|
|
|
|
|
+ printer.id,
|
|
|
|
|
+ status="failed",
|
|
|
|
|
+ started_at=datetime(2024, 1, 1, 10, 0, 0, tzinfo=timezone.utc),
|
|
|
|
|
+ completed_at=datetime(2024, 1, 1, 11, 0, 0, tzinfo=timezone.utc),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ response = await async_client.get("/api/v1/archives/slim")
|
|
|
|
|
+
|
|
|
|
|
+ assert response.status_code == 200
|
|
|
|
|
+ item = response.json()[0]
|
|
|
|
|
+ assert item["actual_time_seconds"] is None
|
|
|
|
|
+
|
|
|
|
|
+ @pytest.mark.asyncio
|
|
|
|
|
+ @pytest.mark.integration
|
|
|
|
|
+ async def test_slim_date_filtering(self, async_client: AsyncClient, archive_factory, printer_factory, db_session):
|
|
|
|
|
+ """Verify date_from and date_to filters work."""
|
|
|
|
|
+ from datetime import datetime, timezone
|
|
|
|
|
+
|
|
|
|
|
+ printer = await printer_factory()
|
|
|
|
|
+ await archive_factory(
|
|
|
|
|
+ printer.id,
|
|
|
|
|
+ print_name="Old Print",
|
|
|
|
|
+ created_at=datetime(2024, 1, 1, tzinfo=timezone.utc),
|
|
|
|
|
+ )
|
|
|
|
|
+ await archive_factory(
|
|
|
|
|
+ printer.id,
|
|
|
|
|
+ print_name="New Print",
|
|
|
|
|
+ created_at=datetime(2024, 6, 15, tzinfo=timezone.utc),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # Filter to only June 2024
|
|
|
|
|
+ response = await async_client.get("/api/v1/archives/slim?date_from=2024-06-01&date_to=2024-06-30")
|
|
|
|
|
+
|
|
|
|
|
+ assert response.status_code == 200
|
|
|
|
|
+ data = response.json()
|
|
|
|
|
+ assert len(data) == 1
|
|
|
|
|
+ assert data[0]["print_name"] == "New Print"
|
|
|
|
|
+
|
|
|
|
|
+ @pytest.mark.asyncio
|
|
|
|
|
+ @pytest.mark.integration
|
|
|
|
|
+ async def test_slim_pagination(self, async_client: AsyncClient, archive_factory, printer_factory, db_session):
|
|
|
|
|
+ """Verify limit and offset work."""
|
|
|
|
|
+ printer = await printer_factory()
|
|
|
|
|
+ for i in range(5):
|
|
|
|
|
+ await archive_factory(printer.id, print_name=f"Print {i}")
|
|
|
|
|
+
|
|
|
|
|
+ response = await async_client.get("/api/v1/archives/slim?limit=2&offset=0")
|
|
|
|
|
+
|
|
|
|
|
+ assert response.status_code == 200
|
|
|
|
|
+ assert len(response.json()) == 2
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
class TestArchiveDataIntegrity:
|
|
class TestArchiveDataIntegrity:
|
|
|
"""Tests for archive data integrity."""
|
|
"""Tests for archive data integrity."""
|
|
|
|
|
|