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:
2026-08-05 10:48:20 -04:00
commit 1a4c80958f
365 changed files with 43541 additions and 0 deletions
@@ -0,0 +1,126 @@
"""run_monitoring's per-source due-ness filter: a SCHEDULED run only
collects sources that are actually due (an overdue fast-cadence source
alongside a not-yet-due default-cadence one), while a MANUAL "Run now"
always collects every active source regardless of individual cadence."""
from __future__ import annotations
import uuid
from datetime import UTC, datetime, timedelta
from unittest.mock import patch
import httpx
import pytest
import respx
from app.models.company import Company
from app.models.enums import MonitoringFrequency, MonitoringRunTrigger, SourceType
from app.models.monitor_configuration import MonitorConfiguration
from app.models.source import Source
from app.repositories.monitoring_run_repository import MonitoringRunRepository
from app.tasks.scheduler import _sync_schedules_async
RSS_FEED = (
'<?xml version="1.0"?><rss version="2.0"><channel><title>News</title>'
"<item><title>Update</title><link>https://example.com/n</link>"
"<description>Something happened.</description></item></channel></rss>"
)
def _register_and_login(client) -> dict[str, str]:
email = f"user-{uuid.uuid4().hex[:12]}@example.com"
client.post(
"/api/v1/auth/register",
json={"email": email, "password": "correct-horse-1", "display_name": "Test User"},
)
tokens = client.post(
"/api/v1/auth/login", json={"email": email, "password": "correct-horse-1"}
).json()
return {"Authorization": f"Bearer {tokens['access_token']}"}
async def _make_company_with_two_sources(db_session):
company = Company(
id=uuid.uuid4(),
user_id=uuid.uuid4(),
name="Scheduling Co",
slug=f"scheduling-co-{uuid.uuid4().hex[:6]}",
)
db_session.add(company)
db_session.add(
MonitorConfiguration(
company_id=company.id,
frequency_type=MonitoringFrequency.WEEKLY,
enabled=True,
next_run=datetime.now(UTC) + timedelta(hours=1), # not due
)
)
due_source = Source(
company_id=company.id,
source_type=SourceType.RSS,
name="Daily News",
base_url="https://example.com/feed",
active=True,
frequency_type=MonitoringFrequency.DAILY,
next_check=datetime.now(UTC) - timedelta(minutes=5), # overdue
)
not_due_source = Source(
company_id=company.id,
source_type=SourceType.RSS,
name="Default Cadence Feed",
base_url="https://example.com/other-feed",
active=True,
# frequency_type left None - inherits the company's weekly default,
# which per MonitorConfiguration.next_run above isn't due yet.
)
db_session.add_all([due_source, not_due_source])
await db_session.commit()
return company
@pytest.mark.asyncio
async def test_scheduled_run_only_collects_the_due_source(db_session):
company = await _make_company_with_two_sources(db_session)
with respx.mock:
respx.get("https://example.com/feed").mock(return_value=httpx.Response(200, text=RSS_FEED))
await _sync_schedules_async()
runs = await MonitoringRunRepository(db_session).list_for_company(company.id)
assert len(runs) == 1
run = runs[0]
assert run.trigger_type == MonitoringRunTrigger.SCHEDULED
assert run.sources_attempted == 1
assert run.sources_successful == 1
def test_manual_run_collects_every_active_source_regardless_of_cadence(client):
headers = _register_and_login(client)
company = client.post(
"/api/v1/companies",
json={"name": f"Manual Sched Co {uuid.uuid4().hex[:6]}", "frequency_type": "weekly"},
headers=headers,
).json()
for name, url in [("A", "https://example.com/a"), ("B", "https://example.com/b")]:
client.post(
f"/api/v1/companies/{company['id']}/sources",
json={"source_type": "custom_url", "name": name, "base_url": url},
headers=headers,
)
with patch("socket.getaddrinfo", return_value=[(2, 1, 6, "", ("93.184.216.34", 0))]):
with respx.mock:
respx.get("https://example.com/a/robots.txt").mock(return_value=httpx.Response(404))
respx.get("https://example.com/a").mock(
return_value=httpx.Response(200, html="<html><body>A</body></html>")
)
respx.get("https://example.com/b/robots.txt").mock(return_value=httpx.Response(404))
respx.get("https://example.com/b").mock(
return_value=httpx.Response(200, html="<html><body>B</body></html>")
)
run = client.post(f"/api/v1/companies/{company['id']}/run", headers=headers).json()
detail = client.get(f"/api/v1/runs/{run['id']}", headers=headers).json()
assert detail["sources_attempted"] == 2
assert detail["sources_successful"] == 2