@@ -44,15 +44,14 @@ const genKeyStrings = (n, identifier) => {
4444 return ( new Array ( n ) . fill ( 0 ) . map ( ( _ , idx ) => {
4545 const char = String . fromCharCode ( 97 + idx % 26 )
4646 // list-[a-z]-[0-(N-1)]
47- return `${ uniquePrefix } __ ${ identifier } _${ char } _${ idx } `
47+ return `${ identifier } _${ char } _${ idx } `
4848 } ) )
4949}
50- const putKeys = async ( state , keys , ttl ) => {
50+ const putKeys = async ( state , keys , { ttl, batchSize = 50 } ) => {
5151 const _putKeys = async ( keys , ttl ) => {
5252 await Promise . all ( keys . map ( async ( k , idx ) => await state . put ( k , `value-${ idx } ` , { ttl } ) ) )
5353 }
5454
55- const batchSize = 20
5655 let i = 0
5756 while ( i < keys . length - batchSize ) {
5857 await _putKeys ( keys . slice ( i , i + batchSize ) , ttl )
@@ -62,6 +61,13 @@ const putKeys = async (state, keys, ttl) => {
6261 await _putKeys ( keys . slice ( i ) , ttl )
6362}
6463const waitFor = ( ms ) => new Promise ( resolve => setTimeout ( resolve , ms ) )
64+ const listAll = async ( state , options = { } ) => {
65+ const acc = [ ]
66+ for await ( const { keys } of state . list ( options ) ) {
67+ acc . push ( ...keys )
68+ }
69+ return acc
70+ }
6571
6672test ( 'env vars' , ( ) => {
6773 expect ( process . env . TEST_AUTH_1 ) . toBeDefined ( )
@@ -166,113 +172,72 @@ describe('e2e tests using OpenWhisk credentials (as env vars)', () => {
166172 await expect ( state . put ( testKey , testValue , { ttl : - 1 } ) ) . rejects . toThrow ( )
167173 } )
168174
169- test ( 'listKeys test: few, many, and expired entries ' , async ( ) => {
175+ test ( 'list: countHint & match ' , async ( ) => {
170176 const state = await initStateEnv ( )
171177
172- // 1. test with not many elements, one iteration should return all
173- const keys90 = genKeyStrings ( 90 , 'list' ) . sort ( )
174- await putKeys ( state , keys90 , 60 )
178+ const prefix = `${ uniquePrefix } __list`
179+ const keys900 = genKeyStrings ( 900 , prefix ) . sort ( )
180+ await putKeys ( state , keys900 , { ttl : 60 } )
181+
182+ // listAll without match, note that other keys may be stored in namespace.
183+ const retAll = await listAll ( state )
184+ expect ( retAll . length ) . toBeGreaterThanOrEqual ( 900 )
185+
186+ // default countHint = 100
187+ const retHint100 = await listAll ( state , { match : `${ uniquePrefix } __list*` } )
188+ expect ( retHint100 . length ) . toEqual ( 900 )
189+ expect ( retHint100 . sort ( ) ) . toEqual ( keys900 )
190+
191+ // set countHint = 1000
192+ // in most cases, list should return in 1 iteration,
193+ // but we can't guarantee this as the server may return with less keys and
194+ // require additional iterations, especially if there are many keys in the namespace.
195+ // This is why we call listAll with countHint 1000 too.
196+ const retHint1000 = await listAll ( state , { match : `${ uniquePrefix } __list*` , countHint : 1000 } )
197+ expect ( retHint1000 . length ) . toEqual ( 900 )
198+ expect ( retHint1000 . sort ( ) ) . toEqual ( keys900 )
199+
200+ // sub patterns
201+ const retA = await listAll ( state , { match : `${ uniquePrefix } __list_a*` } )
202+ expect ( retA . length ) . toEqual ( 35 )
203+ expect ( retA ) . toContain (
204+ `${ uniquePrefix } __list_a_26`
205+ )
206+
207+ const ret1 = await listAll ( state , { match : `${ uniquePrefix } __list_*_1` } )
208+ expect ( ret1 . length ) . toEqual ( 1 )
209+
210+ const retstar = await listAll ( state , { match : `${ uniquePrefix } __l*st_*` } )
211+ expect ( retstar . length ) . toEqual ( 900 )
212+ } )
175213
176- let it , ret
177-
178- // note: when listing all we are not in isolation
179- it = state . list ( )
180- ret = await it . next ( )
181- expect ( ret . value . keys . length ) . toBeGreaterThanOrEqual ( 90 )
182-
183- it = state . list ( { match : `${ uniquePrefix } __list_*` } )
184- ret = await it . next ( )
185- expect ( ret . value . keys . sort ( ) ) . toEqual ( keys90 )
186- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
187-
188- it = state . list ( { match : `${ uniquePrefix } __list_a*` } )
189- ret = await it . next ( )
190- expect ( ret . value . keys . sort ( ) ) . toEqual ( [
191- `${ uniquePrefix } __list_a_0` ,
192- `${ uniquePrefix } __list_a_26` ,
193- `${ uniquePrefix } __list_a_52` ,
194- `${ uniquePrefix } __list_a_78`
195- ] )
196- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
197-
198- it = state . list ( { match : `${ uniquePrefix } __list_*_1` } )
199- ret = await it . next ( )
200- expect ( ret . value . keys . sort ( ) ) . toEqual ( [ `${ uniquePrefix } __list_b_1` ] )
201- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
202-
203- // 2. test with many elements and large countHint
204- const keys900 = genKeyStrings ( 900 , 'list' )
205- await putKeys ( state , keys900 , 60 )
206-
207- // note: we can't list in isolation without prefix
208- it = state . list ( { countHint : 1000 } )
209- ret = await it . next ( )
210- expect ( ret . value . keys . length ) . toBeGreaterThanOrEqual ( 900 )
211-
212- it = state . list ( { countHint : 1000 , match : `${ uniquePrefix } __li*t_*` } )
213- ret = await it . next ( )
214- expect ( ret . value . keys . length ) . toEqual ( 900 )
215- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
216-
217- it = state . list ( { countHint : 1000 , match : `${ uniquePrefix } __list_z*` } )
218- ret = await it . next ( )
219- expect ( ret . value . keys . length ) . toEqual ( 34 )
220- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
221-
222- it = state . list ( { match : `${ uniquePrefix } __list_*_1` } )
223- ret = await it . next ( )
224- expect ( ret . value . keys . sort ( ) ) . toEqual ( [ `${ uniquePrefix } __list_b_1` ] )
225- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
226-
227- // 3. test with many elements while iterating
228- let iterations = 0
229- let retArray = [ ]
230- for await ( const { keys } of state . list ( { match : `${ uniquePrefix } __l*st_*` } ) ) {
231- iterations ++
232- retArray . push ( ...keys )
233- }
234- expect ( iterations ) . toBeGreaterThan ( 5 ) // should be around 9-10
235- expect ( retArray . length ) . toEqual ( 900 )
236-
237- iterations = 0
238- retArray = [ ]
239- for await ( const { keys } of state . list ( { match : `${ uniquePrefix } __list_z*` } ) ) {
240- iterations ++
241- retArray . push ( ...keys )
242- }
243- expect ( iterations ) . toEqual ( 1 )
244- expect ( retArray . length ) . toEqual ( 34 )
245-
246- iterations = 0
247- retArray = [ ]
248- for await ( const { keys } of state . list ( { match : `${ uniquePrefix } __list_*_1` } ) ) {
249- iterations ++
250- retArray . push ( ...keys )
251- }
252- expect ( iterations ) . toEqual ( 1 )
253- expect ( retArray . length ) . toEqual ( 1 )
214+ test ( 'list expired keys' , async ( ) => {
215+ const state = await initStateEnv ( )
254216
255- // 4. make sure expired keys aren't listed
256- await putKeys ( state , keys90 , 1 )
217+ // make sure expired keys aren't listed
218+ const keysExpired = genKeyStrings ( 90 , `${ uniquePrefix } __exp_yes` )
219+ const keysNotExpired = genKeyStrings ( 90 , `${ uniquePrefix } __exp_no` ) . sort ( )
220+ await putKeys ( state , keysExpired , { ttl : 1 } )
221+ await putKeys ( state , keysNotExpired , { ttl : 120 } )
257222 await waitFor ( 2000 )
258223
259- it = state . list ( { countHint : 1000 , match : ` ${ uniquePrefix } __list_*` } )
260- ret = await it . next ( )
261- expect ( ret . value . keys . length ) . toEqual ( 810 ) // 900 - 90
262- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
224+ // Note, we don't guarantee not returning expired keys, and in some rare cases it may happen.
225+ // if the test fails we should disable it.
226+ const ret = await listAll ( state , { match : ` ${ uniquePrefix } __exp*` } )
227+ expect ( ret . sort ( ) ) . toEqual ( keysNotExpired )
263228 } )
264229
265230 test ( 'deleteAll test' , async ( ) => {
266231 const state = await initStateEnv ( )
267232
268233 // < 100 keys
269- const keys90 = genKeyStrings ( 90 , 'deleteAll' ) . sort ( )
234+ const keys90 = genKeyStrings ( 90 , ` ${ uniquePrefix } __deleteAll` ) . sort ( )
270235 await putKeys ( state , keys90 , 60 )
271236 expect ( await state . deleteAll ( { match : `${ uniquePrefix } __deleteAll_a*` } ) ) . toEqual ( { keys : 4 } )
272237 expect ( await state . deleteAll ( { match : `${ uniquePrefix } __deleteAll_*` } ) ) . toEqual ( { keys : 86 } )
273238
274239 // > 1000 keys
275- const keys1100 = genKeyStrings ( 1100 , 'deleteAll' ) . sort ( )
240+ const keys1100 = genKeyStrings ( 1100 , ` ${ uniquePrefix } __deleteAll` ) . sort ( )
276241 await putKeys ( state , keys1100 , 60 )
277242 expect ( await state . deleteAll ( { match : `${ uniquePrefix } __deleteAll_*_1` } ) ) . toEqual ( { keys : 1 } )
278243 expect ( await state . deleteAll ( { match : `${ uniquePrefix } __deleteAll_*_1*0` } ) ) . toEqual ( { keys : 21 } ) // 10, 100 - 190, 1000-1090
@@ -328,4 +293,25 @@ describe('e2e tests using OpenWhisk credentials (as env vars)', () => {
328293 code : 'ERROR_PAYLOAD_TOO_LARGE'
329294 } ) )
330295 } )
296+
297+ // this test is slow to execute uncomment if needed (we could also pre-load the data set in the future)
298+ // eslint-disable-next-line jest/no-commented-out-tests
299+ // test('list while having a large dataset stored', async () => {
300+ // // reason: https://github.com/adobe/aio-lib-state/issues/194
301+ // const state = await initStateEnv()
302+
303+ // const keysBig = genKeyStrings(15000, `${uniquePrefix}__big_list`).sort()
304+ // await putKeys(state, keysBig, { ttl: 300 })
305+
306+ // const keysSmall = genKeyStrings(82, `${uniquePrefix}__small_list`).sort()
307+ // await putKeys(state, keysSmall, { ttl: 300 }) // ttl=300s
308+
309+ // // ensure we can list adhoc data
310+ // const retArray = []
311+ // for await (const { keys } of state.list({ match: `${uniquePrefix}__small_list*`, countHint: 100 })) {
312+ // retArray.push(...keys)
313+ // }
314+ // // in this test we want to make sure that list works even when many keys are included
315+ // expect(retArray.length).toEqual(82)
316+ // }, 300 * 1000)
331317} )
0 commit comments