@@ -8,6 +8,12 @@ const storeGet = vi.fn();
88const storeSet = vi . fn ( ) ;
99const storeDelete = vi . fn ( ) ;
1010
11+ type AsyncSafeStorage = {
12+ encryptStringAsync : ( value : string ) => Promise < Buffer > ;
13+ decryptStringAsync : ( encrypted : Buffer ) => Promise < { result : string ; shouldReEncrypt : boolean } > ;
14+ isAsyncEncryptionAvailable : ( ) => Promise < boolean > ;
15+ } ;
16+
1117// `safeStorage` starts empty; each test installs only the methods for the backend it exercises.
1218// This mirrors reality: Electron < 42 has no async methods at all, so `resolveCipher` only takes the
1319// async path when both the methods exist and `isAsyncEncryptionAvailable()` confirms it.
@@ -24,6 +30,7 @@ vi.mock('electron-store', () => ({
2430} ) ) ;
2531
2632const ss = safeStorage as unknown as Record < string , unknown > ;
33+ const asyncSafeStorage = safeStorage as typeof safeStorage & AsyncSafeStorage ;
2734
2835/** Installs the synchronous `safeStorage` API. */
2936function installSync ( { available = true } : { available ?: boolean } = { } ) {
@@ -134,28 +141,28 @@ describe('getItem', () => {
134141 installSync ( ) ;
135142 installAsync ( ) ;
136143 storeGet . mockReturnValue ( `enc:${ Buffer . from ( 'cipher' ) . toString ( 'base64' ) } ` ) ;
137- vi . mocked ( safeStorage . decryptStringAsync ) . mockResolvedValue ( { result : 'jwt' , shouldReEncrypt : false } ) ;
144+ vi . mocked ( asyncSafeStorage . decryptStringAsync ) . mockResolvedValue ( { result : 'jwt' , shouldReEncrypt : false } ) ;
138145
139146 await expect ( storage ( ) . getItem ( 'token-key' ) ) . resolves . toBe ( 'jwt' ) ;
140- expect ( safeStorage . decryptStringAsync ) . toHaveBeenCalledWith ( Buffer . from ( 'cipher' ) ) ;
147+ expect ( asyncSafeStorage . decryptStringAsync ) . toHaveBeenCalledWith ( Buffer . from ( 'cipher' ) ) ;
141148 } ) ;
142149
143150 it ( 're-encrypts and re-saves when the OS key has rotated (shouldReEncrypt)' , async ( ) => {
144151 installSync ( ) ;
145152 installAsync ( ) ;
146153 storeGet . mockReturnValue ( `enc:${ Buffer . from ( 'old-cipher' ) . toString ( 'base64' ) } ` ) ;
147- vi . mocked ( safeStorage . decryptStringAsync ) . mockResolvedValue ( { result : 'jwt' , shouldReEncrypt : true } ) ;
154+ vi . mocked ( asyncSafeStorage . decryptStringAsync ) . mockResolvedValue ( { result : 'jwt' , shouldReEncrypt : true } ) ;
148155
149156 await expect ( storage ( ) . getItem ( 'token-key' ) ) . resolves . toBe ( 'jwt' ) ;
150- expect ( safeStorage . encryptStringAsync ) . toHaveBeenCalledWith ( 'jwt' ) ;
157+ expect ( asyncSafeStorage . encryptStringAsync ) . toHaveBeenCalledWith ( 'jwt' ) ;
151158 expect ( storeSet ) . toHaveBeenCalledWith ( 'token-key' , `enc:${ Buffer . from ( 'enc(jwt)' ) . toString ( 'base64' ) } ` ) ;
152159 } ) ;
153160
154161 it ( 'returns null without deleting the entry when decryption rejects' , async ( ) => {
155162 installSync ( ) ;
156163 installAsync ( ) ;
157164 storeGet . mockReturnValue ( `enc:${ Buffer . from ( 'cipher' ) . toString ( 'base64' ) } ` ) ;
158- vi . mocked ( safeStorage . decryptStringAsync ) . mockRejectedValue ( new Error ( 'decrypt failed' ) ) ;
165+ vi . mocked ( asyncSafeStorage . decryptStringAsync ) . mockRejectedValue ( new Error ( 'decrypt failed' ) ) ;
159166
160167 await expect ( storage ( ) . getItem ( 'token-key' ) ) . resolves . toBeNull ( ) ;
161168 expect ( storeDelete ) . not . toHaveBeenCalled ( ) ;
@@ -170,7 +177,7 @@ describe('getItem', () => {
170177 await expect ( storage ( ) . getItem ( 'token-key' ) ) . resolves . toBe ( 'jwt' ) ;
171178 expect ( safeStorage . decryptString ) . toHaveBeenCalledWith ( Buffer . from ( 'cipher' ) ) ;
172179 // Critical: calling the async crypto while unavailable crashes the process on Electron 42.x.
173- expect ( safeStorage . decryptStringAsync ) . not . toHaveBeenCalled ( ) ;
180+ expect ( asyncSafeStorage . decryptStringAsync ) . not . toHaveBeenCalled ( ) ;
174181 } ) ;
175182 } ) ;
176183} ) ;
@@ -191,7 +198,7 @@ describe('setItem', () => {
191198
192199 await storage ( ) . setItem ( 'token-key' , 'jwt' ) ;
193200
194- expect ( safeStorage . encryptStringAsync ) . toHaveBeenCalledWith ( 'jwt' ) ;
201+ expect ( asyncSafeStorage . encryptStringAsync ) . toHaveBeenCalledWith ( 'jwt' ) ;
195202 expect ( storeSet ) . toHaveBeenCalledWith ( 'token-key' , `enc:${ Buffer . from ( 'enc(jwt)' ) . toString ( 'base64' ) } ` ) ;
196203 } ) ;
197204
@@ -202,7 +209,7 @@ describe('setItem', () => {
202209 await storage ( ) . setItem ( 'token-key' , 'jwt' ) ;
203210
204211 expect ( safeStorage . encryptString ) . toHaveBeenCalledWith ( 'jwt' ) ;
205- expect ( safeStorage . encryptStringAsync ) . not . toHaveBeenCalled ( ) ;
212+ expect ( asyncSafeStorage . encryptStringAsync ) . not . toHaveBeenCalled ( ) ;
206213 expect ( storeSet ) . toHaveBeenCalledWith ( 'token-key' , `enc:${ Buffer . from ( 'enc(jwt)' ) . toString ( 'base64' ) } ` ) ;
207214 } ) ;
208215
@@ -290,7 +297,7 @@ describe('setItem', () => {
290297 await storage ( ) . setItem ( 'token-key' , 'jwt' ) ;
291298
292299 expect ( safeStorage . encryptString ) . toHaveBeenCalledWith ( 'jwt' ) ;
293- expect ( safeStorage . encryptStringAsync ) . not . toHaveBeenCalled ( ) ;
300+ expect ( asyncSafeStorage . encryptStringAsync ) . not . toHaveBeenCalled ( ) ;
294301 } ) ;
295302} ) ;
296303
0 commit comments