|
1 | | -import { runSettingsMigrations, migrations, CURRENT_MIGRATION_VERSION } from "../settingsMigrations" |
| 1 | +import { |
| 2 | + runSettingsMigrations, |
| 3 | + migrations, |
| 4 | + CURRENT_MIGRATION_VERSION, |
| 5 | + clearDefaultSettings, |
| 6 | + runStartupSettingsMaintenance, |
| 7 | +} from "../settingsMigrations" |
2 | 8 | import type { ContextProxy } from "../../core/config/ContextProxy" |
3 | 9 | import type { GlobalState } from "@roo-code/types" |
| 10 | +import { settingDefaults } from "@roo-code/types" |
4 | 11 |
|
5 | 12 | // Mock the logger |
6 | 13 | vi.mock("../logging", () => ({ |
@@ -296,4 +303,136 @@ describe("settingsMigrations", () => { |
296 | 303 | expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("codebaseIndexConfig", undefined) |
297 | 304 | }) |
298 | 305 | }) |
| 306 | + |
| 307 | + describe("clearDefaultSettings", () => { |
| 308 | + it("should clear settings that match current defaults", async () => { |
| 309 | + // Setup: user has settings that match current defaults |
| 310 | + mockContextProxy.getGlobalState.mockImplementation((key: keyof GlobalState) => { |
| 311 | + if (key === "browserToolEnabled") return settingDefaults.browserToolEnabled |
| 312 | + if (key === "soundVolume") return settingDefaults.soundVolume |
| 313 | + if (key === "maxWorkspaceFiles") return settingDefaults.maxWorkspaceFiles |
| 314 | + return undefined |
| 315 | + }) |
| 316 | + |
| 317 | + const clearedCount = await clearDefaultSettings(mockContextProxy as unknown as ContextProxy) |
| 318 | + |
| 319 | + // All matching defaults should be cleared |
| 320 | + expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("browserToolEnabled", undefined) |
| 321 | + expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("soundVolume", undefined) |
| 322 | + expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("maxWorkspaceFiles", undefined) |
| 323 | + expect(clearedCount).toBe(3) |
| 324 | + }) |
| 325 | + |
| 326 | + it("should preserve custom values that don't match defaults", async () => { |
| 327 | + // Setup: user has custom values that don't match defaults |
| 328 | + mockContextProxy.getGlobalState.mockImplementation((key: keyof GlobalState) => { |
| 329 | + if (key === "browserToolEnabled") return false // default is true |
| 330 | + if (key === "soundVolume") return 0.8 // default is 0.5 |
| 331 | + if (key === "maxWorkspaceFiles") return 500 // default is 200 |
| 332 | + return undefined |
| 333 | + }) |
| 334 | + |
| 335 | + const clearedCount = await clearDefaultSettings(mockContextProxy as unknown as ContextProxy) |
| 336 | + |
| 337 | + // No settings should be cleared |
| 338 | + expect(mockContextProxy.updateGlobalState).not.toHaveBeenCalled() |
| 339 | + expect(clearedCount).toBe(0) |
| 340 | + }) |
| 341 | + |
| 342 | + it("should not clear already undefined values", async () => { |
| 343 | + // Setup: all settings are undefined |
| 344 | + mockContextProxy.getGlobalState.mockReturnValue(undefined) |
| 345 | + |
| 346 | + const clearedCount = await clearDefaultSettings(mockContextProxy as unknown as ContextProxy) |
| 347 | + |
| 348 | + // No settings should be cleared (already undefined) |
| 349 | + expect(mockContextProxy.updateGlobalState).not.toHaveBeenCalled() |
| 350 | + expect(clearedCount).toBe(0) |
| 351 | + }) |
| 352 | + |
| 353 | + it("should only clear settings in settingDefaults", async () => { |
| 354 | + // Setup: user has settings - some in defaults, some not |
| 355 | + mockContextProxy.getGlobalState.mockImplementation((key: keyof GlobalState) => { |
| 356 | + if (key === "browserToolEnabled") return settingDefaults.browserToolEnabled |
| 357 | + if (key === "customInstructions") return "my instructions" // not in settingDefaults |
| 358 | + return undefined |
| 359 | + }) |
| 360 | + |
| 361 | + await clearDefaultSettings(mockContextProxy as unknown as ContextProxy) |
| 362 | + |
| 363 | + // browserToolEnabled should be cleared |
| 364 | + expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("browserToolEnabled", undefined) |
| 365 | + |
| 366 | + // customInstructions should NOT be touched (not in settingDefaults) |
| 367 | + expect(mockContextProxy.updateGlobalState).not.toHaveBeenCalledWith("customInstructions", undefined) |
| 368 | + }) |
| 369 | + |
| 370 | + it("should handle string settings correctly", async () => { |
| 371 | + mockContextProxy.getGlobalState.mockImplementation((key: keyof GlobalState) => { |
| 372 | + if (key === "enterBehavior") return "send" // matches default |
| 373 | + if (key === "language") return "en" // matches default |
| 374 | + return undefined |
| 375 | + }) |
| 376 | + |
| 377 | + await clearDefaultSettings(mockContextProxy as unknown as ContextProxy) |
| 378 | + |
| 379 | + expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("enterBehavior", undefined) |
| 380 | + expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("language", undefined) |
| 381 | + }) |
| 382 | + |
| 383 | + it("should return the count of cleared settings", async () => { |
| 384 | + mockContextProxy.getGlobalState.mockImplementation((key: keyof GlobalState) => { |
| 385 | + if (key === "browserToolEnabled") return true // matches default |
| 386 | + if (key === "soundEnabled") return true // matches default |
| 387 | + if (key === "soundVolume") return 0.8 // does NOT match default (0.5) |
| 388 | + return undefined |
| 389 | + }) |
| 390 | + |
| 391 | + const clearedCount = await clearDefaultSettings(mockContextProxy as unknown as ContextProxy) |
| 392 | + |
| 393 | + expect(clearedCount).toBe(2) // Only browserToolEnabled and soundEnabled match |
| 394 | + }) |
| 395 | + }) |
| 396 | + |
| 397 | + describe("runStartupSettingsMaintenance", () => { |
| 398 | + it("should run both migrations and default clearing", async () => { |
| 399 | + // Setup: migration not run, and has a setting matching default |
| 400 | + mockContextProxy.getGlobalState.mockImplementation((key: keyof GlobalState) => { |
| 401 | + if (key === "settingsMigrationVersion") return 0 |
| 402 | + if (key === "browserToolEnabled") return true // matches both historical and current default |
| 403 | + return undefined |
| 404 | + }) |
| 405 | + |
| 406 | + await runStartupSettingsMaintenance(mockContextProxy as unknown as ContextProxy) |
| 407 | + |
| 408 | + // Should have updated migration version |
| 409 | + expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith( |
| 410 | + "settingsMigrationVersion", |
| 411 | + CURRENT_MIGRATION_VERSION, |
| 412 | + ) |
| 413 | + |
| 414 | + // browserToolEnabled should be cleared (by migration or clearDefaults) |
| 415 | + expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("browserToolEnabled", undefined) |
| 416 | + }) |
| 417 | + |
| 418 | + it("should run clearDefaultSettings even after migrations are complete", async () => { |
| 419 | + // Setup: migrations already complete, but has setting matching default |
| 420 | + mockContextProxy.getGlobalState.mockImplementation((key: keyof GlobalState) => { |
| 421 | + if (key === "settingsMigrationVersion") return CURRENT_MIGRATION_VERSION |
| 422 | + if (key === "soundVolume") return 0.5 // matches current default |
| 423 | + return undefined |
| 424 | + }) |
| 425 | + |
| 426 | + await runStartupSettingsMaintenance(mockContextProxy as unknown as ContextProxy) |
| 427 | + |
| 428 | + // Migration version should NOT be updated (already current) |
| 429 | + expect(mockContextProxy.updateGlobalState).not.toHaveBeenCalledWith( |
| 430 | + "settingsMigrationVersion", |
| 431 | + expect.anything(), |
| 432 | + ) |
| 433 | + |
| 434 | + // soundVolume should be cleared by clearDefaultSettings |
| 435 | + expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("soundVolume", undefined) |
| 436 | + }) |
| 437 | + }) |
299 | 438 | }) |
0 commit comments