backend apis updation

This commit is contained in:
sriram
2026-08-13 15:54:29 +05:30
parent b8d93fbbf2
commit 241fd237f8
13 changed files with 973 additions and 105 deletions

View File

@@ -0,0 +1,113 @@
"""
Guard rails for brand -> parent-brand resolution.
`resolve_parent_brand` decides which `brand_*` table, which S3 folder, and
which seed catalog file a product lands in. An edit to BRAND_ALIASES that
quietly changes an existing brand's answer would relocate live rows, so the
first test pins the answer for every realistic input: all alias keys, all
parent values, and the `brand` field of every bundled seed catalog.
No database, no network - the registry is a plain dict.
"""
from __future__ import annotations
import json
from pathlib import Path
import pytest
from app.services.brand_registry import BRAND_ALIASES, resolve_parent_brand
from app.services.vector_store import _sanitize_name
SEED_DIR = Path(__file__).resolve().parents[1] / "data" / "seed_catalogs"
def _seed_brand_fields() -> list[str]:
"""The `brand` value of every seed catalog (skipping non-catalog files)."""
brands = []
for path in sorted(SEED_DIR.glob("*.json")):
try:
data = json.loads(path.read_text(encoding="utf-8-sig"))
except Exception:
continue
brand = data.get("brand")
if brand:
brands.append(brand)
return brands
# Every seed catalog and the table it must continue to feed. `tata` mapping to
# hindustan_unilever is not a typo: alias "hul tata tea" claims it, which is
# where all 121 of that file's products already live.
EXPECTED_SEED_TABLES = {
"amul": "brand_amul",
"cadbury": "brand_cadbury",
"cavinkare": "brand_cavinkare",
"coca-cola": "brand_coca_cola",
"colgate-palmolive": "brand_colgate_palmolive",
"dabur": "brand_dabur",
"godrej": "brand_godrej",
"grb": "brand_grb",
"hindustan unilever": "brand_hindustan_unilever",
"lion dates": "brand_lion_dates",
"Manna": "brand_manna",
"milky mist": "brand_milky_mist",
"naga": "brand_naga",
"Nestle": "brand_nestle",
"p&g": "brand_p_g",
"pepsico": "brand_pepsico",
"tata": "brand_hindustan_unilever",
}
@pytest.mark.parametrize("alias,parent", sorted(BRAND_ALIASES.items()))
def test_every_alias_resolves_to_its_declared_parent(alias: str, parent: str) -> None:
assert resolve_parent_brand(alias) == parent
# Every parent brand resolves to itself except "sunfeast", which the alias
# "itc sunfeast" claims for ITC. That collapse predates the word-boundary
# change and is pinned here so it stays a deliberate quirk rather than drift.
_NON_SELF_PARENTS = {"sunfeast": "itc"}
@pytest.mark.parametrize("parent", sorted(set(BRAND_ALIASES.values())))
def test_parent_brands_resolve_to_their_pinned_answer(parent: str) -> None:
assert resolve_parent_brand(parent) == _NON_SELF_PARENTS.get(parent, parent)
def test_seed_catalogs_keep_their_current_tables() -> None:
"""The load-bearing assertion: no seed file changes destination table."""
actual = {
brand: f"brand_{_sanitize_name(resolve_parent_brand(brand))}"
for brand in _seed_brand_fields()
}
assert actual == EXPECTED_SEED_TABLES
def test_known_sub_brands_still_route_to_their_family() -> None:
"""Word-boundary matching must not break legitimate sub-brand routing."""
assert resolve_parent_brand("Dove") == "hindustan unilever"
assert resolve_parent_brand("Silk") == "cadbury"
assert resolve_parent_brand("Bingo") == "itc"
assert resolve_parent_brand("Sunfeast") == "itc"
def test_new_brand_is_not_swallowed_by_a_fragment_match() -> None:
"""
"sun" appears inside "hul sunsilk" but is not a word there, so a new brand
called Sun must stay Sun. Under the old substring fallback it silently
became "hindustan unilever" - the bug this guards.
"""
assert resolve_parent_brand("Sun") == "Sun"
assert resolve_parent_brand("Bing") == "Bing"
def test_unknown_brand_passes_through_unchanged() -> None:
assert resolve_parent_brand("Totally Made Up Brand") == "Totally Made Up Brand"
def test_new_brand_round_trips_to_its_own_table() -> None:
"""ITC is the worked example: it must own brand_itc, not merge elsewhere."""
assert resolve_parent_brand("ITC") == "itc"
assert _sanitize_name(resolve_parent_brand("ITC")) == "itc"