Skip to content

Commit d9d930c

Browse files
authored
chore: e2e fixes for list tests (#197)
1 parent 2ce4a07 commit d9d930c

File tree

2 files changed

+79
-93
lines changed

2 files changed

+79
-93
lines changed

e2e/e2e.js

Lines changed: 79 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}
6463
const 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

6672
test('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
})

test.html

Whitespace-only changes.

0 commit comments

Comments
 (0)