diff --git a/backend/tests/test_operations.py b/backend/tests/test_operations.py new file mode 100644 index 00000000..8fb85392 --- /dev/null +++ b/backend/tests/test_operations.py @@ -0,0 +1,189 @@ +import pytest +from fastapi import status +from datetime import datetime + + +class TestStockOperations: + """Test check-in and check-out operations.""" + + def test_check_in_item(self, test_client, test_db): + """Test checking in inventory (increasing quantity).""" + from backend.models import Item + + item = Item( + name="Test Item", + category="Electronics", + item_type="Component", + quantity=10, + barcode="123456789", + part_number="PN-12345" + ) + test_db.add(item) + test_db.commit() + + response = test_client.post( + "/api/operations/check-in", + json={"item_id": item.id, "quantity": 5} + ) + assert response.status_code == status.HTTP_200_OK + data = response.json() + assert data["new_quantity"] == 15 + + def test_check_out_item(self, test_client, test_db): + """Test checking out inventory (decreasing quantity).""" + from backend.models import Item + + item = Item( + name="Test Item", + category="Electronics", + item_type="Component", + quantity=10, + barcode="123456789", + part_number="PN-12345" + ) + test_db.add(item) + test_db.commit() + + response = test_client.post( + "/api/operations/check-out", + json={"item_id": item.id, "quantity": 3} + ) + assert response.status_code == status.HTTP_200_OK + data = response.json() + assert data["new_quantity"] == 7 + + def test_check_out_insufficient_quantity(self, test_client, test_db): + """Test that check-out fails if quantity insufficient.""" + from backend.models import Item + + item = Item( + name="Test Item", + category="Electronics", + item_type="Component", + quantity=5, + barcode="123456789", + part_number="PN-12345" + ) + test_db.add(item) + test_db.commit() + + response = test_client.post( + "/api/operations/check-out", + json={"item_id": item.id, "quantity": 10} + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST + + def test_audit_log_created(self, test_client, test_db): + """Test that audit log entry created for each operation.""" + from backend.models import Item + + item = Item( + name="Test Item", + category="Electronics", + item_type="Component", + quantity=10, + barcode="123456789", + part_number="PN-12345" + ) + test_db.add(item) + test_db.commit() + + # Perform operation + response = test_client.post( + "/api/operations/check-in", + json={"item_id": item.id, "quantity": 5} + ) + assert response.status_code == status.HTTP_200_OK + + # Verify audit log + response = test_client.get(f"/api/audit-logs?item_id={item.id}") + assert response.status_code == status.HTTP_200_OK + logs = response.json() + assert len(logs) > 0 + assert logs[-1]["operation"] == "CHECK_IN" + assert logs[-1]["quantity_change"] == 5 + + +class TestBulkSync: + """Test offline sync with UUID idempotency.""" + + def test_bulk_sync_offline_operations(self, test_client, test_db): + """Test syncing multiple offline-generated operations.""" + from backend.models import Item + + item = Item( + name="Test Item", + category="Electronics", + item_type="Component", + quantity=10, + barcode="123456789", + part_number="PN-12345" + ) + test_db.add(item) + test_db.commit() + + # Simulate offline operations with UUIDs + response = test_client.post( + "/api/bulk-sync", + json={ + "operations": [ + { + "id": "uuid-1", + "type": "CHECK_IN", + "item_id": item.id, + "quantity": 5 + }, + { + "id": "uuid-2", + "type": "CHECK_IN", + "item_id": item.id, + "quantity": 3 + } + ] + } + ) + assert response.status_code == status.HTTP_200_OK + data = response.json() + assert data["synced"] == 2 + assert data["final_quantity"] == 18 + + def test_bulk_sync_idempotent(self, test_client, test_db): + """Test that syncing same UUID twice doesn't duplicate.""" + from backend.models import Item + + item = Item( + name="Test Item", + category="Electronics", + item_type="Component", + quantity=10, + barcode="123456789", + part_number="PN-12345" + ) + test_db.add(item) + test_db.commit() + + operation = { + "id": "uuid-1", + "type": "CHECK_IN", + "item_id": item.id, + "quantity": 5 + } + + # Sync once + response1 = test_client.post( + "/api/bulk-sync", + json={"operations": [operation]} + ) + assert response1.status_code == status.HTTP_200_OK + q1 = response1.json()["final_quantity"] + + # Sync again (same UUID) + response2 = test_client.post( + "/api/bulk-sync", + json={"operations": [operation]} + ) + assert response2.status_code == status.HTTP_200_OK + q2 = response2.json()["final_quantity"] + + # Quantity should not increase twice + assert q1 == q2 == 15