mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-11-07 07:57:11 +00:00
294 lines
9.5 KiB
TypeScript
294 lines
9.5 KiB
TypeScript
import * as vscode from "vscode"
|
|
|
|
import { migrateSettings } from "../migrateSettings"
|
|
|
|
// Mock vscode module
|
|
vi.mock("vscode", () => ({
|
|
window: {
|
|
showInformationMessage: vi.fn(),
|
|
createOutputChannel: vi.fn(() => ({
|
|
appendLine: vi.fn(),
|
|
})),
|
|
},
|
|
commands: {
|
|
executeCommand: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
// Mock fs module
|
|
vi.mock("fs/promises")
|
|
|
|
// Mock fs utils
|
|
vi.mock("../fs", () => ({
|
|
fileExistsAtPath: vi.fn(),
|
|
}))
|
|
|
|
// Mock yaml module
|
|
vi.mock("yaml", () => ({
|
|
parse: vi.fn((content) => JSON.parse(content)),
|
|
stringify: vi.fn((obj) => JSON.stringify(obj, null, 2)),
|
|
}))
|
|
|
|
describe("migrateSettings", () => {
|
|
let mockContext: any
|
|
let mockOutputChannel: any
|
|
let mockGlobalState: Map<string, any>
|
|
|
|
beforeEach(() => {
|
|
// Reset all mocks
|
|
vi.clearAllMocks()
|
|
|
|
// Create a mock global state
|
|
mockGlobalState = new Map()
|
|
|
|
// Create mock output channel
|
|
mockOutputChannel = {
|
|
appendLine: vi.fn(),
|
|
}
|
|
|
|
// Create mock context
|
|
mockContext = {
|
|
globalState: {
|
|
get: vi.fn((key: string) => mockGlobalState.get(key)),
|
|
update: vi.fn(async (key: string, value: any) => {
|
|
mockGlobalState.set(key, value)
|
|
}),
|
|
},
|
|
globalStorageUri: {
|
|
fsPath: "/mock/storage/path",
|
|
},
|
|
}
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks()
|
|
})
|
|
|
|
describe("default commands migration", () => {
|
|
it("should only run migration once", async () => {
|
|
// Set up initial state with old default commands
|
|
const initialCommands = ["npm install", "npm test", "tsc", "git log"]
|
|
mockGlobalState.set("allowedCommands", initialCommands)
|
|
|
|
// Mock file system
|
|
const { fileExistsAtPath } = await import("../fs")
|
|
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
|
|
|
// Run migration first time
|
|
await migrateSettings(mockContext, mockOutputChannel)
|
|
|
|
// Check that old default commands were removed
|
|
expect(mockGlobalState.get("allowedCommands")).toEqual(["git log"])
|
|
|
|
// Check that migration was marked as complete
|
|
expect(mockContext.globalState.update).toHaveBeenCalledWith("defaultCommandsMigrationCompleted", true)
|
|
|
|
// Reset mocks but keep the migration flag
|
|
mockGlobalState.set("defaultCommandsMigrationCompleted", true)
|
|
mockGlobalState.set("allowedCommands", ["npm install", "npm test"])
|
|
vi.mocked(mockContext.globalState.update).mockClear()
|
|
|
|
// Run migration again
|
|
await migrateSettings(mockContext, mockOutputChannel)
|
|
|
|
// Verify commands were NOT modified the second time
|
|
expect(mockGlobalState.get("allowedCommands")).toEqual(["npm install", "npm test"])
|
|
expect(mockContext.globalState.update).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it("should remove npm install, npm test, and tsc from allowed commands", async () => {
|
|
// Set up initial state with old default commands
|
|
const initialCommands = ["git log", "npm install", "npm test", "tsc", "git diff", "echo hello"]
|
|
mockGlobalState.set("allowedCommands", initialCommands)
|
|
|
|
// Mock file system to indicate no settings directory exists
|
|
const { fileExistsAtPath } = await import("../fs")
|
|
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
|
|
|
// Run migration
|
|
await migrateSettings(mockContext, mockOutputChannel)
|
|
|
|
// Check that old default commands were removed
|
|
const updatedCommands = mockGlobalState.get("allowedCommands")
|
|
expect(updatedCommands).toEqual(["git log", "git diff", "echo hello"])
|
|
|
|
// Verify the update was called
|
|
expect(mockContext.globalState.update).toHaveBeenCalledWith("allowedCommands", [
|
|
"git log",
|
|
"git diff",
|
|
"echo hello",
|
|
])
|
|
|
|
// Verify migration was marked as complete
|
|
expect(mockContext.globalState.update).toHaveBeenCalledWith("defaultCommandsMigrationCompleted", true)
|
|
|
|
// No notification should be shown
|
|
expect(vscode.window.showInformationMessage).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it("should not remove commands with arguments (only exact matches)", async () => {
|
|
// Set up initial state with commands that have arguments
|
|
const initialCommands = [
|
|
"npm install express",
|
|
"npm test --coverage",
|
|
"tsc --watch",
|
|
"npm list",
|
|
"npm view",
|
|
"yarn list",
|
|
"git status",
|
|
]
|
|
mockGlobalState.set("allowedCommands", initialCommands)
|
|
|
|
// Mock file system
|
|
const { fileExistsAtPath } = await import("../fs")
|
|
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
|
|
|
// Run migration
|
|
await migrateSettings(mockContext, mockOutputChannel)
|
|
|
|
// Check that commands with arguments were NOT removed (only exact matches are removed)
|
|
const updatedCommands = mockGlobalState.get("allowedCommands")
|
|
expect(updatedCommands).toEqual([
|
|
"npm install express",
|
|
"npm test --coverage",
|
|
"tsc --watch",
|
|
"npm list",
|
|
"npm view",
|
|
"yarn list",
|
|
"git status",
|
|
])
|
|
|
|
// Migration should still be marked as complete
|
|
expect(mockContext.globalState.update).toHaveBeenCalledWith("defaultCommandsMigrationCompleted", true)
|
|
|
|
// No notification should be shown
|
|
expect(vscode.window.showInformationMessage).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it("should handle case-insensitive matching", async () => {
|
|
// Set up initial state with mixed case commands
|
|
const initialCommands = ["NPM INSTALL", "Npm Test", "TSC", "git log"]
|
|
mockGlobalState.set("allowedCommands", initialCommands)
|
|
|
|
// Mock file system
|
|
const { fileExistsAtPath } = await import("../fs")
|
|
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
|
|
|
// Run migration
|
|
await migrateSettings(mockContext, mockOutputChannel)
|
|
|
|
// Check that unsafe commands were removed regardless of case
|
|
const updatedCommands = mockGlobalState.get("allowedCommands")
|
|
expect(updatedCommands).toEqual(["git log"])
|
|
})
|
|
|
|
it("should not modify commands if no old defaults are present", async () => {
|
|
// Set up initial state with only safe commands
|
|
const initialCommands = ["git log", "git diff", "ls -la", "echo hello"]
|
|
mockGlobalState.set("allowedCommands", initialCommands)
|
|
|
|
// Mock file system
|
|
const { fileExistsAtPath } = await import("../fs")
|
|
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
|
|
|
// Run migration
|
|
await migrateSettings(mockContext, mockOutputChannel)
|
|
|
|
// Check that commands remain unchanged
|
|
const updatedCommands = mockGlobalState.get("allowedCommands")
|
|
expect(updatedCommands).toEqual(initialCommands)
|
|
|
|
// Verify no notification was shown
|
|
expect(vscode.window.showInformationMessage).not.toHaveBeenCalled()
|
|
|
|
// Verify migration was still marked as complete
|
|
expect(mockContext.globalState.update).toHaveBeenCalledWith("defaultCommandsMigrationCompleted", true)
|
|
})
|
|
|
|
it("should handle missing or invalid allowedCommands gracefully", async () => {
|
|
// Test with no allowedCommands set
|
|
const { fileExistsAtPath } = await import("../fs")
|
|
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
|
|
|
await migrateSettings(mockContext, mockOutputChannel)
|
|
// Should still mark migration as complete
|
|
expect(mockContext.globalState.update).toHaveBeenCalledWith("defaultCommandsMigrationCompleted", true)
|
|
|
|
// Verify appropriate log messages
|
|
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith(
|
|
expect.stringContaining("marking migration as complete"),
|
|
)
|
|
})
|
|
|
|
it("should handle non-array allowedCommands gracefully", async () => {
|
|
// Test with non-array value
|
|
mockGlobalState.set("allowedCommands", "not an array")
|
|
|
|
const { fileExistsAtPath } = await import("../fs")
|
|
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
|
|
|
await migrateSettings(mockContext, mockOutputChannel)
|
|
|
|
// Should still mark migration as complete
|
|
expect(mockContext.globalState.update).toHaveBeenCalledWith("defaultCommandsMigrationCompleted", true)
|
|
|
|
// Verify appropriate log messages
|
|
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith(
|
|
expect.stringContaining("marking migration as complete"),
|
|
)
|
|
})
|
|
|
|
it("should handle errors gracefully", async () => {
|
|
// Set up state
|
|
mockGlobalState.set("allowedCommands", ["npm install"])
|
|
|
|
// Make update throw an error
|
|
mockContext.globalState.update = vi.fn().mockRejectedValue(new Error("Update failed"))
|
|
|
|
// Mock file system
|
|
const { fileExistsAtPath } = await import("../fs")
|
|
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
|
|
|
// Run migration - should not throw
|
|
await expect(migrateSettings(mockContext, mockOutputChannel)).resolves.toBeUndefined()
|
|
|
|
// Verify error was logged
|
|
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith(
|
|
expect.stringContaining("[Default Commands Migration] Error"),
|
|
)
|
|
})
|
|
|
|
it("should only remove exact matches, not commands with arguments", async () => {
|
|
// Set up initial state with exact matches and commands with arguments
|
|
const initialCommands = [
|
|
"npm install", // exact match - should be removed
|
|
"npm install --save-dev typescript", // has arguments - should NOT be removed
|
|
"npm test", // exact match - should be removed
|
|
"npm test --coverage", // has arguments - should NOT be removed
|
|
"tsc", // exact match - should be removed
|
|
"tsc --noEmit", // has arguments - should NOT be removed
|
|
"git log --oneline",
|
|
]
|
|
mockGlobalState.set("allowedCommands", initialCommands)
|
|
|
|
// Mock file system
|
|
const { fileExistsAtPath } = await import("../fs")
|
|
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
|
|
|
// Run migration
|
|
await migrateSettings(mockContext, mockOutputChannel)
|
|
|
|
// Check that only exact matches were removed
|
|
const updatedCommands = mockGlobalState.get("allowedCommands")
|
|
expect(updatedCommands).toEqual([
|
|
"npm install --save-dev typescript",
|
|
"npm test --coverage",
|
|
"tsc --noEmit",
|
|
"git log --oneline",
|
|
])
|
|
|
|
// No notification should be shown
|
|
expect(vscode.window.showInformationMessage).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
})
|