Skip to content

Commit 9bbd902

Browse files
committed
update new way to manage state
1 parent 0a06344 commit 9bbd902

File tree

5 files changed

+124
-286
lines changed

5 files changed

+124
-286
lines changed
Lines changed: 53 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from "vscode"
22
import { ContextProxy } from "../contextProxy"
33
import { logger } from "../../utils/logging"
4+
import { GLOBAL_STATE_KEYS, SECRET_KEYS } from "../../shared/globalState"
45

56
// Mock the logger
67
jest.mock("../../utils/logging", () => ({
@@ -12,6 +13,12 @@ jest.mock("../../utils/logging", () => ({
1213
},
1314
}))
1415

16+
// Mock shared/globalState
17+
jest.mock("../../shared/globalState", () => ({
18+
GLOBAL_STATE_KEYS: ["apiProvider", "apiModelId", "mode"],
19+
SECRET_KEYS: ["apiKey", "openAiApiKey"],
20+
}))
21+
1522
// Mock VSCode API
1623
jest.mock("vscode", () => ({
1724
Uri: {
@@ -42,7 +49,7 @@ describe("ContextProxy", () => {
4249

4350
// Mock secrets
4451
mockSecrets = {
45-
get: jest.fn(),
52+
get: jest.fn().mockResolvedValue("test-secret"),
4653
store: jest.fn().mockResolvedValue(undefined),
4754
delete: jest.fn().mockResolvedValue(undefined),
4855
}
@@ -74,209 +81,93 @@ describe("ContextProxy", () => {
7481
})
7582
})
7683

77-
describe("getGlobalState", () => {
78-
it("should return pending change when it exists", async () => {
79-
// Set up a pending change
80-
await proxy.updateGlobalState("test-key", "new-value")
81-
82-
// Should return the pending value
83-
const result = await proxy.getGlobalState("test-key")
84-
expect(result).toBe("new-value")
84+
describe("constructor", () => {
85+
it("should initialize state cache with all global state keys", () => {
86+
expect(mockGlobalState.get).toHaveBeenCalledTimes(GLOBAL_STATE_KEYS.length)
87+
for (const key of GLOBAL_STATE_KEYS) {
88+
expect(mockGlobalState.get).toHaveBeenCalledWith(key)
89+
}
90+
})
8591

86-
// Original context should not be called
87-
expect(mockGlobalState.get).not.toHaveBeenCalled()
92+
it("should initialize secret cache with all secret keys", () => {
93+
expect(mockSecrets.get).toHaveBeenCalledTimes(SECRET_KEYS.length)
94+
for (const key of SECRET_KEYS) {
95+
expect(mockSecrets.get).toHaveBeenCalledWith(key)
96+
}
8897
})
98+
})
99+
100+
describe("getGlobalState", () => {
101+
it("should return value from cache when it exists", async () => {
102+
// Manually set a value in the cache
103+
await proxy.updateGlobalState("test-key", "cached-value")
89104

90-
it("should fall back to original context when no pending change exists", async () => {
91-
// Set up original context value
92-
mockGlobalState.get.mockReturnValue("original-value")
105+
// Should return the cached value
106+
const result = proxy.getGlobalState("test-key")
107+
expect(result).toBe("cached-value")
93108

94-
// Should get from original context
95-
const result = await proxy.getGlobalState("test-key")
96-
expect(result).toBe("original-value")
97-
expect(mockGlobalState.get).toHaveBeenCalledWith("test-key", undefined)
109+
// Original context should be called once during updateGlobalState
110+
expect(mockGlobalState.get).toHaveBeenCalledTimes(GLOBAL_STATE_KEYS.length) // Only from initialization
98111
})
99112

100113
it("should handle default values correctly", async () => {
101-
// No value in either pending or original
102-
mockGlobalState.get.mockImplementation((key: string, defaultValue: any) => defaultValue)
103-
104-
// Should return the default value
105-
const result = await proxy.getGlobalState("test-key", "default-value")
114+
// No value in cache
115+
const result = proxy.getGlobalState("unknown-key", "default-value")
106116
expect(result).toBe("default-value")
107117
})
108118
})
109119

110120
describe("updateGlobalState", () => {
111-
it("should buffer changes without calling original context", async () => {
121+
it("should update state directly in original context", async () => {
112122
await proxy.updateGlobalState("test-key", "new-value")
113123

114124
// Should have called logger.debug
115-
expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining("buffering state update"))
125+
expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining("updating state for key"))
116126

117-
// Should not have called original context
118-
expect(mockGlobalState.update).not.toHaveBeenCalled()
127+
// Should have called original context
128+
expect(mockGlobalState.update).toHaveBeenCalledWith("test-key", "new-value")
119129

120-
// Should have stored the value in pendingStateChanges
130+
// Should have stored the value in cache
121131
const storedValue = await proxy.getGlobalState("test-key")
122132
expect(storedValue).toBe("new-value")
123133
})
124-
125-
it("should throw an error when context is disposed", async () => {
126-
await proxy.dispose()
127-
128-
await expect(proxy.updateGlobalState("test-key", "new-value")).rejects.toThrow(
129-
"Cannot update state on disposed context",
130-
)
131-
})
132134
})
133135

134136
describe("getSecret", () => {
135-
it("should return pending secret when it exists", async () => {
136-
// Set up a pending secret
137-
await proxy.storeSecret("api-key", "secret123")
137+
it("should return value from cache when it exists", async () => {
138+
// Manually set a value in the cache
139+
await proxy.storeSecret("api-key", "cached-secret")
138140

139-
// Should return the pending value
140-
const result = await proxy.getSecret("api-key")
141-
expect(result).toBe("secret123")
142-
143-
// Original context should not be called
144-
expect(mockSecrets.get).not.toHaveBeenCalled()
145-
})
146-
147-
it("should fall back to original context when no pending secret exists", async () => {
148-
// Set up original context value
149-
mockSecrets.get.mockResolvedValue("original-secret")
150-
151-
// Should get from original context
152-
const result = await proxy.getSecret("api-key")
153-
expect(result).toBe("original-secret")
154-
expect(mockSecrets.get).toHaveBeenCalledWith("api-key")
141+
// Should return the cached value
142+
const result = proxy.getSecret("api-key")
143+
expect(result).toBe("cached-secret")
155144
})
156145
})
157146

158147
describe("storeSecret", () => {
159-
it("should buffer secret changes without calling original context", async () => {
148+
it("should store secret directly in original context", async () => {
160149
await proxy.storeSecret("api-key", "new-secret")
161150

162151
// Should have called logger.debug
163-
expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining("buffering secret update"))
152+
expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining("storing secret for key"))
164153

165-
// Should not have called original context
166-
expect(mockSecrets.store).not.toHaveBeenCalled()
154+
// Should have called original context
155+
expect(mockSecrets.store).toHaveBeenCalledWith("api-key", "new-secret")
167156

168-
// Should have stored the value in pendingSecretChanges
157+
// Should have stored the value in cache
169158
const storedValue = await proxy.getSecret("api-key")
170159
expect(storedValue).toBe("new-secret")
171160
})
172161

173162
it("should handle undefined value for secret deletion", async () => {
174163
await proxy.storeSecret("api-key", undefined)
175164

176-
// Should have stored undefined in pendingSecretChanges
165+
// Should have called delete on original context
166+
expect(mockSecrets.delete).toHaveBeenCalledWith("api-key")
167+
168+
// Should have stored undefined in cache
177169
const storedValue = await proxy.getSecret("api-key")
178170
expect(storedValue).toBeUndefined()
179171
})
180-
181-
it("should throw an error when context is disposed", async () => {
182-
await proxy.dispose()
183-
184-
await expect(proxy.storeSecret("api-key", "new-secret")).rejects.toThrow(
185-
"Cannot store secret on disposed context",
186-
)
187-
})
188-
})
189-
190-
describe("saveChanges", () => {
191-
it("should apply state changes to original context", async () => {
192-
// Set up pending changes
193-
await proxy.updateGlobalState("key1", "value1")
194-
await proxy.updateGlobalState("key2", "value2")
195-
196-
// Save changes
197-
await proxy.saveChanges()
198-
199-
// Should have called update on original context
200-
expect(mockGlobalState.update).toHaveBeenCalledTimes(2)
201-
expect(mockGlobalState.update).toHaveBeenCalledWith("key1", "value1")
202-
expect(mockGlobalState.update).toHaveBeenCalledWith("key2", "value2")
203-
204-
// Should have cleared pending changes
205-
expect(proxy.hasPendingChanges()).toBe(false)
206-
})
207-
208-
it("should apply secret changes to original context", async () => {
209-
// Set up pending changes
210-
await proxy.storeSecret("secret1", "value1")
211-
await proxy.storeSecret("secret2", undefined)
212-
213-
// Save changes
214-
await proxy.saveChanges()
215-
216-
// Should have called store and delete on original context
217-
expect(mockSecrets.store).toHaveBeenCalledTimes(1)
218-
expect(mockSecrets.store).toHaveBeenCalledWith("secret1", "value1")
219-
expect(mockSecrets.delete).toHaveBeenCalledTimes(1)
220-
expect(mockSecrets.delete).toHaveBeenCalledWith("secret2")
221-
222-
// Should have cleared pending changes
223-
expect(proxy.hasPendingChanges()).toBe(false)
224-
})
225-
226-
it("should do nothing when there are no pending changes", async () => {
227-
await proxy.saveChanges()
228-
229-
expect(mockGlobalState.update).not.toHaveBeenCalled()
230-
expect(mockSecrets.store).not.toHaveBeenCalled()
231-
expect(mockSecrets.delete).not.toHaveBeenCalled()
232-
})
233-
234-
it("should throw an error when context is disposed", async () => {
235-
await proxy.dispose()
236-
237-
await expect(proxy.saveChanges()).rejects.toThrow("Cannot save changes on disposed context")
238-
})
239-
})
240-
241-
describe("dispose", () => {
242-
it("should save pending changes to original context", async () => {
243-
// Set up pending changes
244-
await proxy.updateGlobalState("key1", "value1")
245-
await proxy.storeSecret("secret1", "value1")
246-
247-
// Dispose
248-
await proxy.dispose()
249-
250-
// Should have saved changes
251-
expect(mockGlobalState.update).toHaveBeenCalledWith("key1", "value1")
252-
expect(mockSecrets.store).toHaveBeenCalledWith("secret1", "value1")
253-
254-
// Should be marked as disposed
255-
expect(proxy.hasPendingChanges()).toBe(false)
256-
})
257-
})
258-
259-
describe("hasPendingChanges", () => {
260-
it("should return false when no changes are pending", () => {
261-
expect(proxy.hasPendingChanges()).toBe(false)
262-
})
263-
264-
it("should return true when state changes are pending", async () => {
265-
await proxy.updateGlobalState("key", "value")
266-
expect(proxy.hasPendingChanges()).toBe(true)
267-
})
268-
269-
it("should return true when secret changes are pending", async () => {
270-
await proxy.storeSecret("key", "value")
271-
expect(proxy.hasPendingChanges()).toBe(true)
272-
})
273-
274-
it("should return false after changes are saved", async () => {
275-
await proxy.updateGlobalState("key", "value")
276-
expect(proxy.hasPendingChanges()).toBe(true)
277-
278-
await proxy.saveChanges()
279-
expect(proxy.hasPendingChanges()).toBe(false)
280-
})
281172
})
282173
})

0 commit comments

Comments
 (0)