Initial commit: CI Agent competitive-intelligence monitoring app
FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
"""app.tasks.enrichment.enrich_company: the Celery task end-to-end
|
||||
(mocked HTTP, real DB) - confirms the CompanyEnrichment row persists with
|
||||
the expected status after the task runs, and that a missing company is a
|
||||
clean no-op rather than a crash."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
import respx
|
||||
|
||||
from app.core.config import Settings
|
||||
from app.models.company import Company
|
||||
from app.repositories.company_enrichment_repository import CompanyEnrichmentRepository
|
||||
from app.tasks.enrichment import _enrich_company_async
|
||||
|
||||
|
||||
async def _make_company(db_session) -> Company:
|
||||
company = Company(
|
||||
id=uuid.uuid4(),
|
||||
user_id=uuid.uuid4(),
|
||||
name="Acme Corp",
|
||||
slug=f"acme-{uuid.uuid4().hex[:6]}",
|
||||
official_website="https://acme.example.com",
|
||||
)
|
||||
db_session.add(company)
|
||||
await db_session.commit()
|
||||
return company
|
||||
|
||||
|
||||
def _mock_empty_endpoints() -> None:
|
||||
for path in (
|
||||
"company/details",
|
||||
"company/funding",
|
||||
"company/updates",
|
||||
"competitor/listing",
|
||||
"product/listing",
|
||||
"customer/listing",
|
||||
):
|
||||
respx.get(f"https://nubela.co/api/v1/{path}").mock(
|
||||
return_value=httpx.Response(200, json={})
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enrich_company_task_persists_a_complete_result(db_session, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"app.tasks.enrichment.get_settings", lambda: Settings(ninjapear_api_key="test-key")
|
||||
)
|
||||
company = await _make_company(db_session)
|
||||
|
||||
with respx.mock:
|
||||
_mock_empty_endpoints()
|
||||
await _enrich_company_async(str(company.id))
|
||||
|
||||
enrichment = await CompanyEnrichmentRepository(db_session).get_for_company(company.id)
|
||||
assert enrichment is not None
|
||||
assert enrichment.status.value == "complete"
|
||||
assert enrichment.fetched_at is not None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enrich_company_task_records_partial_status_on_a_failed_section(
|
||||
db_session, monkeypatch
|
||||
):
|
||||
monkeypatch.setattr(
|
||||
"app.tasks.enrichment.get_settings", lambda: Settings(ninjapear_api_key="test-key")
|
||||
)
|
||||
company = await _make_company(db_session)
|
||||
|
||||
with respx.mock:
|
||||
respx.get("https://nubela.co/api/v1/company/details").mock(
|
||||
return_value=httpx.Response(200, json={})
|
||||
)
|
||||
respx.get("https://nubela.co/api/v1/company/funding").mock(
|
||||
return_value=httpx.Response(500, text="boom")
|
||||
)
|
||||
respx.get("https://nubela.co/api/v1/company/updates").mock(
|
||||
return_value=httpx.Response(200, json={})
|
||||
)
|
||||
respx.get("https://nubela.co/api/v1/competitor/listing").mock(
|
||||
return_value=httpx.Response(200, json={})
|
||||
)
|
||||
respx.get("https://nubela.co/api/v1/product/listing").mock(
|
||||
return_value=httpx.Response(200, json={})
|
||||
)
|
||||
respx.get("https://nubela.co/api/v1/customer/listing").mock(
|
||||
return_value=httpx.Response(200, json={})
|
||||
)
|
||||
await _enrich_company_async(str(company.id))
|
||||
|
||||
enrichment = await CompanyEnrichmentRepository(db_session).get_for_company(company.id)
|
||||
assert enrichment.status.value == "partial"
|
||||
assert "funding" in enrichment.errors
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enrich_company_task_is_a_no_op_for_a_missing_company(db_session, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"app.tasks.enrichment.get_settings", lambda: Settings(ninjapear_api_key="test-key")
|
||||
)
|
||||
# Should return cleanly rather than raising - no company, nothing to do.
|
||||
await _enrich_company_async(str(uuid.uuid4()))
|
||||
Reference in New Issue
Block a user