11import * as vscode from "vscode"
22import { ContextProxy } from "../contextProxy"
33import { logger } from "../../utils/logging"
4+ import { GLOBAL_STATE_KEYS , SECRET_KEYS } from "../../shared/globalState"
45
56// Mock the logger
67jest . 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
1623jest . 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