158 lines
6.4 KiB
Python
158 lines
6.4 KiB
Python
import pytest
|
|
from fastapi import status
|
|
|
|
|
|
class TestStockOperations:
|
|
"""Test check-in and check-out operations."""
|
|
|
|
def test_check_in_item(self, test_client, test_db, user_token):
|
|
"""Test checking in inventory (increasing quantity)."""
|
|
from backend.models import Item, User
|
|
|
|
item = Item(name="Test Item", category="Electronics", type="Component",
|
|
quantity=10, barcode="BC-CHECKIN", part_number="PN-CI")
|
|
test_db.add(item)
|
|
test_db.commit()
|
|
|
|
user = test_db.query(User).filter(User.username == "user").first()
|
|
response = test_client.post(
|
|
"/operations/check-in",
|
|
json={"barcode": "BC-CHECKIN", "quantity": 5, "user_id": user.id},
|
|
headers={"Authorization": f"Bearer {user_token}"}
|
|
)
|
|
assert response.status_code == status.HTTP_200_OK
|
|
data = response.json()
|
|
assert data["quantity"] == 15
|
|
|
|
def test_check_out_item(self, test_client, test_db, user_token):
|
|
"""Test checking out inventory (decreasing quantity)."""
|
|
from backend.models import Item, User
|
|
|
|
item = Item(name="Test Item", category="Electronics", type="Component",
|
|
quantity=10, barcode="BC-CHECKOUT", part_number="PN-CO")
|
|
test_db.add(item)
|
|
test_db.commit()
|
|
|
|
user = test_db.query(User).filter(User.username == "user").first()
|
|
response = test_client.post(
|
|
"/operations/check-out",
|
|
json={"barcode": "BC-CHECKOUT", "quantity": 3, "user_id": user.id},
|
|
headers={"Authorization": f"Bearer {user_token}"}
|
|
)
|
|
assert response.status_code == status.HTTP_200_OK
|
|
data = response.json()
|
|
assert data["quantity"] == 7
|
|
|
|
def test_check_out_insufficient_quantity(self, test_client, test_db, user_token):
|
|
"""Test that check-out fails if quantity insufficient."""
|
|
from backend.models import Item, User
|
|
|
|
item = Item(name="Test Item", category="Electronics", type="Component",
|
|
quantity=5, barcode="BC-INSUFF", part_number="PN-INS")
|
|
test_db.add(item)
|
|
test_db.commit()
|
|
|
|
user = test_db.query(User).filter(User.username == "user").first()
|
|
response = test_client.post(
|
|
"/operations/check-out",
|
|
json={"barcode": "BC-INSUFF", "quantity": 10, "user_id": user.id},
|
|
headers={"Authorization": f"Bearer {user_token}"}
|
|
)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
|
|
def test_audit_log_created(self, test_client, test_db, user_token):
|
|
"""Test that audit log entry created for each operation."""
|
|
from backend.models import Item, User
|
|
|
|
item = Item(name="Test Item", category="Electronics", type="Component",
|
|
quantity=10, barcode="BC-AUDIT", part_number="PN-AUD")
|
|
test_db.add(item)
|
|
test_db.commit()
|
|
|
|
user = test_db.query(User).filter(User.username == "user").first()
|
|
response = test_client.post(
|
|
"/operations/check-in",
|
|
json={"barcode": "BC-AUDIT", "quantity": 5, "user_id": user.id},
|
|
headers={"Authorization": f"Bearer {user_token}"}
|
|
)
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
# Verify audit log via logs endpoint
|
|
response = test_client.get(
|
|
"/operations/logs",
|
|
headers={"Authorization": f"Bearer {user_token}"}
|
|
)
|
|
assert response.status_code == status.HTTP_200_OK
|
|
logs = response.json()
|
|
assert len(logs) > 0
|
|
assert any(log["action"] == "CHECK_IN" for log in logs)
|
|
|
|
|
|
class TestBulkSync:
|
|
"""Test offline sync with UUID idempotency."""
|
|
|
|
def test_bulk_sync_offline_operations(self, test_client, test_db, user_token):
|
|
"""Test syncing multiple offline-generated operations."""
|
|
from backend.models import Item, User
|
|
from datetime import datetime
|
|
|
|
item = Item(name="Test Item", category="Electronics", type="Component",
|
|
quantity=10, barcode="BC-SYNC1", part_number="PN-SYN")
|
|
test_db.add(item)
|
|
test_db.commit()
|
|
|
|
user = test_db.query(User).filter(User.username == "user").first()
|
|
response = test_client.post(
|
|
"/operations/bulk-sync",
|
|
json={
|
|
"user_id": user.id,
|
|
"operations": [
|
|
{"type": "CHECK_IN", "barcode": "BC-SYNC1", "quantity": 5,
|
|
"uuid": "uuid-sync-1", "timestamp": datetime.utcnow().isoformat()},
|
|
{"type": "CHECK_IN", "barcode": "BC-SYNC1", "quantity": 3,
|
|
"uuid": "uuid-sync-2", "timestamp": datetime.utcnow().isoformat()},
|
|
]
|
|
},
|
|
headers={"Authorization": f"Bearer {user_token}"}
|
|
)
|
|
assert response.status_code == status.HTTP_200_OK
|
|
data = response.json()
|
|
assert "success" in data
|
|
assert len(data["success"]) == 2
|
|
|
|
def test_bulk_sync_idempotent(self, test_client, test_db, user_token):
|
|
"""Test that syncing same UUID twice doesn't duplicate."""
|
|
from backend.models import Item, User
|
|
from datetime import datetime
|
|
|
|
item = Item(name="Test Item", category="Electronics", type="Component",
|
|
quantity=10, barcode="BC-IDEMP", part_number="PN-IDP")
|
|
test_db.add(item)
|
|
test_db.commit()
|
|
|
|
user = test_db.query(User).filter(User.username == "user").first()
|
|
operation = {
|
|
"user_id": user.id,
|
|
"operations": [
|
|
{"type": "CHECK_IN", "barcode": "BC-IDEMP", "quantity": 5,
|
|
"uuid": "uuid-idempotent-1", "timestamp": datetime.utcnow().isoformat()}
|
|
]
|
|
}
|
|
|
|
# Sync once
|
|
response1 = test_client.post(
|
|
"/operations/bulk-sync", json=operation,
|
|
headers={"Authorization": f"Bearer {user_token}"}
|
|
)
|
|
assert response1.status_code == status.HTTP_200_OK
|
|
assert len(response1.json()["success"]) == 1
|
|
|
|
# Sync again with same UUID — should be idempotent (returns "Already synced")
|
|
response2 = test_client.post(
|
|
"/operations/bulk-sync", json=operation,
|
|
headers={"Authorization": f"Bearer {user_token}"}
|
|
)
|
|
assert response2.status_code == status.HTTP_200_OK
|
|
# "Already synced" note in success means idempotent
|
|
assert response2.json()["success"][0].get("note") == "Already synced"
|