Skip to content

Commit 34d904f

Browse files
committed
refactor(test): optimize claude-plugin test suite
Delete dead code (code-mask.ts) and low-value tests (bootstrap, esbuild-config). Rewrite hook-handler tests to exercise handlePromptSubmit directly instead of duplicating mention-extractor and context-formatter coverage. Add toAbsolutePath, expansion bundle, and subagent bundle tests. Replace setTimeout polling with vi.waitFor in stdin-runner tests. Assisted-by: Claude
1 parent 799308d commit 34d904f

7 files changed

Lines changed: 293 additions & 439 deletions

File tree

packages/claude-plugin/src/bootstrap.test.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

packages/claude-plugin/src/bundle-execution.test.ts

Lines changed: 132 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,99 +2,161 @@ import { spawn } from "node:child_process";
22
import { resolve } from "node:path";
33
import { describe, expect, it } from "vitest";
44

5-
describe("bundle execution", () => {
6-
const binPath = resolve(import.meta.dirname, "../../../bin/obs-hook-handler");
5+
/** Execute a bin script with JSON input via stdin. */
6+
async function runBin(
7+
binScript: string,
8+
input: string,
9+
): Promise<{ stdout: string; stderr: string }> {
10+
const binPath = resolve(import.meta.dirname, "../../../bin", binScript);
11+
return new Promise((res, reject) => {
12+
const proc = spawn("node", [binPath], {
13+
stdio: ["pipe", "pipe", "pipe"],
14+
});
715

8-
/** Execute hook handler bin script with JSON input via stdin. */
9-
async function runHookHandler(input: string): Promise<{ stdout: string; stderr: string }> {
10-
return new Promise((resolve, reject) => {
11-
const proc = spawn("node", [binPath], {
12-
stdio: ["pipe", "pipe", "pipe"],
13-
});
16+
let stdout = "";
17+
let stderr = "";
1418

15-
let stdout = "";
16-
let stderr = "";
19+
proc.stdout.on("data", (chunk: Buffer) => {
20+
stdout += chunk.toString();
21+
});
1722

18-
proc.stdout.on("data", (chunk: Buffer) => {
19-
stdout += chunk.toString();
20-
});
23+
proc.stderr.on("data", (chunk: Buffer) => {
24+
stderr += chunk.toString();
25+
});
2126

22-
proc.stderr.on("data", (chunk: Buffer) => {
23-
stderr += chunk.toString();
24-
});
27+
proc.on("close", () => {
28+
res({ stdout, stderr });
29+
});
2530

26-
proc.on("close", () => {
27-
resolve({ stdout, stderr });
28-
});
31+
proc.on("error", (err) => {
32+
reject(err);
33+
});
34+
35+
proc.stdin.write(input);
36+
proc.stdin.end();
37+
});
38+
}
2939

30-
proc.on("error", (err) => {
31-
reject(err);
40+
describe("bundle execution", () => {
41+
describe("obs-hook-handler", () => {
42+
it("returns empty output for no mentions", async () => {
43+
const input = JSON.stringify({
44+
hook_event_name: "UserPromptSubmit",
45+
session_id: "test",
46+
transcript_path: "/tmp",
47+
cwd: "/tmp",
48+
prompt: "hello world",
3249
});
3350

34-
proc.stdin.write(input);
35-
proc.stdin.end();
51+
const result = await runBin("obs-hook-handler", input);
52+
expect(result.stdout.trim()).toBe("{}");
53+
expect(result.stderr).toBe("");
3654
});
37-
}
38-
39-
it("bundle executes via bin script and returns empty output for no mentions", async () => {
40-
const input = JSON.stringify({
41-
hook_event_name: "UserPromptSubmit",
42-
session_id: "test",
43-
transcript_path: "/tmp",
44-
cwd: "/tmp",
45-
prompt: "hello world",
55+
56+
it("returns empty output for invalid JSON input", async () => {
57+
const result = await runBin("obs-hook-handler", "not json");
58+
expect(result.stdout.trim()).toBe("{}");
4659
});
4760

48-
const result = await runHookHandler(input);
49-
expect(result.stdout.trim()).toBe("{}");
50-
expect(result.stderr).toBe("");
51-
});
61+
it("returns empty output for wrong hook event", async () => {
62+
const input = JSON.stringify({
63+
hook_event_name: "PreToolUse",
64+
session_id: "test",
65+
transcript_path: "/tmp",
66+
cwd: "/tmp",
67+
prompt: "@obs:test",
68+
});
5269

53-
it("bundle executes and returns empty output for invalid JSON input", async () => {
54-
const result = await runHookHandler("not json");
55-
expect(result.stdout.trim()).toBe("{}");
56-
});
70+
const result = await runBin("obs-hook-handler", input);
71+
expect(result.stdout.trim()).toBe("{}");
72+
});
73+
74+
it("returns empty output for missing required fields", async () => {
75+
const input = JSON.stringify({
76+
hook_event_name: "UserPromptSubmit",
77+
prompt: "@obs:test",
78+
});
5779

58-
it("bundle executes and returns empty output for wrong hook event", async () => {
59-
const input = JSON.stringify({
60-
hook_event_name: "PreToolUse",
61-
session_id: "test",
62-
transcript_path: "/tmp",
63-
cwd: "/tmp",
64-
prompt: "@obs:test",
80+
const result = await runBin("obs-hook-handler", input);
81+
expect(result.stdout.trim()).toBe("{}");
6582
});
6683

67-
const result = await runHookHandler(input);
68-
expect(result.stdout.trim()).toBe("{}");
84+
it("handles @obs: mention (bootstrap failure expected)", async () => {
85+
const input = JSON.stringify({
86+
hook_event_name: "UserPromptSubmit",
87+
session_id: "test",
88+
transcript_path: "/tmp",
89+
cwd: "/tmp",
90+
prompt: "Check @obs:architect",
91+
});
92+
93+
const result = await runBin("obs-hook-handler", input);
94+
const output = JSON.parse(result.stdout.trim()) as {
95+
hookSpecificOutput?: { additionalContext?: string };
96+
};
97+
98+
expect(output).toHaveProperty("hookSpecificOutput");
99+
expect(output.hookSpecificOutput).toHaveProperty("additionalContext");
100+
expect(output.hookSpecificOutput?.additionalContext).toContain("@obs:architect");
101+
expect(output.hookSpecificOutput?.additionalContext).toContain("Error:");
102+
});
69103
});
70104

71-
it("bundle executes and returns empty output for missing required fields", async () => {
72-
const input = JSON.stringify({
73-
hook_event_name: "UserPromptSubmit",
74-
prompt: "@obs:test",
105+
describe("obs-expansion-handler", () => {
106+
it("returns empty output for non-proxy skill", async () => {
107+
const input = JSON.stringify({
108+
hook_event_name: "UserPromptExpansion",
109+
session_id: "test",
110+
transcript_path: "/tmp",
111+
cwd: "/tmp",
112+
command_name: "nonexistent-skill",
113+
expansion_type: "skill",
114+
command_source: "project",
115+
});
116+
117+
const result = await runBin("obs-expansion-handler", input);
118+
expect(result.stdout.trim()).toBe("{}");
75119
});
76120

77-
const result = await runHookHandler(input);
78-
expect(result.stdout.trim()).toBe("{}");
121+
it("returns empty output for wrong hook event", async () => {
122+
const input = JSON.stringify({
123+
hook_event_name: "UserPromptSubmit",
124+
session_id: "test",
125+
transcript_path: "/tmp",
126+
cwd: "/tmp",
127+
command_name: "test",
128+
});
129+
130+
const result = await runBin("obs-expansion-handler", input);
131+
expect(result.stdout.trim()).toBe("{}");
132+
});
79133
});
80134

81-
it("bundle executes and handles @obs: mention (bootstrap failure expected)", async () => {
82-
const input = JSON.stringify({
83-
hook_event_name: "UserPromptSubmit",
84-
session_id: "test",
85-
transcript_path: "/tmp",
86-
cwd: "/tmp",
87-
prompt: "Check @obs:architect",
135+
describe("obs-subagent-handler", () => {
136+
it("returns empty output for nonexistent agent", async () => {
137+
const input = JSON.stringify({
138+
hook_event_name: "SubagentStart",
139+
session_id: "test",
140+
transcript_path: "/tmp",
141+
cwd: "/tmp",
142+
agent_type: "nonexistent-agent",
143+
});
144+
145+
const result = await runBin("obs-subagent-handler", input);
146+
expect(result.stdout.trim()).toBe("{}");
88147
});
89148

90-
const result = await runHookHandler(input);
91-
const output = JSON.parse(result.stdout.trim()) as {
92-
hookSpecificOutput?: { additionalContext?: string };
93-
};
149+
it("returns empty output for wrong hook event", async () => {
150+
const input = JSON.stringify({
151+
hook_event_name: "UserPromptSubmit",
152+
session_id: "test",
153+
transcript_path: "/tmp",
154+
cwd: "/tmp",
155+
agent_type: "test",
156+
});
94157

95-
expect(output).toHaveProperty("hookSpecificOutput");
96-
expect(output.hookSpecificOutput).toHaveProperty("additionalContext");
97-
expect(output.hookSpecificOutput?.additionalContext).toContain("@obs:architect");
98-
expect(output.hookSpecificOutput?.additionalContext).toContain("Error:");
158+
const result = await runBin("obs-subagent-handler", input);
159+
expect(result.stdout.trim()).toBe("{}");
160+
});
99161
});
100162
});

packages/claude-plugin/src/code-mask.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

packages/claude-plugin/src/esbuild-config.test.ts

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)