HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1"""API 1.1 contract tests against the live local database (docs/API.md). Read-only. Counts are never asserted as fixed numbers."""2from __future__ import annotations34from collections.abc import AsyncIterator56import pytest7from httpx import ASGITransport, AsyncClient89from aiatlas import db10from aiatlas.api.main import app11from aiatlas.config import settings12from aiatlas.services import cache1314ADMIN = {"x-aia-admin-token": settings.admin_token or "dev-admin-token"}15MODEL, MODEL_B, BENCH = "claude-opus-5", "claude-sonnet-5", "gpqa"161718@pytest.fixture19async def client() -> AsyncIterator[AsyncClient]:20 await cache.cache_invalidate()21 async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test", timeout=120) as c:22 yield c23 await cache.close()24 await db.dispose()252627async def test_models_canonical_universe_and_artifacts(client: AsyncClient) -> None:28 r = await client.get("/api/v1/models", params={"limit": 200})29 assert r.status_code == 20030 body = r.json()31 assert body["universe"] == "canonical models" and body["items"]32 assert all(it["entity_type"] == "model" for it in body["items"])33 assert all("identity_confidence" in it for it in body["items"])34 r2 = await client.get("/api/v1/models", params={"limit": 200, "include": "artifacts"})35 assert r2.status_code == 200 and r2.json()["universe"] == "models+artifacts" and r2.json()["total"] >= body["total"]36 for it in r2.json()["items"]:37 if it["entity_type"] == "artifact":38 assert "canonical" in it and "artifact_kind" in it39 facets = (await client.get("/api/v1/models", params={"facets": 1, "limit": 1})).json()["facets"]40 assert {"families", "licenses", "trust", "definitions"} <= set(facets)41 assert all({"value", "label", "category", "count"} <= set(x) for x in facets["licenses"])42 lic = await client.get("/api/v1/models", params={"license": "Apache 2.0", "limit": 5}) # raw label → canonical key43 assert lic.status_code == 20044 assert (await client.get("/api/v1/models", params={"family": "Claude", "limit": 2})).status_code == 200454647async def test_model_detail_v11_blocks(client: AsyncClient) -> None:48 d = (await client.get(f"/api/v1/models/{MODEL}")).json()49 for block in ("family", "artifacts", "deployments", "identity", "openness", "version_history", "benchmarks"):50 assert block in d, block51 assert {"items", "total"} <= set(d["artifacts"]) and {"canonical_model", "official_checkpoints", "third_party_artifacts", "provider_deployments", "api_aliases"} <= set(d["identity"])52 assert d["openness"]["category"] in ("open-source", "open-weights", "restricted-weights", "proprietary", "unknown") and "dimensions" in d["openness"]53 assert all({"property", "transitions"} <= set(v) for v in d["version_history"])54 if d["deployments"]:55 dep = d["deployments"][0]56 assert {"model", "provider", "prices", "features", "status", "valid_from"} <= set(dep) and {"input", "output", "native_units"} <= set(dep["prices"])57 assert {"items", "total_rows", "note"} <= set(d["benchmarks"])58 for b in d["benchmarks"]["items"]:59 for m in b["metrics"]:60 for g in m["groups"]:61 assert {"config_key", "comparability_group", "n_rows", "best"} <= set(g) and "trust_level" in g["best"]626364async def test_leaderboard_one_row_per_model(client: AsyncClient) -> None:65 r = await client.get(f"/api/v1/benchmarks/{BENCH}/leaderboard", params={"limit": 1000})66 assert r.status_code == 20067 body = r.json()68 assert body["group"] and {"metric", "config_key", "label", "n", "model_count"} <= set(body["group"]) and body["groups"]69 ids = [it["model"]["id"] for it in body["items"]]70 assert ids and len(ids) == len(set(ids)), "one row per model"71 ranks = [it["rank"] for it in body["items"]]72 assert ranks[0] == 1 and ranks == sorted(ranks)73 first = body["items"][0]74 assert {"rank", "model", "score", "trust_level", "config", "config_key", "comparability", "comparability_reasons", "delta_rank", "n_rows"} <= set(first)75 assert first["comparability"] in ("comparable", "partially-comparable", "not-comparable")76 only = (await client.get(f"/api/v1/benchmarks/{BENCH}/leaderboard", params={"comparable_only": 1, "limit": 50})).json()77 assert all(it["comparability"] == "comparable" for it in only["items"])78 alias = await client.get("/api/v1/benchmarks/GPQA Diamond/leaderboard", params={"limit": 3}) # alias resolution79 assert alias.status_code == 200 and alias.json()["benchmark"]["slug"] == BENCH80 listing = (await client.get("/api/v1/benchmarks")).json()81 b = next(x for x in listing["items"] if x["slug"] == BENCH)82 assert {"family", "variant", "metric", "direction", "result_count", "model_count", "leader", "groups", "trust_mix", "category"} <= set(b)83 res = (await client.get(f"/api/v1/benchmarks/{BENCH}/results", params={"limit": 3, "metric": "accuracy"})).json()84 assert res["items"] and all(it["metric"].lower() == "accuracy" for it in res["items"])85 fr = (await client.get(f"/api/v1/benchmarks/{BENCH}/frontier")).json()86 assert "series" in fr and all({"group", "points", "current_leader"} <= set(s) for s in fr["series"])878889async def test_matrix_shape(client: AsyncClient) -> None:90 body = (await client.get("/api/v1/benchmarks/matrix", params={"limit": 5})).json()91 assert {"columns", "rows", "total_rows", "methodology", "min_cells"} <= set(body)92 assert 1 <= len(body["columns"]) <= 12 and all({"id", "slug", "metric", "config_key", "group_label"} <= set(c) for c in body["columns"])93 col_ids = [c["id"] for c in body["columns"]]94 for row in body["rows"]:95 assert set(row["cells"]) == set(col_ids) and row["n_cells"] >= 3 and {"model", "mean_rank"} <= set(row)96 for cell in row["cells"].values():97 if cell:98 assert {"score", "rank", "trust_level", "config_key", "comparability"} <= set(cell)99 sub = (await client.get("/api/v1/benchmarks/matrix", params={"benchmarks": f"{BENCH},swe-bench-verified", "org": "anthropic"})).json()100 assert [c["slug"] for c in sub["columns"]] == [BENCH, "swe-bench-verified"]101102103async def test_changes_default_excludes_backfill(client: AsyncClient) -> None:104 body = (await client.get("/api/v1/changes", params={"limit": 20})).json()105 assert body["date_field"] == "occurred" and body["include_backfill"] is False106 assert all(e["is_backfill"] is False and "occurred_at" in e for e in body["items"])107 if body["items"] and body["next_before"]:108 assert body["next_before"] == body["items"][-1]["occurred_at"]109 obs = (await client.get("/api/v1/changes", params={"limit": 3, "include_backfill": 1, "date_field": "observed"})).json()110 assert obs["date_field"] == "observed" and obs["total"] >= body["total"] or obs["total"] == 10000111 daily = (await client.get("/api/v1/changes/daily")).json()112 assert {"date", "counts", "sections", "new_models", "today", "backfill_excluded"} <= set(daily)113 for s in daily["today"]:114 assert {"key", "label", "items", "total"} <= set(s) and all({"sources", "documents", "grouped_events"} <= set(i) for i in s["items"])115 tl = (await client.get(f"/api/v1/entities/{MODEL}/timeline", params={"limit": 3})).json()116 assert tl["date_field"] == "occurred" and all(e["is_backfill"] is False for e in tl["items"])117 gt = (await client.get("/api/v1/timeline", params={"limit": 5})).json()118 assert gt["include_backfill"] is False119120121async def test_stats_definitions(client: AsyncClient) -> None:122 s = (await client.get("/api/v1/stats")).json()123 assert {"organizations_total", "artifacts", "model_families", "definitions", "change_events_live_24h", "change_events_24h"} <= set(s)124 assert s["organizations_total"] == sum(s["entities"].get(t, 0) for t in ("company", "organization", "lab", "university"))125 companies = (await client.get("/api/v1/companies", params={"limit": 1})).json()126 assert companies["total"] == s["organizations_total"]127 assert "models" in s["definitions"] and "artifact" in s["entities"] and "model_family" in s["entities"]128129130async def test_frontier_shape(client: AsyncClient) -> None:131 body = (await client.get("/api/v1/frontier")).json()132 for key in ("latest_major_models", "benchmark_frontier", "price_frontier", "context_frontier", "open_weight_frontier", "efficiency_frontier", "agentic_frontier",133 "multimodal_frontier", "recent_frontier_movements", "methodology"):134 assert key in body, key135 for b in body["benchmark_frontier"]:136 assert {"benchmark", "group", "leader", "second", "gap"} <= set(b) and b["group"]["n"] >= 20137 assert {"cheapest_output", "cheapest_output_1m_context", "frontier_models", "composition"} <= set(body["price_frontier"])138 assert {"points", "frontier"} <= set(body["efficiency_frontier"])139 assert "dimensions" in body["open_weight_frontier"] and "score" not in str(body["open_weight_frontier"]["dimensions"])140141142async def test_pareto_frontier_ids_are_efficient(client: AsyncClient) -> None:143 body = (await client.get("/api/v1/pareto", params={"benchmark": BENCH})).json()144 assert {"group", "points", "frontier", "methodology", "x", "y"} <= set(body)145 pts = {p["id"]: p for p in body["points"]}146 front = set(body["frontier"])147 assert front <= set(pts)148 hib = body["group"]["higher_is_better"]149 for fid in front: # no other point is at least as good on both axes and strictly better on one150 p = pts[fid]151 for o in pts.values():152 if o["id"] == fid:153 continue154 better_y = o["y"] > p["y"] if hib else o["y"] < p["y"]155 ge_y = o["y"] >= p["y"] if hib else o["y"] <= p["y"]156 assert not (o["x"] <= p["x"] and ge_y and (o["x"] < p["x"] or better_y)), (fid, o["id"])157 assert all(p["pareto"] == (p["id"] in front) for p in body["points"])158 assert (await client.get("/api/v1/pareto", params={"benchmark": BENCH, "x": "latency"})).status_code == 400159160161async def test_cost_routes(client: AsyncClient) -> None:162 body = (await client.get("/api/v1/cost", params={"model": MODEL, "input_tokens": 1000, "output_tokens": 500, "requests_per_day": 100, "cached_share": 0.5})).json()163 assert {"items", "methodology", "inputs"} <= set(body)164 for it in body["items"]:165 c, p = it["cost"], it["deployment"]["prices"]166 if c["per_request"] is not None and p["input"] is not None and p["output"] is not None:167 assert c["daily"] == pytest.approx(c["per_request"] * 100) and c["monthly"] == pytest.approx(c["daily"] * 30)168 ctx = (await client.get("/api/v1/cost/context", params={"tokens": 100000, "limit": 5})).json()169 assert ctx["items"] and all(it["context_length"] >= 100000 and it["cost_usd"] == pytest.approx(it["deployment"]["prices"]["input"] * 0.1) for it in ctx["items"])170 deps = (await client.get("/api/v1/deployments", params={"model": MODEL})).json()171 assert deps["items"] and all(d["status"] == "active" for d in deps["items"])172173174async def test_find_a_model_returns_why(client: AsyncClient) -> None:175 body = (await client.get("/api/v1/find-a-model", params={"use_case": "coding", "limit": 5})).json()176 assert {"matches", "filters_applied", "note", "rules"} <= set(body) and body["matches"]177 for m in body["matches"]:178 assert m["why"] and {"model", "observed"} <= set(m) and "best_rank" in m["observed"]179 assert "score" not in m # no composite winner score180 local = (await client.get("/api/v1/find-a-model", params={"use_case": "local", "memory_gb": 64, "limit": 3})).json()181 assert all("estimated_fit" in m and m["estimated_fit"]["estimated"] is True for m in local["matches"])182 assert (await client.get("/api/v1/find-a-model", params={"use_case": "nope"})).status_code == 400183184185async def test_open_run_locally_families_graph(client: AsyncClient) -> None:186 op = (await client.get("/api/v1/open", params={"limit": 3})).json()187 assert {"items", "summary", "note"} <= set(op) and all({"licence", "dimensions", "best_results", "hardware_fit", "providers"} <= set(i) for i in op["items"])188 rl = (await client.get("/api/v1/run-locally", params={"memory_gb": 64, "limit": 3})).json()189 assert rl["estimated"] is True and all(i["fit"]["fits"] and "breakdown" in i["fit"] for i in rl["items"])190 fams = (await client.get("/api/v1/families", params={"limit": 3})).json()191 assert fams["items"] and all({"name", "model_count", "canonical", "licenses", "benchmark_best"} <= set(f) for f in fams["items"])192 fd = await client.get(f"/api/v1/families/{fams['items'][0]['slug']}")193 assert fd.status_code == 200 and {"members", "timeline", "lineage", "artifacts_count"} <= set(fd.json())194 g = (await client.get("/api/v1/graph/explore", params={"node": MODEL, "mode": "company", "depth": 2, "limit": 20})).json()195 assert {"nodes", "edges", "truncated", "counts"} <= set(g) and len(g["nodes"]) <= 20196 assert (await client.get("/api/v1/graph/explore", params={"node": MODEL, "mode": "nope"})).status_code == 400197198199async def test_search_compiler_v2_response(client: AsyncClient) -> None:200 body = (await client.get("/api/v1/search", params={"q": "Anthropic models released since 2025"})).json()201 q = body["query"]202 assert q["version"] == 2 and {"compiled", "residual", "unrecognised"} <= set(q)203 assert q["organization"] == "Anthropic" and q["year_from"] == 2025204 assert any(c["filter"] == "organization" and isinstance(c["value"], dict) for c in q["compiled"])205 assert body["items"] and all(it["organization"]["slug"] == "anthropic" for it in body["items"])206 unknown = (await client.get("/api/v1/search", params={"q": "Foobarbaz models released since 2025"})).json()["query"]207 assert "organization" not in unknown and "Foobarbaz" in unknown["unrecognised"]208209210async def test_frontier_price_join_and_cache_namespace(client: AsyncClient) -> None:211 """Regression: frontier ids and prices come from the SAME database — cheapest_frontier must exist whenever frontier offers exist."""212 from aiatlas.services import cache213214 assert cache.api_key("x").startswith(f"aia:api:{cache.namespace()}:") and len(cache.namespace()) == 8215 idx = (await client.get("/api/v1/prices/index", params={"days": 7})).json()216 offers_today = idx["series"][-1]["sample"]["frontier_offers"]217 if offers_today > 0:218 assert idx["cheapest_frontier"] is not None and idx["cheapest_frontier"]["output"] > 0219 cheapest = (await client.get("/api/v1/prices", params={"sort": "cheapest_frontier", "limit": 5})).json()220 assert cheapest["items"] and cheapest["items"][0]["output_per_mtok"] > 0221 fr = (await client.get("/api/v1/frontier")).json()222 assert fr["price_frontier"]["cheapest_output"] is not None and fr["price_frontier"]["frontier_models"] > 0223 for key in ("new_listings_30d", "delistings_30d", "price_changes_30d"):224 assert {"count", "items", "definition"} <= set(idx[key]) and len(idx[key]["items"]) <= 50225226227async def test_pulse_items_and_price_delta(client: AsyncClient) -> None:228 from aiatlas.api.routers.intelligence import price_delta229230 d = price_delta({"input_per_mtok": 1.0, "output_per_mtok": 4.0}, {"input_per_mtok": 0.5, "output_per_mtok": 5.0})231 assert d["output_percent"] == 25.0 and d["input_percent"] == -50.0 and d["percent"] == 25.0232 assert price_delta(None, {"output_per_mtok": 1}) is None and price_delta(2.0, 3.0)["percent"] == 50.0233 body = (await client.get("/api/v1/pulse", params={"days": 90})).json()234 pc = body["counters"]["price_changes"]235 assert "items" in pc and "median_percent" in pc and isinstance(body["counters"]["new_models_1m_context"]["items"], list)236 if pc["value"]:237 assert pc["items"] and {"summary", "percent_change", "provider", "occurred_at"} <= set(pc["items"][0])238239240async def test_provider_detail_deployments_and_listing_status(client: AsyncClient) -> None:241 providers = (await client.get("/api/v1/providers")).json()["items"]242 slug = providers[0]["slug"]243 d = (await client.get(f"/api/v1/providers/{slug}")).json()244 assert "deployments" in d and "removed" in d245 outs = [x["prices"]["output"] for x in d["deployments"] if x["prices"]["output"] is not None]246 assert outs == sorted(outs) and all(x["status"] == "active" for x in d["deployments"]) and all(x["status"] == "delisted" for x in d["removed"])247 gone = (await client.get("/api/v1/deployments", params={"current": 0, "status": "delisted", "limit": 5})).json()248 assert gone["status"] == "delisted" and all(x["status"] == "delisted" for x in gone["items"])249 both = (await client.get("/api/v1/deployments", params={"current": 0, "limit": 200})).json()250 assert both["status"] == "all" and both["total"] >= gone["total"]251252253async def test_models_best_price_and_family_scores_and_matrix_cells(client: AsyncClient) -> None:254 rows = (await client.get("/api/v1/models", params={"limit": 50, "sort": "cheapest"})).json()["items"]255 priced = [r for r in rows if r.get("best_price")]256 assert priced and {"input_per_mtok", "output_per_mtok", "provider", "providers"} <= set(priced[0]["best_price"]) and priced[0]["best_price"]["provider"]["slug"]257 fams = (await client.get("/api/v1/families", params={"limit": 3})).json()["items"]258 for b in fams[0]["benchmark_best"].values():259 assert {"rank", "score", "metric", "model"} <= set(b)260 fd = (await client.get(f"/api/v1/families/{fams[0]['slug']}")).json()261 assert all("benchmark_best" in m and all({"benchmark", "rank", "score"} <= set(x) for x in m["benchmark_best"]) for m in fd["members"])262 mx = (await client.get("/api/v1/benchmarks/matrix", params={"limit": 3, "since": "2024-01-01"})).json()263 assert mx["filters"]["since"] == "2024-01-01"264 for row in mx["rows"]:265 assert row["model"]["release_date"] and row["model"]["release_date"][:10] >= "2024-01-01"266 assert all("observed_at" in c for c in row["cells"].values() if c)267 pts = (await client.get("/api/v1/pareto", params={"benchmark": BENCH})).json()["points"]268 assert pts and {"context_length", "parameter_count"} <= set(pts[0])269 fw = (await client.get("/api/v1/explore/framework", params={"limit": 20})).json()270 assert all("kind" in it for it in fw["items"])271 op = (await client.get("/api/v1/open", params={"limit": 1})).json()272 assert op["summary"]["new_30d"] <= op["total"] and "new_30d_definition" in op["summary"]273274275async def test_etag_304(client: AsyncClient) -> None:276 r1 = await client.get("/api/v1/stats")277 etag = r1.headers.get("etag")278 assert etag and etag.startswith('W/"') and "max-age" in r1.headers.get("cache-control", "")279 r2 = await client.get("/api/v1/stats", headers={"if-none-match": etag})280 assert r2.status_code == 304 and r2.content == b"" and r2.headers.get("etag") == etag281 admin = await client.get("/api/v1/admin/overview", headers=ADMIN)282 assert "etag" not in admin.headers # admin responses are never cacheable283284285async def test_admin_rate_limit_before_auth(client: AsyncClient) -> None:286 from aiatlas.api.common import _hits287288 _hits.clear()289 codes = [(await client.get("/api/v1/admin/overview", headers={"x-aia-admin-token": "wrong"})).status_code for _ in range(11)]290 assert codes[:10] == [401] * 10 and codes[10] == 429, codes # 10 failed auths per minute per IP, then 429 before any token check291 _hits.clear()292 assert (await client.get("/api/v1/admin/overview", headers=ADMIN)).status_code == 200293294295async def test_admin_workbenches_and_audit(client: AsyncClient) -> None:296 from aiatlas.api.common import _hits297298 _hits.clear()299 q = (await client.get("/api/v1/admin/quality", headers=ADMIN)).json()300 for key in ("duplicate_candidates", "taxonomy_violations", "impossible_values", "conflicting_t1_claims", "models_without_organization", "orphan_benchmark_results",301 "unresolved_provider_deployments", "quantisations_typed_as_models", "stale_sources", "empty_public_categories", "quarantined_runs_pending", "review_queue_priority"):302 assert key in q, key303 assert {"count", "sample"} <= set(q["models_without_organization"])304 er = (await client.get("/api/v1/admin/entity-resolution", params={"limit": 3}, headers=ADMIN)).json()305 assert {"items", "decisions"} <= set(er) and all({"a", "b", "signals", "hint"} <= set(i) for i in er["items"])306 for path in ("/api/v1/admin/anomalies", "/api/v1/admin/quarantine", "/api/v1/admin/audit?limit=5"):307 assert (await client.get(path, headers=ADMIN)).status_code == 200, path308 audit = (await client.get("/api/v1/admin/audit?limit=5", headers=ADMIN)).json()309 assert audit["items"] and audit["items"][0]["actor"] == "admin" and audit["items"][0]["action"].startswith("GET /api/v1/admin/")310 assert (await client.post("/api/v1/admin/quarantine/nope", headers=ADMIN, json={"action": "release"})).status_code in (404, 501)311 assert (await client.post("/api/v1/admin/anomalies/nope", headers=ADMIN, json={"status": "resolved"})).status_code == 404312313314async def test_claims_provenance_licenses_misc(client: AsyncClient) -> None:315 cl = (await client.get(f"/api/v1/entities/{MODEL}/claims", params={"limit": 2})).json()316 assert cl["items"] and cl["status"] == "current"317 detail = (await client.get(f"/api/v1/claims/{cl['items'][0]['id']}")).json()318 assert {"claim", "entity", "chain", "source", "extractor", "evidence"} <= set(detail) and {"snapshot_id", "document_url", "archived"} <= set(detail["evidence"])319 prov = (await client.get(f"/api/v1/entities/{MODEL}/provenance/context_length")).json()320 assert {"value", "source", "tier", "observed_at", "claim_id", "conflicts", "history_count", "snapshot_id"} <= set(prov)321 assert (await client.get(f"/api/v1/entities/{MODEL}/provenance/not_a_property")).status_code == 404322 lic = (await client.get("/api/v1/licenses")).json()323 assert lic["items"] and all({"key", "commercial_use", "models"} <= set(i) for i in lic["items"])324 assert (await client.get("/api/v1/licenses/apache-2.0")).json()["key"] == "Apache-2.0"325 meth = (await client.get("/api/v1/methodology")).json()326 assert {"openness", "trust_levels", "comparability", "counters", "anomaly_checks", "event_semantics"} <= set(meth)327 tr = (await client.get("/api/v1/trending", params={"kind": "most_changed"})).json()328 assert tr["kind"] == "most_changed" and "definition" in tr329 cmp_ = (await client.get("/api/v1/compare", params={"ids": f"{MODEL},{MODEL_B}", "diff_only": 1, "mode": "models"})).json()330 assert cmp_["diff_only"] is True and "comparability" in cmp_331 for d in cmp_["dimensions"]:332 vals = [repr(sorted(map(str, it["values"][d["key"]])) if isinstance(it["values"][d["key"]], list) else it["values"][d["key"]]) for it in cmp_["items"]]333 assert len(set(vals)) > 1, d["key"]334 diff = (await client.get(f"/api/v1/models/{MODEL}/diff/{MODEL_B}")).json()335 assert all("delta" in d for d in diff["dimensions"])336 assert (await client.get("/api/v1/compare", params={"ids": f"{MODEL},{MODEL_B}", "mode": "providers"})).status_code == 400337 df = (await client.get("/api/v1/diff", params={"a": "2026-01-01", "b": "2026-12-31", "limit": 5})).json()338 assert {"new_benchmark_leaders", "provider_changes", "hardware_changes", "context_changes", "retired_models"} <= set(df) and df["include_artifacts"] is False339 tm = (await client.get("/api/v1/time-machine", params={"date": "2025-06-01", "scope": "models", "limit": 3})).json()340 assert {"reconstructed", "first_entity_at", "note", "models"} <= set(tm) and all("attributes_as_of" in m for m in tm["models"]["items"])341 pulse = (await client.get("/api/v1/pulse", params={"days": 7})).json()342 assert all({"value", "definition"} <= set(v) for v in pulse["counters"].values())343 idx = (await client.get("/api/v1/prices/index", params={"days": 14})).json()344 assert {"series", "cheapest_frontier", "distribution", "new_listings_30d", "delistings_30d", "methodology"} <= set(idx)345 assert all({"median_frontier_output", "median_open_output", "median_embedding_input", "sample"} <= set(s) for s in idx["series"])346 prov_list = (await client.get("/api/v1/providers")).json()["items"]347 assert all({"input_price_distribution", "output_price_distribution", "models_added_30d", "models_removed_30d", "price_changes_30d", "organizations_covered", "features_supported"} <= set(p) for p in prov_list)348 hw = (await client.get("/api/v1/hardware/apple-m3-ultra/fit", params={"limit": 3})).json()349 assert hw["estimated"] is True and hw["memory_options_gb"]350