153 lines
6.7 KiB
Python
153 lines
6.7 KiB
Python
"""Unit tests for the COSMOS mapping layer (no network).
|
|
|
|
Uses a fake client that returns a captured real take-home response, so we verify
|
|
the FeeQuote mapping and the stack assembly without hitting the API.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from config.settings import PricingRules
|
|
from pricing_agent.cosmos.service import CosmosPricingService
|
|
from pricing_agent.tools.margin_engine import stack_from_quote
|
|
|
|
# A real captured take-home response for UBMICROFIBERGUSSETPILLOWWHITEQUEEN @ $24.99.
|
|
REAL_TAKEHOME = {
|
|
"itemPrice": 24.99, "referralFee": 3.75, "fbaFee": 8.97, "lowInventoryFee": 0.0,
|
|
"inboundPlacementFee": 0.11, "returnFee": 0.5, "eprFee": 0, "vat": 0.0,
|
|
"costSubtotal": -12.72, "marginImpact": 37.71, "logisticsCost": 0.0,
|
|
"dutyAmount": 0.0, "costPerUnit": 8.59, "orderHandling": 0, "weightHandling": 0,
|
|
"warehouseExpense": 0,
|
|
"costBreakdown": {"purchasePrice": 6.28, "freight": 1.27, "duty": 1.03},
|
|
"takeHome": 3.07,
|
|
}
|
|
|
|
RULES = PricingRules(margin_floor=0.25, worst_case_referral_pct=0.15)
|
|
|
|
|
|
class FakeClient:
|
|
def get(self, path, params=None):
|
|
assert "takehome-calculator" in path
|
|
return REAL_TAKEHOME
|
|
|
|
|
|
def test_fee_quote_maps_takehome():
|
|
svc = CosmosPricingService(FakeClient())
|
|
q = svc.fee_quote("UBTEST", 24.99)
|
|
assert q.selling_price == 24.99
|
|
assert q.landed_cost == 8.59
|
|
assert q.fba_fee == 8.97
|
|
assert q.referral_amt == 3.75
|
|
assert q.returns_reserve == 0.5
|
|
assert q.other_fees == 0.11 # inbound placement only
|
|
assert q.net_takehome == 3.07
|
|
assert q.source == "cosmos"
|
|
|
|
|
|
def test_stack_from_quote_matches_cosmos_net():
|
|
svc = CosmosPricingService(FakeClient())
|
|
q = svc.fee_quote("UBTEST", 24.99)
|
|
stack = stack_from_quote(q, RULES)
|
|
# Net profit must equal COSMOS take-home.
|
|
assert round(stack.profit, 2) == 3.07
|
|
# CM% = takeHome / price.
|
|
assert round(stack.contribution_margin_pct, 4) == round(3.07 / 24.99, 4)
|
|
# Break-even uses the fixed cost base and the derived referral %.
|
|
cost_base = 8.59 + 8.97 + 0.5 + 0.11
|
|
assert round(stack.break_even_price, 2) == round(cost_base / (1 - 0.15), 2)
|
|
assert round(stack.map_floor, 2) == round(cost_base / (1 - 0.25), 2)
|
|
|
|
|
|
# ---------------------------------------------------------------- exact SKU joins
|
|
# COSMOS's /api/products and /api/invp-insight filters are CONTAINS/relevance searches,
|
|
# not equality. Both lookups used to fall back to `data[0]` "if COSMOS returned a fuzzy
|
|
# match set", which bound one product's ASIN, cost, brand and inventory to a different
|
|
# product's SKU. Observed live: get_product("UBCFKFITTEDSHEETWHITECALKING") — a SKU COSMOS
|
|
# does not have — returned UBMICROFIBERGUSSETPILLOWWHITEQUEEN / B08DTH86Q2.
|
|
class RowClient:
|
|
"""Returns a fixed row set, and records the params it was asked for."""
|
|
|
|
def __init__(self, rows):
|
|
self.rows, self.calls = rows, []
|
|
|
|
def get(self, path, params=None):
|
|
self.calls.append((path, dict(params or {})))
|
|
return {"data": self.rows, "page": 1, "size": 20, "total": len(self.rows)}
|
|
|
|
|
|
# The real response for sku=UBCFKMATTRESSPROTECTORTWIN88: the SKU itself, a Walmart row
|
|
# whose "ASIN" is not an ASIN, and the BOX variant. All three are AMAZON_USA, so only the
|
|
# exact SKU test tells them apart.
|
|
PROTECTOR_ROWS = [
|
|
{"sku": "UBCFKMATTRESSPROTECTORTWIN88", "marketplace": "AMAZON_USA",
|
|
"asin": "B00MRH9NCK", "status": "Registered", "cost": 4.11},
|
|
{"sku": "WALUBCFKMATTRESSPROTECTORTWIN88", "marketplace": "AMAZON_USA",
|
|
"asin": "8946709597", "cost": 9.99},
|
|
{"sku": "UBCFKMATTRESSPROTECTORTWIN88BOX", "marketplace": "AMAZON_USA",
|
|
"asin": "B09K7HXJ4M", "cost": 7.77},
|
|
]
|
|
|
|
|
|
def test_get_product_picks_the_exact_sku_not_a_box_or_walmart_variant():
|
|
c = RowClient(PROTECTOR_ROWS)
|
|
p = CosmosPricingService(c).get_product("UBCFKMATTRESSPROTECTORTWIN88")
|
|
assert p is not None
|
|
assert p.sku == "UBCFKMATTRESSPROTECTORTWIN88"
|
|
assert p.asin == "B00MRH9NCK" # not 8946709597, not B09K7HXJ4M
|
|
assert p.cost == 4.11 # not another variant's COGS
|
|
|
|
|
|
def test_get_product_queries_the_sku_filter_not_the_relevance_search():
|
|
"""`q` ranks across the whole catalogue and often omits the SKU entirely."""
|
|
c = RowClient(PROTECTOR_ROWS)
|
|
CosmosPricingService(c).get_product("UBCFKMATTRESSPROTECTORTWIN88")
|
|
_path, params = c.calls[0]
|
|
assert params.get("sku") == "UBCFKMATTRESSPROTECTORTWIN88"
|
|
assert "q" not in params
|
|
|
|
|
|
def test_get_product_returns_none_rather_than_another_skus_row():
|
|
"""The live failure: a SKU COSMOS does not carry at all."""
|
|
c = RowClient([
|
|
{"sku": "UBMICROFIBERGUSSETPILLOWWHITEQUEEN", "marketplace": "AMAZON_USA",
|
|
"asin": "B08DTH86Q2", "cost": 8.59},
|
|
{"sku": "UBPILLOWSQUARECOTTONCOVER18X18", "marketplace": "AMAZON_USA",
|
|
"asin": "B07XYZ1234"},
|
|
])
|
|
assert CosmosPricingService(c).get_product("UBCFKFITTEDSHEETWHITECALKING") is None
|
|
|
|
|
|
def test_get_product_rejects_a_foreign_marketplace_row():
|
|
c = RowClient([{"sku": "UBTEST", "marketplace": "AMAZON_CA", "asin": "BCA0000001"}])
|
|
svc = CosmosPricingService(c, marketplace="AMAZON_USA")
|
|
assert svc.get_product("UBTEST") is None
|
|
# ...and accepts it when that IS the marketplace we asked about.
|
|
assert CosmosPricingService(c, marketplace="AMAZON_CA").get_product("UBTEST") is not None
|
|
|
|
|
|
def test_get_invp_does_not_take_a_sibling_variants_inventory():
|
|
"""`skuPrefix` matches every colour/size variant; data[0] was an arbitrary sibling.
|
|
|
|
Inventory, cover days and 6-month velocity feed LOW_STOCK and EXCESS_STOCK, so a
|
|
sibling's numbers here move real prices on the wrong SKU.
|
|
"""
|
|
def _row(sku, inv, cover):
|
|
# cover_days is derived from the EARLIEST dateMap snapshot, not a top-level field.
|
|
return {"sku": sku, "marketplace": "AMAZON_USA", "inventory": inv,
|
|
"averageSale6Months": inv / 100.0,
|
|
"dateMap": {"01/05/2026": {"dataDate": "01/05/2026", "inventory": inv,
|
|
"coverDays": cover}}}
|
|
|
|
rows = [_row("UBMICROFIBERDUVETTWINGREY", 12, 3),
|
|
_row("UBMICROFIBERDUVETTWINWHITE", 4000, 78)]
|
|
invp = CosmosPricingService(RowClient(rows)).get_invp("UBMICROFIBERDUVETTWINWHITE")
|
|
assert invp is not None
|
|
assert invp.sku == "UBMICROFIBERDUVETTWINWHITE"
|
|
# Not the GREY sibling's 12 units / 3 days — which would read as a stockout emergency
|
|
# and fire LOW_STOCK on a SKU sitting on 4,000 units.
|
|
assert (invp.inventory, invp.cover_days, invp.avg_6m) == (4000, 78, 40.0)
|
|
|
|
|
|
def test_get_invp_returns_none_when_only_siblings_come_back():
|
|
rows = [{"sku": "UBMICROFIBERDUVETTWINGREY", "marketplace": "AMAZON_USA",
|
|
"inventory": 12}]
|
|
assert CosmosPricingService(RowClient(rows)).get_invp("UBMICROFIBERDUVETTWINWHITE") is None
|