datastore-api 2.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,461 @@
1
+ /*
2
+ * dstore-api-simulator.spec.ts - test against the in-process-datastore simulator
3
+ *
4
+ * Created by Dr. Maximilian Dornseif 2021-12-10 in huwawi3backend 11.10.0
5
+ * Copyright (c) 2021, 2023 Dr. Maximilian Dornseif
6
+ */
7
+ // @ts-nocheck
8
+ // import { Datastore, Key } from '@google-cloud/datastore';
9
+ import { afterAll, assert, beforeAll, describe, expect, test } from 'vitest'
10
+
11
+ import { Datastore } from '../mock'
12
+
13
+ import { Dstore } from './dstore-api'
14
+
15
+ process.env.GCLOUD_PROJECT = 'project-id' // Set the datastore project Id globally
16
+ let emulator
17
+
18
+ function getDstore() {
19
+ return new Dstore(new Datastore({ namespace: 'test', projectId: process.env.GCLOUD_PROJECT }))
20
+ }
21
+
22
+ test('keySerialize', async () => {
23
+ const kvStore = getDstore()
24
+ assert.deepEqual(kvStore.key(['testYodel', 123]).path, ['testYodel', 123])
25
+ assert.deepEqual(JSON.parse(JSON.stringify(kvStore.key(['testYodel', 123]))), {
26
+ id: 123 as any, // typing in inconclusive here
27
+ kind: 'testYodel',
28
+ namespace: 'test',
29
+ path: ['testYodel', 123],
30
+ } as any)
31
+ const ser = kvStore.keySerialize(kvStore.key(['testYodel', 123]))
32
+ expect(ser).toMatchInlineSnapshot('"agByDwsSCXRlc3RZb2RlbBh7DKIBBHRlc3Q"')
33
+ expect(JSON.parse(JSON.stringify(kvStore.keyFromSerialized(ser)))).toMatchInlineSnapshot(`
34
+ {
35
+ "id": "123",
36
+ "kind": "testYodel",
37
+ "namespace": "test",
38
+ "path": [
39
+ "testYodel",
40
+ "123",
41
+ ],
42
+ }
43
+ `)
44
+ })
45
+ test('keyFromLegacyUrlsafe', async () => {
46
+ const kvStore = getDstore()
47
+ const ser = await kvStore.datastore.keyToLegacyUrlSafe(kvStore.key(['testYodel', 123]))
48
+ expect(ser).toMatchInlineSnapshot(`
49
+ [
50
+ "agpwcm9qZWN0LWlkcg8LEgl0ZXN0WW9kZWwYewyiAQR0ZXN0",
51
+ ]
52
+ `)
53
+
54
+ const key = kvStore.datastore.keyFromLegacyUrlsafe(ser[0])
55
+ expect(key).toMatchInlineSnapshot(`
56
+ Key {
57
+ "id": "123",
58
+ "kind": "testYodel",
59
+ "namespace": "test",
60
+ "path": [
61
+ "testYodel",
62
+ "123",
63
+ ],
64
+ }
65
+ `)
66
+ expect(kvStore.datastore.isKey(key)).toBeTruthy()
67
+ })
68
+
69
+ describe('Allocation', () => {
70
+ test('allocateIds', async () => {
71
+ const kvStore = getDstore()
72
+ const keys = await kvStore.datastore.allocateIds(kvStore.datastore.key(['testYodel']), 2)
73
+ expect(Array.isArray(keys)).toBeTruthy()
74
+ expect(keys[0].length).toBe(2)
75
+ expect(keys[0][0].kind).toBe('testYodel')
76
+ expect(keys[0][0].id).toMatch(/\d+/)
77
+ expect(keys?.[1]?.keys?.length).toBe(2)
78
+ expect(keys[1].keys[0].partitionId.namespaceId).toMatchInlineSnapshot('"test"')
79
+ expect(keys[1].keys[0].path[0].idType).toMatchInlineSnapshot('"id"')
80
+ expect(keys[1].keys[0].path[0].kind).toMatchInlineSnapshot('"testYodel"')
81
+ })
82
+
83
+ test('allocateOneId', async () => {
84
+ const kvStore = getDstore()
85
+ const id = await kvStore.allocateOneId()
86
+ expect(id).toMatch(/\d+/)
87
+ })
88
+ })
89
+
90
+ describe('Read', () => {
91
+ test('get num_id', async () => {
92
+ const kvStore = getDstore()
93
+ const entity = { key: kvStore.key(['testYodel', 2]), data: { foo: 'bar' } }
94
+ const entity2 = {
95
+ key: kvStore.key(['testYodel', 3]),
96
+ data: { foo: 'bar' },
97
+ }
98
+ const commitResponse = await kvStore.save([entity, entity2])
99
+ // expect(isNumber(commitResponse?.[0]?.indexUpdates)).toBeTruthy();
100
+ // expect(commitResponse).toMatchInlineSnapshot(`
101
+ // Array [
102
+ // Object {
103
+ // "indexUpdates": 0,
104
+ // "mutationResults": Array [
105
+ // Object {
106
+ // "conflictDetected": false,
107
+ // "key": null,
108
+ // "version": "1234567890123456",
109
+ // },
110
+ // ],
111
+ // },
112
+ // ]
113
+ // `);
114
+ expect(entity).toMatchInlineSnapshot(`
115
+ {
116
+ "data": {
117
+ "_keyStr": "agByDwsSCXRlc3RZb2RlbBgCDKIBBHRlc3Q",
118
+ "foo": "bar",
119
+ Symbol(KEY): Key {
120
+ "id": 2,
121
+ "kind": "testYodel",
122
+ "namespace": "test",
123
+ "path": [
124
+ "testYodel",
125
+ 2,
126
+ ],
127
+ },
128
+ },
129
+ "excludeLargeProperties": true,
130
+ "key": Key {
131
+ "id": 2,
132
+ "kind": "testYodel",
133
+ "namespace": "test",
134
+ "path": [
135
+ "testYodel",
136
+ 2,
137
+ ],
138
+ },
139
+ }
140
+ `)
141
+
142
+ const result = await kvStore.get(entity.key)
143
+ // get returns a single Entity
144
+ expect(Array.isArray(result)).toBeFalsy()
145
+ expect(result).toMatchInlineSnapshot('null')
146
+ // expect(kvStore.readKey(result)).toBeInstanceOf(Key);
147
+
148
+ const result2 = await kvStore.getMulti([entity.key])
149
+ // getMulti returns a Array even for single keys
150
+ expect(result2).toMatchInlineSnapshot(`
151
+ [
152
+ null,
153
+ ]
154
+ `)
155
+ const result3 = await kvStore.getMulti([entity.key, kvStore.key(['testYodel', 3])])
156
+ // getMulti returns a Array with multiple keys
157
+ // expect(Array.isArray(result)).toBeTruthy();
158
+ expect(result3).toMatchInlineSnapshot(`
159
+ [
160
+ null,
161
+ null,
162
+ ]
163
+ `)
164
+ const result4 = await kvStore.getMulti([entity.key, entity.key])
165
+ // getMulti returns a Array but collapses duplicate keys
166
+ // expect(Array.isArray(result)).toBeTruthy();
167
+ // Firestore in Datastore returns the entity once
168
+ // Datastore Emulator returns the Entity twice
169
+ // kvStore should normalize that.
170
+ expect(result4).toMatchInlineSnapshot(`
171
+ [
172
+ null,
173
+ null,
174
+ ]
175
+ `)
176
+
177
+ const result5 = await kvStore.getMulti([entity.key, kvStore.key(['YodelNotThere', 3])])
178
+ // getMulti returns a Array but omits unknown keys
179
+ // expect(Array.isArray(result)).toBeTruthy();
180
+ expect(result5).toMatchInlineSnapshot(`
181
+ [
182
+ null,
183
+ null,
184
+ ]
185
+ `)
186
+ const result6 = await kvStore.getMulti([])
187
+ // getMulti returns a empty Array for an empty array
188
+ // expect(Array.isArray(result)).toBeTruthy();
189
+ expect(result6).toMatchInlineSnapshot('[]')
190
+ })
191
+
192
+ test('get name', async (t) => {
193
+ const kvStore = getDstore()
194
+ const entity = {
195
+ key: kvStore.key(['testYodel', 'two']),
196
+ data: { foo: 'bar' },
197
+ }
198
+ await kvStore.save([entity])
199
+ const result = await kvStore.get(entity.key)
200
+ expect(result?._keyStr).toMatchInlineSnapshot('"agByEgsSCXRlc3RZb2RlbCIDdHdvDKIBBHRlc3Q"')
201
+ expect(result?.foo).toBe('bar')
202
+ })
203
+ })
204
+ describe('query', async () => {
205
+ test.skip('raw', async () => {
206
+ const kvStore = getDstore()
207
+ const entity = {
208
+ key: kvStore.key(['testYodel', '3']),
209
+ data: { foo: 'bar', baz: 'baz' },
210
+ }
211
+
212
+ await kvStore.save([entity])
213
+ const query = kvStore.datastore.createQuery('testYodel')
214
+ query.limit(1)
215
+ const [entities, runQueryInfo] = await kvStore.datastore.runQuery(query)
216
+ expect(entities.length).toBe(1)
217
+ expect(entities).toMatchInlineSnapshot(`
218
+ [
219
+ {
220
+ "foo": "bar",
221
+ Symbol(KEY): Key {
222
+ "id": "2",
223
+ "kind": "testYodel",
224
+ "namespace": "test",
225
+ "path": [
226
+ "testYodel",
227
+ "2",
228
+ ],
229
+ },
230
+ },
231
+ ]
232
+ `)
233
+ })
234
+
235
+ test('query', async () => {
236
+ const kvStore = getDstore()
237
+ const entity = {
238
+ key: kvStore.key(['testYodel', '3']),
239
+ data: { foo: 'bar', baz: 'baz' },
240
+ }
241
+
242
+ const saveResult = await kvStore.save([entity])
243
+ expect(saveResult).toMatchInlineSnapshot(`
244
+ [
245
+ {
246
+ "indexUpdates": 1,
247
+ "mutationResults": [
248
+ {
249
+ "conflictDetected": false,
250
+ "createTime": {
251
+ "nanos": 1,
252
+ "seconds": 2,
253
+ },
254
+ "key": null,
255
+ "updateTime": {
256
+ "nanos": 3,
257
+ "seconds": 4,
258
+ },
259
+ "version": 1,
260
+ },
261
+ ],
262
+ },
263
+ ]
264
+ `)
265
+ expect(await kvStore.get(entity.key)).toMatchInlineSnapshot(`
266
+ {
267
+ "_keyStr": "agByEAsSCXRlc3RZb2RlbCIBMwyiAQR0ZXN0",
268
+ "baz": "baz",
269
+ "foo": "bar",
270
+ Symbol(KEY): Key {
271
+ "kind": "testYodel",
272
+ "name": "3",
273
+ "namespace": "test",
274
+ "path": [
275
+ "testYodel",
276
+ "3",
277
+ ],
278
+ },
279
+ }
280
+ `)
281
+
282
+ // Give Datastore time to become consistent
283
+ do {} while ((await kvStore.get(entity.key)) === null)
284
+
285
+ const query = kvStore.createQuery('testYodel')
286
+ query.limit(1)
287
+ const [entities, runQueryInfo] = await kvStore.runQuery(query)
288
+ // expect(entities.length).toBe(1)
289
+ expect(entities).toMatchInlineSnapshot(`
290
+ [
291
+ {
292
+ "_keyStr": "agByEAsSCXRlc3RZb2RlbCIBMwyiAQR0ZXN0",
293
+ "baz": "baz",
294
+ "foo": "bar",
295
+ Symbol(KEY): Key {
296
+ "kind": "testYodel",
297
+ "name": "3",
298
+ "namespace": "test",
299
+ "path": [
300
+ "testYodel",
301
+ "3",
302
+ ],
303
+ },
304
+ },
305
+ ]
306
+ `)
307
+ expect(runQueryInfo).toMatchInlineSnapshot(`
308
+ {
309
+ "moreResults": "MORE_RESULTS_AFTER_LIMIT",
310
+ }
311
+ `)
312
+ expect(entities?.[0]?.foo).toBe('bar')
313
+ expect(entities?.[0]?.[Datastore.KEY]?.kind).toBe('testYodel')
314
+ expect(runQueryInfo?.moreResults).toBe('MORE_RESULTS_AFTER_LIMIT')
315
+
316
+ // modern interface
317
+ const [result2] = await kvStore.query('testYodel', [], 1, [], ['baz'])
318
+ expect(result2.length).toBe(1)
319
+ // foo is removed by selection
320
+ expect(JSON.parse(JSON.stringify(result2?.[0]))).toMatchInlineSnapshot(`
321
+ {
322
+ "_keyStr": "agByEAsSCXRlc3RZb2RlbCIBMwyiAQR0ZXN0",
323
+ "baz": "baz",
324
+ "foo": "bar",
325
+ }
326
+ `)
327
+
328
+ const key = kvStore.readKey(result2?.[0])
329
+ expect(key.id).toBe(entity.key.id)
330
+ })
331
+ })
332
+
333
+ test('set', async () => {
334
+ // expect.assertions(2);
335
+ const kvStore = getDstore()
336
+ const result = await kvStore.set(kvStore.key(['testYodel', '5e7']), {
337
+ foo: 'bar',
338
+ })
339
+ expect(result.name).toBe('5e7')
340
+ expect(result.kind).toBe('testYodel')
341
+
342
+ // autogenerate key
343
+ const result2 = await kvStore.set(kvStore.key(['testYodel']), { foo: 'bar' })
344
+ expect(result2.kind).toBe('testYodel')
345
+ })
346
+
347
+ test('save / upsert', async () => {
348
+ // expect.assertions(2);
349
+ const kvStore = getDstore()
350
+ const entity = {
351
+ key: kvStore.key(['testYodel', 3]),
352
+ data: { foo: 'bar' } as any,
353
+ }
354
+ const result = await kvStore.save([entity])
355
+ // const result2 = await kvStore.upsert([entity]);
356
+ expect(result?.[0]?.mutationResults?.[0]?.conflictDetected).toBe(false)
357
+ expect(entity.data._keyStr).toMatchInlineSnapshot('"agByDwsSCXRlc3RZb2RlbBgDDKIBBHRlc3Q"')
358
+ expect(entity.data.foo).toBe('bar')
359
+ expect(entity.data[Datastore.KEY].kind).toBe('testYodel')
360
+ })
361
+
362
+ test('update', async (t) => {
363
+ // expect.assertions(3);
364
+ const kvStore = getDstore()
365
+ const keyName = `4insert${Math.random()}`
366
+ const entity = {
367
+ key: kvStore.key(['testYodel', keyName]),
368
+ data: { foo: 'bar' },
369
+ }
370
+ // const request = kvStore.update([entity]);
371
+ // await expect(request).rejects.toThrowError(Error);
372
+
373
+ await kvStore.save([entity])
374
+ const result = await kvStore.update([entity])
375
+ expect(result?.[0]?.mutationResults?.[0]?.conflictDetected).toBe(false)
376
+ expect(result?.[0]?.mutationResults?.[0]?.key).toBe(null)
377
+ // expect(result?.[0]?.indexUpdates).toBe(2);
378
+ })
379
+
380
+ test('insert / delete', async (t) => {
381
+ // expect.assertions(2);
382
+ const kvStore = getDstore()
383
+ const testkey = kvStore.key(['testYodel', 4])
384
+ await kvStore.delete([testkey])
385
+ const entity = {
386
+ key: testkey,
387
+ data: { foo: 'bar' } as any,
388
+ }
389
+ const result = await kvStore.insert([entity])
390
+
391
+ expect(result?.[0]?.mutationResults?.[0]?.conflictDetected).toBe(false)
392
+ expect(result?.[0]?.mutationResults?.[0]?.version).toMatch(/\d+/)
393
+ expect(entity.data.foo).toBe('bar')
394
+ expect(entity.key.path[0]).toBe('testYodel')
395
+ // expect(result?.[0]?.indexUpdates).toBe(3);
396
+
397
+ const result2 = await kvStore.delete([entity.key])
398
+ expect(result2?.[0]?.mutationResults?.[0]?.conflictDetected).toBe(false)
399
+ })
400
+
401
+ test('exception', async () => {
402
+ // expect.assertions(2);
403
+ const kvStore = getDstore()
404
+ try {
405
+ const result = await kvStore.set(kvStore.key(['testYodel', NaN]), {
406
+ foo: 'bar',
407
+ })
408
+ } catch (e) {
409
+ expect(e.stack).toMatch(/Dstore\.set/)
410
+ }
411
+ })
412
+ // describe("Transactions", () => {
413
+ // it("simple", async () => {
414
+ // expect.assertions(2);
415
+ // const kvStore = getDstore("huwawi3Datastore");
416
+ // const outerResult = await kvStore.runInTransaction<any>(async () => {
417
+ // // write inside the transaction
418
+ // const entity = { key: kvStore.key(["testYodel", 6]), data: { foo: "foobar" } };
419
+ // const innerResult = await kvStore.save([entity]);
420
+ // // save does not return anything within transactions
421
+ // expect(innerResult).toMatchInlineSnapshot(`undefined`);
422
+ // return 123;
423
+ // });
424
+ // expect(outerResult).toMatchInlineSnapshot(`123`);
425
+
426
+ // // this fails in the Datastore Emulator
427
+ // const entitiey = await kvStore.get(kvStore.key(["testYodel", 6]));
428
+ // // expect(entitiey).toMatchInlineSnapshot(`
429
+ // // Object {
430
+ // // "foo": "foobar",
431
+ // // Symbol(KEY): Key {
432
+ // // "id": "6",
433
+ // // "kind": "testYodel",
434
+ // // "namespace": "test",
435
+ // // "path": Array [
436
+ // // "testYodel",
437
+ // // "6",
438
+ // // ],
439
+ // // },
440
+ // // }
441
+ // // `);
442
+ // });
443
+
444
+ // it("throws", async () => {
445
+ // expect.assertions(1);
446
+ // const kvStore = getDstore("huwawi3Datastore");
447
+ // const request = kvStore.runInTransaction<any>(async () => {
448
+ // throw new DstoreError("TestError", undefined);
449
+ // });
450
+ // await expect(request).rejects.toThrowError(DstoreError);
451
+ // });
452
+ // });
453
+
454
+ // describe('Exceptions', () => {
455
+ // it('simple', async () => {
456
+ // const t = () => {
457
+ // throw new DstoreError('bla', undefined);
458
+ // };
459
+ // expect(t).toThrow(DstoreError);
460
+ // });
461
+ // });