w-orm-mongodb 1.1.37 → 1.1.39

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,1200 @@
1
+ import assert from 'assert'
2
+ import path from 'path'
3
+ import { spawn } from 'child_process'
4
+ import WOrm from '../src/WOrmMongodb.mjs'
5
+ import { genUrl, startContainer, stopContainer } from './lib/api-setup.mjs'
6
+
7
+
8
+ let ctName = 'worm-test-mongodb-basic'
9
+ let ctDb = 'worm'
10
+ let ctPort = null //容器對外埠, 由startContainer動態取得
11
+
12
+
13
+ //genOpt
14
+ let genOpt = (cl) => {
15
+ return {
16
+ url: genUrl(ctPort),
17
+ db: ctDb,
18
+ cl,
19
+ }
20
+ }
21
+
22
+ //genKeys, 取回傳物件之鍵集合, 用於驗證鍵集合固定
23
+ let genKeys = (v) => {
24
+ return Object.keys(v).sort().join(',')
25
+ }
26
+
27
+ //runProc, 另起行程操作同一資料表, 用於驗證跨行程之原子性
28
+ let runProc = (tag, cl, mode, ids) => {
29
+ return new Promise(function(resolve) {
30
+ let code = `
31
+ import { pathToFileURL } from 'url'
32
+ let { default: WOrm } = await import(pathToFileURL(process.env.SRC).href)
33
+ let wo = WOrm({ url: process.env.URL, db: process.env.DB, cl: process.env.CL })
34
+ let ids = JSON.parse(process.env.IDS)
35
+ let n = 0
36
+ for (let id of ids) {
37
+ if (process.env.MODE === 'insert') {
38
+ let r = await wo.insert({ id, from: process.env.TAG })
39
+ n += r.nInserted
40
+ }
41
+ else {
42
+ let r = await wo.save({ id: 'x1', [process.env.TAG + id]: 1 })
43
+ n += r[0].ok
44
+ }
45
+ }
46
+ console.log(JSON.stringify({ tag: process.env.TAG, n }))
47
+ `
48
+ let out = ''
49
+ let p = spawn(process.execPath, ['--input-type=module', '-e', code], {
50
+ shell: false,
51
+ env: {
52
+ ...process.env,
53
+ SRC: path.resolve('./src/WOrmMongodb.mjs'),
54
+ URL: genUrl(ctPort),
55
+ DB: ctDb,
56
+ CL: cl,
57
+ MODE: mode,
58
+ IDS: JSON.stringify(ids),
59
+ TAG: tag,
60
+ },
61
+ })
62
+ p.stdout.on('data', function(d) {
63
+ out += d.toString()
64
+ })
65
+ p.stderr.on('data', function(d) {
66
+ out += d.toString()
67
+ })
68
+ p.on('close', function() {
69
+ let r = null
70
+ try {
71
+ r = JSON.parse(out.trim())
72
+ }
73
+ catch (err) {
74
+ r = { tag, error: out.trim() }
75
+ }
76
+ resolve(r)
77
+ })
78
+ })
79
+ }
80
+
81
+
82
+ before(async function() {
83
+ this.timeout(600000) //含拉取映像與服務啟動
84
+ ctPort = await startContainer(ctName)
85
+ })
86
+
87
+ after(async function() {
88
+ this.timeout(120000)
89
+ await stopContainer(ctName)
90
+ })
91
+
92
+
93
+ describe('basic', function() {
94
+ let rt = null
95
+ let vans = {}
96
+ let vget = {}
97
+
98
+ before(async function () {
99
+ this.timeout(120000)
100
+
101
+ let opt = genOpt('users')
102
+
103
+ let rs = [
104
+ {
105
+ id: 'id-peter',
106
+ name: 'peter',
107
+ value: 123,
108
+ },
109
+ {
110
+ id: 'id-rosemary',
111
+ name: 'rosemary',
112
+ value: 123.456,
113
+ },
114
+ {
115
+ id: '',
116
+ name: 'kettle',
117
+ value: 456,
118
+ },
119
+ ]
120
+
121
+ let rsm = [
122
+ {
123
+ id: 'id-peter',
124
+ name: 'peter(modify)'
125
+ },
126
+ {
127
+ id: 'id-rosemary',
128
+ name: 'rosemary(modify)'
129
+ },
130
+ {
131
+ id: '',
132
+ name: 'kettle(modify)'
133
+ },
134
+ ]
135
+
136
+ //wo
137
+ let wo = WOrm(opt)
138
+
139
+ //on
140
+ wo.on('change', function(mode, data, res) {
141
+ // console.log('change', mode)
142
+ })
143
+
144
+ //delAll
145
+ rt = null
146
+ // vans[1] = { n: 0, nDeleted: 0, ok: 1 }
147
+ await wo.delAll()
148
+ .then(function(msg) {
149
+ // console.log('delAll then', msg)
150
+ rt = msg
151
+ })
152
+ .catch(function(msg) {
153
+ // console.log('delAll catch', msg)
154
+ rt = msg.toString()
155
+ })
156
+ vget[1] = rt
157
+
158
+ //insert
159
+ rt = null
160
+ // vans[2] = { n: 3, nInserted: 3, ok: 1 }
161
+ await wo.insert(rs)
162
+ .then(function(msg) {
163
+ // console.log('insert then', msg)
164
+ rt = msg
165
+ })
166
+ .catch(function(msg) {
167
+ // console.log('insert catch', msg)
168
+ rt = msg.toString()
169
+ })
170
+ vget[2] = rt
171
+
172
+ //save, rsm末筆之id為空字串故會自動產生新id, 於資料表內不存在且未開啟autoInsert, 因此不寫入
173
+ rt = null
174
+ // vans[3] = [
175
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
176
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
177
+ // { n: 0, nInserted: 0, nModified: 0, ok: 1 }
178
+ // ]
179
+ await wo.save(rsm, { autoInsert: false })
180
+ .then(function(msg) {
181
+ // console.log('save then', msg)
182
+ rt = msg
183
+ })
184
+ .catch(function(msg) {
185
+ // console.log('save catch', msg)
186
+ rt = msg.toString()
187
+ })
188
+ vget[3] = rt
189
+
190
+ //save之逐筆結果鍵集合須固定
191
+ // vans[16] = ['n,nInserted,nModified,ok', 'n,nInserted,nModified,ok', 'n,nInserted,nModified,ok']
192
+ vget[16] = vget[3].map(genKeys)
193
+
194
+ //select all
195
+ rt = null
196
+ // vans[4] = [
197
+ // { id: 'id-peter', name: 'peter(modify)', value: 123 },
198
+ // { id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 },
199
+ // { name: 'kettle', value: 456 } //id為隨機產生故不比對
200
+ // ]
201
+ let ssAll = null
202
+ await wo.select()
203
+ .then(function(msg) {
204
+ // console.log('select all then', msg)
205
+ ssAll = msg
206
+ rt = [
207
+ msg[0],
208
+ msg[1],
209
+ {
210
+ name: msg[2].name,
211
+ value: msg[2].value,
212
+ },
213
+ ]
214
+ })
215
+ .catch(function(msg) {
216
+ // console.log('select all catch', msg)
217
+ rt = msg.toString()
218
+ })
219
+ vget[4] = rt
220
+
221
+ //select
222
+ rt = null
223
+ // vans[5] = [{ id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }]
224
+ await wo.select({ id: 'id-rosemary' })
225
+ .then(function(msg) {
226
+ rt = msg
227
+ })
228
+ .catch(function(msg) {
229
+ rt = msg.toString()
230
+ })
231
+ vget[5] = rt
232
+
233
+ //select by $and, $gt, $lt
234
+ rt = null
235
+ // vans[6] = [{ id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }]
236
+ await wo.select({ '$and': [{ value: { '$gt': 123 } }, { value: { '$lt': 200 } }] })
237
+ .then(function(msg) {
238
+ rt = msg
239
+ })
240
+ .catch(function(msg) {
241
+ rt = msg.toString()
242
+ })
243
+ vget[6] = rt
244
+
245
+ //select by $or, $gte, $lte
246
+ rt = null
247
+ // vans[7] = [{ name: 'kettle', value: 456 }] //id為隨機產生故不比對
248
+ await wo.select({ '$or': [{ value: { '$lte': -1 } }, { value: { '$gte': 200 } }] })
249
+ .then(function(msg) {
250
+ rt = [
251
+ {
252
+ name: msg[0].name,
253
+ value: msg[0].value,
254
+ },
255
+ ]
256
+ })
257
+ .catch(function(msg) {
258
+ rt = msg.toString()
259
+ })
260
+ vget[7] = rt
261
+
262
+ //select by $or, $and, $ne, $in, $nin
263
+ rt = null
264
+ // vans[8] = [
265
+ // { id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 },
266
+ // { name: 'kettle', value: 456 } //id為隨機產生故不比對
267
+ // ]
268
+ await wo.select({ '$or': [{ '$and': [{ value: { '$ne': 123 } }, { value: { '$in': [123, 321, 123.456, 456] } }, { value: { '$nin': [456, 654] } }] }, { '$or': [{ value: { '$lte': -1 } }, { value: { '$gte': 400 } }] }] })
269
+ .then(function(msg) {
270
+ rt = [
271
+ msg[0],
272
+ {
273
+ name: msg[1].name,
274
+ value: msg[1].value,
275
+ },
276
+ ]
277
+ })
278
+ .catch(function(msg) {
279
+ rt = msg.toString()
280
+ })
281
+ vget[8] = rt
282
+
283
+ //select by regex, $options之合法flag僅有i、m、x、s, 給予無效flag時MongoDB會回MongoServerError
284
+ rt = null
285
+ // vans[9] = [{ id: 'id-peter', name: 'peter(modify)', value: 123 }]
286
+ await wo.select({ name: { $regex: 'PeT', $options: 'i' } })
287
+ .then(function(msg) {
288
+ rt = msg
289
+ })
290
+ .catch(function(msg) {
291
+ rt = msg.toString()
292
+ })
293
+ vget[9] = rt
294
+
295
+ //select無符合數據須回空陣列
296
+ // vans[17] = []
297
+ vget[17] = await wo.select({ name: 'not-existed' })
298
+
299
+ //selectByPk, 由id直接查找單筆
300
+ rt = null
301
+ // vans[10] = { id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }
302
+ await wo.selectByPk('id-rosemary')
303
+ .then(function(msg) {
304
+ rt = msg
305
+ })
306
+ .catch(function(msg) {
307
+ rt = msg.toString()
308
+ })
309
+ vget[10] = rt
310
+
311
+ //selectByPk, 與select(find)取得同一筆之內容須一致
312
+ // vans[11] = true
313
+ let sbi = await wo.selectByPk('id-peter')
314
+ let sbf = await wo.select({ id: 'id-peter' })
315
+ vget[11] = JSON.stringify(sbi) === JSON.stringify(sbf[0])
316
+
317
+ //selectByPk by id not existed
318
+ rt = null
319
+ // vans[12] = null
320
+ await wo.selectByPk('id-not-existed')
321
+ .then(function(msg) {
322
+ rt = msg
323
+ })
324
+ .catch(function(msg) {
325
+ rt = msg.toString()
326
+ })
327
+ vget[12] = rt
328
+
329
+ //selectByPk by id invalid, 未給有效id視為查無數據
330
+ // vans[13] = [null, null, null]
331
+ vget[13] = [
332
+ await wo.selectByPk(''),
333
+ await wo.selectByPk(123),
334
+ await wo.selectByPk(null),
335
+ ]
336
+
337
+ //del
338
+ rt = null
339
+ // vans[14] = [{ n: 1, nDeleted: 1, ok: 1 }]
340
+ let d = ssAll.filter(function(v) {
341
+ return v.name === 'kettle'
342
+ })
343
+ await wo.del(d)
344
+ .then(function(msg) {
345
+ rt = msg
346
+ })
347
+ .catch(function(msg) {
348
+ rt = msg.toString()
349
+ })
350
+ vget[14] = rt
351
+
352
+ //selectByPk, 已刪除者須回傳null
353
+ // vans[15] = null
354
+ vget[15] = await wo.selectByPk(d[0].id)
355
+
356
+ })
357
+
358
+ vans[1] = { n: 0, nDeleted: 0, ok: 1 }
359
+ it(`should get ${JSON.stringify(vans[1])} for delAll`, async function() {
360
+ assert.strict.deepStrictEqual(vget[1], vans[1])
361
+ })
362
+
363
+ vans[2] = { n: 3, nInserted: 3, ok: 1 }
364
+ it(`should get ${JSON.stringify(vans[2])} for insert`, async function() {
365
+ assert.strict.deepStrictEqual(vget[2], vans[2])
366
+ })
367
+
368
+ vans[3] = [
369
+ { n: 1, nInserted: 0, nModified: 1, ok: 1 },
370
+ { n: 1, nInserted: 0, nModified: 1, ok: 1 },
371
+ { n: 0, nInserted: 0, nModified: 0, ok: 1 }
372
+ ]
373
+ it(`should get ${JSON.stringify(vans[3])} for save(autoInsert=false)`, async function() {
374
+ assert.strict.deepStrictEqual(vget[3], vans[3])
375
+ })
376
+
377
+ vans[16] = ['n,nInserted,nModified,ok', 'n,nInserted,nModified,ok', 'n,nInserted,nModified,ok']
378
+ it(`should get ${JSON.stringify(vans[16])} for fixed key set of save`, async function() {
379
+ assert.strict.deepStrictEqual(vget[16], vans[16])
380
+ })
381
+
382
+ vans[4] = [
383
+ { id: 'id-peter', name: 'peter(modify)', value: 123 },
384
+ { id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 },
385
+ { name: 'kettle', value: 456 }
386
+ ]
387
+ it(`should get ${JSON.stringify(vans[4])} for select all`, async function() {
388
+ assert.strict.deepStrictEqual(vget[4], vans[4])
389
+ })
390
+
391
+ vans[5] = [{ id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }]
392
+ it(`should get ${JSON.stringify(vans[5])} for select`, async function() {
393
+ assert.strict.deepStrictEqual(vget[5], vans[5])
394
+ })
395
+
396
+ vans[6] = [{ id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }]
397
+ it(`should get ${JSON.stringify(vans[6])} for select by $and, $gt, $lt`, async function() {
398
+ assert.strict.deepStrictEqual(vget[6], vans[6])
399
+ })
400
+
401
+ vans[7] = [{ name: 'kettle', value: 456 }]
402
+ it(`should get ${JSON.stringify(vans[7])} for select by $or, $gte, $lte`, async function() {
403
+ assert.strict.deepStrictEqual(vget[7], vans[7])
404
+ })
405
+
406
+ vans[8] = [
407
+ { id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 },
408
+ { name: 'kettle', value: 456 }
409
+ ]
410
+ it(`should get ${JSON.stringify(vans[8])} for select by $or, $and, $ne, $in, $nin`, async function() {
411
+ assert.strict.deepStrictEqual(vget[8], vans[8])
412
+ })
413
+
414
+ vans[9] = [{ id: 'id-peter', name: 'peter(modify)', value: 123 }]
415
+ it(`should get ${JSON.stringify(vans[9])} for select by regex`, async function() {
416
+ assert.strict.deepStrictEqual(vget[9], vans[9])
417
+ })
418
+
419
+ vans[17] = []
420
+ it(`should get ${JSON.stringify(vans[17])} for select with no matched data`, async function() {
421
+ assert.strict.deepStrictEqual(vget[17], vans[17])
422
+ })
423
+
424
+ vans[10] = { id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }
425
+ it(`should get ${JSON.stringify(vans[10])} for selectByPk`, async function() {
426
+ assert.strict.deepStrictEqual(vget[10], vans[10])
427
+ })
428
+
429
+ vans[11] = true
430
+ it(`should get ${JSON.stringify(vans[11])} for same content between selectByPk and select`, async function() {
431
+ assert.strict.deepStrictEqual(vget[11], vans[11])
432
+ })
433
+
434
+ vans[12] = null
435
+ it(`should get ${JSON.stringify(vans[12])} for selectByPk by id not existed`, async function() {
436
+ assert.strict.deepStrictEqual(vget[12], vans[12])
437
+ })
438
+
439
+ vans[13] = [null, null, null]
440
+ it(`should get ${JSON.stringify(vans[13])} for selectByPk by id invalid`, async function() {
441
+ assert.strict.deepStrictEqual(vget[13], vans[13])
442
+ })
443
+
444
+ vans[14] = [{ n: 1, nDeleted: 1, ok: 1 }]
445
+ it(`should get ${JSON.stringify(vans[14])} for del`, async function() {
446
+ assert.strict.deepStrictEqual(vget[14], vans[14])
447
+ })
448
+
449
+ vans[15] = null
450
+ it(`should get ${JSON.stringify(vans[15])} for selectByPk after del`, async function() {
451
+ assert.strict.deepStrictEqual(vget[15], vans[15])
452
+ })
453
+
454
+ })
455
+
456
+
457
+ //insert與save之併發測試, 因各操作皆各自new MongoClient另開連線, 原子性全由MongoDB提供,
458
+ //故同行程以Promise.all併發即等價於跨行程併發, 惟仍另以子行程實測以取得跨行程之依據
459
+
460
+
461
+ describe('insert', function() {
462
+ let vans = {}
463
+ let vget = {}
464
+
465
+ before(async function() {
466
+ this.timeout(120000)
467
+
468
+ //wo
469
+ let wo = WOrm(genOpt('insertcnt'))
470
+ await wo.delAll()
471
+
472
+ //全新3筆
473
+ vget[1] = await wo.insert([
474
+ { id: 'i1', name: 'peter', value: 1 },
475
+ { id: 'i2', name: 'rosemary', value: 2 },
476
+ { id: 'i3', name: 'kettle', value: 3 },
477
+ ])
478
+
479
+ //重複插入同3筆, 全數已存在故皆不插入, 且不得reject
480
+ vget[2] = await wo.insert([
481
+ { id: 'i1', name: 'peter', value: 1 },
482
+ { id: 'i2', name: 'rosemary', value: 2 },
483
+ { id: 'i3', name: 'kettle', value: 3 },
484
+ ])
485
+
486
+ //部份重複, 1筆已存在2筆為新
487
+ vget[3] = await wo.insert([
488
+ { id: 'i2', name: 'rosemary', value: 2 },
489
+ { id: 'i4', name: 'sandler', value: 4 },
490
+ { id: 'i5', name: 'joyce', value: 5 },
491
+ ])
492
+
493
+ //同批含重複id, 僅首筆成功
494
+ vget[4] = await wo.insert([
495
+ { id: 'i-dup', name: 'dup-1', value: 6 },
496
+ { id: 'i-dup', name: 'dup-2', value: 7 },
497
+ { id: 'i-uniq', name: 'uniq', value: 8 },
498
+ ])
499
+
500
+ //併發對同一id插入10次, nInserted總和須為1
501
+ let rs = await Promise.all(Array.from({ length: 10 }, (v, k) => {
502
+ return wo.insert({ id: 'i-race', name: `race-${k}`, value: k })
503
+ }))
504
+ vget[5] = rs.reduce((sum, v) => sum + v.nInserted, 0)
505
+
506
+ //併發皆不得報錯
507
+ vget[6] = rs.filter((v) => v.ok !== 1).length
508
+
509
+ //最終筆數, 3+0+2+2+1=8
510
+ vget[7] = (await wo.select()).length
511
+
512
+ //同批重複者僅首筆入庫
513
+ vget[8] = (await wo.selectByPk('i-dup')).name
514
+
515
+ //已存在id不得被insert覆寫
516
+ vget[9] = await wo.selectByPk('i2')
517
+
518
+ //併發後僅存單筆
519
+ vget[10] = (await wo.select({ id: 'i-race' })).length
520
+
521
+ //回傳鍵集合須固定
522
+ vget[11] = [genKeys(vget[1]), genKeys(vget[2])]
523
+
524
+ //輸入無效視為空結果
525
+ vget[12] = await wo.insert(null)
526
+
527
+ })
528
+
529
+ vans[1] = { n: 3, nInserted: 3, ok: 1 }
530
+ it(`should get ${JSON.stringify(vans[1])} for insert 3 new records`, async function() {
531
+ assert.strict.deepStrictEqual(vget[1], vans[1])
532
+ })
533
+
534
+ vans[2] = { n: 3, nInserted: 0, ok: 1 }
535
+ it(`should get ${JSON.stringify(vans[2])} for insert 3 existed records`, async function() {
536
+ assert.strict.deepStrictEqual(vget[2], vans[2])
537
+ })
538
+
539
+ vans[3] = { n: 3, nInserted: 2, ok: 1 }
540
+ it(`should get ${JSON.stringify(vans[3])} for insert 1 existed and 2 new records`, async function() {
541
+ assert.strict.deepStrictEqual(vget[3], vans[3])
542
+ })
543
+
544
+ vans[4] = { n: 3, nInserted: 2, ok: 1 }
545
+ it(`should get ${JSON.stringify(vans[4])} for insert with duplicated id in same batch`, async function() {
546
+ assert.strict.deepStrictEqual(vget[4], vans[4])
547
+ })
548
+
549
+ vans[5] = 1
550
+ it(`should get ${JSON.stringify(vans[5])} for sum of nInserted by 10 concurrent insert with same id`, async function() {
551
+ assert.strict.deepStrictEqual(vget[5], vans[5])
552
+ })
553
+
554
+ vans[6] = 0
555
+ it(`should get ${JSON.stringify(vans[6])} for count of ok!==1 in 10 concurrent insert`, async function() {
556
+ assert.strict.deepStrictEqual(vget[6], vans[6])
557
+ })
558
+
559
+ vans[7] = 8
560
+ it(`should get ${JSON.stringify(vans[7])} for records after all insert`, async function() {
561
+ assert.strict.deepStrictEqual(vget[7], vans[7])
562
+ })
563
+
564
+ vans[8] = 'dup-1'
565
+ it(`should get ${JSON.stringify(vans[8])} for keeping first one of duplicated id in same batch`, async function() {
566
+ assert.strict.deepStrictEqual(vget[8], vans[8])
567
+ })
568
+
569
+ vans[9] = { id: 'i2', name: 'rosemary', value: 2 }
570
+ it(`should get ${JSON.stringify(vans[9])} for not overwriting existed id`, async function() {
571
+ assert.strict.deepStrictEqual(vget[9], vans[9])
572
+ })
573
+
574
+ vans[10] = 1
575
+ it(`should get ${JSON.stringify(vans[10])} for records after 10 concurrent insert with same id`, async function() {
576
+ assert.strict.deepStrictEqual(vget[10], vans[10])
577
+ })
578
+
579
+ vans[11] = ['n,nInserted,ok', 'n,nInserted,ok']
580
+ it(`should get ${JSON.stringify(vans[11])} for fixed key set of insert`, async function() {
581
+ assert.strict.deepStrictEqual(vget[11], vans[11])
582
+ })
583
+
584
+ vans[12] = { n: 0, nInserted: 0, ok: 1 }
585
+ it(`should get ${JSON.stringify(vans[12])} for insert with invalid data`, async function() {
586
+ assert.strict.deepStrictEqual(vget[12], vans[12])
587
+ })
588
+
589
+ })
590
+
591
+
592
+ describe('save', function() {
593
+ let vans = {}
594
+ let vget = {}
595
+
596
+ before(async function() {
597
+ this.timeout(120000)
598
+
599
+ //wo
600
+ let wo = WOrm(genOpt('saverace'))
601
+ await wo.delAll()
602
+
603
+ //併發save對既有id之不同欄位, 各欄位皆須保留
604
+ await wo.insert({ id: 'u1', base: 1 })
605
+ await Promise.all(Array.from({ length: 10 }, (v, k) => {
606
+ return wo.save({ id: 'u1', [`f${k}`]: k })
607
+ }))
608
+ let vu1 = await wo.selectByPk('u1')
609
+ vget[1] = Object.keys(vu1).filter((k) => k.indexOf('f') === 0).length
610
+ vget[2] = vu1.base
611
+
612
+ //併發save對全新id, autoInsert僅一次且不得報錯
613
+ let rsn = await Promise.all(Array.from({ length: 5 }, (v, k) => {
614
+ return wo.save({ id: 'w1', [`g${k}`]: k })
615
+ }))
616
+ vget[3] = rsn.filter((v) => v[0].nInserted === 1).length
617
+ vget[4] = rsn.filter((v) => v[0].ok !== 1).length
618
+ vget[5] = (await wo.select({ id: 'w1' })).length
619
+ vget[6] = Object.keys(await wo.selectByPk('w1')).filter((k) => k.indexOf('g') === 0).length
620
+
621
+ //save內容相同不更新, 由MongoDB於伺服器端比對合併後結果與現值
622
+ await wo.insert({ id: 's1', name: 'same', value: 5 })
623
+ vget[7] = await wo.save({ id: 's1', name: 'same', value: 5 })
624
+
625
+ //save僅給既有數據之部份欄位且值相同, 合併後等於現值, 故亦不更新
626
+ vget[16] = await wo.save({ id: 's1', name: 'same' })
627
+
628
+ //save之巢狀物件與陣列內容相同亦不更新
629
+ await wo.insert({ id: 's2', obj: { k: 1 }, arr: [1, 2] })
630
+ vget[17] = await wo.save({ id: 's2', obj: { k: 1 }, arr: [1, 2] })
631
+ vget[18] = await wo.save({ id: 's2', obj: { k: 2 }, arr: [1, 2] })
632
+
633
+ //save內容不同須更新, 未給之欄位須保留
634
+ await wo.insert({ id: 'd1', name: 'diff', value: 5 })
635
+ vget[8] = await wo.save({ id: 'd1', value: 6 })
636
+ vget[9] = await wo.selectByPk('d1')
637
+
638
+ //save(autoInsert=false)對不存在之id不得插入
639
+ vget[10] = await wo.save({ id: 'n1', name: 'none' }, { autoInsert: false })
640
+ vget[11] = await wo.selectByPk('n1')
641
+
642
+ //併發save(autoInsert=false)對既有id之不同欄位, 各欄位皆須保留且不得報錯
643
+ await wo.insert({ id: 'p1', name: 'origin', value: 1 })
644
+ let rsu = await Promise.all([
645
+ wo.save({ id: 'p1', name: 'upd-name' }, { autoInsert: false }),
646
+ wo.save({ id: 'p1', value: 88 }, { autoInsert: false }),
647
+ ])
648
+ vget[12] = rsu.filter((v) => v[0].ok !== 1).length
649
+ vget[13] = await wo.selectByPk('p1')
650
+
651
+ //change事件, autoInsert時仍須發出insert事件
652
+ let woEv = WOrm(genOpt('saveevent'))
653
+ await woEv.delAll()
654
+ let modes = []
655
+ woEv.on('change', function(mode) {
656
+ modes.push(mode)
657
+ })
658
+ await woEv.save({ id: 'e1', name: 'new' }) //不存在, 走autoInsert
659
+ vget[14] = [...modes]
660
+ modes = []
661
+ await woEv.save({ id: 'e1', name: 'modify' }) //已存在, 走更新
662
+ vget[15] = [...modes]
663
+
664
+ //輸入無效視為空結果
665
+ vget[19] = await wo.save(null)
666
+
667
+ //單一物件輸入亦須回傳長度為1之陣列
668
+ vget[20] = (await wo.save({ id: 'u1', arrlen: 1 })).length
669
+
670
+ })
671
+
672
+ vans[1] = 10
673
+ it(`should get ${JSON.stringify(vans[1])} for fields after 10 concurrent save with different field`, async function() {
674
+ assert.strict.deepStrictEqual(vget[1], vans[1])
675
+ })
676
+
677
+ vans[2] = 1
678
+ it(`should get ${JSON.stringify(vans[2])} for keeping original field after concurrent save`, async function() {
679
+ assert.strict.deepStrictEqual(vget[2], vans[2])
680
+ })
681
+
682
+ vans[3] = 1
683
+ it(`should get ${JSON.stringify(vans[3])} for count of nInserted===1 in 5 concurrent save with id not existed`, async function() {
684
+ assert.strict.deepStrictEqual(vget[3], vans[3])
685
+ })
686
+
687
+ vans[4] = 0
688
+ it(`should get ${JSON.stringify(vans[4])} for count of ok!==1 in 5 concurrent save with id not existed`, async function() {
689
+ assert.strict.deepStrictEqual(vget[4], vans[4])
690
+ })
691
+
692
+ vans[5] = 1
693
+ it(`should get ${JSON.stringify(vans[5])} for records after 5 concurrent save with id not existed`, async function() {
694
+ assert.strict.deepStrictEqual(vget[5], vans[5])
695
+ })
696
+
697
+ vans[6] = 5
698
+ it(`should get ${JSON.stringify(vans[6])} for fields after 5 concurrent save with id not existed`, async function() {
699
+ assert.strict.deepStrictEqual(vget[6], vans[6])
700
+ })
701
+
702
+ vans[7] = [{ n: 1, nInserted: 0, nModified: 0, ok: 1 }]
703
+ it(`should get ${JSON.stringify(vans[7])} for save with same content`, async function() {
704
+ assert.strict.deepStrictEqual(vget[7], vans[7])
705
+ })
706
+
707
+ vans[16] = [{ n: 1, nInserted: 0, nModified: 0, ok: 1 }]
708
+ it(`should get ${JSON.stringify(vans[16])} for save with part of fields and same content`, async function() {
709
+ assert.strict.deepStrictEqual(vget[16], vans[16])
710
+ })
711
+
712
+ vans[17] = [{ n: 1, nInserted: 0, nModified: 0, ok: 1 }]
713
+ it(`should get ${JSON.stringify(vans[17])} for save with same object and array`, async function() {
714
+ assert.strict.deepStrictEqual(vget[17], vans[17])
715
+ })
716
+
717
+ vans[18] = [{ n: 1, nInserted: 0, nModified: 1, ok: 1 }]
718
+ it(`should get ${JSON.stringify(vans[18])} for save with different object`, async function() {
719
+ assert.strict.deepStrictEqual(vget[18], vans[18])
720
+ })
721
+
722
+ vans[8] = [{ n: 1, nInserted: 0, nModified: 1, ok: 1 }]
723
+ it(`should get ${JSON.stringify(vans[8])} for save with different content`, async function() {
724
+ assert.strict.deepStrictEqual(vget[8], vans[8])
725
+ })
726
+
727
+ vans[9] = { id: 'd1', name: 'diff', value: 6 }
728
+ it(`should get ${JSON.stringify(vans[9])} for keeping field not given in save`, async function() {
729
+ assert.strict.deepStrictEqual(vget[9], vans[9])
730
+ })
731
+
732
+ vans[10] = [{ n: 0, nInserted: 0, nModified: 0, ok: 1 }]
733
+ it(`should get ${JSON.stringify(vans[10])} for save(autoInsert=false) with id not existed`, async function() {
734
+ assert.strict.deepStrictEqual(vget[10], vans[10])
735
+ })
736
+
737
+ vans[11] = null
738
+ it(`should get ${JSON.stringify(vans[11])} for record after save(autoInsert=false) with id not existed`, async function() {
739
+ assert.strict.deepStrictEqual(vget[11], vans[11])
740
+ })
741
+
742
+ vans[12] = 0
743
+ it(`should get ${JSON.stringify(vans[12])} for count of ok!==1 in 2 concurrent save(autoInsert=false)`, async function() {
744
+ assert.strict.deepStrictEqual(vget[12], vans[12])
745
+ })
746
+
747
+ vans[13] = { id: 'p1', name: 'upd-name', value: 88 }
748
+ it(`should get ${JSON.stringify(vans[13])} for fields after 2 concurrent save(autoInsert=false) with different field`, async function() {
749
+ assert.strict.deepStrictEqual(vget[13], vans[13])
750
+ })
751
+
752
+ vans[14] = ['insert', 'save']
753
+ it(`should get ${JSON.stringify(vans[14])} for change events of save(autoInsert) with id not existed`, async function() {
754
+ assert.strict.deepStrictEqual(vget[14], vans[14])
755
+ })
756
+
757
+ vans[15] = ['save']
758
+ it(`should get ${JSON.stringify(vans[15])} for change events of save with id existed`, async function() {
759
+ assert.strict.deepStrictEqual(vget[15], vans[15])
760
+ })
761
+
762
+ vans[19] = []
763
+ it(`should get ${JSON.stringify(vans[19])} for save with invalid data`, async function() {
764
+ assert.strict.deepStrictEqual(vget[19], vans[19])
765
+ })
766
+
767
+ vans[20] = 1
768
+ it(`should get ${JSON.stringify(vans[20])} for length of save with single object`, async function() {
769
+ assert.strict.deepStrictEqual(vget[20], vans[20])
770
+ })
771
+
772
+ })
773
+
774
+
775
+ describe('save single failure', function() {
776
+ let vans = {}
777
+ let vget = {}
778
+
779
+ before(async function() {
780
+ this.timeout(120000)
781
+
782
+ //wo
783
+ let wo = WOrm(genOpt('savefail'))
784
+ await wo.delAll()
785
+
786
+ //以$開頭欄位名令MongoDB拒絕該筆更新, 此錯誤僅影響該筆
787
+ await wo.insert([{ id: 'f1' }, { id: 'f2' }, { id: 'f3' }])
788
+ let rf = await wo.save([
789
+ { id: 'f1', name: 'ok1' },
790
+ { id: 'f2', $bad: 1 },
791
+ { id: 'f3', name: 'ok3' },
792
+ ])
793
+
794
+ //整批仍resolve且長度與輸入相同
795
+ vget[1] = rf.length
796
+
797
+ //各筆之ok
798
+ vget[2] = rf.map((v) => v.ok)
799
+
800
+ //失敗筆須附err字串
801
+ vget[3] = typeof rf[1].err === 'string' && rf[1].err.length > 0
802
+
803
+ //失敗筆之鍵集合, err僅於ok為0時出現
804
+ vget[4] = genKeys(rf[1])
805
+ vget[5] = genKeys(rf[0])
806
+
807
+ //失敗筆之計數欄位
808
+ vget[6] = {
809
+ n: rf[1].n,
810
+ nInserted: rf[1].nInserted,
811
+ nModified: rf[1].nModified,
812
+ }
813
+
814
+ //單筆失敗不得中斷整批, 後續筆仍須寫入
815
+ vget[7] = (await wo.selectByPk('f3')).name
816
+
817
+ //失敗筆不得被寫入
818
+ vget[8] = await wo.selectByPk('f2')
819
+
820
+ })
821
+
822
+ vans[1] = 3
823
+ it(`should get ${JSON.stringify(vans[1])} for length of save with 1 failed record`, async function() {
824
+ assert.strict.deepStrictEqual(vget[1], vans[1])
825
+ })
826
+
827
+ vans[2] = [1, 0, 1]
828
+ it(`should get ${JSON.stringify(vans[2])} for ok of each record`, async function() {
829
+ assert.strict.deepStrictEqual(vget[2], vans[2])
830
+ })
831
+
832
+ vans[3] = true
833
+ it(`should get ${JSON.stringify(vans[3])} for err of failed record`, async function() {
834
+ assert.strict.deepStrictEqual(vget[3], vans[3])
835
+ })
836
+
837
+ vans[4] = 'err,n,nInserted,nModified,ok'
838
+ it(`should get ${JSON.stringify(vans[4])} for key set of failed record`, async function() {
839
+ assert.strict.deepStrictEqual(vget[4], vans[4])
840
+ })
841
+
842
+ vans[5] = 'n,nInserted,nModified,ok'
843
+ it(`should get ${JSON.stringify(vans[5])} for key set of succeeded record`, async function() {
844
+ assert.strict.deepStrictEqual(vget[5], vans[5])
845
+ })
846
+
847
+ vans[6] = { n: 1, nInserted: 0, nModified: 0 }
848
+ it(`should get ${JSON.stringify(vans[6])} for counts of failed record`, async function() {
849
+ assert.strict.deepStrictEqual(vget[6], vans[6])
850
+ })
851
+
852
+ vans[7] = 'ok3'
853
+ it(`should get ${JSON.stringify(vans[7])} for not breaking batch by single failure`, async function() {
854
+ assert.strict.deepStrictEqual(vget[7], vans[7])
855
+ })
856
+
857
+ vans[8] = { id: 'f2' }
858
+ it(`should get ${JSON.stringify(vans[8])} for not writing failed record`, async function() {
859
+ assert.strict.deepStrictEqual(vget[8], vans[8])
860
+ })
861
+
862
+ })
863
+
864
+
865
+ describe('del', function() {
866
+ let vans = {}
867
+ let vget = {}
868
+
869
+ before(async function() {
870
+ this.timeout(120000)
871
+
872
+ //wo
873
+ let wo = WOrm(genOpt('delcase'))
874
+ await wo.delAll()
875
+
876
+ await wo.insert([{ id: 'k1', name: 'a' }, { id: 'k2', name: 'b' }])
877
+
878
+ //id為null之數據, 用於驗證未帶有效id者不得誤中
879
+ await wo.insert({ id: 'k-null-guard', name: 'guard' })
880
+
881
+ //命中並刪除
882
+ vget[1] = await wo.del({ id: 'k1' })
883
+
884
+ //未命中屬正常結果
885
+ vget[2] = await wo.del({ id: 'k-not-existed' })
886
+
887
+ //未帶有效id, 不得送查詢條件, 須以ok為0回報
888
+ vget[3] = await wo.del({ name: '完全沒給id' })
889
+ vget[4] = genKeys(vget[3][0])
890
+ vget[5] = typeof vget[3][0].err === 'string' && vget[3][0].err.length > 0
891
+
892
+ //id為空字串亦視為未帶有效id
893
+ vget[6] = await wo.del({ id: '' })
894
+
895
+ //未帶有效id者不得刪到任何數據
896
+ vget[7] = (await wo.select()).length
897
+
898
+ //混合輸入, 有效與無效並存時須逐筆回報且長度相同
899
+ vget[8] = await wo.del([{ id: 'k2' }, { name: 'no-id' }])
900
+
901
+ //輸入無效視為空結果
902
+ vget[9] = await wo.del(null)
903
+
904
+ //成功筆之鍵集合
905
+ vget[10] = genKeys(vget[1][0])
906
+
907
+ //該筆執行失敗, 以超過BSON上限(16mb)之id令驅動拒絕該筆刪除, 此錯誤僅影響該筆
908
+ await wo.insert({ id: 'k3', name: 'c' })
909
+ let rbig = await wo.del([
910
+ { id: 'x'.repeat(17 * 1024 * 1024) },
911
+ { id: 'k3' },
912
+ ])
913
+ vget[11] = rbig.length
914
+ vget[12] = rbig.map((v) => v.ok)
915
+ vget[13] = {
916
+ n: rbig[0].n,
917
+ nDeleted: rbig[0].nDeleted,
918
+ }
919
+ vget[14] = genKeys(rbig[0])
920
+ vget[15] = typeof rbig[0].err === 'string' && rbig[0].err.length > 0
921
+
922
+ //單筆失敗不得中斷整批, 後續筆仍須刪除
923
+ vget[16] = await wo.selectByPk('k3')
924
+
925
+ })
926
+
927
+ vans[1] = [{ n: 1, nDeleted: 1, ok: 1 }]
928
+ it(`should get ${JSON.stringify(vans[1])} for del with id matched`, async function() {
929
+ assert.strict.deepStrictEqual(vget[1], vans[1])
930
+ })
931
+
932
+ vans[2] = [{ n: 0, nDeleted: 0, ok: 1 }]
933
+ it(`should get ${JSON.stringify(vans[2])} for del with id not existed`, async function() {
934
+ assert.strict.deepStrictEqual(vget[2], vans[2])
935
+ })
936
+
937
+ vans[3] = [{ n: 0, nDeleted: 0, ok: 0, err: 'invalid id[undefined]' }]
938
+ it(`should get ${JSON.stringify(vans[3])} for del without valid id`, async function() {
939
+ assert.strict.deepStrictEqual(vget[3], vans[3])
940
+ })
941
+
942
+ vans[4] = 'err,n,nDeleted,ok'
943
+ it(`should get ${JSON.stringify(vans[4])} for key set of del without valid id`, async function() {
944
+ assert.strict.deepStrictEqual(vget[4], vans[4])
945
+ })
946
+
947
+ vans[5] = true
948
+ it(`should get ${JSON.stringify(vans[5])} for err of del without valid id`, async function() {
949
+ assert.strict.deepStrictEqual(vget[5], vans[5])
950
+ })
951
+
952
+ vans[6] = [{ n: 0, nDeleted: 0, ok: 0, err: 'invalid id[]' }]
953
+ it(`should get ${JSON.stringify(vans[6])} for del with empty id`, async function() {
954
+ assert.strict.deepStrictEqual(vget[6], vans[6])
955
+ })
956
+
957
+ vans[7] = 2
958
+ it(`should get ${JSON.stringify(vans[7])} for not deleting anything by invalid id`, async function() {
959
+ assert.strict.deepStrictEqual(vget[7], vans[7])
960
+ })
961
+
962
+ vans[8] = [
963
+ { n: 1, nDeleted: 1, ok: 1 },
964
+ { n: 0, nDeleted: 0, ok: 0, err: 'invalid id[undefined]' }
965
+ ]
966
+ it(`should get ${JSON.stringify(vans[8])} for del with mixed valid and invalid id`, async function() {
967
+ assert.strict.deepStrictEqual(vget[8], vans[8])
968
+ })
969
+
970
+ vans[9] = []
971
+ it(`should get ${JSON.stringify(vans[9])} for del with invalid data`, async function() {
972
+ assert.strict.deepStrictEqual(vget[9], vans[9])
973
+ })
974
+
975
+ vans[10] = 'n,nDeleted,ok'
976
+ it(`should get ${JSON.stringify(vans[10])} for key set of del succeeded`, async function() {
977
+ assert.strict.deepStrictEqual(vget[10], vans[10])
978
+ })
979
+
980
+ vans[11] = 2
981
+ it(`should get ${JSON.stringify(vans[11])} for length of del with 1 failed record`, async function() {
982
+ assert.strict.deepStrictEqual(vget[11], vans[11])
983
+ })
984
+
985
+ vans[12] = [0, 1]
986
+ it(`should get ${JSON.stringify(vans[12])} for ok of each record in del`, async function() {
987
+ assert.strict.deepStrictEqual(vget[12], vans[12])
988
+ })
989
+
990
+ vans[13] = { n: 1, nDeleted: 0 }
991
+ it(`should get ${JSON.stringify(vans[13])} for counts of failed record in del`, async function() {
992
+ assert.strict.deepStrictEqual(vget[13], vans[13])
993
+ })
994
+
995
+ vans[14] = 'err,n,nDeleted,ok'
996
+ it(`should get ${JSON.stringify(vans[14])} for key set of failed record in del`, async function() {
997
+ assert.strict.deepStrictEqual(vget[14], vans[14])
998
+ })
999
+
1000
+ vans[15] = true
1001
+ it(`should get ${JSON.stringify(vans[15])} for err of failed record in del`, async function() {
1002
+ assert.strict.deepStrictEqual(vget[15], vans[15])
1003
+ })
1004
+
1005
+ vans[16] = null
1006
+ it(`should get ${JSON.stringify(vans[16])} for not breaking batch by single failure in del`, async function() {
1007
+ assert.strict.deepStrictEqual(vget[16], vans[16])
1008
+ })
1009
+
1010
+ })
1011
+
1012
+
1013
+ describe('delAll', function() {
1014
+ let vans = {}
1015
+ let vget = {}
1016
+
1017
+ before(async function() {
1018
+ this.timeout(120000)
1019
+
1020
+ //wo
1021
+ let wo = WOrm(genOpt('delallcase'))
1022
+ await wo.delAll()
1023
+
1024
+ await wo.insert([
1025
+ { id: 'a1', name: 'peter' },
1026
+ { id: 'a2', name: 'rosemary' },
1027
+ { id: 'a3', name: 'peter' },
1028
+ { id: 'a4', name: 'kettle' },
1029
+ ])
1030
+
1031
+ //帶條件且僅部份命中
1032
+ vget[1] = await wo.delAll({ name: 'peter' })
1033
+ vget[2] = (await wo.select()).length
1034
+
1035
+ //帶條件且無命中屬正常結果
1036
+ vget[3] = await wo.delAll({ name: 'not-existed' })
1037
+
1038
+ //未帶條件則刪除全部
1039
+ vget[4] = await wo.delAll()
1040
+ vget[5] = (await wo.select()).length
1041
+
1042
+ //鍵集合須固定
1043
+ vget[6] = [genKeys(vget[1]), genKeys(vget[3])]
1044
+
1045
+ })
1046
+
1047
+ vans[1] = { n: 2, nDeleted: 2, ok: 1 }
1048
+ it(`should get ${JSON.stringify(vans[1])} for delAll with find matched partially`, async function() {
1049
+ assert.strict.deepStrictEqual(vget[1], vans[1])
1050
+ })
1051
+
1052
+ vans[2] = 2
1053
+ it(`should get ${JSON.stringify(vans[2])} for records after delAll with find`, async function() {
1054
+ assert.strict.deepStrictEqual(vget[2], vans[2])
1055
+ })
1056
+
1057
+ vans[3] = { n: 0, nDeleted: 0, ok: 1 }
1058
+ it(`should get ${JSON.stringify(vans[3])} for delAll with find not matched`, async function() {
1059
+ assert.strict.deepStrictEqual(vget[3], vans[3])
1060
+ })
1061
+
1062
+ vans[4] = { n: 2, nDeleted: 2, ok: 1 }
1063
+ it(`should get ${JSON.stringify(vans[4])} for delAll without find`, async function() {
1064
+ assert.strict.deepStrictEqual(vget[4], vans[4])
1065
+ })
1066
+
1067
+ vans[5] = 0
1068
+ it(`should get ${JSON.stringify(vans[5])} for records after delAll without find`, async function() {
1069
+ assert.strict.deepStrictEqual(vget[5], vans[5])
1070
+ })
1071
+
1072
+ vans[6] = ['n,nDeleted,ok', 'n,nDeleted,ok']
1073
+ it(`should get ${JSON.stringify(vans[6])} for fixed key set of delAll`, async function() {
1074
+ assert.strict.deepStrictEqual(vget[6], vans[6])
1075
+ })
1076
+
1077
+ })
1078
+
1079
+
1080
+ describe('change event', function() {
1081
+ let vans = {}
1082
+ let vget = {}
1083
+
1084
+ before(async function() {
1085
+ this.timeout(120000)
1086
+
1087
+ //wo
1088
+ let wo = WOrm(genOpt('evthrow'))
1089
+ await wo.delAll()
1090
+
1091
+ //訂閱函數拋錯不得影響本次操作之結果
1092
+ wo.on('change', function() {
1093
+ throw new Error('訂閱者拋錯')
1094
+ })
1095
+
1096
+ vget[1] = await wo.insert({ id: 'e1', name: 'a' })
1097
+ vget[2] = await wo.selectByPk('e1')
1098
+ vget[3] = await wo.save({ id: 'e1', name: 'b' })
1099
+ vget[4] = await wo.del({ id: 'e1' })
1100
+ vget[5] = await wo.delAll()
1101
+
1102
+ })
1103
+
1104
+ vans[1] = { n: 1, nInserted: 1, ok: 1 }
1105
+ it(`should get ${JSON.stringify(vans[1])} for insert with throwing listener`, async function() {
1106
+ assert.strict.deepStrictEqual(vget[1], vans[1])
1107
+ })
1108
+
1109
+ vans[2] = { id: 'e1', name: 'a' }
1110
+ it(`should get ${JSON.stringify(vans[2])} for record after insert with throwing listener`, async function() {
1111
+ assert.strict.deepStrictEqual(vget[2], vans[2])
1112
+ })
1113
+
1114
+ vans[3] = [{ n: 1, nInserted: 0, nModified: 1, ok: 1 }]
1115
+ it(`should get ${JSON.stringify(vans[3])} for save with throwing listener`, async function() {
1116
+ assert.strict.deepStrictEqual(vget[3], vans[3])
1117
+ })
1118
+
1119
+ vans[4] = [{ n: 1, nDeleted: 1, ok: 1 }]
1120
+ it(`should get ${JSON.stringify(vans[4])} for del with throwing listener`, async function() {
1121
+ assert.strict.deepStrictEqual(vget[4], vans[4])
1122
+ })
1123
+
1124
+ vans[5] = { n: 0, nDeleted: 0, ok: 1 }
1125
+ it(`should get ${JSON.stringify(vans[5])} for delAll with throwing listener`, async function() {
1126
+ assert.strict.deepStrictEqual(vget[5], vans[5])
1127
+ })
1128
+
1129
+ })
1130
+
1131
+
1132
+ describe('cross process concurrency', function() {
1133
+ let vans = {}
1134
+ let vget = {}
1135
+
1136
+ before(async function() {
1137
+ this.timeout(300000)
1138
+
1139
+ //跨行程併發insert同20個id, nInserted總和須為20且資料表僅20筆
1140
+ let clIns = 'xprocins'
1141
+ let woIns = WOrm(genOpt(clIns))
1142
+ await woIns.delAll()
1143
+ let ids = Array.from({ length: 20 }, (v, k) => `x${k}`)
1144
+ let rps = await Promise.all([
1145
+ runProc('P1', clIns, 'insert', ids),
1146
+ runProc('P2', clIns, 'insert', ids),
1147
+ ])
1148
+ // console.log('跨行程insert結果', rps)
1149
+ vget[1] = rps.reduce((sum, v) => sum + (v.n || 0), 0)
1150
+ vget[2] = (await woIns.select()).length
1151
+ vget[3] = rps.filter((v) => v.error).map((v) => v.error)
1152
+
1153
+ //跨行程併發save對同一既有id之不同欄位, 各欄位皆須保留
1154
+ let clSav = 'xprocsav'
1155
+ let woSav = WOrm(genOpt(clSav))
1156
+ await woSav.delAll()
1157
+ await woSav.insert({ id: 'x1', base: 1 })
1158
+ let keys = Array.from({ length: 20 }, (v, k) => `k${k}`)
1159
+ await Promise.all([
1160
+ runProc('P1', clSav, 'save', keys),
1161
+ runProc('P2', clSav, 'save', keys),
1162
+ ])
1163
+ let vx1 = await woSav.selectByPk('x1')
1164
+ vget[4] = Object.keys(vx1).filter((k) => k.indexOf('P1k') === 0 || k.indexOf('P2k') === 0).length
1165
+ vget[5] = vx1.base
1166
+ vget[6] = (await woSav.select()).length
1167
+
1168
+ })
1169
+
1170
+ vans[3] = []
1171
+ it(`should get no error for 2 processes inserting same ids`, async function() {
1172
+ assert.strict.deepStrictEqual(vget[3], vans[3])
1173
+ })
1174
+
1175
+ vans[1] = 20
1176
+ it(`should get ${JSON.stringify(vans[1])} for sum of nInserted by 2 processes inserting same 20 ids`, async function() {
1177
+ assert.strict.deepStrictEqual(vget[1], vans[1])
1178
+ })
1179
+
1180
+ vans[2] = 20
1181
+ it(`should get ${JSON.stringify(vans[2])} for records after 2 processes inserting same 20 ids`, async function() {
1182
+ assert.strict.deepStrictEqual(vget[2], vans[2])
1183
+ })
1184
+
1185
+ vans[4] = 40
1186
+ it(`should get ${JSON.stringify(vans[4])} for fields after 2 processes saving different fields`, async function() {
1187
+ assert.strict.deepStrictEqual(vget[4], vans[4])
1188
+ })
1189
+
1190
+ vans[5] = 1
1191
+ it(`should get ${JSON.stringify(vans[5])} for keeping original field after cross process save`, async function() {
1192
+ assert.strict.deepStrictEqual(vget[5], vans[5])
1193
+ })
1194
+
1195
+ vans[6] = 1
1196
+ it(`should get ${JSON.stringify(vans[6])} for records after cross process save`, async function() {
1197
+ assert.strict.deepStrictEqual(vget[6], vans[6])
1198
+ })
1199
+
1200
+ })