- Turborepo monorepo structure (packages: api, db, types, jobs; services: darkwatch) - Prisma schema: User, WatchListItem, Exposure, Alert, ScanJob models - WatchListService: CRUD with normalization, dedup, tier-based limits - HIBPService: API integration with severity scoring - MatchingEngine: exact-match with content hash dedup - AlertPipeline: dedup window, email notifications - ScanService: orchestrates watch list -> HIBP -> match -> alert flow - BullMQ job workers for scan and alert processing - Fastify API routes: watchlist, exposures, alerts, scan - Docker Compose: PostgreSQL 16 + Redis 7 - 15 unit tests passing - Implementation plan document uploaded
82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
import { describe, it, expect, beforeEach } from "vitest";
|
|
import { AlertPipeline } from "../src/alerts/AlertPipeline";
|
|
import prisma from "@shieldai/db";
|
|
import { Severity } from "@shieldai/types";
|
|
|
|
describe("AlertPipeline", () => {
|
|
let pipeline: AlertPipeline;
|
|
|
|
beforeEach(() => {
|
|
pipeline = new AlertPipeline();
|
|
});
|
|
|
|
it("creates alert with dedup key", async () => {
|
|
const user = await prisma.user.create({
|
|
data: { email: `alert-test-${Date.now()}@shieldai.local`, subscriptionTier: "BASIC" },
|
|
});
|
|
|
|
const item = await prisma.watchListItem.create({
|
|
data: {
|
|
userId: user.id,
|
|
identifierType: "EMAIL",
|
|
identifierValue: "test@example.com",
|
|
identifierHash: "hash-" + Date.now(),
|
|
},
|
|
});
|
|
|
|
const exposure = await prisma.exposure.create({
|
|
data: {
|
|
watchListItemId: item.id,
|
|
dataSource: "HIBP",
|
|
breachName: "TestBreach",
|
|
exposedAt: new Date(),
|
|
dataType: ["Email Address"],
|
|
severity: Severity.CRITICAL,
|
|
contentHash: "content-" + Date.now(),
|
|
},
|
|
});
|
|
|
|
const created = await pipeline.createAlert(user.id, exposure.id, Severity.CRITICAL);
|
|
expect(created).toBe(true);
|
|
});
|
|
|
|
it("deduplicates alerts within window", async () => {
|
|
const user = await prisma.user.create({
|
|
data: { email: `dedup-test-${Date.now()}@shieldai.local`, subscriptionTier: "BASIC" },
|
|
});
|
|
|
|
const item = await prisma.watchListItem.create({
|
|
data: {
|
|
userId: user.id,
|
|
identifierType: "EMAIL",
|
|
identifierValue: "dedup@example.com",
|
|
identifierHash: "dedup-hash-" + Date.now(),
|
|
},
|
|
});
|
|
|
|
const exposure = await prisma.exposure.create({
|
|
data: {
|
|
watchListItemId: item.id,
|
|
dataSource: "HIBP",
|
|
breachName: "DedupBreach",
|
|
exposedAt: new Date(),
|
|
dataType: ["Email Address"],
|
|
severity: Severity.CRITICAL,
|
|
contentHash: "dedup-content-" + Date.now(),
|
|
},
|
|
});
|
|
|
|
const first = await pipeline.createAlert(user.id, exposure.id, Severity.CRITICAL);
|
|
const second = await pipeline.createAlert(user.id, exposure.id, Severity.CRITICAL);
|
|
expect(first).toBe(true);
|
|
expect(second).toBe(false);
|
|
});
|
|
|
|
it("computes consistent dedup keys", () => {
|
|
const key1 = pipeline.computeDedupKey("user-1", "exposure-1");
|
|
const key2 = pipeline.computeDedupKey("user-1", "exposure-1");
|
|
expect(key1).toBe(key2);
|
|
expect(key1).toHaveLength(64);
|
|
});
|
|
});
|