Skip to content

Commit a589f36

Browse files
committed
fixup! fixup! fixup! fixup! fixup! fixup! fix(cache): update cache with O(1) data structures
1 parent 7eea087 commit a589f36

File tree

1 file changed

+134
-21
lines changed

1 file changed

+134
-21
lines changed

src/cache_test.ts

Lines changed: 134 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import http = require('http');
88
import { Duplex } from 'stream';
99
import { EventEmitter } from 'ws';
1010

11-
import { V1Namespace, V1NamespaceList, V1ObjectMeta, V1Pod, V1ListMeta } from './api';
11+
import { V1Namespace, V1NamespaceList, V1ObjectMeta, V1Pod, V1PodList, V1ListMeta } from './api';
1212
import { deleteObject, ListWatch, deleteItems, CacheMap, cacheMapFromList } from './cache';
1313
import { KubeConfig } from './config';
1414
import { Cluster, Context, User } from './config_types';
@@ -80,18 +80,141 @@ describe('ListWatchCache', () => {
8080

8181
it('should perform basic caching', async () => {
8282
const fakeWatch = mock.mock(Watch);
83-
const list: V1Namespace[] = [
83+
const list: V1Pod[] = [
8484
{
8585
metadata: {
8686
name: 'name1',
8787
namespace: 'default',
8888
} as V1ObjectMeta,
89-
} as V1Namespace,
89+
} as V1Pod,
9090
{
9191
metadata: {
9292
name: 'name2',
9393
namespace: 'default',
9494
} as V1ObjectMeta,
95+
} as V1Pod,
96+
];
97+
const listObj = {
98+
metadata: {
99+
resourceVersion: '12345',
100+
} as V1ListMeta,
101+
items: list,
102+
} as V1PodList;
103+
104+
const emptyObj = {
105+
metadata: {
106+
resourceVersion: '123456',
107+
} as V1ListMeta,
108+
items: [
109+
{
110+
metadata: {
111+
name: 'name3',
112+
namespace: 'default',
113+
} as V1ObjectMeta,
114+
} as V1Pod,
115+
],
116+
} as V1PodList;
117+
118+
let calls = 0;
119+
const listFn: ListPromise<V1Pod> = function(): Promise<{
120+
response: http.IncomingMessage;
121+
body: V1PodList;
122+
}> {
123+
return new Promise<{ response: http.IncomingMessage; body: V1PodList }>((resolve, reject) => {
124+
if (calls++ === 0) {
125+
resolve({ response: {} as http.IncomingMessage, body: listObj });
126+
} else {
127+
resolve({ response: {} as http.IncomingMessage, body: emptyObj });
128+
}
129+
});
130+
};
131+
const promise = new Promise((resolve) => {
132+
mock.when(
133+
fakeWatch.watch(mock.anything(), mock.anything(), mock.anything(), mock.anything()),
134+
).thenCall(() => {
135+
resolve(new FakeRequest());
136+
});
137+
});
138+
const cache = new ListWatch('/some/path', mock.instance(fakeWatch), listFn);
139+
await promise;
140+
const [pathOut, , watchHandler, doneHandler] = mock.capture(fakeWatch.watch).last();
141+
expect(pathOut).to.equal('/some/path');
142+
expect(cache.list()).to.deep.equal(list);
143+
144+
expect(cache.get('name1', 'default')).to.equal(list[0]);
145+
expect(cache.get('name2', 'default')).to.equal(list[1]);
146+
147+
expect(cache.list('default')).to.deep.equal(list);
148+
expect(cache.list('non-existent')).to.deep.equal([]);
149+
150+
watchHandler('ADDED', {
151+
metadata: {
152+
name: 'name3',
153+
namespace: 'other',
154+
} as V1ObjectMeta,
155+
} as V1Pod);
156+
157+
expect(cache.list().length).to.equal(3);
158+
expect(cache.get('name3', 'other')).to.not.equal(null);
159+
160+
expect(cache.list('default').length).to.equal(2);
161+
expect(cache.list('other').length).to.equal(1);
162+
expect(cache.list('non-existent')).to.deep.equal([]);
163+
164+
watchHandler('MODIFIED', {
165+
metadata: {
166+
name: 'name3',
167+
namespace: 'other',
168+
resourceVersion: 'baz',
169+
} as V1ObjectMeta,
170+
} as V1Pod);
171+
expect(cache.list().length).to.equal(3);
172+
const obj3 = cache.get('name3', 'other');
173+
expect(obj3).to.not.equal(null);
174+
if (obj3) {
175+
expect(obj3.metadata!.name).to.equal('name3');
176+
expect(obj3.metadata!.resourceVersion).to.equal('baz');
177+
}
178+
179+
watchHandler('DELETED', {
180+
metadata: {
181+
name: 'name2',
182+
namespace: 'default',
183+
} as V1ObjectMeta,
184+
} as V1Pod);
185+
expect(cache.list().length).to.equal(2);
186+
expect(cache.get('name2', 'default')).to.equal(undefined);
187+
188+
expect(cache.list('default').length).to.equal(1);
189+
expect(cache.list('other').length).to.equal(1);
190+
191+
watchHandler('ADDED', {
192+
metadata: {
193+
name: 'name2',
194+
namespace: 'default',
195+
} as V1ObjectMeta,
196+
} as V1Pod);
197+
198+
const error = new Error('Gone') as Error & { statusCode: number | undefined };
199+
error.statusCode = 410;
200+
await doneHandler(error);
201+
expect(cache.list().length, 'all namespace list').to.equal(1);
202+
expect(cache.list('default').length, 'default namespace list').to.equal(1);
203+
expect(cache.list('other'), 'other namespace list').to.deep.equal([]);
204+
});
205+
206+
it('should perform basic caching of non-namespaced objects', async () => {
207+
const fakeWatch = mock.mock(Watch);
208+
const list: V1Namespace[] = [
209+
{
210+
metadata: {
211+
name: 'name1',
212+
} as V1ObjectMeta,
213+
} as V1Namespace,
214+
{
215+
metadata: {
216+
name: 'name2',
217+
} as V1ObjectMeta,
95218
} as V1Namespace,
96219
];
97220
const listObj = {
@@ -109,7 +232,6 @@ describe('ListWatchCache', () => {
109232
{
110233
metadata: {
111234
name: 'name3',
112-
namespace: 'default',
113235
} as V1ObjectMeta,
114236
} as V1Namespace,
115237
],
@@ -143,35 +265,33 @@ describe('ListWatchCache', () => {
143265
expect(pathOut).to.equal('/some/path');
144266
expect(cache.list()).to.deep.equal(list);
145267

146-
expect(cache.get('name1', 'default')).to.equal(list[0]);
147-
expect(cache.get('name2', 'default')).to.equal(list[1]);
268+
expect(cache.get('name1')).to.equal(list[0]);
269+
expect(cache.get('name2')).to.equal(list[1]);
148270

149-
expect(cache.list('default')).to.deep.equal(list);
271+
expect(cache.list('default')).to.deep.equal([]);
150272
expect(cache.list('non-existent')).to.deep.equal([]);
151273

152274
watchHandler('ADDED', {
153275
metadata: {
154276
name: 'name3',
155-
namespace: 'other',
156277
} as V1ObjectMeta,
157278
} as V1Namespace);
158279

159280
expect(cache.list().length).to.equal(3);
160-
expect(cache.get('name3', 'default')).to.not.equal(null);
281+
expect(cache.get('name3')).to.not.equal(null);
161282

162-
expect(cache.list('default').length).to.equal(2);
163-
expect(cache.list('other').length).to.equal(1);
283+
expect(cache.list('default').length).to.equal(0);
284+
expect(cache.list('other').length).to.equal(0);
164285
expect(cache.list('non-existent')).to.deep.equal([]);
165286

166287
watchHandler('MODIFIED', {
167288
metadata: {
168289
name: 'name3',
169-
namespace: 'other',
170290
resourceVersion: 'baz',
171291
} as V1ObjectMeta,
172292
} as V1Namespace);
173293
expect(cache.list().length).to.equal(3);
174-
const obj3 = cache.get('name3', 'other');
294+
const obj3 = cache.get('name3');
175295
expect(obj3).to.not.equal(null);
176296
if (obj3) {
177297
expect(obj3.metadata!.name).to.equal('name3');
@@ -181,28 +301,21 @@ describe('ListWatchCache', () => {
181301
watchHandler('DELETED', {
182302
metadata: {
183303
name: 'name2',
184-
namespace: 'default',
185304
} as V1ObjectMeta,
186305
} as V1Namespace);
187306
expect(cache.list().length).to.equal(2);
188-
expect(cache.get('name2', 'default')).to.equal(undefined);
189-
190-
expect(cache.list('default').length).to.equal(1);
191-
expect(cache.list('other').length).to.equal(1);
307+
expect(cache.get('name2')).to.equal(undefined);
192308

193309
watchHandler('ADDED', {
194310
metadata: {
195311
name: 'name2',
196-
namespace: 'default',
197312
} as V1ObjectMeta,
198313
} as V1Namespace);
199314

200315
const error = new Error('Gone') as Error & { statusCode: number | undefined };
201316
error.statusCode = 410;
202317
await doneHandler(error);
203318
expect(cache.list().length, 'all namespace list').to.equal(1);
204-
expect(cache.list('default').length, 'default namespace list').to.equal(1);
205-
expect(cache.list('other'), 'other namespace list').to.deep.equal([]);
206319
});
207320

208321
it('should perform work as an informer', async () => {

0 commit comments

Comments
 (0)