mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-04-28 03:49:31 +00:00
test: add coverage for spawn feedback command (#2609)
Agent: test-engineer Co-authored-by: B <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4d4935c6f9
commit
c4ce4a1b24
2 changed files with 145 additions and 1 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@openrouter/spawn",
|
||||
"version": "0.17.17",
|
||||
"version": "0.17.18",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"spawn": "cli.js"
|
||||
|
|
|
|||
144
packages/cli/src/__tests__/cmd-feedback.test.ts
Normal file
144
packages/cli/src/__tests__/cmd-feedback.test.ts
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* cmd-feedback.test.ts — Tests for the `spawn feedback` command.
|
||||
*
|
||||
* Verifies:
|
||||
* - Empty message exits with error
|
||||
* - Successful PostHog submission prints thank-you
|
||||
* - PostHog non-2xx response exits with error
|
||||
* - Fetch network failure exits with error
|
||||
* - Correct PostHog payload structure (token, survey ID, event shape)
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from "bun:test";
|
||||
import { isString } from "../shared/type-guards";
|
||||
import { createConsoleMocks, restoreMocks } from "./test-helpers";
|
||||
|
||||
// ── Import module under test ──────────────────────────────────────────────────
|
||||
|
||||
const { cmdFeedback } = await import("../commands/feedback.js");
|
||||
|
||||
// ── Test Setup ────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("cmdFeedback", () => {
|
||||
let consoleMocks: ReturnType<typeof createConsoleMocks>;
|
||||
let originalFetch: typeof global.fetch;
|
||||
let exitSpy: ReturnType<typeof spyOn>;
|
||||
|
||||
beforeEach(() => {
|
||||
consoleMocks = createConsoleMocks();
|
||||
originalFetch = global.fetch;
|
||||
exitSpy = spyOn(process, "exit").mockImplementation(() => {
|
||||
throw new Error("process.exit called");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
restoreMocks(consoleMocks.log, consoleMocks.error, exitSpy);
|
||||
global.fetch = originalFetch;
|
||||
});
|
||||
|
||||
it("exits with error when no message is provided", async () => {
|
||||
await expect(cmdFeedback([])).rejects.toThrow("process.exit called");
|
||||
|
||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||
expect(consoleMocks.error).toHaveBeenCalled();
|
||||
const errorOutput = consoleMocks.error.mock.calls.map((c) => String(c[0])).join(" ");
|
||||
expect(errorOutput).toContain("Please provide your feedback message");
|
||||
});
|
||||
|
||||
it("exits with error when message is only whitespace", async () => {
|
||||
await expect(
|
||||
cmdFeedback([
|
||||
" ",
|
||||
" ",
|
||||
]),
|
||||
).rejects.toThrow("process.exit called");
|
||||
|
||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("sends feedback to PostHog and prints success", async () => {
|
||||
global.fetch = mock(() => Promise.resolve(new Response("ok")));
|
||||
|
||||
await cmdFeedback([
|
||||
"Great",
|
||||
"tool!",
|
||||
]);
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledTimes(1);
|
||||
const logOutput = consoleMocks.log.mock.calls.map((c) => String(c[0])).join(" ");
|
||||
expect(logOutput).toContain("Thanks for your feedback");
|
||||
});
|
||||
|
||||
it("sends correct PostHog payload shape", async () => {
|
||||
let capturedBody: string | undefined;
|
||||
global.fetch = mock((_url: string | URL | Request, init?: RequestInit) => {
|
||||
capturedBody = isString(init?.body) ? init.body : undefined;
|
||||
return Promise.resolve(new Response("ok"));
|
||||
});
|
||||
|
||||
await cmdFeedback([
|
||||
"test message",
|
||||
]);
|
||||
|
||||
expect(capturedBody).toBeDefined();
|
||||
const payload = JSON.parse(capturedBody ?? "{}");
|
||||
expect(payload.token).toBeString();
|
||||
expect(payload.distinct_id).toBe("anon");
|
||||
expect(payload.event).toBe("survey sent");
|
||||
expect(payload.properties.$survey_response).toBe("test message");
|
||||
expect(payload.properties.$survey_completed).toBe(true);
|
||||
expect(payload.properties.source).toBe("cli");
|
||||
});
|
||||
|
||||
it("joins multiple args into a single message", async () => {
|
||||
let capturedBody: string | undefined;
|
||||
global.fetch = mock((_url: string | URL | Request, init?: RequestInit) => {
|
||||
capturedBody = isString(init?.body) ? init.body : undefined;
|
||||
return Promise.resolve(new Response("ok"));
|
||||
});
|
||||
|
||||
await cmdFeedback([
|
||||
"hello",
|
||||
"world",
|
||||
"test",
|
||||
]);
|
||||
|
||||
const payload = JSON.parse(capturedBody ?? "{}");
|
||||
expect(payload.properties.$survey_response).toBe("hello world test");
|
||||
});
|
||||
|
||||
it("exits with error when PostHog returns non-2xx", async () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve(
|
||||
new Response("Server Error", {
|
||||
status: 500,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
await expect(
|
||||
cmdFeedback([
|
||||
"some feedback",
|
||||
]),
|
||||
).rejects.toThrow("process.exit called");
|
||||
|
||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||
const errorOutput = consoleMocks.error.mock.calls.map((c) => String(c[0])).join(" ");
|
||||
expect(errorOutput).toContain("Failed to send feedback");
|
||||
});
|
||||
|
||||
it("exits with error when fetch throws (network failure)", async () => {
|
||||
global.fetch = mock(() => Promise.reject(new Error("Network unreachable")));
|
||||
|
||||
await expect(
|
||||
cmdFeedback([
|
||||
"some feedback",
|
||||
]),
|
||||
).rejects.toThrow("process.exit called");
|
||||
|
||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||
const errorOutput = consoleMocks.error.mock.calls.map((c) => String(c[0])).join(" ");
|
||||
expect(errorOutput).toContain("Failed to send feedback");
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue