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,153 @@
|
||||
"""Report generation end-to-end against a real (SQLite) DB: real
|
||||
SourceDocument/DetectedChange rows in, a persisted Report (JSON + Markdown)
|
||||
out, via the mock LLM provider."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.analysis.llm.mock import MockLLMProvider
|
||||
from app.models.company import Company
|
||||
from app.models.detected_change import DetectedChange
|
||||
from app.models.enums import ChangeType, MonitoringRunTrigger, ReportType, SeverityLevel, SourceType
|
||||
from app.models.monitoring_run import MonitoringRun
|
||||
from app.models.report import Report
|
||||
from app.models.snapshot import Snapshot
|
||||
from app.models.source import Source
|
||||
from app.models.source_document import SourceDocument
|
||||
from app.repositories.company_repository import CompanyRepository
|
||||
from app.services.report_service import generate_and_persist_report
|
||||
|
||||
|
||||
async def _make_company_with_evidence(db_session) -> Company:
|
||||
company = Company(
|
||||
id=uuid.uuid4(),
|
||||
user_id=uuid.uuid4(),
|
||||
name="Acme Mobility Systems",
|
||||
slug=f"acme-mobility-{uuid.uuid4().hex[:6]}",
|
||||
monitoring_focus="EV manufacturing expansion and battery technology",
|
||||
)
|
||||
db_session.add(company)
|
||||
await db_session.flush()
|
||||
|
||||
source = Source(
|
||||
company_id=company.id,
|
||||
source_type=SourceType.JOB_POSTING,
|
||||
name="Careers",
|
||||
base_url="https://acme.example/careers",
|
||||
)
|
||||
db_session.add(source)
|
||||
await db_session.flush()
|
||||
|
||||
db_session.add(
|
||||
SourceDocument(
|
||||
source_id=source.id,
|
||||
company_id=company.id,
|
||||
url="https://acme.example/careers/battery-engineer",
|
||||
canonical_url="https://acme.example/careers/battery-engineer",
|
||||
title="Senior Battery Engineer",
|
||||
author=None,
|
||||
publication_date=None,
|
||||
retrieved_date=datetime.now(UTC),
|
||||
content_text="We are hiring a senior battery engineer to lead our new EV platform.",
|
||||
content_hash="hash1",
|
||||
metadata_json={},
|
||||
extraction_method="job_link_heuristic",
|
||||
trust_score=0.7,
|
||||
)
|
||||
)
|
||||
|
||||
run = MonitoringRun(company_id=company.id, trigger_type=MonitoringRunTrigger.MANUAL)
|
||||
db_session.add(run)
|
||||
await db_session.flush()
|
||||
|
||||
snapshot = Snapshot(
|
||||
company_id=company.id,
|
||||
source_id=source.id,
|
||||
snapshot_type=source.source_type.value,
|
||||
hash="hash1",
|
||||
structured_summary={"urls": ["/careers/battery-engineer"]},
|
||||
text_summary="Senior Battery Engineer",
|
||||
monitoring_run_id=run.id,
|
||||
)
|
||||
db_session.add(snapshot)
|
||||
await db_session.flush()
|
||||
|
||||
db_session.add(
|
||||
DetectedChange(
|
||||
company_id=company.id,
|
||||
source_id=source.id,
|
||||
monitoring_run_id=run.id,
|
||||
current_snapshot_id=snapshot.id,
|
||||
change_type=ChangeType.NEW_DOCUMENT,
|
||||
raw_diff={"structured_added": ["/careers/battery-engineer"], "structured_removed": []},
|
||||
significance_score=0.6,
|
||||
confidence_score=0.75,
|
||||
severity=SeverityLevel.HIGH,
|
||||
summary="1 new item detected",
|
||||
)
|
||||
)
|
||||
await db_session.commit()
|
||||
|
||||
return await CompanyRepository(db_session).get_for_user(company.id, company.user_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_report_persists_structured_and_markdown(db_session, settings):
|
||||
company = await _make_company_with_evidence(db_session)
|
||||
llm = MockLLMProvider()
|
||||
|
||||
report = await generate_and_persist_report(
|
||||
db_session, settings, llm, company, report_type=ReportType.BASELINE
|
||||
)
|
||||
|
||||
assert report.id is not None
|
||||
assert report.report_type == ReportType.BASELINE
|
||||
assert report.model_provider == "mock"
|
||||
assert "Acme Mobility Systems" in report.executive_summary
|
||||
assert report.structured_report["executive_summary"] == report.executive_summary
|
||||
assert len(report.structured_report["recent_developments"]) == 1
|
||||
assert len(report.structured_report["hiring_signals"]) == 1
|
||||
|
||||
assert "# Competitive Intelligence Report: Acme Mobility Systems" in report.markdown_content
|
||||
assert "## 15. Sources" in report.markdown_content
|
||||
assert "battery-engineer" in report.markdown_content
|
||||
|
||||
persisted = (
|
||||
await db_session.execute(select(Report).where(Report.id == report.id))
|
||||
).scalar_one()
|
||||
assert persisted.company_id == company.id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_report_with_no_evidence_is_still_honest(db_session, settings):
|
||||
company = Company(
|
||||
id=uuid.uuid4(),
|
||||
user_id=uuid.uuid4(),
|
||||
name="Empty Co",
|
||||
slug=f"empty-co-{uuid.uuid4().hex[:6]}",
|
||||
)
|
||||
db_session.add(company)
|
||||
await db_session.commit()
|
||||
result = await db_session.execute(
|
||||
select(Company)
|
||||
.where(Company.id == company.id)
|
||||
.options(
|
||||
selectinload(Company.aliases),
|
||||
selectinload(Company.competitors),
|
||||
selectinload(Company.enrichment),
|
||||
)
|
||||
)
|
||||
company = result.scalar_one()
|
||||
|
||||
report = await generate_and_persist_report(
|
||||
db_session, settings, MockLLMProvider(), company, report_type=ReportType.BASELINE
|
||||
)
|
||||
|
||||
assert "0 collected document" in report.executive_summary
|
||||
assert report.structured_report["recent_developments"] == []
|
||||
Reference in New Issue
Block a user