"""workflows/inventory.py — stock reads + adjust_inventory (plan §6, §10).""" import pytest from mcp_server.errors import ClarificationNeededError, NotFoundError # --- stock reads ------------------------------------------------------------ async def test_stock_for_resource_lists_containers(inventory): stock = await inventory.stock_for_resource("ethanol_absolute") assert [s.container_id for s in stock] == [31, 32] assert stock[0].available == 50.0 assert stock[0].unit == "mL" assert stock[0].amount == "50.0 mL" async def test_stock_for_unknown_resource_raises_clarification(inventory, resolver_stub): resolver_stub.mappings.clear() with pytest.raises(ClarificationNeededError): await inventory.stock_for_resource("unobtainium") # --- adjust_inventory ------------------------------------------------------- async def test_adjust_adds_to_container_by_id(inventory, client): result = await inventory.adjust_inventory(amount=500.0, unit="mL", container_id=31) assert result.ok is True assert result.container_id == 31 assert result.amount == "500.0 mL" assert result.remaining == "550.0 mL" assert client.items[12].containers[0].qty_stored == 550.0 async def test_adjust_supports_correction_negative_delta(inventory, client): await inventory.adjust_inventory(amount=-2.0, unit="mL", container_id=31) assert client.items[12].containers[0].qty_stored == 48.0 async def test_adjust_by_resource_key_uses_allocable_container(inventory, client): result = await inventory.adjust_inventory( amount=100.0, unit="mL", resource_key="ethanol_absolute" ) assert result.resource_key == "ethanol_absolute" assert result.container_id == 31 assert result.remaining == "150.0 mL" async def test_adjust_converts_units(inventory, client): """“Add half a litre” against an mL-stocked container.""" await inventory.adjust_inventory(amount=0.5, unit="L", container_id=31) assert client.items[12].containers[0].qty_stored == 550.0 async def test_adjust_rejects_non_whitelisted_unit(inventory): with pytest.raises(ClarificationNeededError): await inventory.adjust_inventory(amount=1.0, unit="fl-oz", container_id=31) async def test_adjust_rejects_cross_dimension_unit(inventory): with pytest.raises(ClarificationNeededError): await inventory.adjust_inventory(amount=5.0, unit="g", container_id=31) async def test_adjust_unknown_container_fails_cleanly(inventory): with pytest.raises(NotFoundError): await inventory.adjust_inventory(amount=1.0, unit="mL", container_id=9999) async def test_adjust_requires_container_or_resource(inventory): with pytest.raises(ClarificationNeededError): await inventory.adjust_inventory(amount=1.0, unit="mL") async def test_adjust_is_journaled(inventory, journal): await inventory.adjust_inventory(amount=500.0, unit="mL", container_id=31) assert journal.db_path.exists() async def test_adjust_failure_does_not_silently_lose_data(inventory, client, monkeypatch): """If the write fails, the workflow surfaces the error (journaled saga).""" from mcp_server.errors import ApiError async def fail_write(entity_type, entity_id, subitem_id, qty_stored): raise ApiError("write refused") monkeypatch.setattr(inventory.client, "set_container_quantity", fail_write) with pytest.raises(ApiError): await inventory.adjust_inventory(amount=500.0, unit="mL", container_id=31) assert client.items[12].containers[0].qty_stored == 50.0