"""Companies: list/filters/pagination, detail, sub-resources, compare, events feed and event detail.""" from __future__ import annotations import pytest import test_api_support as support client = support.client fixture_data = support.fixture_data pytestmark = pytest.mark.asyncio(loop_scope="session") V = "/api/v1" CARD_KEYS = {"id", "slug", "display_name", "legal_name", "canonical_domain", "website", "description", "industries", "industry_primary", "country", "hq_city", "hq_region", "public_company", "ticker", "exchange", "founded_year", "employees_band", "logo_url", "status", "onboarding_status", "importance", "tier", "metrics", "counts", "last_event_at", "last_observed_at"} async def test_list_filters_sort_pagination(client, fixture_data): # type: ignore[no-untyped-def] r = await client.get(f"{V}/companies", params={"country": "ZZ", "per_page": 1, "page": 1, "sort": "activity", "sparkline": 1}) body = r.json() assert r.status_code == 200 and set(body) == {"items", "page", "per_page", "total", "pages"} assert body["total"] == 2 and body["pages"] == 2 and len(body["items"]) == 1 card = body["items"][0] assert CARD_KEYS <= set(card) and card["slug"] == fixture_data["alpha_slug"] assert card["metrics"]["activity_score"] == 72.3 and card["metrics"]["open_jobs"] == 2 and card["metrics"]["hiring_momentum_30d"] == 12.5 assert card["counts"] == {"sensors": 2, "observations": 11, "changes": 1, "events": 3, "jobs_open": 2} assert len(card["sparkline"]) == 10 page2 = (await client.get(f"{V}/companies", params={"country": "ZZ", "per_page": 1, "page": 2})).json() assert page2["items"][0]["slug"] == fixture_data["beta_slug"] hiring = (await client.get(f"{V}/companies", params={"country": "ZZ", "sort": "hiring"})).json()["items"] assert [c["slug"] for c in hiring] == [fixture_data["alpha_slug"], fixture_data["beta_slug"]] name = (await client.get(f"{V}/companies", params={"industry": fixture_data["industry"], "sort": "name"})).json()["items"] assert [c["slug"] for c in name] == [fixture_data["alpha_slug"], fixture_data["beta_slug"]] q = (await client.get(f"{V}/companies", params={"q": f"ztest beta {fixture_data['suffix']}"})).json()["items"] assert q and q[0]["slug"] == fixture_data["beta_slug"] assert (await client.get(f"{V}/companies", params={"country": "ZZ", "public": "true"})).json()["total"] == 0 assert (await client.get(f"{V}/companies", params={"country": "ZZ", "has_events": "1", "tier": 2})).json()["total"] == 2 assert (await client.get(f"{V}/companies", params={"per_page": 999})).status_code == 422 async def test_detail_by_slug_and_id(client, fixture_data): # type: ignore[no-untyped-def] for key in (fixture_data["alpha_slug"], fixture_data["alpha"]): r = await client.get(f"{V}/companies/{key}") assert r.status_code == 200, key body = r.json() for k in ("aliases", "domains", "relationships", "metrics_detail", "sensors_by_surface", "coverage", "signals", "sparklines", "sparkline"): assert k in body, k assert body["aliases"] == [f"Alphaz {fixture_data['suffix']}"] assert body["relationships"][0]["kind"] == "PARTNER_OF" and body["relationships"][0]["company"]["slug"] == fixture_data["beta_slug"] assert body["sensors_by_surface"] == {"pricing": 1, "careers": 1} assert body["coverage"]["days_observed"] == 1 and body["coverage"]["sensor_uptime"] == 1.0 and body["coverage"]["historical_coverage"] is None assert len(body["sparklines"]["activity_30d"]) == 10 and body["sparklines"]["hiring_90d"] == [] assert body["signals"][0]["kind"] == "hiring_surge" md = {m["metric"]: m for m in body["metrics_detail"]} assert md["activity_score"]["value"] == 72.3 and md["activity_score"]["formula_version"] == "metrics-v1" and md["activity_score"]["inputs"] == {"events": 3} r = await client.get(f"{V}/companies/does-not-exist") assert r.status_code == 404 and r.json() == {"detail": "company not found"} async def test_events_timeline_metrics(client, fixture_data): # type: ignore[no-untyped-def] s = fixture_data["alpha_slug"] r = await client.get(f"{V}/companies/{s}/events") body = r.json() assert r.status_code == 200 and body["total"] == 3 and [e["event_type"] for e in body["items"]] == ["PRICING", "HIRING", "PRODUCT"] imp = (await client.get(f"{V}/companies/{s}/events?sort=importance")).json()["items"] assert imp[0]["event_type"] == "PRICING" assert (await client.get(f"{V}/companies/{s}/events?event_type=HIRING")).json()["total"] == 1 assert (await client.get(f"{V}/companies/{s}/events?surface=careers")).json()["total"] == 1 assert (await client.get(f"{V}/companies/{s}/events?min_importance=0.75")).json()["total"] == 1 tl = (await client.get(f"{V}/companies/{s}/timeline?filter=pricing")).json() assert [e["event_type"] for e in tl["items"]] == ["PRICING"] and tl["items"][0]["day"] == tl["days"][0]["day"] and tl["days"][0]["count"] == 1 tl_all = (await client.get(f"{V}/companies/{s}/timeline")).json() assert len(tl_all["items"]) == 3 and sum(d["count"] for d in tl_all["days"]) == 3 m = (await client.get(f"{V}/companies/{s}/metrics?days=30")).json() assert {c["metric"] for c in m["current"]} >= {"activity_score", "open_jobs"} and len(m["series"]["activity_score"]) == 10 assert set(m["series"]["activity_score"][0]) == {"day", "value", "confidence"} only = (await client.get(f"{V}/companies/{s}/metrics?metric=activity_score")).json() assert list(only["series"]) == ["activity_score"] async def test_jobs_people_products_pricing_locations_news(client, fixture_data): # type: ignore[no-untyped-def] s = fixture_data["alpha_slug"] r = await client.get(f"{V}/companies/{s}/jobs") body = r.json() assert r.status_code == 200 and body["total"] == 2 and body["meta"]["summary"]["open"] == 2 and body["meta"]["summary"]["ai_open"] == 1 assert body["meta"]["summary"]["remote_ratio"] == 0.5 and body["meta"]["summary"]["by_country"] == [{"country": "ZZ", "n": 2}] job = body["items"][0] assert {"id", "title", "department", "location_text", "city", "country", "remote", "employment_type", "seniority", "url", "posted_at", "first_seen_at", "last_seen_at", "removed_at", "status", "is_ai"} <= set(job) assert (await client.get(f"{V}/companies/{s}/jobs?status=removed")).json()["items"][0]["status"] == "no_longer_listed" assert (await client.get(f"{V}/companies/{s}/jobs?status=all")).json()["total"] == 3 assert (await client.get(f"{V}/companies/{s}/jobs?ai=1")).json()["items"][0]["title"] == "Senior ML Engineer" assert (await client.get(f"{V}/companies/{s}/jobs?q=account")).json()["total"] == 1 p = (await client.get(f"{V}/companies/{s}/people")).json() assert p["listed"][0]["name"] == "Jane Ztest" and p["no_longer_listed"][0]["name"] == "John Former" pr = (await client.get(f"{V}/companies/{s}/products")).json() assert pr["listed"][0]["name"] == "Ztest Widget" and pr["removed"] == [] plans = (await client.get(f"{V}/companies/{s}/pricing")).json() assert plans["current"][0]["price"] == 12.0 and plans["current"][0]["features"] == ["1 seat"] and plans["history"][0]["price"] == 10.0 loc = (await client.get(f"{V}/companies/{s}/locations")).json() assert loc["countries"] == ["ZZ"] and loc["items"][0]["kind"] == "headquarters" and loc["items"][0]["lat"] == 45.5 news = (await client.get(f"{V}/companies/{s}/news")).json() assert news["items"][0]["title"].startswith("Ztest Alpha announces") async def test_sensors_history_similar(client, fixture_data): # type: ignore[no-untyped-def] s = fixture_data["alpha_slug"] sensors = (await client.get(f"{V}/companies/{s}/sensors")).json()["items"] assert {x["surface"] for x in sensors} == {"pricing", "careers"} assert {"id", "company_id", "surface", "connector_id", "url", "canonical_url", "domain", "status", "tier", "quality_score", "discovery_confidence", "discovery_method", "current_interval_s", "next_run_at", "last_run_at", "last_success_at", "last_change_at", "last_status", "last_failure_class", "consecutive_failures", "observation_count", "snapshot_count", "change_count", "meaningful_change_count", "event_count", "created_at"} <= set(sensors[0]) hist = (await client.get(f"{V}/companies/{s}/history")).json()["sensors"] pricing = next(x for x in hist if x["surface"] == "pricing") assert [v["version_no"] for v in pricing["versions"]] == [3, 2, 1] assert (await client.get(f"{V}/companies/{s}/history?versions=1")).json()["sensors"][0]["versions"].__len__() <= 1 similar = (await client.get(f"{V}/companies/{s}/similar")).json()["items"] assert [c["slug"] for c in similar] == [fixture_data["beta_slug"]] async def test_compare(client, fixture_data): # type: ignore[no-untyped-def] a, b = fixture_data["alpha_slug"], fixture_data["beta_slug"] r = await client.get(f"{V}/companies/compare", params={"companies": f"{a},{b}"}) body = r.json() assert r.status_code == 200 assert [c["slug"] for c in body["companies"]] == [a, b] assert body["metrics"]["activity_score"] == {a: 72.3, b: 30.0} assert len(body["series"][a]) == 10 and body["series"][b] == [] assert body["events_30d"][a] == {"PRICING": 1, "HIRING": 1, "PRODUCT": 1} and body["events_30d"][b] == {"LEADERSHIP": 1} assert body["jobs"][a] == {"open": 2, "ai_open": 1, "new_30d": 3} and body["jobs"][b] == {"open": 0, "ai_open": 0, "new_30d": 0} assert body["locations"] == {a: 1, b: 0} assert (await client.get(f"{V}/companies/compare", params={"companies": a})).status_code == 422 assert (await client.get(f"{V}/companies/compare", params={"companies": f"{a},nope"})).status_code == 404 async def test_events_feed_types_summary_detail(client, fixture_data): # type: ignore[no-untyped-def] r = await client.get(f"{V}/events", params={"country": "ZZ"}) body = r.json() assert r.status_code == 200 and body["total"] == 4 assert (await client.get(f"{V}/events", params={"country": "ZZ", "status": "retracted"})).json()["total"] == 1 assert (await client.get(f"{V}/events", params={"industry": fixture_data["industry"], "event_type": "LEADERSHIP"})).json()["items"][0]["company"]["slug"] == fixture_data["beta_slug"] assert (await client.get(f"{V}/events", params={"company": fixture_data["alpha_slug"], "min_confidence": 0.9})).json()["total"] == 1 assert (await client.get(f"{V}/events", params={"q": "executive listed"})).json()["items"][0]["id"] == fixture_data["ev_leader"] assert (await client.get(f"{V}/events", params={"country": "ZZ", "origin": "llm"})).json()["total"] == 0 assert (await client.get(f"{V}/events", params={"company": "nope"})).status_code == 404 types = (await client.get(f"{V}/events/types")).json()["types"] pricing = next(t for t in types if t["event_type"] == "PRICING") assert pricing["count_30d"] >= 1 and any(s["event_subtype"] == "PRICE_INCREASE" and s["count_30d"] >= 1 for s in pricing["subtypes"]) summ = (await client.get(f"{V}/events/summary?days=7&group=country")).json()["items"] zz = next(i for i in summ if i["key"] == "ZZ") assert zz["count"] == 4 and "delta_pct" in zz assert (await client.get(f"{V}/events/summary?group=nope")).status_code == 422 r = await client.get(f"{V}/events/{fixture_data['ev_pricing']}") ev = r.json() assert r.status_code == 200 and ev["sources"][0]["source_url"].endswith("/pricing") and ev["change"]["id"] == fixture_data["change"] assert ev["company"]["slug"] == fixture_data["alpha_slug"] and CARD_KEYS <= set(ev["company"]) assert (await client.get(f"{V}/events/evt_nope")).status_code == 404