working with login and building inventory

This commit is contained in:
2026-11-12 11:40:31 -07:00
parent d11e5e70c7
commit 0a93925e11
45 changed files with 2389 additions and 77 deletions

View File

@@ -0,0 +1,191 @@
"""Personal inventory tests: auth gating, CRUD, isolation, catalog immutability."""
from fastapi.testclient import TestClient
from app.api import app
PASSWORD = "correct horse battery staple"
CARROCK = "ANVL_Carrack"
RIFLE = "BEHR_Rifle_Ballistic_01"
def authed_client(username: str = "alice") -> TestClient:
client = TestClient(app)
resp = client.post(
"/api/v1/auth/register", json={"username": username, "password": PASSWORD}
)
assert resp.status_code == 201, resp.text
return client
def add(client: TestClient, class_name: str, quantity: int = 1, notes: str = ""):
return client.post(
"/api/v1/me/inventory",
json={"class_name": class_name, "quantity": quantity, "notes": notes},
)
def test_new_account_starts_with_empty_inventory():
client = authed_client("freshpilot")
listing = client.get("/api/v1/me/inventory").json()
assert listing["total"] == 0
assert listing["items"] == []
assert client.get("/api/v1/me/inventory/summary").json() == {
"items": 0,
"quantity": 0,
"categories": {},
"unavailable": 0,
}
def test_inventory_requires_authentication():
client = TestClient(app)
assert client.get("/api/v1/me/inventory").status_code == 401
assert add(client, CARROCK).status_code == 401
assert client.get("/api/v1/me/inventory/summary").status_code == 401
def test_add_unknown_catalog_item_is_not_found():
client = authed_client()
assert add(client, "NOPE_Does_Not_Exist").status_code == 404
def test_add_creates_enriched_entry():
client = authed_client()
resp = add(client, CARROCK)
assert resp.status_code == 201
body = resp.json()
assert body["created"] is True
entry = body["entry"]
assert entry["class_name"] == CARROCK
assert entry["name"] == "Anvil Carrack"
assert entry["category"] == "ships"
assert entry["quantity"] == 1
assert entry["available"] is True
assert entry["manufacturer"]["code"] == "ANVL"
def test_add_same_item_increments_quantity():
client = authed_client()
assert add(client, CARROCK, quantity=1).status_code == 201
resp = add(client, CARROCK, quantity=2, notes="pledged")
assert resp.status_code == 200
body = resp.json()
assert body["created"] is False
assert body["entry"]["quantity"] == 3
assert body["entry"]["notes"] == "pledged"
listing = client.get("/api/v1/me/inventory").json()
assert listing["total"] == 1
def test_inventories_are_isolated_between_users():
alice = authed_client("alice")
bob = authed_client("bob")
assert add(alice, CARROCK).status_code == 201
assert add(bob, RIFLE).status_code == 201
alice_items = alice.get("/api/v1/me/inventory").json()["items"]
bob_items = bob.get("/api/v1/me/inventory").json()["items"]
assert [i["class_name"] for i in alice_items] == [CARROCK]
assert [i["class_name"] for i in bob_items] == [RIFLE]
def test_patch_updates_quantity_and_notes():
client = authed_client()
assert add(client, CARROCK).status_code == 201
resp = client.patch(
f"/api/v1/me/inventory/{CARROCK}", json={"quantity": 4, "notes": "hangar 3"}
)
assert resp.status_code == 200
body = resp.json()
assert body["quantity"] == 4
assert body["notes"] == "hangar 3"
def test_patch_unknown_entry_is_not_found():
client = authed_client()
assert client.patch(f"/api/v1/me/inventory/{CARROCK}", json={"quantity": 2}).status_code == 404
def test_patch_rejects_zero_quantity():
client = authed_client()
assert add(client, CARROCK).status_code == 201
resp = client.patch(f"/api/v1/me/inventory/{CARROCK}", json={"quantity": 0})
assert resp.status_code == 422
def test_delete_removes_entry():
client = authed_client()
assert add(client, CARROCK).status_code == 201
assert client.delete(f"/api/v1/me/inventory/{CARROCK}").status_code == 204
assert client.get("/api/v1/me/inventory").json()["total"] == 0
assert client.delete(f"/api/v1/me/inventory/{CARROCK}").status_code == 404
def test_list_filters_by_category_and_search():
client = authed_client()
assert add(client, CARROCK).status_code == 201
assert add(client, RIFLE).status_code == 201
ships = client.get("/api/v1/me/inventory", params={"category": "ships"}).json()
assert ships["total"] == 1
assert ships["items"][0]["class_name"] == CARROCK
found = client.get("/api/v1/me/inventory", params={"search": "carrack"}).json()
assert found["total"] == 1
assert found["items"][0]["class_name"] == CARROCK
def test_list_pagination_envelope_and_cap():
client = authed_client()
assert add(client, CARROCK).status_code == 201
assert add(client, RIFLE).status_code == 201
body = client.get(
"/api/v1/me/inventory", params={"page_size": 1, "page": 2}
).json()
assert body["total"] == 2
assert body["page"] == 2
assert body["page_size"] == 1
assert body["total_pages"] == 2
assert len(body["items"]) == 1
capped = client.get("/api/v1/me/inventory", params={"page_size": 500}).json()
assert capped["page_size"] == 100
def test_summary_aggregates_quantity_and_categories():
client = authed_client()
assert add(client, CARROCK, quantity=2).status_code == 201
assert add(client, RIFLE, quantity=3).status_code == 201
summary = client.get("/api/v1/me/inventory/summary").json()
assert summary["items"] == 2
assert summary["quantity"] == 5
assert summary["unavailable"] == 0
assert summary["categories"]["ships"] == 2
def test_notes_length_is_bounded():
client = authed_client()
resp = add(client, CARROCK, notes="x" * 1001)
assert resp.status_code == 422
def test_catalog_is_not_mutated_by_inventory_operations():
client = authed_client()
before = client.get("/api/v1/items", params={"page_size": 100}).json()
assert add(client, CARROCK).status_code == 201
client.patch(f"/api/v1/me/inventory/{CARROCK}", json={"quantity": 9})
client.delete(f"/api/v1/me/inventory/{CARROCK}")
after = client.get("/api/v1/items", params={"page_size": 100}).json()
assert after == before
carrack = client.get(f"/api/v1/items/{CARROCK}").json()
assert "quantity" not in carrack
assert "notes" not in carrack