Skip to content

Commit 8af305d

Browse files
test: alerts summary cards (#226)
* feat: initial work on alerts summary cards * chore: use untitled icons * test: alert summary cards
1 parent efec5fa commit 8af305d

11 files changed

+236
-144
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { server } from "@/mocks/msw/node";
2+
import { test } from "vitest";
3+
import { http, HttpResponse } from "msw";
4+
import { render, waitFor } from "@/lib/test-utils";
5+
import { AlertsSummaryMaliciousPkg } from "../alerts-summary-malicious-pkg";
6+
import { makeMockAlert } from "../../mocks/alert.mock";
7+
8+
test("shows correct count when there is a malicious alert", async () => {
9+
server.use(
10+
http.get("*/api/v1/workspaces/:name/alerts", () => {
11+
return HttpResponse.json([makeMockAlert({ type: "malicious" })]);
12+
}),
13+
);
14+
15+
const { getByTestId } = render(<AlertsSummaryMaliciousPkg />);
16+
17+
await waitFor(() => {
18+
expect(getByTestId("malicious-count")).toHaveTextContent("1");
19+
});
20+
});
21+
22+
test("shows correct count when there is no malicious alert", async () => {
23+
server.use(
24+
http.get("*/api/v1/workspaces/:name/alerts", () => {
25+
return HttpResponse.json([makeMockAlert({ type: "secret" })]);
26+
}),
27+
);
28+
29+
const { getByTestId } = render(<AlertsSummaryMaliciousPkg />);
30+
31+
await waitFor(() => {
32+
expect(getByTestId("malicious-count")).toHaveTextContent("0");
33+
});
34+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { server } from "@/mocks/msw/node";
2+
import { test } from "vitest";
3+
import { http, HttpResponse } from "msw";
4+
import { render, waitFor } from "@/lib/test-utils";
5+
6+
import { AlertsSummaryMaliciousSecrets } from "../alerts-summary-secrets";
7+
import { makeMockAlert } from "../../mocks/alert.mock";
8+
9+
test("shows correct count when there is a secret alert", async () => {
10+
server.use(
11+
http.get("*/api/v1/workspaces/:name/alerts", () => {
12+
return HttpResponse.json([makeMockAlert({ type: "secret" })]);
13+
}),
14+
);
15+
16+
const { getByTestId } = render(<AlertsSummaryMaliciousSecrets />);
17+
18+
await waitFor(() => {
19+
expect(getByTestId("secrets-count")).toHaveTextContent("1");
20+
});
21+
});
22+
23+
test("shows correct count when there is no malicious alert", async () => {
24+
server.use(
25+
http.get("*/api/v1/workspaces/:name/alerts", () => {
26+
return HttpResponse.json([makeMockAlert({ type: "malicious" })]);
27+
}),
28+
);
29+
30+
const { getByTestId } = render(<AlertsSummaryMaliciousSecrets />);
31+
32+
await waitFor(() => {
33+
expect(getByTestId("secrets-count")).toHaveTextContent("0");
34+
});
35+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { server } from "@/mocks/msw/node";
2+
import { test } from "vitest";
3+
import { http, HttpResponse } from "msw";
4+
import { render, waitFor } from "@/lib/test-utils";
5+
6+
import { AlertsSummaryWorkspaceTokenUsage } from "../alerts-summary-workspace-token-usage";
7+
import { TOKEN_USAGE_AGG } from "../../mocks/token-usage.mock";
8+
9+
test("shows correct count when there is token usage", async () => {
10+
server.use(
11+
http.get("*/api/v1/workspaces/:name/token-usage", () => {
12+
return HttpResponse.json(TOKEN_USAGE_AGG);
13+
}),
14+
);
15+
16+
const { getByTestId } = render(<AlertsSummaryWorkspaceTokenUsage />);
17+
18+
await waitFor(() => {
19+
expect(getByTestId("usage-input-tokens")).toHaveTextContent(
20+
TOKEN_USAGE_AGG.token_usage.input_tokens.toString(),
21+
);
22+
expect(getByTestId("usage-output-tokens")).toHaveTextContent(
23+
TOKEN_USAGE_AGG.token_usage.output_tokens.toString(),
24+
);
25+
});
26+
});
27+
28+
test("shows correct count when there is no token usage", async () => {
29+
server.use(
30+
http.get("*/api/v1/workspaces/:name/token-usage", () => {
31+
return HttpResponse.json({});
32+
}),
33+
);
34+
35+
const { getByTestId } = render(<AlertsSummaryWorkspaceTokenUsage />);
36+
37+
await waitFor(() => {
38+
expect(getByTestId("usage-input-tokens")).toHaveTextContent("0");
39+
expect(getByTestId("usage-output-tokens")).toHaveTextContent("0");
40+
});
41+
});

src/features/alerts/components/__tests__/table-alerts.test.tsx

+6-65
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,8 @@ import { TableAlerts } from "../table-alerts";
33
import { render, screen, waitFor, within } from "@/lib/test-utils";
44
import { server } from "@/mocks/msw/node";
55
import { http, HttpResponse } from "msw";
6-
import {
7-
AlertConversation,
8-
ProviderType,
9-
QuestionType,
10-
TokenUsageAggregate,
11-
} from "@/api/generated";
6+
import { makeMockAlert } from "../../mocks/alert.mock";
7+
import { TOKEN_USAGE_AGG } from "../../mocks/token-usage.mock";
128

139
vi.mock("@untitled-ui/icons-react", async () => {
1410
const original = await vi.importActual<
@@ -21,63 +17,6 @@ vi.mock("@untitled-ui/icons-react", async () => {
2117
};
2218
});
2319

24-
const TOKEN_USAGE_AGG = {
25-
tokens_by_model: {
26-
"claude-3-5-sonnet-latest": {
27-
provider_type: ProviderType.ANTHROPIC,
28-
model: "claude-3-5-sonnet-latest",
29-
token_usage: {
30-
input_tokens: 1183,
31-
output_tokens: 433,
32-
input_cost: 0.003549,
33-
output_cost: 0.006495,
34-
},
35-
},
36-
},
37-
token_usage: {
38-
input_tokens: 1183,
39-
output_tokens: 433,
40-
input_cost: 0.003549,
41-
output_cost: 0.006495,
42-
},
43-
} as const satisfies TokenUsageAggregate;
44-
45-
const makeMockAlert = ({
46-
token_usage_agg,
47-
}: {
48-
token_usage_agg: TokenUsageAggregate | null;
49-
}) => {
50-
return {
51-
conversation: {
52-
question_answers: [
53-
{
54-
question: {
55-
message: "foo",
56-
timestamp: "2025-01-28T14:32:57.836445Z",
57-
message_id: "cfdf1acd-999c-430b-bb57-4c4db6cec6c9",
58-
},
59-
answer: {
60-
message: "bar",
61-
timestamp: "2025-01-28T14:32:59.107793Z",
62-
message_id: "c2b88968-06b3-485d-a42b-7b54f973eef9",
63-
},
64-
},
65-
],
66-
provider: "anthropic",
67-
type: QuestionType.CHAT,
68-
chat_id: "cfdf1acd-999c-430b-bb57-4c4db6cec6c9",
69-
conversation_timestamp: "2025-01-28T14:32:57.836445Z",
70-
token_usage_agg,
71-
},
72-
alert_id: "2379b08b-1e2b-4d6b-b425-e58a0b4fe7bc",
73-
code_snippet: null,
74-
trigger_string: "foo",
75-
trigger_type: "codegate-secrets",
76-
trigger_category: "critical",
77-
timestamp: "2025-01-28T14:32:57.599032Z",
78-
} as const satisfies AlertConversation;
79-
};
80-
8120
const INPUT_TOKENS =
8221
TOKEN_USAGE_AGG.tokens_by_model[
8322
"claude-3-5-sonnet-latest"
@@ -92,7 +31,7 @@ test("renders token usage cell correctly", async () => {
9231
server.use(
9332
http.get("*/workspaces/:name/alerts", () => {
9433
return HttpResponse.json([
95-
makeMockAlert({ token_usage_agg: TOKEN_USAGE_AGG }),
34+
makeMockAlert({ token_usage: true, type: "malicious" }),
9635
]);
9736
}),
9837
);
@@ -118,7 +57,9 @@ test("renders token usage cell correctly", async () => {
11857
test("renders N/A when token usage is missing", async () => {
11958
server.use(
12059
http.get("*/workspaces/:name/alerts", () => {
121-
return HttpResponse.json([makeMockAlert({ token_usage_agg: null })]);
60+
return HttpResponse.json([
61+
makeMockAlert({ token_usage: false, type: "malicious" }),
62+
]);
12263
}),
12364
);
12465

src/features/alerts/components/alerts-summary.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ function AlertsSummaryStatistic({
1111
Icon: (props: React.SVGProps<SVGSVGElement>) => React.JSX.Element;
1212
}) {
1313
return (
14-
<div id={id} className="text-5xl flex items-center gap-1">
14+
<div data-testid={id} className="text-5xl flex items-center gap-1">
1515
<Icon className="size-11" />
1616
{count}
1717
</div>
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { test, expect } from "vitest";
22
import { isAlertMalicious } from "../is-alert-malicious";
3-
import { ALERT_MALICIOUS } from "../../mocks/alert-malicious.mock";
4-
import { ALERT_SECRET } from "../../mocks/alert-secret.mock";
3+
import { makeMockAlert } from "../../mocks/alert.mock";
54

65
test("matches malicious alert", () => {
7-
expect(isAlertMalicious(ALERT_MALICIOUS)).toBe(true);
6+
expect(isAlertMalicious(makeMockAlert({ type: "malicious" }))).toBe(true);
87
});
98

109
test("doesn't match secret", () => {
11-
expect(isAlertMalicious(ALERT_SECRET)).toBe(false);
10+
expect(isAlertMalicious(makeMockAlert({ type: "secret" }))).toBe(false);
1211
});
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { test, expect } from "vitest";
2-
import { ALERT_MALICIOUS } from "../../mocks/alert-malicious.mock";
3-
import { ALERT_SECRET } from "../../mocks/alert-secret.mock";
42
import { isAlertSecret } from "../is-alert-secret";
3+
import { makeMockAlert } from "../../mocks/alert.mock";
54

65
test("matches secret alert", () => {
7-
expect(isAlertSecret(ALERT_SECRET)).toBe(true);
6+
expect(isAlertSecret(makeMockAlert({ type: "secret" }))).toBe(true);
87
});
98

109
test("doesn't match malicious", () => {
11-
expect(isAlertSecret(ALERT_MALICIOUS)).toBe(false);
10+
expect(isAlertSecret(makeMockAlert({ type: "malicious" }))).toBe(false);
1211
});

src/features/alerts/mocks/alert-malicious.mock.ts

-38
This file was deleted.

src/features/alerts/mocks/alert-secret.mock.ts

-32
This file was deleted.

0 commit comments

Comments
 (0)