This repository has been archived by the owner on May 30, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 65
/
Copy pathbig_segments-test.js
182 lines (157 loc) · 6.59 KB
/
big_segments-test.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
172
173
174
175
176
177
178
179
180
181
182
const { BigSegmentStoreManager, hashForUserKey } = require('../big_segments');
const { nullLogger } = require('../loggers');
const { AsyncQueue } = require('launchdarkly-js-test-helpers');
describe('BigSegmentStoreManager', () => {
const userKey = 'userkey', userHash = hashForUserKey(userKey);
const logger = nullLogger();
const alwaysUpToDate = async () => {
return { lastUpToDate: new Date().getTime() };
};
const alwaysStale = async () => {
return { lastUpToDate: new Date().getTime() - 1000000 };
};
function membershipForExpectedUser(expectedMembership) {
return async (hash) => {
expect(hash).toEqual(userHash);
return expectedMembership;
}
}
async function withManager(store, config, action) {
const m = BigSegmentStoreManager(store, config, logger);
try {
await action(m);
} finally {
m.close();
}
}
describe('membership query', () => {
it('with uncached result and healthy status', async () => {
const expectedMembership = { key1: true, key2: true };
const store = {
getMetadata: alwaysUpToDate,
getUserMembership: membershipForExpectedUser(expectedMembership),
};
await withManager(store, {}, async m => {
const result = await m.getUserMembership(userKey);
expect(result).toEqual([ expectedMembership, 'HEALTHY' ]);
});
});
it('with cached result and healthy status', async () => {
const expectedMembership = { key1: true, key2: true };
let queryCount = 0;
const store = {
getMetadata: alwaysUpToDate,
getUserMembership: async hash => {
queryCount++;
return await membershipForExpectedUser(expectedMembership)(hash);
},
};
await withManager(store, {}, async m => {
const result1 = await m.getUserMembership(userKey);
expect(result1).toEqual([ expectedMembership, 'HEALTHY' ]);
const result2 = await m.getUserMembership(userKey);
expect(result2).toEqual(result1);
expect(queryCount).toEqual(1);
});
});
it('with stale status', async () => {
const expectedMembership = { key1: true, key2: true };
const store = {
getMetadata: alwaysStale,
getUserMembership: membershipForExpectedUser(expectedMembership),
};
await withManager(store, {}, async m => {
const result = await m.getUserMembership(userKey);
expect(result).toEqual([ expectedMembership, 'STALE' ]);
});
});
it('with stale status due to no store metadata', async () => {
const expectedMembership = { key1: true, key2: true };
const store = {
getMetadata: async () => undefined,
getUserMembership: membershipForExpectedUser(expectedMembership),
};
await withManager(store, {}, async m => {
const result = await m.getUserMembership(userKey);
expect(result).toEqual([ expectedMembership, 'STALE' ]);
});
});
it('least recent user is evicted from cache', async () => {
const userKey1 = 'userkey1', userKey2 = 'userkey2', userKey3 = 'userkey3';
const userHash1 = hashForUserKey(userKey1), userHash2 = hashForUserKey(userKey2), userHash3 = hashForUserKey(userKey3);
const memberships = {};
memberships[userHash1] = { seg1: true };
memberships[userHash2] = { seg2: true };
memberships[userHash3] = { seg3: true };
let queriedUsers = [];
const store = {
getMetadata: alwaysUpToDate,
getUserMembership: async hash => {
queriedUsers.push(hash);
return memberships[hash];
},
};
const config = { userCacheSize: 2 };
await withManager(store, config, async m => {
const result1 = await m.getUserMembership(userKey1);
const result2 = await m.getUserMembership(userKey2);
const result3 = await m.getUserMembership(userKey3);
expect(result1).toEqual([ memberships[userHash1], 'HEALTHY' ]);
expect(result2).toEqual([ memberships[userHash2], 'HEALTHY' ]);
expect(result3).toEqual([ memberships[userHash3], 'HEALTHY' ]);
expect(queriedUsers).toEqual([ userHash1, userHash2, userHash3 ]);
// Since the capacity is only 2 and userKey1 was the least recently used, that key should be
// evicted by the userKey3 query. Now only userKey2 and userKey3 are in the cache, and
// querying them again should not cause a new query to the store.
const result2a = await m.getUserMembership(userKey2);
const result3a = await m.getUserMembership(userKey3);
expect(result2a).toEqual(result2);
expect(result3a).toEqual(result3);
expect(queriedUsers).toEqual([ userHash1, userHash2, userHash3 ]);
const result1a = await m.getUserMembership(userKey1);
expect(result1a).toEqual(result1);
expect(queriedUsers).toEqual([ userHash1, userHash2, userHash3, userHash1 ]);
});
});
});
describe('status polling', () => {
it('detects store unavailability', async () => {
const store = {
getMetadata: alwaysUpToDate,
};
await withManager(store, { statusPollInterval: 0.01 }, async m => {
const status1 = await m.statusProvider.requireStatus();
expect(status1.available).toBe(true);
const statuses = new AsyncQueue();
m.statusProvider.on('change', s => statuses.add(s));
store.getMetadata = async () => { throw new Error('sorry'); };
const status2 = await statuses.take();
expect(status2.available).toBe(false);
expect(m.statusProvider.getStatus()).toEqual(status2);
store.getMetadata = alwaysUpToDate;
const status3 = await statuses.take();
expect(status3.available).toBe(true);
expect(m.statusProvider.getStatus()).toEqual(status3);
});
});
it('detects stale status', async () => {
const store = {
getMetadata: alwaysUpToDate,
};
await withManager(store, { statusPollInterval: 0.01, staleAfter: 0.2 }, async m => {
const status1 = await m.statusProvider.requireStatus();
expect(status1.stale).toBe(false);
const statuses = new AsyncQueue();
m.statusProvider.on('change', s => statuses.add(s));
store.getMetadata = alwaysStale;
const status2 = await statuses.take();
expect(status2.stale).toBe(true);
expect(m.statusProvider.getStatus()).toEqual(status2);
store.getMetadata = alwaysUpToDate;
const status3 = await statuses.take();
expect(status3.stale).toBe(false);
expect(m.statusProvider.getStatus()).toEqual(status3);
});
});
});
});