2022-02-16 21:57:27 -07:00
|
|
|
import json
|
2022-01-15 18:05:51 -07:00
|
|
|
from http import HTTPStatus
|
|
|
|
|
|
2023-05-29 18:04:03 -06:00
|
|
|
import pytest
|
|
|
|
|
|
2022-01-15 18:05:51 -07:00
|
|
|
|
2025-10-29 22:16:33 -06:00
|
|
|
@pytest.mark.asyncio
|
Rebuild Inventory as a config-only system with food catalog and calculators
Replace the DB-backed inventory module with a config-only design and build out
food-storage tooling around it.
Inventory (config-only, no database)
- Each inventory is a YAML file at config/inventory/<slug>.yaml, loaded once
into memory and the single source of truth. New reusable MultiFileConfig
(wrolpi/common.py) manages a directory of per-entity files.
- Inventory types (food/fuel/tool) seed a customizable field schema
(text/number/quantity/date/select/location/calories). Unit math/aggregation
moved to the frontend (mathjs); Pint dropped from the backend.
- Minimal API: GET / (all inventories full), POST /, PUT /<slug> (whole-
inventory save with version-based optimistic concurrency; 404 for missing),
DELETE. Legacy inventories.yaml / old tables migrate to per-inventory files
(pound->lb), and an alembic migration drops the tables.
Frontend
- New app/src/components/inventory/ package: spreadsheet-style table with
Tab/Enter keyboard entry, sortable columns, expired-row highlighting,
customizable field editor, and pooled cross-inventory location autocomplete.
- A single useInventories store loads everything once and persists whole
inventories optimistically; list/items/summary/locations derive client-side.
- Summary groups items (default by Category), totals size x count, shows a
calories column, and embeds a ration estimate (how long the stock lasts).
Food catalog
- Shared, editable catalog (config/inventory/catalog.yaml) seeded from
catalog_defaults.yaml (~60 generic items, total calories per package) with
merge-keep-edits + tombstones. GET/PUT /api/inventory/catalog, a management
modal, and entry-row pre-fill of category/size/calories from a name match.
Calculators
- Generic Food Storage planner (like the Water calculator): household +
editable per-person/year amounts -> total to store over a chosen duration,
with women/children weighted by DGA calorie shares and an imperial/metric
toggle.
Also fixes ConfigFile.save() to accept nested file_names. Backend pytest and
frontend jest suites updated/added throughout; alembic check clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:20:10 -06:00
|
|
|
async def test_inventory_lifecycle_api(test_inventory_configs, async_client):
|
|
|
|
|
"""Create, list (full), rename, and delete an inventory via the API."""
|
|
|
|
|
request, response = await async_client.post('/api/inventory',
|
|
|
|
|
content=json.dumps(dict(name='Food Storage', type='food')))
|
|
|
|
|
assert response.status_code == HTTPStatus.CREATED, response.status_code
|
|
|
|
|
inventory = response.json['inventory']
|
|
|
|
|
slug = inventory['slug']
|
|
|
|
|
assert slug == 'food-storage'
|
|
|
|
|
assert inventory['fields'], 'food inventory should be seeded with default fields'
|
2022-01-15 18:05:51 -07:00
|
|
|
|
Rebuild Inventory as a config-only system with food catalog and calculators
Replace the DB-backed inventory module with a config-only design and build out
food-storage tooling around it.
Inventory (config-only, no database)
- Each inventory is a YAML file at config/inventory/<slug>.yaml, loaded once
into memory and the single source of truth. New reusable MultiFileConfig
(wrolpi/common.py) manages a directory of per-entity files.
- Inventory types (food/fuel/tool) seed a customizable field schema
(text/number/quantity/date/select/location/calories). Unit math/aggregation
moved to the frontend (mathjs); Pint dropped from the backend.
- Minimal API: GET / (all inventories full), POST /, PUT /<slug> (whole-
inventory save with version-based optimistic concurrency; 404 for missing),
DELETE. Legacy inventories.yaml / old tables migrate to per-inventory files
(pound->lb), and an alembic migration drops the tables.
Frontend
- New app/src/components/inventory/ package: spreadsheet-style table with
Tab/Enter keyboard entry, sortable columns, expired-row highlighting,
customizable field editor, and pooled cross-inventory location autocomplete.
- A single useInventories store loads everything once and persists whole
inventories optimistically; list/items/summary/locations derive client-side.
- Summary groups items (default by Category), totals size x count, shows a
calories column, and embeds a ration estimate (how long the stock lasts).
Food catalog
- Shared, editable catalog (config/inventory/catalog.yaml) seeded from
catalog_defaults.yaml (~60 generic items, total calories per package) with
merge-keep-edits + tombstones. GET/PUT /api/inventory/catalog, a management
modal, and entry-row pre-fill of category/size/calories from a name match.
Calculators
- Generic Food Storage planner (like the Water calculator): household +
editable per-person/year amounts -> total to store over a chosen duration,
with women/children weighted by DGA calorie shares and an imperial/metric
toggle.
Also fixes ConfigFile.save() to accept nested file_names. Backend pytest and
frontend jest suites updated/added throughout; alembic check clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:20:10 -06:00
|
|
|
# GET / returns every inventory in full (fields + items) in one request.
|
|
|
|
|
request, response = await async_client.get('/api/inventory')
|
|
|
|
|
assert response.status_code == HTTPStatus.OK
|
|
|
|
|
assert response.json['inventories'][0]['name'] == 'Food Storage'
|
|
|
|
|
assert 'items' in response.json['inventories'][0]
|
2022-02-16 21:57:27 -07:00
|
|
|
|
Rebuild Inventory as a config-only system with food catalog and calculators
Replace the DB-backed inventory module with a config-only design and build out
food-storage tooling around it.
Inventory (config-only, no database)
- Each inventory is a YAML file at config/inventory/<slug>.yaml, loaded once
into memory and the single source of truth. New reusable MultiFileConfig
(wrolpi/common.py) manages a directory of per-entity files.
- Inventory types (food/fuel/tool) seed a customizable field schema
(text/number/quantity/date/select/location/calories). Unit math/aggregation
moved to the frontend (mathjs); Pint dropped from the backend.
- Minimal API: GET / (all inventories full), POST /, PUT /<slug> (whole-
inventory save with version-based optimistic concurrency; 404 for missing),
DELETE. Legacy inventories.yaml / old tables migrate to per-inventory files
(pound->lb), and an alembic migration drops the tables.
Frontend
- New app/src/components/inventory/ package: spreadsheet-style table with
Tab/Enter keyboard entry, sortable columns, expired-row highlighting,
customizable field editor, and pooled cross-inventory location autocomplete.
- A single useInventories store loads everything once and persists whole
inventories optimistically; list/items/summary/locations derive client-side.
- Summary groups items (default by Category), totals size x count, shows a
calories column, and embeds a ration estimate (how long the stock lasts).
Food catalog
- Shared, editable catalog (config/inventory/catalog.yaml) seeded from
catalog_defaults.yaml (~60 generic items, total calories per package) with
merge-keep-edits + tombstones. GET/PUT /api/inventory/catalog, a management
modal, and entry-row pre-fill of category/size/calories from a name match.
Calculators
- Generic Food Storage planner (like the Water calculator): household +
editable per-person/year amounts -> total to store over a chosen duration,
with women/children weighted by DGA calorie shares and an imperial/metric
toggle.
Also fixes ConfigFile.save() to accept nested file_names. Backend pytest and
frontend jest suites updated/added throughout; alembic check clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:20:10 -06:00
|
|
|
# Rename via whole-inventory PUT; slug stays stable.
|
|
|
|
|
request, response = await async_client.put(f'/api/inventory/{slug}', content=json.dumps(dict(name='Pantry')))
|
|
|
|
|
assert response.status_code == HTTPStatus.OK, response.status_code
|
|
|
|
|
assert response.json['inventory']['name'] == 'Pantry'
|
|
|
|
|
assert response.json['inventory']['slug'] == slug
|
2022-02-16 21:57:27 -07:00
|
|
|
|
Rebuild Inventory as a config-only system with food catalog and calculators
Replace the DB-backed inventory module with a config-only design and build out
food-storage tooling around it.
Inventory (config-only, no database)
- Each inventory is a YAML file at config/inventory/<slug>.yaml, loaded once
into memory and the single source of truth. New reusable MultiFileConfig
(wrolpi/common.py) manages a directory of per-entity files.
- Inventory types (food/fuel/tool) seed a customizable field schema
(text/number/quantity/date/select/location/calories). Unit math/aggregation
moved to the frontend (mathjs); Pint dropped from the backend.
- Minimal API: GET / (all inventories full), POST /, PUT /<slug> (whole-
inventory save with version-based optimistic concurrency; 404 for missing),
DELETE. Legacy inventories.yaml / old tables migrate to per-inventory files
(pound->lb), and an alembic migration drops the tables.
Frontend
- New app/src/components/inventory/ package: spreadsheet-style table with
Tab/Enter keyboard entry, sortable columns, expired-row highlighting,
customizable field editor, and pooled cross-inventory location autocomplete.
- A single useInventories store loads everything once and persists whole
inventories optimistically; list/items/summary/locations derive client-side.
- Summary groups items (default by Category), totals size x count, shows a
calories column, and embeds a ration estimate (how long the stock lasts).
Food catalog
- Shared, editable catalog (config/inventory/catalog.yaml) seeded from
catalog_defaults.yaml (~60 generic items, total calories per package) with
merge-keep-edits + tombstones. GET/PUT /api/inventory/catalog, a management
modal, and entry-row pre-fill of category/size/calories from a name match.
Calculators
- Generic Food Storage planner (like the Water calculator): household +
editable per-person/year amounts -> total to store over a chosen duration,
with women/children weighted by DGA calorie shares and an imperial/metric
toggle.
Also fixes ConfigFile.save() to accept nested file_names. Backend pytest and
frontend jest suites updated/added throughout; alembic check clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:20:10 -06:00
|
|
|
request, response = await async_client.delete(f'/api/inventory/{slug}')
|
2022-02-16 21:57:27 -07:00
|
|
|
assert response.status_code == HTTPStatus.NO_CONTENT, response.status_code
|
|
|
|
|
|
Rebuild Inventory as a config-only system with food catalog and calculators
Replace the DB-backed inventory module with a config-only design and build out
food-storage tooling around it.
Inventory (config-only, no database)
- Each inventory is a YAML file at config/inventory/<slug>.yaml, loaded once
into memory and the single source of truth. New reusable MultiFileConfig
(wrolpi/common.py) manages a directory of per-entity files.
- Inventory types (food/fuel/tool) seed a customizable field schema
(text/number/quantity/date/select/location/calories). Unit math/aggregation
moved to the frontend (mathjs); Pint dropped from the backend.
- Minimal API: GET / (all inventories full), POST /, PUT /<slug> (whole-
inventory save with version-based optimistic concurrency; 404 for missing),
DELETE. Legacy inventories.yaml / old tables migrate to per-inventory files
(pound->lb), and an alembic migration drops the tables.
Frontend
- New app/src/components/inventory/ package: spreadsheet-style table with
Tab/Enter keyboard entry, sortable columns, expired-row highlighting,
customizable field editor, and pooled cross-inventory location autocomplete.
- A single useInventories store loads everything once and persists whole
inventories optimistically; list/items/summary/locations derive client-side.
- Summary groups items (default by Category), totals size x count, shows a
calories column, and embeds a ration estimate (how long the stock lasts).
Food catalog
- Shared, editable catalog (config/inventory/catalog.yaml) seeded from
catalog_defaults.yaml (~60 generic items, total calories per package) with
merge-keep-edits + tombstones. GET/PUT /api/inventory/catalog, a management
modal, and entry-row pre-fill of category/size/calories from a name match.
Calculators
- Generic Food Storage planner (like the Water calculator): household +
editable per-person/year amounts -> total to store over a chosen duration,
with women/children weighted by DGA calorie shares and an imperial/metric
toggle.
Also fixes ConfigFile.save() to accept nested file_names. Backend pytest and
frontend jest suites updated/added throughout; alembic check clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:20:10 -06:00
|
|
|
request, response = await async_client.get('/api/inventory')
|
|
|
|
|
assert len(response.json['inventories']) == 0
|
2022-02-16 21:57:27 -07:00
|
|
|
|
|
|
|
|
|
2023-05-29 18:04:03 -06:00
|
|
|
@pytest.mark.asyncio
|
Rebuild Inventory as a config-only system with food catalog and calculators
Replace the DB-backed inventory module with a config-only design and build out
food-storage tooling around it.
Inventory (config-only, no database)
- Each inventory is a YAML file at config/inventory/<slug>.yaml, loaded once
into memory and the single source of truth. New reusable MultiFileConfig
(wrolpi/common.py) manages a directory of per-entity files.
- Inventory types (food/fuel/tool) seed a customizable field schema
(text/number/quantity/date/select/location/calories). Unit math/aggregation
moved to the frontend (mathjs); Pint dropped from the backend.
- Minimal API: GET / (all inventories full), POST /, PUT /<slug> (whole-
inventory save with version-based optimistic concurrency; 404 for missing),
DELETE. Legacy inventories.yaml / old tables migrate to per-inventory files
(pound->lb), and an alembic migration drops the tables.
Frontend
- New app/src/components/inventory/ package: spreadsheet-style table with
Tab/Enter keyboard entry, sortable columns, expired-row highlighting,
customizable field editor, and pooled cross-inventory location autocomplete.
- A single useInventories store loads everything once and persists whole
inventories optimistically; list/items/summary/locations derive client-side.
- Summary groups items (default by Category), totals size x count, shows a
calories column, and embeds a ration estimate (how long the stock lasts).
Food catalog
- Shared, editable catalog (config/inventory/catalog.yaml) seeded from
catalog_defaults.yaml (~60 generic items, total calories per package) with
merge-keep-edits + tombstones. GET/PUT /api/inventory/catalog, a management
modal, and entry-row pre-fill of category/size/calories from a name match.
Calculators
- Generic Food Storage planner (like the Water calculator): household +
editable per-person/year amounts -> total to store over a chosen duration,
with women/children weighted by DGA calorie shares and an imperial/metric
toggle.
Also fixes ConfigFile.save() to accept nested file_names. Backend pytest and
frontend jest suites updated/added throughout; alembic check clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:20:10 -06:00
|
|
|
async def test_save_items_and_fields_api(food_inventory_factory, async_client):
|
|
|
|
|
"""Items and fields are persisted with a single whole-inventory PUT."""
|
|
|
|
|
slug = food_inventory_factory()
|
|
|
|
|
|
|
|
|
|
items = [
|
|
|
|
|
{'brand': 'Salty', 'name': 'Salt', 'item_size': '1', 'item_size_unit': 'lb', 'count': '5'},
|
|
|
|
|
{'brand': 'Ricey', 'name': 'Rice', 'count': '4'},
|
|
|
|
|
]
|
|
|
|
|
request, response = await async_client.put(f'/api/inventory/{slug}', content=json.dumps(dict(items=items)))
|
|
|
|
|
assert response.status_code == HTTPStatus.OK, response.json
|
|
|
|
|
saved_items = response.json['inventory']['items']
|
|
|
|
|
assert {i['name'] for i in saved_items} == {'Salt', 'Rice'}
|
|
|
|
|
assert all(isinstance(i['id'], int) for i in saved_items)
|
|
|
|
|
|
|
|
|
|
# Replace the field schema in the same kind of request.
|
|
|
|
|
new_fields = [
|
|
|
|
|
{'key': 'name', 'label': 'Name', 'type': 'text'},
|
|
|
|
|
{'key': 'location', 'label': 'Location', 'type': 'location'},
|
|
|
|
|
]
|
|
|
|
|
request, response = await async_client.put(f'/api/inventory/{slug}', content=json.dumps(dict(fields=new_fields)))
|
|
|
|
|
assert response.status_code == HTTPStatus.OK, response.json
|
|
|
|
|
assert [f['key'] for f in response.json['inventory']['fields']] == ['name', 'location']
|
2022-02-16 21:57:27 -07:00
|
|
|
|
|
|
|
|
|
Rebuild Inventory as a config-only system with food catalog and calculators
Replace the DB-backed inventory module with a config-only design and build out
food-storage tooling around it.
Inventory (config-only, no database)
- Each inventory is a YAML file at config/inventory/<slug>.yaml, loaded once
into memory and the single source of truth. New reusable MultiFileConfig
(wrolpi/common.py) manages a directory of per-entity files.
- Inventory types (food/fuel/tool) seed a customizable field schema
(text/number/quantity/date/select/location/calories). Unit math/aggregation
moved to the frontend (mathjs); Pint dropped from the backend.
- Minimal API: GET / (all inventories full), POST /, PUT /<slug> (whole-
inventory save with version-based optimistic concurrency; 404 for missing),
DELETE. Legacy inventories.yaml / old tables migrate to per-inventory files
(pound->lb), and an alembic migration drops the tables.
Frontend
- New app/src/components/inventory/ package: spreadsheet-style table with
Tab/Enter keyboard entry, sortable columns, expired-row highlighting,
customizable field editor, and pooled cross-inventory location autocomplete.
- A single useInventories store loads everything once and persists whole
inventories optimistically; list/items/summary/locations derive client-side.
- Summary groups items (default by Category), totals size x count, shows a
calories column, and embeds a ration estimate (how long the stock lasts).
Food catalog
- Shared, editable catalog (config/inventory/catalog.yaml) seeded from
catalog_defaults.yaml (~60 generic items, total calories per package) with
merge-keep-edits + tombstones. GET/PUT /api/inventory/catalog, a management
modal, and entry-row pre-fill of category/size/calories from a name match.
Calculators
- Generic Food Storage planner (like the Water calculator): household +
editable per-person/year amounts -> total to store over a chosen duration,
with women/children weighted by DGA calorie shares and an imperial/metric
toggle.
Also fixes ConfigFile.save() to accept nested file_names. Backend pytest and
frontend jest suites updated/added throughout; alembic check clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:20:10 -06:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_put_version_conflict_api(food_inventory_factory, async_client):
|
|
|
|
|
"""A stale version is rejected with 409 instead of clobbering a newer save."""
|
|
|
|
|
slug = food_inventory_factory()
|
|
|
|
|
request, response = await async_client.get('/api/inventory')
|
|
|
|
|
inventory = next(i for i in response.json['inventories'] if i['slug'] == slug)
|
|
|
|
|
version = inventory['version']
|
2022-08-06 05:22:07 +00:00
|
|
|
|
Rebuild Inventory as a config-only system with food catalog and calculators
Replace the DB-backed inventory module with a config-only design and build out
food-storage tooling around it.
Inventory (config-only, no database)
- Each inventory is a YAML file at config/inventory/<slug>.yaml, loaded once
into memory and the single source of truth. New reusable MultiFileConfig
(wrolpi/common.py) manages a directory of per-entity files.
- Inventory types (food/fuel/tool) seed a customizable field schema
(text/number/quantity/date/select/location/calories). Unit math/aggregation
moved to the frontend (mathjs); Pint dropped from the backend.
- Minimal API: GET / (all inventories full), POST /, PUT /<slug> (whole-
inventory save with version-based optimistic concurrency; 404 for missing),
DELETE. Legacy inventories.yaml / old tables migrate to per-inventory files
(pound->lb), and an alembic migration drops the tables.
Frontend
- New app/src/components/inventory/ package: spreadsheet-style table with
Tab/Enter keyboard entry, sortable columns, expired-row highlighting,
customizable field editor, and pooled cross-inventory location autocomplete.
- A single useInventories store loads everything once and persists whole
inventories optimistically; list/items/summary/locations derive client-side.
- Summary groups items (default by Category), totals size x count, shows a
calories column, and embeds a ration estimate (how long the stock lasts).
Food catalog
- Shared, editable catalog (config/inventory/catalog.yaml) seeded from
catalog_defaults.yaml (~60 generic items, total calories per package) with
merge-keep-edits + tombstones. GET/PUT /api/inventory/catalog, a management
modal, and entry-row pre-fill of category/size/calories from a name match.
Calculators
- Generic Food Storage planner (like the Water calculator): household +
editable per-person/year amounts -> total to store over a chosen duration,
with women/children weighted by DGA calorie shares and an imperial/metric
toggle.
Also fixes ConfigFile.save() to accept nested file_names. Backend pytest and
frontend jest suites updated/added throughout; alembic check clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:20:10 -06:00
|
|
|
request, response = await async_client.put(f'/api/inventory/{slug}',
|
|
|
|
|
content=json.dumps(dict(name='First', version=version)))
|
2022-08-06 05:22:07 +00:00
|
|
|
assert response.status_code == HTTPStatus.OK
|
|
|
|
|
|
Rebuild Inventory as a config-only system with food catalog and calculators
Replace the DB-backed inventory module with a config-only design and build out
food-storage tooling around it.
Inventory (config-only, no database)
- Each inventory is a YAML file at config/inventory/<slug>.yaml, loaded once
into memory and the single source of truth. New reusable MultiFileConfig
(wrolpi/common.py) manages a directory of per-entity files.
- Inventory types (food/fuel/tool) seed a customizable field schema
(text/number/quantity/date/select/location/calories). Unit math/aggregation
moved to the frontend (mathjs); Pint dropped from the backend.
- Minimal API: GET / (all inventories full), POST /, PUT /<slug> (whole-
inventory save with version-based optimistic concurrency; 404 for missing),
DELETE. Legacy inventories.yaml / old tables migrate to per-inventory files
(pound->lb), and an alembic migration drops the tables.
Frontend
- New app/src/components/inventory/ package: spreadsheet-style table with
Tab/Enter keyboard entry, sortable columns, expired-row highlighting,
customizable field editor, and pooled cross-inventory location autocomplete.
- A single useInventories store loads everything once and persists whole
inventories optimistically; list/items/summary/locations derive client-side.
- Summary groups items (default by Category), totals size x count, shows a
calories column, and embeds a ration estimate (how long the stock lasts).
Food catalog
- Shared, editable catalog (config/inventory/catalog.yaml) seeded from
catalog_defaults.yaml (~60 generic items, total calories per package) with
merge-keep-edits + tombstones. GET/PUT /api/inventory/catalog, a management
modal, and entry-row pre-fill of category/size/calories from a name match.
Calculators
- Generic Food Storage planner (like the Water calculator): household +
editable per-person/year amounts -> total to store over a chosen duration,
with women/children weighted by DGA calorie shares and an imperial/metric
toggle.
Also fixes ConfigFile.save() to accept nested file_names. Backend pytest and
frontend jest suites updated/added throughout; alembic check clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:20:10 -06:00
|
|
|
request, response = await async_client.put(f'/api/inventory/{slug}',
|
|
|
|
|
content=json.dumps(dict(name='Stale', version=version)))
|
|
|
|
|
assert response.status_code == HTTPStatus.CONFLICT, response.status_code
|
2022-08-06 05:22:07 +00:00
|
|
|
|
2022-02-16 21:57:27 -07:00
|
|
|
|
Rebuild Inventory as a config-only system with food catalog and calculators
Replace the DB-backed inventory module with a config-only design and build out
food-storage tooling around it.
Inventory (config-only, no database)
- Each inventory is a YAML file at config/inventory/<slug>.yaml, loaded once
into memory and the single source of truth. New reusable MultiFileConfig
(wrolpi/common.py) manages a directory of per-entity files.
- Inventory types (food/fuel/tool) seed a customizable field schema
(text/number/quantity/date/select/location/calories). Unit math/aggregation
moved to the frontend (mathjs); Pint dropped from the backend.
- Minimal API: GET / (all inventories full), POST /, PUT /<slug> (whole-
inventory save with version-based optimistic concurrency; 404 for missing),
DELETE. Legacy inventories.yaml / old tables migrate to per-inventory files
(pound->lb), and an alembic migration drops the tables.
Frontend
- New app/src/components/inventory/ package: spreadsheet-style table with
Tab/Enter keyboard entry, sortable columns, expired-row highlighting,
customizable field editor, and pooled cross-inventory location autocomplete.
- A single useInventories store loads everything once and persists whole
inventories optimistically; list/items/summary/locations derive client-side.
- Summary groups items (default by Category), totals size x count, shows a
calories column, and embeds a ration estimate (how long the stock lasts).
Food catalog
- Shared, editable catalog (config/inventory/catalog.yaml) seeded from
catalog_defaults.yaml (~60 generic items, total calories per package) with
merge-keep-edits + tombstones. GET/PUT /api/inventory/catalog, a management
modal, and entry-row pre-fill of category/size/calories from a name match.
Calculators
- Generic Food Storage planner (like the Water calculator): household +
editable per-person/year amounts -> total to store over a chosen duration,
with women/children weighted by DGA calorie shares and an imperial/metric
toggle.
Also fixes ConfigFile.save() to accept nested file_names. Backend pytest and
frontend jest suites updated/added throughout; alembic check clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:20:10 -06:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_missing_inventory_404(test_inventory_configs, async_client):
|
|
|
|
|
request, response = await async_client.put('/api/inventory/does-not-exist', content=json.dumps(dict(name='x')))
|
|
|
|
|
assert response.status_code == HTTPStatus.NOT_FOUND, response.status_code
|
2026-06-19 16:13:56 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_backups_and_restore_api(food_inventory_factory, test_inventory_configs, async_client):
|
|
|
|
|
"""List backups, preview, and apply a merge restore via the API."""
|
|
|
|
|
from modules.inventory.defaults import default_fields
|
|
|
|
|
from wrolpi.common import write_config_data
|
|
|
|
|
config = test_inventory_configs
|
|
|
|
|
slug = food_inventory_factory(items=[{'id': 1, 'name': 'Rice', 'count': '4'}])
|
|
|
|
|
backup = config._get_backup_file(slug, '20260101')
|
|
|
|
|
backup.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
write_config_data(dict(version=9, slug=slug, name='Food Storage', type='food', fields=default_fields('food'),
|
|
|
|
|
items=[{'id': 1, 'name': 'Rice', 'count': '4'}, {'id': 2, 'name': 'Beans', 'count': '2'}]),
|
|
|
|
|
backup)
|
|
|
|
|
|
|
|
|
|
request, response = await async_client.get(f'/api/inventory/{slug}/backups')
|
|
|
|
|
assert response.status_code == HTTPStatus.OK
|
|
|
|
|
# Saving the seed items also wrote today's backup, so just assert our dated backup is listed.
|
|
|
|
|
assert '20260101' in response.json['dates']
|
|
|
|
|
|
|
|
|
|
request, response = await async_client.post(f'/api/inventory/{slug}/restore/preview',
|
|
|
|
|
content=json.dumps(dict(backup_date='20260101', mode='merge')))
|
|
|
|
|
assert response.status_code == HTTPStatus.OK, response.json
|
|
|
|
|
assert {i['id'] for i in response.json['preview']['add']} == {2}
|
|
|
|
|
|
|
|
|
|
request, response = await async_client.post(f'/api/inventory/{slug}/restore',
|
|
|
|
|
content=json.dumps(dict(backup_date='20260101', mode='merge')))
|
|
|
|
|
assert response.status_code == HTTPStatus.OK, response.json
|
|
|
|
|
assert {i['name'] for i in response.json['inventory']['items']} == {'Rice', 'Beans'}
|
|
|
|
|
|
|
|
|
|
# A bad mode is rejected.
|
|
|
|
|
request, response = await async_client.post(f'/api/inventory/{slug}/restore',
|
|
|
|
|
content=json.dumps(dict(backup_date='20260101', mode='bogus')))
|
|
|
|
|
assert response.status_code == HTTPStatus.BAD_REQUEST, response.status_code
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_reimport_api(food_inventory_factory, test_inventory_configs, async_client):
|
|
|
|
|
"""POST /reimport picks up an inventory file copied onto disk by hand."""
|
|
|
|
|
from modules.inventory.defaults import default_fields
|
|
|
|
|
from wrolpi.common import write_config_data
|
|
|
|
|
config = test_inventory_configs
|
|
|
|
|
food_inventory_factory(name='Food Storage')
|
|
|
|
|
write_config_data(dict(version=1, slug='tools-box', name='Tools Box', type='tool',
|
|
|
|
|
fields=default_fields('tool'), items=[]), config.get_file('tools-box'))
|
|
|
|
|
|
|
|
|
|
request, response = await async_client.post('/api/inventory/reimport')
|
|
|
|
|
assert response.status_code == HTTPStatus.OK, response.json
|
|
|
|
|
assert {i['slug'] for i in response.json['inventories']} == {'food-storage', 'tools-box'}
|