from __future__ import annotations import asyncio import pytest from conftest import FakeFetcher, fixture_bytes, fixture_text from companyatlas.services import discovery as D from companyatlas.taxonomy import OnboardingStatus, Surface BASE = "https://www.acme-cloud.example" COMPANY = {"id": "co_test", "slug": "ztest-acme", "display_name": "Acme Cloud", "canonical_domain": "acme-cloud.example", "website": BASE + "/", "tier": 2, "importance": 0.5} def _fake_site(ff: FakeFetcher) -> None: ff.add(BASE + "/", fixture_text("generic_html", "homepage.html")) ff.add(BASE + "/robots.txt", "User-agent: *\nAllow: /\nSitemap: https://www.acme-cloud.example/sitemap.xml\n", content_type="text/plain") ff.add(BASE + "/sitemap.xml", fixture_bytes("sitemap", "sitemap_index.xml"), content_type="application/xml") ff.add(BASE + "/sitemap-pages.xml", fixture_bytes("sitemap", "sitemap_pages.xml"), content_type="application/xml") ff.add(BASE + "/sitemap-blog.xml", "", content_type="application/xml") ff.add(BASE + "/careers", fixture_text("generic_html", "careers.html")) ff.add(BASE + "/pricing", fixture_text("generic_html", "pricing.html")) ff.add("https://boards-api.greenhouse.io/v1/boards/acmecloud/jobs?content=false", fixture_bytes("greenhouse", "stripe_jobs.json"), content_type="application/json") @pytest.fixture(autouse=True) def _no_dns(monkeypatch: pytest.MonkeyPatch) -> None: async def never(host: str) -> bool: return False monkeypatch.setattr(D, "_resolves", never) def test_discover_company_dry_run(fake_fetcher: FakeFetcher) -> None: _fake_site(fake_fetcher) res = asyncio.run(D.discover_company(COMPANY, fetcher=fake_fetcher, dry_run=True)) # type: ignore[arg-type] assert res.status == OnboardingStatus.ACTIVE, res.error surfaces = {s["surface"]: s for s in res.sensors} assert {"homepage", "pricing", "careers", "jobs_board", "newsroom", "leadership", "locations", "sitemap", "legal_terms", "legal_privacy", "feed"} <= set(surfaces) gh = surfaces["jobs_board"] assert gh["connector_id"] == "greenhouse-v1" and gh["config"]["token"] == "acmecloud" and gh["config"].get("verified_job_count") == 20 assert surfaces["careers"]["url"] == BASE + "/careers" and surfaces["careers"]["connector_id"] == "generic-html-v1" assert surfaces["sitemap"]["connector_id"] == "sitemap-v1" assert surfaces["feed"]["connector_id"] == "feed-v1" # tier 2 → ×0.75 on the surface base interval, clamped assert surfaces["pricing"]["base_interval_s"] == int(12 * 3600 * 0.75) assert surfaces["homepage"]["tier"] in ("C", "D") and 0 < surfaces["pricing"]["quality_score"] <= 100 assert all(s["status"] == "pending" for s in res.sensors) assert len(res.sensors) <= 40 and len({s["canonical_url"] for s in res.sensors}) == len(res.sensors) # bounded politeness: no more than ~25 requests for a whole company assert res.requests <= 25 assert all(c.startswith(("https://www.acme-cloud.example", "https://boards-api.greenhouse.io")) for c in fake_fetcher.calls) def test_discover_no_website(fake_fetcher: FakeFetcher) -> None: from companyatlas.fetch import FetchError from companyatlas.taxonomy import FailureClass for u in (BASE + "/", "https://www.acme-cloud.example/", "https://acme-cloud.example/", "http://www.acme-cloud.example/", "http://acme-cloud.example/"): fake_fetcher.add_error(u, FetchError("dns", url=u, failure=FailureClass.DNS)) res = asyncio.run(D.discover_company(COMPANY, fetcher=fake_fetcher, dry_run=True)) # type: ignore[arg-type] assert res.status == OnboardingStatus.NO_WEBSITE and not res.sensors def test_redirect_to_other_domain_is_recorded(fake_fetcher: FakeFetcher) -> None: fake_fetcher.add(BASE + "/", fixture_text("generic_html", "homepage.html"), final_url="https://www.acme-group.example/") fake_fetcher.add("https://www.acme-group.example/robots.txt", "", content_type="text/plain") res = asyncio.run(D.discover_company(COMPANY, fetcher=fake_fetcher, dry_run=True)) # type: ignore[arg-type] assert res.redirect_domain == "acme-group.example" and res.canonical_domain == "acme-group.example" assert any("redirects" in n for n in res.notes) def test_select_sensors_caps_and_prefers_best_candidate() -> None: from datetime import UTC, datetime cands = [D.Candidate(url=BASE + "/pricing", surface=str(Surface.PRICING), confidence=0.7, method="sitemap"), D.Candidate(url=BASE + "/plans", surface=str(Surface.PRICING), confidence=0.95, method="nav", verified=True), D.Candidate(url=BASE + "/x", surface=str(Surface.OTHER), confidence=0.3, method="nav")] rows = D.select_sensors(cands, company=COMPANY, canonical_domain="acme-cloud.example", now=datetime.now(UTC)) assert len(rows) == 1 and rows[0]["url"] == BASE + "/plans" and rows[0]["discovery_method"] == "nav"