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