/
e2e.js
171 lines (140 loc) · 7.31 KB
/
e2e.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/*
Copyright 2019 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/
/* ************* NOTE 1: these tests must be run sequentially, jest does it by default within a SINGLE file ************* */
/* ************* NOTE 2: requires env vars TEST_AUTH_1, TEST_NS_1 and TEST_AUTH_2, TEST_NS_2 for 2 different namespaces. ************* */
const path = require('node:path')
// load .env values in the e2e folder, if any
require('dotenv').config({ path: path.join(__dirname, '.env') })
const { MAX_TTL_SECONDS } = require('../lib/constants')
const stateLib = require('../index')
const testKey = 'e2e_test_state_key'
const testKey2 = 'e2e_test_state_key2'
jest.setTimeout(30000) // thirty seconds per test
const initStateEnv = async (n = 1) => {
delete process.env.__OW_API_KEY
delete process.env.__OW_NAMESPACE
process.env.__OW_API_KEY = process.env[`TEST_AUTH_${n}`]
process.env.__OW_NAMESPACE = process.env[`TEST_NAMESPACE_${n}`]
const state = await stateLib.init()
// make sure we cleanup the namespace, note that delete might fail as it is an op under test
await state.deleteAll()
return state
}
const waitFor = (ms) => new Promise(resolve => setTimeout(resolve, ms))
describe('e2e tests using OpenWhisk credentials (as env vars)', () => {
test('error bad credentials test: auth is ok but namespace is not', async () => {
delete process.env.__OW_API_KEY
delete process.env.__OW_NAMESPACE
process.env.__OW_API_KEY = process.env.TEST_AUTH_1
process.env.__OW_NAMESPACE = process.env.TEST_NAMESPACE_1 + 'bad'
let expectedError
try {
const store = await stateLib.init()
await store.get('something')
} catch (e) {
expectedError = e
}
expect(expectedError).toBeDefined()
expect(expectedError instanceof Error).toBeTruthy()
expect({ name: expectedError.name, code: expectedError.code, message: expectedError.message, sdkDetails: expectedError.sdkDetails })
.toEqual(expect.objectContaining({
name: 'AdobeStateLibError',
code: 'ERROR_BAD_CREDENTIALS'
}))
})
test('key-value basic test on one key with string value: put, get, delete, any, deleteAll', async () => {
const state = await initStateEnv()
const testValue = 'a string'
expect(await state.get(testKey)).toEqual(undefined)
expect(await state.put(testKey, testValue)).toEqual(testKey)
expect(await state.get(testKey)).toEqual(expect.objectContaining({ value: testValue, expiration: expect.any(String) }))
expect(await state.delete(testKey)).toEqual(testKey)
expect(await state.get(testKey)).toEqual(undefined)
expect(await state.any()).toEqual(false)
expect(await state.put(testKey, testValue)).toEqual(testKey)
expect(await state.put(testKey2, testValue)).toEqual(testKey2)
expect(await state.any()).toEqual(true)
expect(await state.stats()).toEqual({ bytesKeys: testKey.length + testKey2.length, bytesValues: testValue.length * 2, keys: 2 })
expect(await state.deleteAll()).toEqual(true)
expect(await state.get(testKey)).toEqual(undefined)
expect(await state.any()).toEqual(false)
expect(await state.stats()).toEqual(false)
})
test('time-to-live tests: write w/o ttl, get default ttl, write with ttl, get, get after ttl', async () => {
const state = await initStateEnv()
const testValue = 'test value'
let res, resTime
// 1. test default ttl = 1 day
expect(await state.put(testKey, testValue)).toEqual(testKey)
res = await state.get(testKey)
resTime = new Date(res.expiration).getTime()
expect(resTime).toBeLessThanOrEqual(new Date(Date.now() + 86400000).getTime()) // 86400000 ms = 1 day
expect(resTime).toBeGreaterThanOrEqual(new Date(Date.now() + 86400000 - 10000).getTime()) // give more or less 10 seconds clock skew + request time
// 2. test ttl = 0 (should default to default ttl of 1 day)
expect(await state.put(testKey, testValue, { ttl: 0 })).toEqual(testKey)
res = await state.get(testKey)
resTime = new Date(res.expiration).getTime()
expect(resTime).toBeLessThanOrEqual(new Date(Date.now() + 86400000).getTime()) // 86400000 ms = 1 day
expect(resTime).toBeGreaterThanOrEqual(new Date(Date.now() + 86400000 - 10000).getTime()) // give more or less 10 seconds clock skew + request time
// 3. test max ttl
const nowPlus365Days = new Date(MAX_TTL_SECONDS).getTime()
expect(await state.put(testKey, testValue, { ttl: -1 })).toEqual(testKey)
res = await state.get(testKey)
resTime = new Date(res.expiration).getTime()
expect(resTime).toBeGreaterThanOrEqual(nowPlus365Days)
// 4. test that after ttl object is deleted
expect(await state.put(testKey, testValue, { ttl: 2 })).toEqual(testKey)
res = await state.get(testKey)
expect(new Date(res.expiration).getTime()).toBeLessThanOrEqual(new Date(Date.now() + 2000).getTime())
await waitFor(3000) // give it one more sec - ttl is not so precise
expect(await state.get(testKey)).toEqual(undefined)
})
test('throw error when get/put with invalid keys', async () => {
const invalidKey = 'some/invalid:key'
const state = await initStateEnv()
await expect(state.put(invalidKey, 'testValue')).rejects.toThrow('[AdobeStateLib:ERROR_BAD_ARGUMENT] /key must match pattern "^[a-zA-Z0-9-_.]{1,1024}$"')
await expect(state.get(invalidKey)).rejects.toThrow('[AdobeStateLib:ERROR_BAD_ARGUMENT] /key must match pattern "^[a-zA-Z0-9-_.]{1,1024}$"')
})
test('isolation tests: get, write, delete on same key for two namespaces do not interfere', async () => {
const state1 = await initStateEnv(1)
const state2 = await initStateEnv(2)
const testValue1 = 'one value'
const testValue2 = 'some other value'
// 1. test that ns2 cannot get state in ns1
await state1.put(testKey, testValue1)
expect(await state2.get(testKey)).toEqual(undefined)
// 2. test that ns2 cannot update state in ns1
await state2.put(testKey, testValue2)
expect(await state1.get(testKey)).toEqual(expect.objectContaining({ value: testValue1 }))
// 3. test that ns1 cannot delete state in ns2
await state1.delete(testKey)
expect(await state2.get(testKey)).toEqual(expect.objectContaining({ value: testValue2 }))
// cleanup delete ns2 state
await state2.delete(testKey)
})
test('error value bigger than 1MB test', async () => {
const state = await initStateEnv()
const bigValue = ('a').repeat(1024 * 1024 + 1)
let expectedError
try {
await state.put(testKey, bigValue)
} catch (e) {
expectedError = e
}
expect(expectedError).toBeDefined()
expect(expectedError instanceof Error).toBeTruthy()
expect({ name: expectedError.name, code: expectedError.code, message: expectedError.message, sdkDetails: expectedError.sdkDetails })
.toEqual(expect.objectContaining({
name: 'AdobeStateLibError',
code: 'ERROR_PAYLOAD_TOO_LARGE'
}))
})
})