mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-06 02:47:56 +00:00
fix: improve commit message generator quality and test coverage
- Rewrite getGitDiff tests with proper assertions (3 new tests) - Fix markdown stripping regex to handle language-tagged code blocks - Add --no-color flag to git diff commands for clean output - Internationalize hardcoded error strings in setScmInputBoxMessage - Add no_git_extension and no_git_repo i18n keys to all locales
This commit is contained in:
parent
a30365d5db
commit
364941f17c
20 changed files with 123 additions and 22 deletions
2
src/i18n/locales/ca/common.json
generated
2
src/i18n/locales/ca/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "Generant missatge de commit...",
|
||||
"no_changes": "No s'han detectat canvis. Prepara o modifica fitxers abans de generar un missatge de commit.",
|
||||
"generation_failed": "No s'ha pogut generar el missatge de commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Utilitza el perfil actual",
|
||||
"select_profile": "Selecciona un perfil d'API per a la generació de missatges de commit"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/de/common.json
generated
2
src/i18n/locales/de/common.json
generated
|
|
@ -252,6 +252,8 @@
|
|||
"generating": "Commit-Nachricht wird generiert...",
|
||||
"no_changes": "Keine Änderungen erkannt. Bereite Dateien vor oder ändere sie, bevor du eine Commit-Nachricht generierst.",
|
||||
"generation_failed": "Commit-Nachricht konnte nicht generiert werden",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Aktuelles Profil verwenden",
|
||||
"select_profile": "Wähle ein API-Profil für die Commit-Nachricht-Generierung"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,6 +249,8 @@
|
|||
"generating": "Generating commit message...",
|
||||
"no_changes": "No changes detected. Stage or modify files before generating a commit message.",
|
||||
"generation_failed": "Failed to generate commit message",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Use current profile",
|
||||
"select_profile": "Select an API profile for commit message generation"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/es/common.json
generated
2
src/i18n/locales/es/common.json
generated
|
|
@ -252,6 +252,8 @@
|
|||
"generating": "Generando mensaje de commit...",
|
||||
"no_changes": "No se detectaron cambios. Prepara o modifica archivos antes de generar un mensaje de commit.",
|
||||
"generation_failed": "Error al generar el mensaje de commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Usar perfil actual",
|
||||
"select_profile": "Selecciona un perfil de API para la generación de mensajes de commit"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/fr/common.json
generated
2
src/i18n/locales/fr/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "Génération du message de commit...",
|
||||
"no_changes": "Aucun changement détecté. Prépare ou modifie des fichiers avant de générer un message de commit.",
|
||||
"generation_failed": "Échec de la génération du message de commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Utiliser le profil actuel",
|
||||
"select_profile": "Sélectionne un profil d'API pour la génération de messages de commit"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/hi/common.json
generated
2
src/i18n/locales/hi/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "कमिट संदेश बनाया जा रहा है...",
|
||||
"no_changes": "कोई बदलाव नहीं मिला। कमिट संदेश बनाने से पहले फ़ाइलें तैयार करें या बदलें।",
|
||||
"generation_failed": "कमिट संदेश बनाने में विफल",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "वर्तमान प्रोफ़ाइल का उपयोग करें",
|
||||
"select_profile": "कमिट संदेश बनाने के लिए एक API प्रोफ़ाइल चुनें"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/id/common.json
generated
2
src/i18n/locales/id/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "Membuat pesan commit...",
|
||||
"no_changes": "Tidak ada perubahan terdeteksi. Siapkan atau ubah file sebelum membuat pesan commit.",
|
||||
"generation_failed": "Gagal membuat pesan commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Gunakan profil saat ini",
|
||||
"select_profile": "Pilih profil API untuk pembuatan pesan commit"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/it/common.json
generated
2
src/i18n/locales/it/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "Generazione del messaggio di commit...",
|
||||
"no_changes": "Nessuna modifica rilevata. Prepara o modifica i file prima di generare un messaggio di commit.",
|
||||
"generation_failed": "Impossibile generare il messaggio di commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Usa il profilo corrente",
|
||||
"select_profile": "Seleziona un profilo API per la generazione del messaggio di commit"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/ja/common.json
generated
2
src/i18n/locales/ja/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "コミットメッセージを生成中...",
|
||||
"no_changes": "変更が検出されませんでした。コミットメッセージを生成する前にファイルをステージするか変更してください。",
|
||||
"generation_failed": "コミットメッセージの生成に失敗しました",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "現在のプロファイルを使用",
|
||||
"select_profile": "コミットメッセージ生成用のAPIプロファイルを選択"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/ko/common.json
generated
2
src/i18n/locales/ko/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "커밋 메시지 생성 중...",
|
||||
"no_changes": "변경 사항이 없습니다. 커밋 메시지를 생성하기 전에 파일을 스테이지하거나 수정하세요.",
|
||||
"generation_failed": "커밋 메시지 생성에 실패했습니다",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "현재 프로필 사용",
|
||||
"select_profile": "커밋 메시지 생성에 사용할 API 프로필을 선택하세요"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/nl/common.json
generated
2
src/i18n/locales/nl/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "Commitbericht genereren...",
|
||||
"no_changes": "Geen wijzigingen gedetecteerd. Stage of wijzig bestanden voordat je een commitbericht genereert.",
|
||||
"generation_failed": "Kan commitbericht niet genereren",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Huidig profiel gebruiken",
|
||||
"select_profile": "Selecteer een API-profiel voor het genereren van commitberichten"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/pl/common.json
generated
2
src/i18n/locales/pl/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "Generowanie wiadomości commita...",
|
||||
"no_changes": "Nie wykryto zmian. Przygotuj lub zmodyfikuj pliki przed wygenerowaniem wiadomości commita.",
|
||||
"generation_failed": "Nie udało się wygenerować wiadomości commita",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Użyj bieżącego profilu",
|
||||
"select_profile": "Wybierz profil API do generowania wiadomości commita"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/pt-BR/common.json
generated
2
src/i18n/locales/pt-BR/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "Gerando mensagem de commit...",
|
||||
"no_changes": "Nenhuma alteração detectada. Prepare ou modifique arquivos antes de gerar uma mensagem de commit.",
|
||||
"generation_failed": "Falha ao gerar mensagem de commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Usar perfil atual",
|
||||
"select_profile": "Selecione um perfil de API para a geração de mensagens de commit"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/ru/common.json
generated
2
src/i18n/locales/ru/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "Генерация сообщения коммита...",
|
||||
"no_changes": "Изменения не обнаружены. Подготовьте или измените файлы перед генерацией сообщения коммита.",
|
||||
"generation_failed": "Не удалось сгенерировать сообщение коммита",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Использовать текущий профиль",
|
||||
"select_profile": "Выберите профиль API для генерации сообщения коммита"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/tr/common.json
generated
2
src/i18n/locales/tr/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "Commit mesajı oluşturuluyor...",
|
||||
"no_changes": "Değişiklik algılanmadı. Commit mesajı oluşturmadan önce dosyaları hazırla veya değiştir.",
|
||||
"generation_failed": "Commit mesajı oluşturulamadı",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Mevcut profili kullan",
|
||||
"select_profile": "Commit mesajı oluşturma için bir API profili seç"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/vi/common.json
generated
2
src/i18n/locales/vi/common.json
generated
|
|
@ -264,6 +264,8 @@
|
|||
"generating": "Đang tạo thông điệp commit...",
|
||||
"no_changes": "Không phát hiện thay đổi nào. Hãy chuẩn bị hoặc chỉnh sửa tệp trước khi tạo thông điệp commit.",
|
||||
"generation_failed": "Không thể tạo thông điệp commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Sử dụng hồ sơ hiện tại",
|
||||
"select_profile": "Chọn một hồ sơ API để tạo thông điệp commit"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/zh-CN/common.json
generated
2
src/i18n/locales/zh-CN/common.json
generated
|
|
@ -262,6 +262,8 @@
|
|||
"generating": "正在生成提交信息...",
|
||||
"no_changes": "未检测到更改。请在生成提交信息之前暂存或修改文件。",
|
||||
"generation_failed": "生成提交信息失败",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "使用当前配置",
|
||||
"select_profile": "选择用于生成提交信息的 API 配置"
|
||||
}
|
||||
|
|
|
|||
2
src/i18n/locales/zh-TW/common.json
generated
2
src/i18n/locales/zh-TW/common.json
generated
|
|
@ -257,6 +257,8 @@
|
|||
"generating": "正在產生提交訊息...",
|
||||
"no_changes": "未偵測到變更。請在產生提交訊息之前暫存或修改檔案。",
|
||||
"generation_failed": "產生提交訊息失敗",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "使用目前的設定檔",
|
||||
"select_profile": "選擇用於產生提交訊息的 API 設定檔"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,34 @@ import * as singleCompletionHandlerModule from "../single-completion-handler"
|
|||
import type { ProviderSettings } from "@roo-code/types"
|
||||
|
||||
vi.mock("../single-completion-handler")
|
||||
vi.mock("child_process")
|
||||
|
||||
// Mock child_process.exec to simulate git commands
|
||||
const mockExecImpl = vi.fn()
|
||||
|
||||
vi.mock("child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("child_process")>()
|
||||
return {
|
||||
...actual,
|
||||
exec: (...args: any[]) => mockExecImpl(...args),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock("util", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("util")>()
|
||||
return {
|
||||
...actual,
|
||||
promisify: vi.fn((fn: any) => fn),
|
||||
promisify:
|
||||
(fn: any) =>
|
||||
(...args: any[]) =>
|
||||
new Promise((resolve, reject) => {
|
||||
fn(...args, (err: Error | null, result: any) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(result)
|
||||
}
|
||||
})
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -57,6 +79,14 @@ describe("commit-message-generator", () => {
|
|||
expect(result).toBe("feat: add feature")
|
||||
})
|
||||
|
||||
it("strips language-tagged markdown code blocks from the result", async () => {
|
||||
mockSingleCompletionHandler.mockResolvedValue("```text\nfix: resolve null check\n```")
|
||||
|
||||
const result = await generateCommitMessageFromDiff(mockApiConfig, "some diff")
|
||||
|
||||
expect(result).toBe("fix: resolve null check")
|
||||
})
|
||||
|
||||
it("propagates errors from the completion handler", async () => {
|
||||
mockSingleCompletionHandler.mockRejectedValue(new Error("API Error"))
|
||||
|
||||
|
|
@ -66,28 +96,62 @@ describe("commit-message-generator", () => {
|
|||
|
||||
describe("getGitDiff", () => {
|
||||
it("returns staged diff when available", async () => {
|
||||
const { exec } = await import("child_process")
|
||||
const mockExec = vi.mocked(exec) as any
|
||||
mockExec.mockImplementation(
|
||||
mockExecImpl.mockImplementation(
|
||||
(
|
||||
cmd: string,
|
||||
_opts: any,
|
||||
callback?: (err: Error | null, result: { stdout: string; stderr: string }) => void,
|
||||
_opts: unknown,
|
||||
callback: (err: Error | null, result: { stdout: string; stderr: string }) => void,
|
||||
) => {
|
||||
if (callback) {
|
||||
if (cmd === "git diff --cached") {
|
||||
callback(null, { stdout: "staged changes", stderr: "" })
|
||||
} else {
|
||||
callback(null, { stdout: "", stderr: "" })
|
||||
}
|
||||
if (cmd.includes("--cached")) {
|
||||
callback(null, { stdout: "staged changes", stderr: "" })
|
||||
} else {
|
||||
callback(null, { stdout: "unstaged changes", stderr: "" })
|
||||
}
|
||||
return { stdout: cmd === "git diff --cached" ? "staged changes" : "", stderr: "" }
|
||||
},
|
||||
)
|
||||
|
||||
// Since we mock promisify, exec is already "promisified" via our mock
|
||||
// The actual function uses execAsync which is promisify(exec)
|
||||
// We need to test the logic differently since promisify is mocked
|
||||
const result = await getGitDiff("/workspace")
|
||||
|
||||
expect(result).toBe("staged changes")
|
||||
expect(mockExecImpl).toHaveBeenCalledWith(
|
||||
"git diff --cached --no-color",
|
||||
expect.objectContaining({ cwd: "/workspace" }),
|
||||
expect.any(Function),
|
||||
)
|
||||
})
|
||||
|
||||
it("falls back to unstaged diff when nothing is staged", async () => {
|
||||
mockExecImpl.mockImplementation(
|
||||
(
|
||||
cmd: string,
|
||||
_opts: unknown,
|
||||
callback: (err: Error | null, result: { stdout: string; stderr: string }) => void,
|
||||
) => {
|
||||
if (cmd.includes("--cached")) {
|
||||
callback(null, { stdout: "", stderr: "" })
|
||||
} else {
|
||||
callback(null, { stdout: "unstaged changes", stderr: "" })
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const result = await getGitDiff("/workspace")
|
||||
|
||||
expect(result).toBe("unstaged changes")
|
||||
})
|
||||
|
||||
it("throws an error when git command fails", async () => {
|
||||
mockExecImpl.mockImplementation(
|
||||
(
|
||||
_cmd: string,
|
||||
_opts: unknown,
|
||||
callback: (err: Error | null, result: { stdout: string; stderr: string }) => void,
|
||||
) => {
|
||||
callback(new Error("git not found"), { stdout: "", stderr: "" })
|
||||
},
|
||||
)
|
||||
|
||||
await expect(getGitDiff("/workspace")).rejects.toThrow("Failed to get git diff")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { promisify } from "util"
|
|||
import type { ProviderSettings } from "@roo-code/types"
|
||||
|
||||
import { singleCompletionHandler } from "./single-completion-handler"
|
||||
import { t } from "../i18n"
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ Git diff:
|
|||
export async function getGitDiff(workspaceRoot: string): Promise<string> {
|
||||
try {
|
||||
// Try staged changes first
|
||||
const { stdout: stagedDiff } = await execAsync("git diff --cached", {
|
||||
const { stdout: stagedDiff } = await execAsync("git diff --cached --no-color", {
|
||||
cwd: workspaceRoot,
|
||||
maxBuffer: 1024 * 1024,
|
||||
})
|
||||
|
|
@ -40,7 +41,7 @@ export async function getGitDiff(workspaceRoot: string): Promise<string> {
|
|||
}
|
||||
|
||||
// Fall back to unstaged changes
|
||||
const { stdout: unstagedDiff } = await execAsync("git diff", {
|
||||
const { stdout: unstagedDiff } = await execAsync("git diff --no-color", {
|
||||
cwd: workspaceRoot,
|
||||
maxBuffer: 1024 * 1024,
|
||||
})
|
||||
|
|
@ -64,7 +65,7 @@ export async function generateCommitMessageFromDiff(apiConfiguration: ProviderSe
|
|||
|
||||
// Clean up the result - remove any markdown formatting the model might add
|
||||
return result
|
||||
.replace(/^```[\s\S]*?\n/, "")
|
||||
.replace(/^```[^\n]*\n/, "")
|
||||
.replace(/\n```$/, "")
|
||||
.trim()
|
||||
}
|
||||
|
|
@ -87,7 +88,7 @@ export async function setScmInputBoxMessage(message: string): Promise<boolean> {
|
|||
const gitExtension = vscode.extensions.getExtension("vscode.git")
|
||||
|
||||
if (!gitExtension) {
|
||||
vscode.window.showErrorMessage("Git extension is not available.")
|
||||
vscode.window.showErrorMessage(t("common:commit.no_git_extension"))
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +96,7 @@ export async function setScmInputBoxMessage(message: string): Promise<boolean> {
|
|||
const api = git.getAPI(1)
|
||||
|
||||
if (!api || api.repositories.length === 0) {
|
||||
vscode.window.showErrorMessage("No git repository found.")
|
||||
vscode.window.showErrorMessage(t("common:commit.no_git_repo"))
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue