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
+112
View File
@@ -0,0 +1,112 @@
import { screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";
import ForgotPasswordPage from "@/app/forgot-password/page";
import ResetPasswordPage from "@/app/reset-password/page";
import UnbanRequestPage from "@/app/unban-request/page";
import VerifyEmailPage from "@/app/verify-email/page";
import { renderWithQueryClient } from "./test-utils";
let searchParams = new URLSearchParams();
vi.mock("next/navigation", () => ({
useRouter: () => ({ push: vi.fn(), replace: vi.fn() }),
useSearchParams: () => searchParams,
}));
beforeEach(() => {
searchParams = new URLSearchParams();
vi.stubGlobal(
"fetch",
vi.fn().mockResolvedValue({
ok: false,
status: 401,
json: async () => ({ detail: "Not authenticated" }),
}),
);
});
describe("VerifyEmailPage", () => {
it("shows a validation error for an empty code", async () => {
const user = userEvent.setup();
renderWithQueryClient(<VerifyEmailPage />);
await user.click(await screen.findByRole("button", { name: /verify email/i }));
expect(await screen.findByText(/^enter the 6-digit code$/i)).toBeInTheDocument();
});
it("shows the email address pulled from the query string", async () => {
searchParams = new URLSearchParams({ email: "[email protected]" });
renderWithQueryClient(<VerifyEmailPage />);
expect(await screen.findByText(/ada@example\.com/i)).toBeInTheDocument();
});
});
describe("ForgotPasswordPage", () => {
it("rejects an invalid email", async () => {
const user = userEvent.setup();
renderWithQueryClient(<ForgotPasswordPage />);
await user.type(screen.getByLabelText(/email/i), "not-an-email");
await user.click(screen.getByRole("button", { name: /send reset code/i }));
await waitFor(() => {
expect(screen.getByText(/enter a valid email address/i)).toBeInTheDocument();
});
});
it("links back to sign in", async () => {
renderWithQueryClient(<ForgotPasswordPage />);
expect(await screen.findByRole("link", { name: /back to sign in/i })).toHaveAttribute(
"href",
"/login",
);
});
});
describe("ResetPasswordPage", () => {
it("prefills email from the query string", async () => {
searchParams = new URLSearchParams({ email: "[email protected]" });
renderWithQueryClient(<ResetPasswordPage />);
expect(await screen.findByDisplayValue("[email protected]")).toBeInTheDocument();
});
it("rejects a weak new password", async () => {
const user = userEvent.setup();
renderWithQueryClient(<ResetPasswordPage />);
await user.type(screen.getByLabelText(/^email$/i), "[email protected]");
await user.type(screen.getByLabelText(/reset code/i), "123456");
await user.type(screen.getByLabelText(/new password/i), "allletters");
await user.click(screen.getByRole("button", { name: /reset password/i }));
await waitFor(() => {
expect(
screen.getByText(/must contain at least one letter and one digit/i),
).toBeInTheDocument();
});
});
});
describe("UnbanRequestPage", () => {
it("submits and shows a confirmation", async () => {
vi.stubGlobal(
"fetch",
vi.fn().mockResolvedValue({
ok: true,
status: 204,
json: async () => ({}),
}),
);
const user = userEvent.setup();
renderWithQueryClient(<UnbanRequestPage />);
await user.type(screen.getByLabelText(/message/i), "This looks like a mistake.");
await user.click(screen.getByRole("button", { name: /send request/i }));
expect(await screen.findByText(/request received/i)).toBeInTheDocument();
});
it("links back to sign in", async () => {
renderWithQueryClient(<UnbanRequestPage />);
expect(await screen.findByRole("link", { name: /back to sign in/i })).toHaveAttribute(
"href",
"/login",
);
});
});