meal-order-manager/tests/test_fill_cart.py
Adam Moussa dce33918b7
feat(cart): add a local Redefine cart filler
Match the admin order-list CSV to the live menu and add each meal to a guest cart so the weekly bulk order does not have to be typed in by hand.
2026-09-25 19:50:12 -04:00

137 lines
3.7 KiB
Python

"""CSV parsing and menu matching for the Redefine cart filler."""
from scraper.fill_cart import build_plan, cart_lines, error_text, quantity_for
from scraper.parse_menu import extract_catalog_products
PRODUCTS = [
{
"name": "Korean Steak Bowl",
"uuid": "korean-id",
"available": True,
},
{
"name": "Sale Bowl",
"uuid": "sale-id",
"available": True,
},
{
"name": "Hidden Bowl",
"uuid": "hidden-id",
"available": False,
},
{
"name": "Twin Bowl",
"uuid": "twin-a",
"available": True,
},
{
"name": "twin bowl",
"uuid": "twin-b",
"available": True,
},
{
"name": "No Id Bowl",
"uuid": "",
"available": True,
},
]
def test_build_plan_matches_names_and_skips_the_rest():
csv_text = "\n".join(
[
"Item,Quantity",
"korean steak bowl,4",
"Sale Bowl,2",
"Sale Bowl,3",
"Hidden Bowl,1",
"Twin Bowl,8",
"Missing Bowl,1",
"No Id Bowl,1",
"=cmd,1",
"Zero Bowl,0",
"Bad Qty,nope",
]
)
# The admin exporter prefixes formula-like names with an apostrophe.
csv_text = csv_text.replace("=cmd", "'=cmd")
plan = build_plan(csv_text, PRODUCTS)
assert [(item.name, item.quantity, item.uuid) for item in plan.adds] == [
("Korean Steak Bowl", 4, "korean-id"),
("Sale Bowl", 5, "sale-id"),
]
assert [(line.name, line.reason) for line in plan.skipped] == [
("Bad Qty", "invalid quantity 'nope'"),
("Hidden Bowl", "unavailable on the menu"),
("Twin Bowl", "matches more than one menu item"),
("Missing Bowl", "not on the menu"),
("No Id Bowl", "not on the menu"),
("=cmd", "not on the menu"),
]
assert plan.ignored_zero == 1
def test_build_plan_reads_quoted_fields_and_a_byte_order_mark():
csv_text = '\ufeffItem,Quantity\r\n"Bowl, Large",2\r\n'
products = [{"name": "Bowl, Large", "uuid": "bowl", "available": True}]
plan = build_plan(csv_text, products)
assert plan.adds[0].name == "Bowl, Large"
assert plan.adds[0].quantity == 2
assert plan.skipped == ()
def test_build_plan_requires_the_admin_columns():
try:
build_plan("Meal,Qty\nSoup,1\n", PRODUCTS)
except ValueError as exc:
assert "Item and Quantity" in str(exc)
else:
raise AssertionError("expected a column error")
def test_extract_catalog_products_reads_the_menu_attribute():
page = """
<orders-page
:products='[{"name":"Korean Steak Bowl","uuid":"korean-id","available":true}]'
:newest-ids='[]'
></orders-page>
"""
assert extract_catalog_products(page)[0]["uuid"] == "korean-id"
def test_cart_lines_use_the_product_uuid_and_quantity():
cart = {
"items": [
{
"uuid": "line-1",
"quantity": "4",
"product": {"name": "Korean Steak Bowl", "uuid": "korean-id"},
}
]
}
lines = cart_lines(cart)
assert lines[0].name == "Korean Steak Bowl"
assert lines[0].product_uuid == "korean-id"
assert quantity_for(lines, "korean-id") == 4
assert quantity_for(lines, "line-1") == 0
def test_error_text_keeps_the_message_and_drops_the_trace():
body = {
"message": "Product is not available.",
"exception": "HttpException",
"file": "/home/app/secret.php",
"trace": [{"file": "/home/app/secret.php"}],
}
text = error_text(422, body)
assert text == "HTTP 422: Product is not available."
assert "secret.php" not in text