contextProxy.test.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import * as vscode from "vscode"
  2. import { ContextProxy } from "../contextProxy"
  3. import { logger } from "../../utils/logging"
  4. import { GLOBAL_STATE_KEYS, SECRET_KEYS } from "../../shared/globalState"
  5. // Mock shared/globalState
  6. jest.mock("../../shared/globalState", () => ({
  7. GLOBAL_STATE_KEYS: ["apiProvider", "apiModelId", "mode"],
  8. SECRET_KEYS: ["apiKey", "openAiApiKey"],
  9. }))
  10. // Mock VSCode API
  11. jest.mock("vscode", () => ({
  12. Uri: {
  13. file: jest.fn((path) => ({ path })),
  14. },
  15. ExtensionMode: {
  16. Development: 1,
  17. Production: 2,
  18. Test: 3,
  19. },
  20. }))
  21. describe("ContextProxy", () => {
  22. let proxy: ContextProxy
  23. let mockContext: any
  24. let mockGlobalState: any
  25. let mockSecrets: any
  26. beforeEach(() => {
  27. // Reset mocks
  28. jest.clearAllMocks()
  29. // Mock globalState
  30. mockGlobalState = {
  31. get: jest.fn(),
  32. update: jest.fn().mockResolvedValue(undefined),
  33. }
  34. // Mock secrets
  35. mockSecrets = {
  36. get: jest.fn().mockResolvedValue("test-secret"),
  37. store: jest.fn().mockResolvedValue(undefined),
  38. delete: jest.fn().mockResolvedValue(undefined),
  39. }
  40. // Mock the extension context
  41. mockContext = {
  42. globalState: mockGlobalState,
  43. secrets: mockSecrets,
  44. extensionUri: { path: "/test/extension" },
  45. extensionPath: "/test/extension",
  46. globalStorageUri: { path: "/test/storage" },
  47. logUri: { path: "/test/logs" },
  48. extension: { packageJSON: { version: "1.0.0" } },
  49. extensionMode: vscode.ExtensionMode.Development,
  50. }
  51. // Create proxy instance
  52. proxy = new ContextProxy(mockContext)
  53. })
  54. describe("read-only pass-through properties", () => {
  55. it("should return extension properties from the original context", () => {
  56. expect(proxy.extensionUri).toBe(mockContext.extensionUri)
  57. expect(proxy.extensionPath).toBe(mockContext.extensionPath)
  58. expect(proxy.globalStorageUri).toBe(mockContext.globalStorageUri)
  59. expect(proxy.logUri).toBe(mockContext.logUri)
  60. expect(proxy.extension).toBe(mockContext.extension)
  61. expect(proxy.extensionMode).toBe(mockContext.extensionMode)
  62. })
  63. })
  64. describe("constructor", () => {
  65. it("should initialize state cache with all global state keys", () => {
  66. expect(mockGlobalState.get).toHaveBeenCalledTimes(GLOBAL_STATE_KEYS.length)
  67. for (const key of GLOBAL_STATE_KEYS) {
  68. expect(mockGlobalState.get).toHaveBeenCalledWith(key)
  69. }
  70. })
  71. it("should initialize secret cache with all secret keys", () => {
  72. expect(mockSecrets.get).toHaveBeenCalledTimes(SECRET_KEYS.length)
  73. for (const key of SECRET_KEYS) {
  74. expect(mockSecrets.get).toHaveBeenCalledWith(key)
  75. }
  76. })
  77. })
  78. describe("getGlobalState", () => {
  79. it("should return value from cache when it exists", async () => {
  80. // Manually set a value in the cache
  81. await proxy.updateGlobalState("test-key", "cached-value")
  82. // Should return the cached value
  83. const result = proxy.getGlobalState("test-key")
  84. expect(result).toBe("cached-value")
  85. // Original context should be called once during updateGlobalState
  86. expect(mockGlobalState.get).toHaveBeenCalledTimes(GLOBAL_STATE_KEYS.length) // Only from initialization
  87. })
  88. it("should handle default values correctly", async () => {
  89. // No value in cache
  90. const result = proxy.getGlobalState("unknown-key", "default-value")
  91. expect(result).toBe("default-value")
  92. })
  93. })
  94. describe("updateGlobalState", () => {
  95. it("should update state directly in original context", async () => {
  96. await proxy.updateGlobalState("test-key", "new-value")
  97. // Should have called original context
  98. expect(mockGlobalState.update).toHaveBeenCalledWith("test-key", "new-value")
  99. // Should have stored the value in cache
  100. const storedValue = await proxy.getGlobalState("test-key")
  101. expect(storedValue).toBe("new-value")
  102. })
  103. })
  104. describe("getSecret", () => {
  105. it("should return value from cache when it exists", async () => {
  106. // Manually set a value in the cache
  107. await proxy.storeSecret("api-key", "cached-secret")
  108. // Should return the cached value
  109. const result = proxy.getSecret("api-key")
  110. expect(result).toBe("cached-secret")
  111. })
  112. })
  113. describe("storeSecret", () => {
  114. it("should store secret directly in original context", async () => {
  115. await proxy.storeSecret("api-key", "new-secret")
  116. // Should have called original context
  117. expect(mockSecrets.store).toHaveBeenCalledWith("api-key", "new-secret")
  118. // Should have stored the value in cache
  119. const storedValue = await proxy.getSecret("api-key")
  120. expect(storedValue).toBe("new-secret")
  121. })
  122. it("should handle undefined value for secret deletion", async () => {
  123. await proxy.storeSecret("api-key", undefined)
  124. // Should have called delete on original context
  125. expect(mockSecrets.delete).toHaveBeenCalledWith("api-key")
  126. // Should have stored undefined in cache
  127. const storedValue = await proxy.getSecret("api-key")
  128. expect(storedValue).toBeUndefined()
  129. })
  130. })
  131. })