Files
Kilo-Org_kilocode/packages/opencode/test/kilocode/commit-message/generate.test.ts
T
Mark IJbema 5d5fab36bb fix(cli): repair broken imports and typecheck after opencode v1.14.29 merge
Fixes 257 typecheck errors surfaced after the upstream merge:

- Update imports to new module paths after upstream PR #24554 removed
  module barrel files (@/config, @/session, @/util, etc.) and PR #24309
  renamed @opencode-ai/shared to @opencode-ai/core.
- Migrate loadMode to use Info.zod.safeParse for consistency with
  loadAgents and ConfigCommand (unblocks KilocodeConfig.handleInvalid).
- Drop removed Npm.outdated mock from tests.
- Add missing Config.defaultLayer to bash-permission-metadata test runtime.
- Fix Tips component signature (kilocode drops upstream's 'connected' prop).
- Fix toast.tsx undefined 'duration' reference (should be toastOptions.duration).
- Move filesystem-containment test from deleted shared package to core.
2026-04-30 17:16:31 +02:00

189 lines
6.2 KiB
TypeScript

import { describe, expect, test, mock, beforeEach } from "bun:test"
import type { GitContext } from "@/kilocode/commit-message/types"
// Mock dependencies before importing the module under test.
// IMPORTANT: Bun's mock.module() is process-wide and permanent. To avoid
// breaking other test files, we spread real exports and only override what
// this test needs.
const realLog = await import("@opencode-ai/core/util/log")
const realProvider = await import("@/provider/provider")
const realLLM = await import("@/session/llm")
const realAgent = await import("@/agent/agent")
const realGitContext = await import("@/kilocode/commit-message/git-context")
let mockStreamText = "feat(src): add hello world logging"
const defaultGitContext: GitContext = {
branch: "main",
recentCommits: ["abc1234 initial commit"],
files: [
{
status: "modified",
path: "src/index.ts",
diff: "+console.log('hello')",
},
],
}
let mockGitContext: GitContext = { ...defaultGitContext }
let captured: { path: string; selected?: string[] } = { path: "" }
mock.module("@/kilocode/commit-message/git-context", () => ({
...realGitContext,
getGitContext: async (repoPath: string, selectedFiles?: string[]) => {
captured = { path: repoPath, selected: selectedFiles }
return mockGitContext
},
}))
mock.module("@/provider/provider", () => ({
...realProvider,
Provider: {
...realProvider.Provider,
defaultModel: async () => ({ providerID: "test", modelID: "test-model" }),
getSmallModel: async () => ({
providerID: "test",
id: "test-small-model",
}),
getModel: async () => ({ providerID: "test", id: "test-model" }),
},
}))
mock.module("@/session/llm", () => ({
...realLLM,
LLM: {
...realLLM.LLM,
stream: async () => ({
textStream: (async function* () {
yield mockStreamText
})(),
text: Promise.resolve(mockStreamText),
}),
},
}))
mock.module("@/agent/agent", () => ({
...realAgent,
Agent: {},
}))
mock.module("@opencode-ai/core/util/log", () => ({
...realLog,
create: () => ({
info: () => {},
error: () => {},
warn: () => {},
debug: () => {},
}),
}))
import { generateCommitMessage } from "../../../src/kilocode/commit-message/generate"
describe("commit-message.generate", () => {
beforeEach(() => {
mockStreamText = "feat(src): add hello world logging"
mockGitContext = { ...defaultGitContext }
captured = { path: "" }
})
describe("prompt construction", () => {
test("passes path to getGitContext", async () => {
const result = await generateCommitMessage({ path: "/repo" })
expect(result.message).toBeTruthy()
expect(captured.path).toBe("/repo")
})
test("generates message from git context with multiple files", async () => {
mockStreamText = "feat(api): add api module"
mockGitContext = {
branch: "main",
recentCommits: ["abc1234 initial commit"],
files: [
{ status: "added", path: "src/api.ts", diff: "+export function api() {}" },
{ status: "modified", path: "src/index.ts", diff: "+import { api } from './api'" },
],
}
const result = await generateCommitMessage({ path: "/repo" })
expect(result.message).toBe("feat(api): add api module")
})
})
describe("response cleaning", () => {
test("strips code block markers from response", async () => {
mockStreamText = "```\nfeat: add feature\n```"
const result = await generateCommitMessage({ path: "/repo" })
expect(result.message).toBe("feat: add feature")
})
test("strips code block markers with language tag", async () => {
mockStreamText = "```text\nfix(auth): resolve token refresh\n```"
const result = await generateCommitMessage({ path: "/repo" })
expect(result.message).toBe("fix(auth): resolve token refresh")
})
test("strips surrounding double quotes", async () => {
mockStreamText = '"feat: add new feature"'
const result = await generateCommitMessage({ path: "/repo" })
expect(result.message).toBe("feat: add new feature")
})
test("strips surrounding single quotes", async () => {
mockStreamText = "'fix: resolve bug'"
const result = await generateCommitMessage({ path: "/repo" })
expect(result.message).toBe("fix: resolve bug")
})
test("strips whitespace around the message", async () => {
mockStreamText = " \n chore: update deps \n "
const result = await generateCommitMessage({ path: "/repo" })
expect(result.message).toBe("chore: update deps")
})
test("strips code blocks AND quotes together", async () => {
mockStreamText = '```\n"refactor: simplify logic"\n```'
const result = await generateCommitMessage({ path: "/repo" })
expect(result.message).toBe("refactor: simplify logic")
})
test("returns clean message when no markers present", async () => {
mockStreamText = "docs: update readme"
const result = await generateCommitMessage({ path: "/repo" })
expect(result.message).toBe("docs: update readme")
})
})
describe("error on no changes", () => {
test("throws when no git changes are found", async () => {
mockGitContext = { branch: "main", recentCommits: [], files: [] }
await expect(generateCommitMessage({ path: "/repo" })).rejects.toThrow(
"No changes found to generate a commit message for",
)
})
})
describe("selectedFiles pass-through", () => {
test("passes selectedFiles to getGitContext", async () => {
const result = await generateCommitMessage({
path: "/repo",
selectedFiles: ["src/a.ts"],
})
expect(result.message).toBeTruthy()
expect(captured.path).toBe("/repo")
expect(captured.selected).toEqual(["src/a.ts"])
})
})
describe("custom prompt", () => {
test("uses default prompt when no custom prompt provided", async () => {
const result = await generateCommitMessage({ path: "/repo" })
expect(result.message).toBeTruthy()
})
test("uses custom prompt when provided", async () => {
const result = await generateCommitMessage({ path: "/repo", prompt: "Write a haiku commit message." })
expect(result.message).toBeTruthy()
})
})
})