Add CLI and infra test coverage

This commit is contained in:
Peter Steinberger
2025-11-25 12:28:00 +01:00
parent ca48350a45
commit ff6e13d274
11 changed files with 451 additions and 0 deletions

76
src/commands/up.test.ts Normal file
View File

@@ -0,0 +1,76 @@
import { describe, expect, it, vi } from "vitest";
import type { CliDeps } from "../cli/deps.js";
import type { RuntimeEnv } from "../runtime.js";
import { upCommand } from "./up.js";
const runtime: RuntimeEnv = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(() => {
throw new Error("exit");
}),
};
const makeDeps = (): CliDeps => ({
ensurePortAvailable: vi.fn().mockResolvedValue(undefined),
readEnv: vi.fn().mockReturnValue({
whatsappFrom: "whatsapp:+1555",
whatsappSenderSid: "WW",
}),
ensureBinary: vi.fn().mockResolvedValue(undefined),
ensureFunnel: vi.fn().mockResolvedValue(undefined),
getTailnetHostname: vi.fn().mockResolvedValue("tailnet-host"),
startWebhook: vi.fn().mockResolvedValue({ server: true }),
createClient: vi.fn().mockReturnValue({ client: true }),
findWhatsappSenderSid: vi.fn().mockResolvedValue("SID123"),
updateWebhook: vi.fn().mockResolvedValue(undefined),
});
describe("upCommand", () => {
it("throws on invalid port", async () => {
await expect(() =>
upCommand({ port: "0", path: "/cb" }, makeDeps(), runtime),
).rejects.toThrow("Port must be between 1 and 65535");
});
it("performs dry run and returns mock data", async () => {
runtime.log.mockClear();
const result = await upCommand(
{ port: "42873", path: "/cb", dryRun: true },
makeDeps(),
runtime,
);
expect(runtime.log).toHaveBeenCalledWith(
"[dry-run] would enable funnel on port 42873",
);
expect(result?.publicUrl).toBe("https://dry-run/cb");
expect(result?.senderSid).toBeUndefined();
});
it("enables funnel, starts webhook, and updates Twilio", async () => {
const deps = makeDeps();
const res = await upCommand(
{ port: "42873", path: "/hook", verbose: true },
deps,
runtime,
);
expect(deps.ensureBinary).toHaveBeenCalledWith(
"tailscale",
undefined,
runtime,
);
expect(deps.ensureFunnel).toHaveBeenCalled();
expect(deps.startWebhook).toHaveBeenCalled();
expect(deps.updateWebhook).toHaveBeenCalledWith(
expect.anything(),
"SID123",
"https://tailnet-host/hook",
"POST",
runtime,
);
expect(res?.publicUrl).toBe("https://tailnet-host/hook");
// waiter is returned to keep the process alive in real use.
expect(typeof res?.waiter).toBe("function");
});
});

View File

@@ -0,0 +1,56 @@
import { describe, expect, it, vi } from "vitest";
import type { CliDeps } from "../cli/deps.js";
import type { RuntimeEnv } from "../runtime.js";
import { webhookCommand } from "./webhook.js";
const runtime: RuntimeEnv = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(() => {
throw new Error("exit");
}),
};
const deps: CliDeps = {
ensurePortAvailable: vi.fn().mockResolvedValue(undefined),
startWebhook: vi.fn().mockResolvedValue({ server: true }),
};
describe("webhookCommand", () => {
it("throws on invalid port", async () => {
await expect(() =>
webhookCommand({ port: "70000", path: "/hook" }, deps, runtime),
).rejects.toThrow("Port must be between 1 and 65535");
});
it("logs dry run instead of starting server", async () => {
runtime.log.mockClear();
const res = await webhookCommand(
{ port: "42873", path: "/hook", reply: "dry-run" },
deps,
runtime,
);
expect(res).toBeUndefined();
expect(runtime.log).toHaveBeenCalledWith(
"[dry-run] would start webhook on port 42873 path /hook",
);
});
it("starts webhook when valid", async () => {
const res = await webhookCommand(
{ port: "42873", path: "/hook", reply: "ok", verbose: true },
deps,
runtime,
);
expect(deps.startWebhook).toHaveBeenCalledWith(
42873,
"/hook",
"ok",
true,
runtime,
);
expect(res).toEqual({ server: true });
});
});