11import * as fs from 'fs/promises' ;
22
3- import type { LDLogger } from '@launchdarkly/js-client-sdk-common' ;
4-
53import ElectronStorage , {
64 getElectronStorage ,
75 resetElectronStorage ,
@@ -17,40 +15,25 @@ jest.mock('electron', () => ({
1715const storageFile = '/user/data/ldcache' ;
1816const tempFile = `${ storageFile } .tmp` ;
1917
20- const logger : LDLogger = {
21- debug : jest . fn ( ) ,
22- info : jest . fn ( ) ,
23- warn : jest . fn ( ) ,
24- error : jest . fn ( ) ,
25- } ;
26-
2718beforeEach ( ( ) => {
2819 jest . clearAllMocks ( ) ;
2920 resetElectronStorage ( ) ;
3021} ) ;
3122
32- it ( 'handles failed initialization when clearing values ' , async ( ) => {
23+ it ( 'throws on clear when initialization failed ' , async ( ) => {
3324 ( fs . readFile as jest . Mock ) . mockRejectedValueOnce ( new Error ( 'file not found' ) ) ;
3425 ( fs . writeFile as jest . Mock ) . mockRejectedValueOnce ( new Error ( 'write failed' ) ) ;
3526
36- const storage = new ElectronStorage ( logger ) ;
37- await storage . clear ( 'key1' ) ;
38-
39- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
40- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
41- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
42- expect ( logger . error ) . toHaveBeenCalledWith ( 'Error initializing storage: write failed' ) ;
43- expect ( logger . error ) . toHaveBeenCalledWith (
44- 'Error clearing key from storage: key1, reason: Storage is not initialized' ,
45- ) ;
27+ const storage = new ElectronStorage ( ) ;
28+ await expect ( storage . clear ( 'key1' ) ) . rejects . toThrow ( 'Storage is not initialized: write failed' ) ;
4629} ) ;
4730
4831it ( 'can clear values' , async ( ) => {
4932 ( fs . readFile as jest . Mock ) . mockReturnValueOnce (
5033 JSON . stringify ( { key1 : 'value1' , key2 : 'value2' } ) ,
5134 ) ;
5235
53- const storage = new ElectronStorage ( logger ) ;
36+ const storage = new ElectronStorage ( ) ;
5437 await storage . clear ( 'key1' ) ;
5538
5639 expect ( await storage . get ( 'key1' ) ) . toBeNull ( ) ;
@@ -61,112 +44,72 @@ it('can clear values', async () => {
6144 expect . anything ( ) ,
6245 ) ;
6346 expect ( fs . rename ) . toHaveBeenCalledWith ( tempFile , storageFile ) ;
64-
65- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
66- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
67- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
68- expect ( logger . error ) . not . toHaveBeenCalled ( ) ;
6947} ) ;
7048
7149it ( 'does nothing when clearing a non-existent key' , async ( ) => {
7250 ( fs . readFile as jest . Mock ) . mockReturnValueOnce (
7351 JSON . stringify ( { key1 : 'value1' , key2 : 'value2' } ) ,
7452 ) ;
7553
76- const storage = new ElectronStorage ( logger ) ;
54+ const storage = new ElectronStorage ( ) ;
7755 await storage . clear ( 'key3' ) ;
7856
7957 expect ( fs . writeFile ) . not . toHaveBeenCalled ( ) ;
8058 expect ( fs . rename ) . not . toHaveBeenCalled ( ) ;
81-
82- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
83- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
84- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
85- expect ( logger . error ) . not . toHaveBeenCalled ( ) ;
8659} ) ;
8760
88- it ( 'handles error when clearing values ' , async ( ) => {
61+ it ( 'updates cache even when disk flush fails on clear ' , async ( ) => {
8962 ( fs . readFile as jest . Mock ) . mockReturnValueOnce (
9063 JSON . stringify ( { key1 : 'value1' , key2 : 'value2' } ) ,
9164 ) ;
9265 ( fs . writeFile as jest . Mock ) . mockRejectedValueOnce ( new Error ( 'write failed' ) ) ;
9366
94- const storage = new ElectronStorage ( logger ) ;
95- await storage . clear ( 'key1' ) ;
67+ const storage = new ElectronStorage ( ) ;
68+ // The flush error propagates so the per-client wrapper can log it
69+ await expect ( storage . clear ( 'key1' ) ) . rejects . toThrow ( 'write failed' ) ;
9670
9771 // Cache is updated synchronously — the in-memory state reflects the clear
98- // even if the disk write failed. The data will be flushed on the next write.
72+ // even if the disk write failed.
9973 expect ( await storage . get ( 'key1' ) ) . toBeNull ( ) ;
100-
101- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
102- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
103- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
104- expect ( logger . error ) . toHaveBeenCalledWith ( 'Error flushing storage to disk: write failed' ) ;
10574} ) ;
10675
107- it ( 'handles failed initialization when getting values ' , async ( ) => {
76+ it ( 'throws on get when initialization failed ' , async ( ) => {
10877 ( fs . readFile as jest . Mock ) . mockRejectedValueOnce ( new Error ( 'file not found' ) ) ;
10978 ( fs . writeFile as jest . Mock ) . mockRejectedValueOnce ( new Error ( 'write failed' ) ) ;
11079
111- const storage = new ElectronStorage ( logger ) ;
112- const value = await storage . get ( 'key1' ) ;
113-
114- expect ( value ) . toBeNull ( ) ;
115-
116- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
117- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
118- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
119- expect ( logger . error ) . toHaveBeenCalledWith ( 'Error initializing storage: write failed' ) ;
120- expect ( logger . error ) . toHaveBeenCalledWith (
121- 'Error getting key from storage: key1, reason: Storage is not initialized' ,
122- ) ;
80+ const storage = new ElectronStorage ( ) ;
81+ await expect ( storage . get ( 'key1' ) ) . rejects . toThrow ( 'Storage is not initialized: write failed' ) ;
12382} ) ;
12483
12584it ( 'can get values' , async ( ) => {
12685 ( fs . readFile as jest . Mock ) . mockReturnValueOnce (
12786 JSON . stringify ( { key1 : 'value1' , key2 : 'value2' } ) ,
12887 ) ;
12988
130- const storage = new ElectronStorage ( logger ) ;
89+ const storage = new ElectronStorage ( ) ;
13190 const value = await storage . get ( 'key1' ) ;
13291
13392 expect ( value ) . toEqual ( 'value1' ) ;
134-
135- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
136- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
137- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
138- expect ( logger . error ) . not . toHaveBeenCalled ( ) ;
13993} ) ;
14094
14195it ( 'returns null when getting a non-existent key' , async ( ) => {
14296 ( fs . readFile as jest . Mock ) . mockReturnValueOnce (
14397 JSON . stringify ( { key1 : 'value1' , key2 : 'value2' } ) ,
14498 ) ;
14599
146- const storage = new ElectronStorage ( logger ) ;
100+ const storage = new ElectronStorage ( ) ;
147101 const value = await storage . get ( 'key3' ) ;
148102
149103 expect ( value ) . toBeNull ( ) ;
150-
151- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
152- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
153- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
154- expect ( logger . error ) . not . toHaveBeenCalled ( ) ;
155104} ) ;
156105
157- it ( 'handles failed initialization when setting values ' , async ( ) => {
106+ it ( 'throws on set when initialization failed ' , async ( ) => {
158107 ( fs . readFile as jest . Mock ) . mockRejectedValueOnce ( new Error ( 'file not found' ) ) ;
159108 ( fs . writeFile as jest . Mock ) . mockRejectedValueOnce ( new Error ( 'write failed' ) ) ;
160109
161- const storage = new ElectronStorage ( logger ) ;
162- await storage . set ( 'key3' , 'value3' ) ;
163-
164- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
165- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
166- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
167- expect ( logger . error ) . toHaveBeenCalledWith ( 'Error initializing storage: write failed' ) ;
168- expect ( logger . error ) . toHaveBeenCalledWith (
169- 'Error setting key in storage: key3, reason: Storage is not initialized' ,
110+ const storage = new ElectronStorage ( ) ;
111+ await expect ( storage . set ( 'key3' , 'value3' ) ) . rejects . toThrow (
112+ 'Storage is not initialized: write failed' ,
170113 ) ;
171114} ) ;
172115
@@ -175,7 +118,7 @@ it('can set values', async () => {
175118 JSON . stringify ( { key1 : 'value1' , key2 : 'value2' } ) ,
176119 ) ;
177120
178- const storage = new ElectronStorage ( logger ) ;
121+ const storage = new ElectronStorage ( ) ;
179122 await storage . set ( 'key3' , 'value3' ) ;
180123
181124 expect ( await storage . get ( 'key3' ) ) . toEqual ( 'value3' ) ;
@@ -186,19 +129,14 @@ it('can set values', async () => {
186129 expect . anything ( ) ,
187130 ) ;
188131 expect ( fs . rename ) . toHaveBeenCalledWith ( tempFile , storageFile ) ;
189-
190- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
191- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
192- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
193- expect ( logger . error ) . not . toHaveBeenCalled ( ) ;
194132} ) ;
195133
196134it ( 'can set values with existing keys' , async ( ) => {
197135 ( fs . readFile as jest . Mock ) . mockReturnValueOnce (
198136 JSON . stringify ( { key1 : 'value1' , key2 : 'value2' } ) ,
199137 ) ;
200138
201- const storage = new ElectronStorage ( logger ) ;
139+ const storage = new ElectronStorage ( ) ;
202140 await storage . set ( 'key1' , 'new-value1' ) ;
203141
204142 expect ( await storage . get ( 'key1' ) ) . toEqual ( 'new-value1' ) ;
@@ -209,41 +147,32 @@ it('can set values with existing keys', async () => {
209147 expect . anything ( ) ,
210148 ) ;
211149 expect ( fs . rename ) . toHaveBeenCalledWith ( tempFile , storageFile ) ;
212-
213- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
214- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
215- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
216- expect ( logger . error ) . not . toHaveBeenCalled ( ) ;
217150} ) ;
218151
219- it ( 'handles error when setting values ' , async ( ) => {
152+ it ( 'updates cache even when disk flush fails on set ' , async ( ) => {
220153 ( fs . readFile as jest . Mock ) . mockReturnValueOnce (
221154 JSON . stringify ( { key1 : 'value1' , key2 : 'value2' } ) ,
222155 ) ;
223156 ( fs . writeFile as jest . Mock ) . mockRejectedValueOnce ( new Error ( 'write failed' ) ) ;
224157
225- const storage = new ElectronStorage ( logger ) ;
226- await storage . set ( 'key3' , 'value3' ) ;
158+ const storage = new ElectronStorage ( ) ;
159+ // The flush error propagates so the per-client wrapper can log it
160+ await expect ( storage . set ( 'key3' , 'value3' ) ) . rejects . toThrow ( 'write failed' ) ;
227161
228162 // Cache is updated synchronously — the in-memory state reflects the set
229- // even if the disk write failed. The data will be flushed on the next write.
163+ // even if the disk write failed.
230164 expect ( await storage . get ( 'key3' ) ) . toEqual ( 'value3' ) ;
231-
232- expect ( logger . debug ) . not . toHaveBeenCalled ( ) ;
233- expect ( logger . info ) . not . toHaveBeenCalled ( ) ;
234- expect ( logger . warn ) . not . toHaveBeenCalled ( ) ;
235- expect ( logger . error ) . toHaveBeenCalledWith ( 'Error flushing storage to disk: write failed' ) ;
236165} ) ;
237166
238167it ( 'getElectronStorage returns the same instance on subsequent calls' , ( ) => {
239- const a = getElectronStorage ( logger ) ;
240- const b = getElectronStorage ( logger ) ;
168+ const a = getElectronStorage ( ) ;
169+ const b = getElectronStorage ( ) ;
241170 expect ( a ) . toBe ( b ) ;
242171} ) ;
243172
244173it ( 'resetElectronStorage causes a fresh instance on next call' , ( ) => {
245- const a = getElectronStorage ( logger ) ;
174+ const a = getElectronStorage ( ) ;
246175 resetElectronStorage ( ) ;
247- const b = getElectronStorage ( logger ) ;
176+ const b = getElectronStorage ( ) ;
248177 expect ( a ) . not . toBe ( b ) ;
249178} ) ;
0 commit comments