"""Tests for provider-group folding (display-only picker grouping). These are invariant tests, not catalog snapshots: they assert how ``group_providers`` folds a flat slug list and how member slugs relate to ``PROVIDER_GROUPS`` / ``CANONICAL_PROVIDERS`` — not the specific set of vendors, which is expected to change over time. """ from hermes_cli.models import ( CANONICAL_PROVIDERS, PROVIDER_GROUPS, group_providers, provider_group_for_slug, ) def _slugs(rows): """Flatten picker rows back to the concrete slugs they expose.""" out = [] for r in rows: if r["kind"] == "single": out.append(r["slug"]) else: out.extend(r["members"]) return out def test_groups_reference_real_canonical_slugs(): """Every group member must be an actual provider slug. Guards typos and stale group entries after a provider is renamed/removed.""" canonical = {p.slug for p in CANONICAL_PROVIDERS} for gid, (label, desc, members) in PROVIDER_GROUPS.items(): assert label, f"group {gid} has empty label" assert desc, f"group {gid} has empty description" assert len(members) >= 1 for m in members: assert m in canonical, f"group {gid} member {m!r} is not a canonical slug" def test_member_slugs_are_unique_across_groups(): """A slug may belong to at most one group.""" seen = {} for gid, (_label, _desc, members) in PROVIDER_GROUPS.items(): for m in members: assert m not in seen, f"{m!r} in both {seen[m]!r} and {gid!r}" seen[m] = gid def test_reverse_index_matches_groups(): for gid, (_label, _desc, members) in PROVIDER_GROUPS.items(): for m in members: assert provider_group_for_slug(m) == gid assert provider_group_for_slug("openrouter") == "" assert provider_group_for_slug("") == "" def test_ungrouped_providers_pass_through_in_order(): rows = group_providers(["nous", "openrouter", "deepseek"]) assert all(r["kind"] == "single" for r in rows) assert [r["slug"] for r in rows] == ["nous", "openrouter", "deepseek"] def test_multi_member_group_folds_to_one_row(): rows = group_providers(["minimax", "minimax-oauth", "minimax-cn"]) assert len(rows) == 1 row = rows[0] assert row["kind"] == "group" assert row["group_id"] == "minimax" assert row["members"] == ["minimax", "minimax-oauth", "minimax-cn"] # group rows carry the short top-level description from PROVIDER_GROUPS assert row["description"] == PROVIDER_GROUPS["minimax"][1] assert row["description"] def test_group_appears_at_first_member_position(): """The group row takes the slot of its earliest-listed present member, and later members do not re-emit.""" rows = group_providers(["nous", "minimax", "deepseek", "minimax-cn"]) kinds = [(r["kind"], r.get("group_id") or r.get("slug")) for r in rows] assert kinds == [ ("single", "nous"), ("group", "minimax"), ("single", "deepseek"), ] # both minimax members folded into the single group row assert rows[1]["members"] == ["minimax", "minimax-cn"] def test_single_present_member_degrades_to_single_row(): """A group with only one present member shows no submenu.""" rows = group_providers(["xai"]) # xai-oauth absent assert len(rows) == 1 assert rows[0]["kind"] == "single" assert rows[0]["slug"] == "xai" def test_member_order_follows_declaration_not_input(): """Inside a folded group, members are ordered by PROVIDER_GROUPS, not by the order they appeared in the input list.""" rows = group_providers(["minimax-cn", "minimax", "minimax-oauth"]) assert rows[0]["members"] == ["minimax", "minimax-oauth", "minimax-cn"] def test_duplicate_slugs_ignored(): rows = group_providers(["nous", "nous", "minimax", "minimax"]) assert [r.get("slug") or r["group_id"] for r in rows] == ["nous", "minimax"] def test_fold_is_lossless_for_present_slugs(): """Every input slug (deduped) must still be reachable through the folded rows — grouping hides nothing.""" flat = [p.slug for p in CANONICAL_PROVIDERS] rows = group_providers(flat) assert set(_slugs(rows)) == set(flat) def test_canonical_fold_row_count_shrinks(): """Folding the full canonical list produces fewer top-level rows than the flat list (proves grouping actually consolidates).""" flat = [p.slug for p in CANONICAL_PROVIDERS] rows = group_providers(flat) assert len(rows) < len(flat)