FRE-4471: Scaffold DarkWatch MVP — monorepo, schema, services, API routes, tests
- 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
This commit is contained in:
81
services/darkwatch/test/alerts.test.ts
Normal file
81
services/darkwatch/test/alerts.test.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user