chore: fix apply-diff test and add ntc apply-diff test

This commit is contained in:
Dennise Bartlett 2025-12-18 11:47:00 +00:00
parent a42387e0af
commit 218ebbd44c
4 changed files with 1308 additions and 61 deletions

50
.vscode/launch.json vendored
View file

@ -24,6 +24,56 @@
"group": "tasks",
"order": 1
}
},
{
"name": "Debug E2E Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"${workspaceFolder}/apps/vscode-e2e/test-workspace",
"--extensionDevelopmentPath=${workspaceFolder}/src",
"--extensionTestsPath=${workspaceFolder}/apps/vscode-e2e/out/suite/index"
],
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/src/dist/**/*.js", "${workspaceFolder}/apps/vscode-e2e/out/**/*.js"],
"preLaunchTask": "build-e2e-tests",
"envFile": "${workspaceFolder}/apps/vscode-e2e/.env.local",
"env": {
"NODE_ENV": "development",
"VSCODE_DEBUG_MODE": "true"
},
"resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"],
"presentation": {
"hidden": false,
"group": "tasks",
"order": 2
}
},
{
"name": "Debug E2E Tests (Quick - extension pre-built)",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"${workspaceFolder}/apps/vscode-e2e/test-workspace",
"--extensionDevelopmentPath=${workspaceFolder}/src",
"--extensionTestsPath=${workspaceFolder}/apps/vscode-e2e/out/suite/index"
],
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/src/dist/**/*.js", "${workspaceFolder}/apps/vscode-e2e/out/**/*.js"],
"preLaunchTask": "compile-e2e-only",
"envFile": "${workspaceFolder}/apps/vscode-e2e/.env.local",
"env": {
"NODE_ENV": "development",
"VSCODE_DEBUG_MODE": "true"
},
"resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"],
"presentation": {
"hidden": false,
"group": "tasks",
"order": 4
}
}
]
}

57
.vscode/tasks.json vendored
View file

@ -69,6 +69,63 @@
"group": "watch",
"reveal": "always"
}
},
{
"label": "build-e2e-tests",
"dependsOn": ["build-e2e:bundle", "build-e2e:webview", "build-e2e:compile"],
"dependsOrder": "sequence",
"group": "build",
"problemMatcher": []
},
{
"label": "build-e2e:bundle",
"type": "shell",
"command": "pnpm -w bundle",
"group": "build",
"problemMatcher": [],
"presentation": {
"reveal": "always",
"panel": "shared"
}
},
{
"label": "build-e2e:webview",
"type": "shell",
"command": "pnpm --filter @roo-code/vscode-webview build",
"group": "build",
"problemMatcher": [],
"presentation": {
"reveal": "always",
"panel": "shared"
}
},
{
"label": "build-e2e:compile",
"type": "shell",
"command": "npx rimraf out; npx tsc -p tsconfig.json",
"options": {
"cwd": "${workspaceFolder}/apps/vscode-e2e"
},
"group": "build",
"problemMatcher": "$tsc",
"presentation": {
"reveal": "always",
"panel": "shared"
}
},
{
"label": "compile-e2e-only",
"type": "shell",
"command": "npx rimraf out; npx tsc -p tsconfig.json",
"options": {
"cwd": "${workspaceFolder}/apps/vscode-e2e"
},
"group": "build",
"problemMatcher": "$tsc",
"presentation": {
"reveal": "always",
"panel": "shared"
}
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,7 @@ import { RooCodeEventName, type ClineMessage } from "@roo-code/types"
import { waitFor, sleep } from "../utils"
import { setDefaultSuiteTimeout } from "../test-utils"
suite.skip("Roo Code apply_diff Tool", function () {
suite("Roo Code apply_diff Tool", function () {
setDefaultSuiteTimeout(this)
let workspaceDir: string
@ -133,6 +133,20 @@ function validateInput(input) {
// Task might not be running
}
// Reset all test files to their original content before each test
// This ensures each test starts with a known clean state, even if a previous
// test or run modified the file content
for (const [key, file] of Object.entries(testFiles)) {
if (file.path) {
try {
await fs.writeFile(file.path, file.content)
console.log(`Reset ${key} test file to original content`)
} catch (error) {
console.log(`Failed to reset ${key} test file:`, error)
}
}
}
// Small delay to ensure clean state
await sleep(100)
})
@ -173,23 +187,22 @@ function validateInput(input) {
}
if (message.type === "ask" && message.ask === "tool") {
console.log("Tool request:", message.text?.substring(0, 200))
// Check for appliedDiff tool execution
try {
const toolData = JSON.parse(message.text || "{}")
if (toolData.tool === "appliedDiff") {
applyDiffExecuted = true
console.log("apply_diff tool executed!")
}
} catch (_e) {
// Not JSON or parsing failed
}
}
if (message.type === "say" && (message.say === "completion_result" || message.say === "text")) {
console.log("AI response:", message.text?.substring(0, 200))
}
// Check for tool execution
if (message.type === "say" && message.say === "api_req_started" && message.text) {
console.log("API request started:", message.text.substring(0, 200))
try {
const requestData = JSON.parse(message.text)
if (requestData.request && requestData.request.includes("apply_diff")) {
applyDiffExecuted = true
console.log("apply_diff tool executed!")
}
} catch (e) {
console.log("Failed to parse api_req_started message:", e)
}
}
}
api.on(RooCodeEventName.Message, messageHandler)
@ -286,23 +299,22 @@ ${testFile.content}\nAssume the file exists and you can modify it directly.`,
messages.push(message)
if (message.type === "ask" && message.ask === "tool") {
console.log("Tool request:", message.text?.substring(0, 200))
// Check for appliedDiff tool execution
try {
const toolData = JSON.parse(message.text || "{}")
if (toolData.tool === "appliedDiff") {
applyDiffExecuted = true
console.log("apply_diff tool executed!")
}
} catch (_e) {
// Not JSON or parsing failed
}
}
if (message.type === "say" && message.text) {
console.log("AI response:", message.text.substring(0, 200))
}
// Check for tool execution
if (message.type === "say" && message.say === "api_req_started" && message.text) {
console.log("API request started:", message.text.substring(0, 200))
try {
const requestData = JSON.parse(message.text)
if (requestData.request && requestData.request.includes("apply_diff")) {
applyDiffExecuted = true
console.log("apply_diff tool executed!")
}
} catch (e) {
console.log("Failed to parse api_req_started message:", e)
}
}
}
api.on(RooCodeEventName.Message, messageHandler)
@ -408,21 +420,20 @@ function keepThis() {
messages.push(message)
if (message.type === "ask" && message.ask === "tool") {
console.log("Tool request:", message.text?.substring(0, 200))
}
// Check for tool execution
if (message.type === "say" && message.say === "api_req_started" && message.text) {
console.log("API request started:", message.text.substring(0, 200))
// Check for appliedDiff tool execution
try {
const requestData = JSON.parse(message.text)
if (requestData.request && requestData.request.includes("apply_diff")) {
const toolData = JSON.parse(message.text || "{}")
if (toolData.tool === "appliedDiff") {
applyDiffExecuted = true
console.log("apply_diff tool executed!")
}
} catch (e) {
console.log("Failed to parse api_req_started message:", e)
} catch (_e) {
// Not JSON or parsing failed
}
}
if (message.type === "say" && message.say === "api_req_started" && message.text) {
console.log("API request started:", message.text.substring(0, 200))
}
}
api.on(RooCodeEventName.Message, messageHandler)
@ -501,6 +512,7 @@ ${testFile.content}\nAssume the file exists and you can modify it directly.`,
let taskCompleted = false
let errorDetected = false
let applyDiffAttempted = false
let writeToFileUsed = false
// Listen for messages
const messageHandler = ({ message }: { message: ClineMessage }) => {
@ -512,25 +524,33 @@ ${testFile.content}\nAssume the file exists and you can modify it directly.`,
console.log("Error detected:", message.text)
}
// Check if AI mentions it couldn't find the content
if (message.type === "say" && message.text?.toLowerCase().includes("could not find")) {
errorDetected = true
console.log("AI reported search failure:", message.text)
}
// Check for tool execution attempt
if (message.type === "say" && message.say === "api_req_started" && message.text) {
console.log("API request started:", message.text.substring(0, 200))
if (message.type === "ask" && message.ask === "tool") {
console.log("Tool ASK request:", message.text?.substring(0, 500))
try {
const requestData = JSON.parse(message.text)
if (requestData.request && requestData.request.includes("apply_diff")) {
const toolData = JSON.parse(message.text || "{}")
if (toolData.tool === "appliedDiff") {
applyDiffAttempted = true
console.log("apply_diff tool attempted!")
console.log("apply_diff tool attempted via ASK!")
}
// Detect if write_to_file was used (shows as editedExistingFile or newFileCreated)
if (toolData.tool === "editedExistingFile" || toolData.tool === "newFileCreated") {
writeToFileUsed = true
console.log("write_to_file tool used!")
}
} catch (e) {
console.log("Failed to parse api_req_started message:", e)
console.error(e)
}
}
// Check for diff_error which indicates apply_diff was attempted but failed
if (message.type === "say" && message.say === "diff_error") {
applyDiffAttempted = true
}
if (message.type === "say" && message.say === "api_req_started" && message.text) {
console.log("API request started:", message.text.substring(0, 200))
}
}
api.on(RooCodeEventName.Message, messageHandler)
@ -552,6 +572,9 @@ ${testFile.content}\nAssume the file exists and you can modify it directly.`,
let taskId: string
try {
// Start task with invalid search content - file already exists
// NOTE: We ask the AI to search for a pattern that doesn't exist, without telling it
// what the replacement should be. This prevents the AI from working around by
// replacing the actual content with the desired replacement.
taskId = await api.startNewTask({
configuration: {
mode: "code",
@ -560,14 +583,18 @@ ${testFile.content}\nAssume the file exists and you can modify it directly.`,
alwaysAllowReadOnly: true,
alwaysAllowReadOnlyOutsideWorkspace: true,
},
text: `Use apply_diff on the file ${testFile.name} to replace "This content does not exist" with "New content".
text: `Use apply_diff on the file ${testFile.name} to find and replace the text "PATTERN_THAT_DOES_NOT_EXIST_xyz123" with "REPLACEMENT_xyz123".
The file already exists with this content:
${testFile.content}
IMPORTANT: The search pattern "This content does not exist" is NOT in the file. When apply_diff cannot find the search pattern, it should fail gracefully and the file content should remain unchanged. Do NOT try to use write_to_file or any other tool to modify the file. Only use apply_diff, and if the search pattern is not found, report that it could not be found.
Assume the file exists and you can modify it directly.`,
CRITICAL INSTRUCTIONS:
1. You MUST attempt the apply_diff with EXACTLY the search pattern "PATTERN_THAT_DOES_NOT_EXIST_xyz123"
2. If apply_diff cannot find this exact search pattern, report that the pattern was not found
3. Do NOT use write_to_file under any circumstances
4. Do NOT use a different search pattern - use EXACTLY "PATTERN_THAT_DOES_NOT_EXIST_xyz123"
5. Do NOT modify the file in any other way
6. Simply report that the replacement could not be made because the search text was not found`,
})
console.log("Task ID:", taskId)
@ -581,13 +608,20 @@ Assume the file exists and you can modify it directly.`,
// Give time for any final operations
await sleep(2000)
// The file content should remain unchanged since the search pattern wasn't found
// Read the file content
const actualContent = await fs.readFile(testFile.path, "utf-8")
console.log("File content after task:", actualContent)
// The AI should have attempted to use apply_diff
assert.strictEqual(applyDiffAttempted, true, "apply_diff tool should have been attempted")
// The AI should NOT have used write_to_file as a fallback
assert.strictEqual(
writeToFileUsed,
false,
"write_to_file should NOT be used when apply_diff fails - the AI should report the error instead",
)
// The content should remain unchanged since the search pattern wasn't found
assert.strictEqual(
actualContent.trim(),
@ -643,25 +677,24 @@ function checkInput(input) {
}
if (message.type === "ask" && message.ask === "tool") {
console.log("Tool request:", message.text?.substring(0, 200))
}
if (message.type === "say" && (message.say === "completion_result" || message.say === "text")) {
console.log("AI response:", message.text?.substring(0, 200))
}
// Check for tool execution
if (message.type === "say" && message.say === "api_req_started" && message.text) {
console.log("API request started:", message.text.substring(0, 200))
// Check for appliedDiff tool execution
try {
const requestData = JSON.parse(message.text)
if (requestData.request && requestData.request.includes("apply_diff")) {
const toolData = JSON.parse(message.text || "{}")
if (toolData.tool === "appliedDiff") {
applyDiffExecuted = true
applyDiffCount++
console.log(`apply_diff tool executed! (count: ${applyDiffCount})`)
}
} catch (e) {
console.log("Failed to parse api_req_started message:", e)
} catch (_e) {
// Not JSON or parsing failed
}
}
if (message.type === "say" && (message.say === "completion_result" || message.say === "text")) {
console.log("AI response:", message.text?.substring(0, 200))
}
if (message.type === "say" && message.say === "api_req_started" && message.text) {
console.log("API request started:", message.text.substring(0, 200))
}
}
api.on(RooCodeEventName.Message, messageHandler)