From f990ce104805046797a52ee3a92fe3cc06e8ebcd Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 2 May 2026 14:26:31 +0200 Subject: [PATCH] test(sf): cover manual rate command --- .../extensions/sf/tests/commands-ops.test.ts | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/resources/extensions/sf/tests/commands-ops.test.ts diff --git a/src/resources/extensions/sf/tests/commands-ops.test.ts b/src/resources/extensions/sf/tests/commands-ops.test.ts new file mode 100644 index 000000000..7bdf0b11b --- /dev/null +++ b/src/resources/extensions/sf/tests/commands-ops.test.ts @@ -0,0 +1,78 @@ +/** + * commands-ops.test.ts — Regression tests for manual /sf ops dispatcher. + * + * Covers: /sf rate dispatch in manual mode (sf-monzctqn-m42nlq). + * + * Previously, handleRate was only invoked from auto.ts and never from ops.ts. + * The /sf rate command appeared in the catalog but silently no-oped in manual + * mode because ops.ts had no `rate` dispatch branch. + */ + +import assert from "node:assert/strict"; +import { afterEach, describe, it, vi } from "vitest"; +import { handleOpsCommand } from "../commands/handlers/ops.js"; + +// ─── Mock ExtensionCommandContext ───────────────────────────────────────────── + +function makeMockCtx() { + return { + ui: { + notify: vi.fn(), + }, + }; +} + +// ─── Tests ─────────────────────────────────────────────────────────────────── + +describe("handleOpsCommand — /sf rate", () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("rate branch calls ui.notify for valid rating", async () => { + const ctx = makeMockCtx(); + const trimmed = "rate ok"; + // handleOpsCommand returns true if dispatched, false otherwise. + // The rate branch shows a notification; verify it dispatches without returning false. + await handleOpsCommand(trimmed, ctx as any, {} as any); + // The key assertion: handleOpsCommand must return true (command was recognised), + // not fall through to return false (unknown command). + // handleRate shows a warning when no ledger exists, but the dispatch succeeds. + // We just verify the rate branch is reached (notify was called, not that it succeeds silently). + assert.ok( + ctx.ui.notify.mock.calls.length >= 1, + "rate branch should call ui.notify", + ); + }); + + it("rate with no args dispatches (shows usage)", async () => { + const ctx = makeMockCtx(); + const trimmed = "rate"; + const result = await handleOpsCommand(trimmed, ctx as any, {} as any); + assert.equal( + result, + true, + "rate command should be recognised (return true)", + ); + assert.ok( + ctx.ui.notify.mock.calls.length >= 1, + "rate with no args should show usage", + ); + }); + + it("skip dispatches (existing behaviour still works)", async () => { + const ctx = makeMockCtx(); + const result = await handleOpsCommand("skip", ctx as any, {} as any); + assert.equal(result, true, "skip command should be recognised"); + }); + + it("unknown command returns false", async () => { + const ctx = makeMockCtx(); + const result = await handleOpsCommand( + "not-a-command", + ctx as any, + {} as any, + ); + assert.equal(result, false, "unknown command should return false"); + }); +});