Skip to content

Commit da215f5

Browse files
committed
test(ecosystem): cover activity result shapes
Protect the JSON and markdown shapes for local resolution errors and the shared 64-repo batch limit without relying on GitHub API responses.
1 parent 1a3bfae commit da215f5

1 file changed

Lines changed: 108 additions & 0 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2+
3+
import { registerEcosystemActivityTool } from "./ecosystem-activity-tool.js";
4+
import { resetAuthCache } from "./github-auth.js";
5+
import { MAX_REPOS_PER_REQUEST } from "./schemas.js";
6+
import { captureTool } from "./test-harness.js";
7+
8+
const ORIGINAL_GITHUB_TOKEN = process.env.GITHUB_TOKEN;
9+
const ORIGINAL_GH_TOKEN = process.env.GH_TOKEN;
10+
11+
beforeEach(() => {
12+
process.env.GITHUB_TOKEN = "test-token";
13+
delete process.env.GH_TOKEN;
14+
resetAuthCache();
15+
});
16+
17+
afterEach(() => {
18+
if (ORIGINAL_GITHUB_TOKEN === undefined) {
19+
delete process.env.GITHUB_TOKEN;
20+
} else {
21+
process.env.GITHUB_TOKEN = ORIGINAL_GITHUB_TOKEN;
22+
}
23+
24+
if (ORIGINAL_GH_TOKEN === undefined) {
25+
delete process.env.GH_TOKEN;
26+
} else {
27+
process.env.GH_TOKEN = ORIGINAL_GH_TOKEN;
28+
}
29+
30+
resetAuthCache();
31+
});
32+
33+
describe("ecosystem_activity tool (captureTool)", () => {
34+
test("JSON format returns stable result shape for local repo resolution errors", async () => {
35+
const run = captureTool(registerEcosystemActivityTool);
36+
37+
const text = await run({
38+
repos: [{ localPath: "/tmp" }],
39+
since: "48h",
40+
format: "json",
41+
});
42+
const parsed = JSON.parse(text) as {
43+
since?: string;
44+
repos?: Array<{
45+
owner: string;
46+
repo: string;
47+
commitCount: number;
48+
error?: { code: string; retryable: boolean };
49+
}>;
50+
commits?: unknown[];
51+
summary?: {
52+
totalCommits: number;
53+
repoBreakdown: Record<string, number>;
54+
};
55+
};
56+
57+
expect(parsed.since).toMatch(/^\d{4}-\d{2}-\d{2}T/);
58+
expect(parsed.repos).toEqual([
59+
{
60+
owner: "unknown",
61+
repo: "/tmp",
62+
commitCount: 0,
63+
error: expect.objectContaining({
64+
code: "LOCAL_REPO_NO_REMOTE",
65+
retryable: false,
66+
}),
67+
},
68+
]);
69+
expect(parsed.commits).toEqual([]);
70+
expect(parsed.summary).toEqual({ totalCommits: 0, repoBreakdown: {} });
71+
});
72+
73+
test(`accepts ${MAX_REPOS_PER_REQUEST} repos in one JSON result`, async () => {
74+
const run = captureTool(registerEcosystemActivityTool);
75+
const repos = Array.from({ length: MAX_REPOS_PER_REQUEST }, () => ({
76+
localPath: "/tmp",
77+
}));
78+
79+
const text = await run({
80+
repos,
81+
since: "48h",
82+
format: "json",
83+
});
84+
const parsed = JSON.parse(text) as {
85+
repos?: Array<{ error?: { code: string } }>;
86+
summary?: { totalCommits: number };
87+
};
88+
89+
expect(parsed.repos).toHaveLength(MAX_REPOS_PER_REQUEST);
90+
expect(parsed.repos?.[0]?.error?.code).toBe("LOCAL_REPO_NO_REMOTE");
91+
expect(parsed.summary?.totalCommits).toBe(0);
92+
});
93+
94+
test("markdown format includes empty activity and error sections", async () => {
95+
const run = captureTool(registerEcosystemActivityTool);
96+
97+
const text = await run({
98+
repos: [{ localPath: "/tmp" }],
99+
since: "48h",
100+
format: "markdown",
101+
});
102+
103+
expect(text).toContain("# Ecosystem Activity");
104+
expect(text).toContain("*(no commits in range)*");
105+
expect(text).toContain("## Errors");
106+
expect(text).toContain("LOCAL_REPO_NO_REMOTE");
107+
});
108+
});

0 commit comments

Comments
 (0)