renamed database schemaCheck method to initialize
[websub-hub] / test / src / db / postgres.js
1 /* eslint-disable sonarjs/no-identical-functions */
2 /* eslint-env mocha */
3 /* eslint-disable sonarjs/no-duplicate-string */
4 'use strict';
5
6 /* This provides implementation coverage, stubbing pg-promise. */
7
8 const assert = require('assert');
9 // eslint-disable-next-line node/no-unpublished-require
10 const sinon = require('sinon');
11 const DBStub = require('../../stub-db');
12 const stubLogger = require('../../stub-logger');
13 const DB = require('../../../src/db/postgres');
14 const DBErrors = require('../../../src/db/errors');
15 const common = require('../../../src/common');
16 const Config = require('../../../config');
17
18 const noExpectedException = 'did not receive expected exception';
19
20 describe('DatabasePostgres', function () {
21 let db, options, pgpStub;
22 let dbCtx, claimant, claimTimeoutSeconds, callback, subscriptionId, topicId, verificationId;
23 let topicUrl, leaseSeconds, secret, httpRemoteAddr, httpFrom, retryDelays, wanted;
24 before(function () {
25 pgpStub = () => {
26 const stub = {
27 result: () => ({ rows: [] }),
28 all: common.nop,
29 get: common.nop,
30 run: common.nop,
31 one: common.nop,
32 manyOrNone: common.nop,
33 oneOrNone: common.nop,
34 query: common.nop,
35 batch: common.nop,
36 multiResult: common.nop,
37 connect: common.nop,
38 };
39 stub.tx = (fn) => fn(stub);
40 stub.txIf = (fn) => fn(stub);
41 stub.task = (fn) => fn(stub);
42 return stub;
43 };
44 pgpStub.utils = {
45 enumSql: () => ({}),
46 };
47 pgpStub.QueryFile = class {};
48 pgpStub.end = common.nop,
49 options = new Config('test');
50 db = new DB(stubLogger, options, pgpStub);
51 });
52 beforeEach(function () {
53 stubLogger._reset();
54 dbCtx = db.db;
55 claimant = '19af19b8-6be3-4a6f-8946-65f5f1ccc5d7';
56 claimTimeoutSeconds = 300;
57 subscriptionId = 'fbaf8f19-ed9c-4a21-89ae-98b7005e3bf6';
58 topicUrl = 'https://example.com/blog';
59 callback = 'https://example.com/callback?id=123';
60 topicId = 'c59d4bda-10ad-41d9-99df-4ce8bc331424';
61 verificationId = '55cd7748-d2d5-11eb-b355-0025905f714a';
62 retryDelays = [60];
63 leaseSeconds = 86400;
64 secret = 'secret';
65 httpRemoteAddr = '127.0.0.1';
66 httpFrom = 'user@example.com';
67 wanted = 5;
68 });
69 afterEach(function () {
70 sinon.restore();
71 });
72
73 // Ensure all interface methods are implemented
74 describe('Implementation', function () {
75 it('implements interface', async function () {
76 const results = await Promise.allSettled(DBStub._implementation.map(async (fn) => {
77 try {
78 // eslint-disable-next-line security/detect-object-injection
79 await db[fn](db.db);
80 } catch (e) {
81 assert(!(e instanceof DBErrors.NotImplemented), `${fn} not implemented`);
82 }
83 }));
84 const failures = results.filter((x) => x.status === 'rejected');
85 assert(!failures.length, failures.map((x) => {
86 x = x.reason.toString();
87 return x.slice(x.indexOf(': '));
88 }));
89 });
90 }); // Implementation
91
92 describe('pgpInitOptions', function () {
93 describe('error', function () {
94 it('covers', function () {
95 const err = {};
96 const event = {};
97 db.pgpInitOptions.error(err, event);
98 assert(db.logger.error.called);
99 });
100 }); // error
101 describe('query', function () {
102 it('covers', function () {
103 const event = {};
104 db.pgpInitOptions.query(event);
105 assert(db.logger.debug.called);
106 });
107 }); // query
108 describe('receive', function () {
109 it('covers', function () {
110 const data = [
111 {
112 column_one: 'one', // eslint-disable-line camelcase
113 column_two: 2, // eslint-disable-line camelcase
114 },
115 {
116 column_one: 'foo', // eslint-disable-line camelcase
117 column_two: 4, // eslint-disable-line camelcase
118 },
119 ];
120 const result = {};
121 const event = {};
122 const expectedData = [
123 {
124 columnOne: 'one',
125 columnTwo: 2,
126 },
127 {
128 columnOne: 'foo',
129 columnTwo: 4,
130 },
131 ];
132 db.pgpInitOptions.receive(data, result, event)
133 assert(db.logger.debug.called);
134 assert.deepStrictEqual(data, expectedData);
135 });
136 }); // receive
137 }); // pgpInitOptions
138
139 describe('_initTables', function () {
140 beforeEach(function () {
141 sinon.stub(db.db, 'oneOrNone');
142 sinon.stub(db.db, 'multiResult');
143 sinon.stub(db, '_currentSchema');
144 });
145
146 it('covers apply', async function() {
147 db.db.oneOrNone.onCall(0).resolves(null).onCall(1).resolves({});
148 db._currentSchema.resolves({ major: 0, minor: 0, patch: 0 });
149 await db._initTables();
150 });
151 it('covers exists', async function() {
152 db.db.oneOrNone.resolves({});
153 db._currentSchema.resolves(db.schemaVersionsSupported.max);
154 await db._initTables();
155 });
156 }); // _initTables
157
158 describe('initialize', function () {
159 it('passes supported version', async function () {
160 const version = { major: 1, minor: 0, patch: 0 };
161 sinon.stub(db.db, 'one').resolves(version);
162 await db.initialize(false);
163 });
164 it('fails low version', async function () {
165 const version = { major: 0, minor: 0, patch: 0 };
166 sinon.stub(db.db, 'one').resolves(version);
167 try {
168 await db.initialize(false);
169 assert.fail(noExpectedException);
170 } catch (e) {
171 assert(e instanceof DBErrors.MigrationNeeded);
172 }
173 });
174 it('fails high version', async function () {
175 const version = { major: 100, minor: 100, patch: 100 };
176 sinon.stub(db.db, 'one').resolves(version);
177 try {
178 await db.initialize(false);
179 assert.fail(noExpectedException);
180 } catch (e) {
181 assert(e instanceof DBErrors.MigrationNeeded);
182 }
183 });
184 it('covers migration', async function() {
185 sinon.stub(db.db, 'oneOrNone').resolves({});
186 sinon.stub(db.db, 'multiResult');
187 sinon.stub(db, '_currentSchema').resolves(db.schemaVersionsSupported.max);
188 sinon.stub(db.db, 'one').resolves(db.schemaVersionsSupported.max);
189 await db.initialize();
190 });
191 }); // initialize
192
193 describe('healthCheck', function () {
194 beforeEach(function () {
195 sinon.stub(db.db, 'connect').resolves({
196 done: () => {},
197 client: {
198 serverVersion: '0.0',
199 },
200 });
201 });
202 it('covers', async function () {
203 const result = await db.healthCheck();
204 assert.deepStrictEqual(result, { serverVersion: '0.0' });
205 });
206 }); // healthCheck
207
208 describe('_queryFileHelper', function () {
209 it('covers success', function () {
210 const _queryFile = db._queryFileHelper(pgpStub);
211 _queryFile();
212 });
213 it('covers failure', function () {
214 const err = new Error();
215 pgpStub.QueryFile = class {
216 constructor() {
217 this.error = err;
218 }
219 };
220 const _queryFile = db._queryFileHelper(pgpStub);
221 try {
222 _queryFile();
223 assert.fail(noExpectedException);
224 } catch (e) {
225 assert.strictEqual(e, err);
226 }
227 });
228 }); // _queryFileHelper
229
230 describe('_closeConnection', function () {
231 it('success', async function () {
232 sinon.stub(db._pgp, 'end');
233 await db._closeConnection();
234 assert(db._pgp.end.called);
235 });
236 it('failure', async function () {
237 const expected = new Error();
238 sinon.stub(db._pgp, 'end').throws(expected);
239 try {
240 await db._closeConnection();
241 assert.fail(noExpectedException);
242 } catch (e) {
243 assert.deepStrictEqual(e, expected);
244 }
245 });
246 }); // _closeConnection
247
248 describe('_purgeTables', function () {
249 it('covers not really', async function () {
250 sinon.stub(db.db, 'tx');
251 await db._purgeTables(false);
252 assert(!db.db.tx.called);
253 });
254 it('success', async function () {
255 sinon.stub(db.db, 'batch');
256 await db._purgeTables(true);
257 assert(db.db.batch.called);
258 });
259 it('failure', async function () {
260 const expected = new Error();
261 sinon.stub(db.db, 'tx').rejects(expected);
262 try {
263 await db._purgeTables(true);
264 assert.fail(noExpectedException);
265 } catch (e) {
266 assert.deepStrictEqual(e, expected);
267 }
268 });
269 }); // _purgeTables
270
271 describe('context', function () {
272 it('covers', async function () {
273 await db.context(common.nop);
274 });
275 }); // context
276
277 describe('transaction', function () {
278 it('covers', async function () {
279 await db.transaction(db.db, common.nop);
280 });
281 }); // transaction
282
283 describe('authenticationSuccess', function () {
284 let identifier;
285 beforeEach(function () {
286 identifier = 'username';
287 });
288 it('success', async function () {
289 const dbResult = {
290 rowCount: 1,
291 rows: undefined,
292 duration: 22,
293 };
294 sinon.stub(db.db, 'result').resolves(dbResult);
295 await db.authenticationSuccess(dbCtx, identifier);
296 });
297 it('failure', async function() {
298 const dbResult = {
299 rowCount: 0,
300 rows: undefined,
301 duration: 22,
302 };
303 sinon.stub(db.db, 'result').resolves(dbResult);
304 try {
305 await db.authenticationSuccess(dbCtx, identifier);
306 assert.fail(noExpectedException);
307 } catch (e) {
308 assert(e instanceof DBErrors.UnexpectedResult);
309 }
310 });
311 }); // authenticationSuccess
312
313 describe('authenticationGet', function () {
314 let identifier, credential;
315 beforeEach(function () {
316 identifier = 'username';
317 credential = '$z$foo';
318 });
319 it('success', async function () {
320 const dbResult = { identifier, credential };
321 sinon.stub(db.db, 'oneOrNone').resolves(dbResult);
322 const result = await db.authenticationGet(dbCtx, identifier);
323 assert.deepStrictEqual(result, dbResult);
324 });
325 it('failure', async function() {
326 const expected = new Error('blah');
327 sinon.stub(db.db, 'oneOrNone').rejects(expected);
328 try {
329 await db.authenticationGet(dbCtx, identifier, credential);
330 assert.fail(noExpectedException);
331 } catch (e) {
332 assert.deepStrictEqual(e, expected);
333 }
334 });
335 }); // authenticationGet
336
337 describe('authenticationUpsert', function () {
338 let identifier, credential;
339 beforeEach(function () {
340 identifier = 'username';
341 credential = '$z$foo';
342 });
343 it('success', async function () {
344 const dbResult = {
345 rowCount: 1,
346 rows: undefined,
347 duration: 22,
348 };
349 sinon.stub(db.db, 'result').resolves(dbResult);
350 await db.authenticationUpsert(dbCtx, identifier, credential);
351 });
352 it('failure', async function() {
353 credential = undefined;
354 const dbResult = {
355 rowCount: 0,
356 rows: undefined,
357 duration: 22,
358 };
359 sinon.stub(db.db, 'result').resolves(dbResult);
360 try {
361 await db.authenticationUpsert(dbCtx, identifier, credential);
362 assert.fail(noExpectedException);
363 } catch (e) {
364 assert(e instanceof DBErrors.UnexpectedResult);
365 }
366 });
367 }); // authenticationUpsert
368
369 describe('subscriptionsByTopicId', function () {
370 it('success', async function () {
371 const expected = [];
372 sinon.stub(db.db, 'manyOrNone').resolves(expected);
373 const result = await db.subscriptionsByTopicId(dbCtx, topicUrl);
374 assert.deepStrictEqual(result, expected);
375 });
376 it('failure', async function () {
377 const expected = new Error();
378 sinon.stub(db.db, 'manyOrNone').throws(expected);
379 try {
380 await db.subscriptionsByTopicId(dbCtx, topicUrl);
381 assert.fail(noExpectedException);
382 } catch (e) {
383 assert.deepStrictEqual(e, expected);
384 }
385 });
386 }); // subscriptionsByTopicId
387
388 describe('subscriptionCountByTopicUrl', function () {
389 it('success', async function () {
390 const expected = { count: 3 };
391 sinon.stub(db.db, 'one').resolves(expected);
392 const result = await db.subscriptionCountByTopicUrl(dbCtx, topicUrl);
393 assert.deepStrictEqual(result, expected);
394 });
395 it('failure', async function () {
396 const expected = new Error();
397 sinon.stub(db.db, 'one').throws(expected);
398 try {
399 await db.subscriptionCountByTopicUrl(dbCtx, topicUrl);
400 assert.fail(noExpectedException);
401 } catch (e) {
402 assert.deepStrictEqual(e, expected);
403 }
404 });
405 }); // subscriptionCountByTopicUrl
406
407 describe('subscriptionDelete', function () {
408 it('success', async function() {
409 const dbResult = {
410 rowCount: 1,
411 rows: [ {} ],
412 duration: 10,
413 };
414 const expected = {
415 changes: 1,
416 lastInsertRowid: undefined,
417 duration: 10,
418 }
419 sinon.stub(db.db, 'result').resolves(dbResult);
420 const result = await db.subscriptionDelete(dbCtx, callback, topicId);
421 assert.deepStrictEqual(result, expected);
422 });
423 it('failure', async function () {
424 const expected = new Error();
425 sinon.stub(db.db, 'result').throws(expected);
426 try {
427 await db.subscriptionDelete(dbCtx, callback, topicId);
428 assert.fail(noExpectedException);
429 } catch (e) {
430 assert.deepStrictEqual(e, expected);
431 }
432 });
433 }); // subscriptionDelete
434
435 describe('subscriptionDeliveryClaim', function () {
436 it('success', async function() {
437 const dbResult = [
438 {
439 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
440 },
441 ];
442 const expected = ['c2e254c5-aa6e-4a8f-b1a1-e474b07392bb'];
443 sinon.stub(db.db, 'manyOrNone').resolves(dbResult);
444 const result = await db.subscriptionDeliveryClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
445 assert.deepStrictEqual(result, expected);
446 });
447 it('failure', async function () {
448 const expected = new Error();
449 sinon.stub(db.db, 'manyOrNone').throws(expected);
450 try {
451 await db.subscriptionDeliveryClaim(dbCtx, wanted, claimTimeoutSeconds, claimant );
452 assert.fail(noExpectedException);
453 } catch (e) {
454 assert.deepStrictEqual(e, expected);
455 }
456 });
457 }); // subscriptionDeliveryClaim
458
459 describe('subscriptionDeliveryClaimById', function () {
460 it('success', async function() {
461 const dbResult = {
462 rowCount: 1,
463 rows: [{ id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb' }],
464 duration: 11,
465 };
466 const expected = {
467 changes: 1,
468 lastInsertRowid: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
469 duration: 11,
470 }
471 sinon.stub(db.db, 'result').resolves(dbResult);
472 const result = await db.subscriptionDeliveryClaimById(dbCtx, subscriptionId, claimTimeoutSeconds, claimant);
473 assert.deepStrictEqual(result, expected);
474 });
475 it('failure', async function () {
476 const dbResult = {
477 rowCount: 0,
478 rows: undefined,
479 duration: 11,
480 };
481 sinon.stub(db.db, 'result').resolves(dbResult);
482 try {
483 await db.subscriptionDeliveryClaimById(dbCtx, callback, topicId);
484 assert.fail(noExpectedException);
485 } catch (e) {
486 assert(e instanceof DBErrors.UnexpectedResult);
487 }
488 });
489 }); // subscriptionDeliveryClaimById
490
491 describe('subscriptionDeliveryComplete', function () {
492 it('success', async function() {
493 const dbResult = {
494 rowCount: 1,
495 };
496 sinon.stub(db.db, 'result').resolves(dbResult);
497 await db.subscriptionDeliveryComplete(dbCtx, callback, topicId);
498 });
499 it('failure', async function () {
500 const dbResult = {
501 rowCount: 0,
502 };
503 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult);
504 try {
505 await db.subscriptionDeliveryComplete(dbCtx, callback, topicId);
506 assert.fail(noExpectedException);
507 } catch (e) {
508 assert(e instanceof DBErrors.UnexpectedResult);
509 }
510 });
511 it('second failure', async function () {
512 const dbResult0 = {
513 rowCount: 1,
514 };
515 const dbResult1 = {
516 rowCount: 0,
517 };
518 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
519 try {
520 await db.subscriptionDeliveryComplete(dbCtx, callback, topicId);
521 assert.fail(noExpectedException);
522 } catch (e) {
523 assert(e instanceof DBErrors.UnexpectedResult);
524 }
525 });
526 }); // subscriptionDeliveryComplete
527
528 describe('subscriptionDeliveryGone', function () {
529 it('success', async function() {
530 const dbResult = {
531 rowCount: 1,
532 };
533 sinon.stub(db.db, 'result').resolves(dbResult);
534 await db.subscriptionDeliveryGone(dbCtx, callback, topicId);
535 });
536 it('failure', async function () {
537 const dbResult = {
538 rowCount: 0,
539 };
540 sinon.stub(db.db, 'result').resolves(dbResult);
541 try {
542 await db.subscriptionDeliveryGone(dbCtx, callback, topicId);
543 assert.fail(noExpectedException);
544 } catch (e) {
545 assert(e instanceof DBErrors.UnexpectedResult);
546 }
547 });
548 }); // subscriptionDeliveryGone
549
550 describe('subscriptionDeliveryIncomplete', function () {
551 it('success', async function() {
552 const dbOne = { deliveryAttemptsSinceSuccess: 0 };
553 const dbResult = {
554 rowCount: 1,
555 };
556 sinon.stub(db.db, 'one').resolves(dbOne);
557 sinon.stub(db.db, 'result').resolves(dbResult);
558 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId, retryDelays);
559 });
560 it('success covers default', async function() {
561 const dbOne = { deliveryAttemptsSinceSuccess: 0 };
562 const dbResult = {
563 rowCount: 1,
564 };
565 sinon.stub(db.db, 'one').resolves(dbOne);
566 sinon.stub(db.db, 'result').resolves(dbResult);
567 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId);
568 });
569 it('failure', async function () {
570 const dbOne = { deliveryAttemptsSinceSuccess: 0 };
571 const dbResult = {
572 rowCount: 0,
573 };
574 sinon.stub(db.db, 'one').resolves(dbOne);
575 sinon.stub(db.db, 'result').resolves(dbResult);
576 try {
577 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId, retryDelays);
578 assert.fail(noExpectedException);
579 } catch (e) {
580 assert(e instanceof DBErrors.UnexpectedResult);
581 }
582 });
583 it('second failure', async function () {
584 const dbOne = { deliveryAttemptsSinceSuccess: 0 };
585 const dbResult0 = {
586 rowCount: 1,
587 };
588 const dbResult1 = {
589 rowCount: 0,
590 };
591 sinon.stub(db.db, 'one').resolves(dbOne);
592 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
593 try {
594 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId, retryDelays);
595 assert.fail(noExpectedException);
596 } catch (e) {
597 assert(e instanceof DBErrors.UnexpectedResult);
598 }
599 });
600 }); // subscriptionDeliveryIncomplete
601
602 describe('subscriptionGet', function () {
603 it('success', async function() {
604 const expected = {
605 id: subscriptionId,
606 };
607 sinon.stub(db.db, 'oneOrNone').resolves(expected);
608 const result = await db.subscriptionGet(dbCtx, callback, topicId);
609 assert.deepStrictEqual(result, expected);
610 });
611 it('failure', async function () {
612 const expected = new Error();
613 sinon.stub(db.db, 'oneOrNone').throws(expected);
614 try {
615 await db.subscriptionGet(dbCtx, callback, topicId);
616 assert.fail(noExpectedException);
617 } catch (e) {
618 assert.deepStrictEqual(e, expected);
619 }
620 });
621 }); // subscriptionGet
622
623 describe('subscriptionGetById', function () {
624 it('success', async function() {
625 const expected = {
626 id: subscriptionId,
627 };
628 sinon.stub(db.db, 'oneOrNone').resolves(expected);
629 const result = await db.subscriptionGetById(dbCtx, subscriptionId);
630 assert.deepStrictEqual(result, expected);
631 });
632 it('failure', async function () {
633 const expected = new Error();
634 sinon.stub(db.db, 'oneOrNone').throws(expected);
635 try {
636 await db.subscriptionGetById(dbCtx, subscriptionId);
637 assert.fail(noExpectedException);
638 } catch (e) {
639 assert.deepStrictEqual(e, expected);
640 }
641 });
642 }); // subscriptionGetById
643
644 describe('subscriptionUpsert', function () {
645 let data;
646 beforeEach(function () {
647 data = {
648 callback,
649 topicId,
650 leaseSeconds,
651 secret,
652 httpRemoteAddr,
653 httpFrom,
654 };
655 });
656 it('success', async function() {
657 const dbResult = {
658 rowCount: 1,
659 rows: [{ id: subscriptionId }],
660 duration: 10,
661 };
662 const expected = {
663 changes: 1,
664 lastInsertRowid: subscriptionId,
665 duration: 10,
666 };
667 sinon.stub(db.db, 'result').resolves(dbResult);
668 const result = await db.subscriptionUpsert(dbCtx, data);
669 assert.deepStrictEqual(result, expected);
670 });
671 it('failure', async function () {
672 const dbResult = {
673 rowCount: 0,
674 };
675 sinon.stub(db.db, 'result').resolves(dbResult);
676 try {
677 await db.subscriptionUpsert(dbCtx, data);
678 assert.fail(noExpectedException);
679 } catch (e) {
680 assert(e instanceof DBErrors.UnexpectedResult);
681 }
682 });
683 }); // subscriptionUpsert
684
685 describe('subscriptionUpdate', function () {
686 let data;
687 beforeEach(function () {
688 data = {
689 signatureAlgorithm: 'sha256',
690 };
691 });
692 it('success', async function() {
693 const dbResult = {
694 rowCount: 1,
695 rows: [],
696 duration: 10,
697 };
698 sinon.stub(db.db, 'result').resolves(dbResult);
699 await db.subscriptionUpdate(dbCtx, data);
700 });
701 it('failure', async function () {
702 const dbResult = {
703 rowCount: 0,
704 };
705 sinon.stub(db.db, 'result').resolves(dbResult);
706 try {
707 await db.subscriptionUpdate(dbCtx, data);
708 assert.fail(noExpectedException);
709 } catch (e) {
710 assert(e instanceof DBErrors.UnexpectedResult);
711 }
712 });
713 }); // subscriptionUpdate
714
715 describe('topicDeleted', function () {
716 it('success', async function() {
717 const dbResult = {
718 rowCount: 1,
719 };
720 sinon.stub(db.db, 'result').resolves(dbResult);
721 await db.topicDeleted(dbCtx, topicId);
722 });
723 it('failure', async function() {
724 const dbResult = {
725 rowCount: 0,
726 };
727 sinon.stub(db.db, 'result').resolves(dbResult);
728 try {
729 await db.topicDeleted(dbCtx, topicId);
730 assert.fail(noExpectedException);
731 } catch (e) {
732 assert(e instanceof DBErrors.UnexpectedResult);
733 }
734 });
735 }); // topicDeleted
736
737 describe('topicFetchClaim', function () {
738 it('success', async function() {
739 const dbResult = [{ id: topicId }];
740 const expected = [topicId];
741 sinon.stub(db.db, 'manyOrNone').resolves(dbResult);
742 const result = await db.topicFetchClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
743 assert.deepStrictEqual(result, expected);
744 });
745 it('failure', async function () {
746 const expected = new Error();
747 sinon.stub(db.db, 'manyOrNone').throws(expected);
748 try {
749 await db.topicFetchClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
750 assert.fail(noExpectedException);
751 } catch (e) {
752 assert.deepStrictEqual(e, expected);
753 }
754 });
755 }); // topicFetchClaim
756
757 describe('topicFetchClaimById', function () {
758 it('success', async function() {
759 const dbResult = {
760 rowCount: 1,
761 rows: [],
762 duration: 10,
763 };
764 const expected = {
765 changes: 1,
766 lastInsertRowid: undefined,
767 duration: 10,
768 };
769 sinon.stub(db.db, 'result').resolves(dbResult);
770 const result = await db.topicFetchClaimById(dbCtx, topicId, claimTimeoutSeconds, claimant);
771 assert.deepStrictEqual(result, expected);
772 });
773 it('failure', async function () {
774 const expected = new Error();
775 sinon.stub(db.db, 'result').throws(expected);
776 try {
777 await db.topicFetchClaimById(dbCtx, topicId, claimTimeoutSeconds, claimant);
778 assert.fail(noExpectedException);
779 } catch (e) {
780 assert.deepStrictEqual(e, expected);
781 }
782 });
783 }); // topicFetchClaimById
784
785 describe('topicFetchComplete', function () {
786 it('success', async function() {
787 const dbResult = {
788 rowCount: 1,
789 rows: [],
790 duration: 10,
791 };
792 sinon.stub(db.db, 'result').resolves(dbResult);
793 await db.topicFetchComplete(dbCtx, topicId);
794 });
795 it('failure', async function () {
796 const dbResult = {
797 rowCount: 0,
798 rows: [],
799 duration: 10,
800 };
801 sinon.stub(db.db, 'result').resolves(dbResult);
802 try {
803 await db.topicFetchComplete(dbCtx, topicId);
804 assert.fail(noExpectedException);
805 } catch (e) {
806 assert(e instanceof DBErrors.UnexpectedResult);
807 }
808 });
809 it('second failure', async function () {
810 const dbResult0 = {
811 rowCount: 1,
812 rows: [],
813 duration: 10,
814 };
815 const dbResult1 = {
816 rowCount: 0,
817 rows: [],
818 duration: 10,
819 };
820 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
821 try {
822 await db.topicFetchComplete(dbCtx, topicId);
823 assert.fail(noExpectedException);
824 } catch (e) {
825 assert(e instanceof DBErrors.UnexpectedResult);
826 }
827 });
828 }); // topicFetchComplete
829
830 describe('topicFetchIncomplete', function () {
831 it('success', async function() {
832 const dbOne = { currentAttempt: 0 };
833 const dbResult0 = {
834 rowCount: 1,
835 rows: [],
836 duration: 10,
837 };
838 const dbResult1 = {
839 rowCount: 1,
840 rows: [],
841 duration: 10,
842 }
843 const expected = {
844 changes: 1,
845 lastInsertRowid: undefined,
846 duration: 10,
847 };
848 sinon.stub(db.db, 'one').resolves(dbOne);
849 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
850 const result = await db.topicFetchIncomplete(dbCtx, topicId, retryDelays);
851 assert.deepStrictEqual(result, expected);
852 });
853 it('covers defaults', async function() {
854 const dbOne = { currentAttempt: 0 };
855 const dbResult0 = {
856 rowCount: 1,
857 rows: [],
858 duration: 10,
859 };
860 const dbResult1 = {
861 rowCount: 1,
862 rows: [],
863 duration: 10,
864 }
865 const expected = {
866 changes: 1,
867 lastInsertRowid: undefined,
868 duration: 10,
869 };
870 sinon.stub(db.db, 'one').resolves(dbOne);
871 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
872 const result = await db.topicFetchIncomplete(dbCtx, topicId);
873 assert.deepStrictEqual(result, expected);
874 });
875 it('failure', async function () {
876 const dbOne = { currentAttempt: 0 };
877 const dbResult0 = {
878 rowCount: 1,
879 rows: [],
880 duration: 10,
881 };
882 const dbResult1 = {
883 rowCount: 0,
884 rows: [],
885 duration: 10,
886 }
887 sinon.stub(db.db, 'one').resolves(dbOne);
888 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
889 try {
890 await db.topicFetchIncomplete(dbCtx, topicId, retryDelays);
891 assert.fail(noExpectedException);
892 } catch (e) {
893 assert(e instanceof DBErrors.UnexpectedResult);
894 }
895 });
896 it('second failure', async function () {
897 const dbOne = { currentAttempt: 0 };
898 const dbResult0 = {
899 rowCount: 0,
900 rows: [],
901 duration: 10,
902 };
903 const dbResult1 = {
904 rowCount: 0,
905 rows: [],
906 duration: 10,
907 }
908 sinon.stub(db.db, 'one').resolves(dbOne);
909 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
910 try {
911 await db.topicFetchIncomplete(dbCtx, topicId, retryDelays);
912 assert.fail(noExpectedException);
913 } catch (e) {
914 assert(e instanceof DBErrors.UnexpectedResult);
915 }
916 });
917 }); // topicFetchIncomplete
918
919 describe('topicFetchRequested', function () {
920 it('success', async function() {
921 const dbResult = {
922 rowCount: 1,
923 rows: [],
924 duration: 10,
925 };
926 const expected = {
927 changes: 1,
928 lastInsertRowid: undefined,
929 duration: 10,
930 };
931 sinon.stub(db.db, 'result').resolves(dbResult);
932 const result = await db.topicFetchRequested(dbCtx, topicId);
933 assert.deepStrictEqual(result, expected);
934 });
935 it('failure', async function () {
936 const dbResult = {
937 rowCount: 0,
938 rows: [],
939 duration: 10,
940 };
941 sinon.stub(db.db, 'result').resolves(dbResult);
942 try {
943 await db.topicFetchRequested(dbCtx, topicId);
944 assert.fail(noExpectedException);
945 } catch (e) {
946 assert(e instanceof DBErrors.UnexpectedResult);
947 }
948 });
949 }); // topicFetchRequested
950
951 describe('topicGetAll', function () {
952 it('success', async function() {
953 const expected = [{ id: topicId }];
954 sinon.stub(db.db, 'manyOrNone').resolves(expected);
955 const result = await db.topicGetAll(dbCtx);
956 assert.deepStrictEqual(result, expected);
957 });
958 it('covers default', async function() {
959 const expected = undefined;
960 sinon.stub(db.db, 'manyOrNone').resolves(expected);
961 const result = await db.topicGetAll(dbCtx);
962 assert.deepStrictEqual(result, expected);
963 });
964 it('failure', async function () {
965 const expected = new Error();
966 sinon.stub(db.db, 'manyOrNone').throws(expected);
967 try {
968 await db.topicGetAll(dbCtx);
969 assert.fail(noExpectedException);
970 } catch (e) {
971 assert.deepStrictEqual(e, expected);
972 }
973 });
974 }); // topicGetById
975
976 describe('topicGetById', function () {
977 it('success', async function() {
978 const expected = { id: topicId };
979 sinon.stub(db.db, 'oneOrNone').resolves(expected);
980 const result = await db.topicGetById(dbCtx, topicId);
981 assert.deepStrictEqual(result, expected);
982 });
983 it('covers none', async function() {
984 const expected = undefined;
985 sinon.stub(db.db, 'oneOrNone').resolves(expected);
986 const result = await db.topicGetById(dbCtx, topicId);
987 assert.deepStrictEqual(result, expected);
988 });
989 it('covers no defaults', async function () {
990 const expected = { id: topicId };
991 sinon.stub(db.db, 'oneOrNone').resolves(expected);
992 const result = await db.topicGetById(dbCtx, topicId, false);
993 assert.deepStrictEqual(result, expected);
994 });
995 it('failure', async function () {
996 const expected = new Error();
997 sinon.stub(db.db, 'oneOrNone').throws(expected);
998 try {
999 await db.topicGetById(dbCtx, topicId);
1000 assert.fail(noExpectedException);
1001 } catch (e) {
1002 assert.deepStrictEqual(e, expected);
1003 }
1004 });
1005 }); // topicGetById
1006
1007 describe('topicGetByUrl', function () {
1008 it('success', async function() {
1009 const expected = [];
1010 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1011 const result = await db.topicGetByUrl(dbCtx, topicUrl);
1012 assert.deepStrictEqual(result, expected);
1013 });
1014 it('failure', async function () {
1015 const expected = new Error();
1016 sinon.stub(db.db, 'oneOrNone').throws(expected);
1017 try {
1018 await db.topicGetByUrl(dbCtx, topicUrl);
1019 assert.fail(noExpectedException);
1020 } catch (e) {
1021 assert.deepStrictEqual(e, expected);
1022 }
1023 });
1024 }); // topicGetByUrl
1025
1026 describe('topicGetContentById', function () {
1027 it('success', async function() {
1028 const expected = { id: topicId };
1029 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1030 const result = await db.topicGetContentById(dbCtx, topicId);
1031 assert.deepStrictEqual(result, expected);
1032 });
1033 it('covers default', async function() {
1034 const expected = undefined;
1035 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1036 const result = await db.topicGetContentById(dbCtx, topicId);
1037 assert.deepStrictEqual(result, expected);
1038 });
1039 it('failure', async function () {
1040 const expected = new Error();
1041 sinon.stub(db.db, 'oneOrNone').throws(expected);
1042 try {
1043 await db.topicGetContentById(dbCtx, topicId);
1044 assert.fail(noExpectedException);
1045 } catch (e) {
1046 assert.deepStrictEqual(e, expected);
1047 }
1048 });
1049 }); // topicGetContentById
1050
1051 describe('topicSet', function () {
1052 let data;
1053 beforeEach(function () {
1054 data = {
1055 url: topicUrl,
1056 };
1057 });
1058 it('success', async function() {
1059 const dbResult = {
1060 rowCount: 1,
1061 rows: [{ id: topicId }],
1062 duration: 10,
1063 };
1064 const expected = {
1065 changes: 1,
1066 lastInsertRowid: topicId,
1067 duration: 10,
1068 };
1069 sinon.stub(db.db, 'result').resolves(dbResult);
1070 const result = await db.topicSet(dbCtx, data);
1071 assert.deepStrictEqual(result, expected);
1072 });
1073 it('failure', async function () {
1074 const dbResult = {
1075 rowCount: 0,
1076 rows: [],
1077 duration: 10,
1078 };
1079 sinon.stub(db.db, 'result').resolves(dbResult);
1080 try {
1081 await db.topicSet(dbCtx, data);
1082 assert.fail(noExpectedException);
1083 } catch (e) {
1084 assert(e instanceof DBErrors.UnexpectedResult);
1085 }
1086 });
1087 it('fails invalid value', async function () {
1088 sinon.stub(db.db, 'result');
1089 try {
1090 data.leaseSecondsPreferred = -100;
1091 await db.topicSet(dbCtx, data);
1092 assert.fail(noExpectedException);
1093 } catch (e) {
1094 assert(e instanceof DBErrors.DataValidation);
1095 }
1096 assert(!db.db.result.called);
1097 });
1098 it('fails invalid values', async function () {
1099 sinon.stub(db.db, 'result');
1100 try {
1101 data.leaseSecondsPreferred = 10;
1102 data.leaseSecondsMax = 100;
1103 data.leaseSecondsMin = 50;
1104 await db.topicSet(dbCtx, data);
1105 assert.fail(noExpectedException);
1106 } catch (e) {
1107 assert(e instanceof DBErrors.DataValidation);
1108 }
1109 assert(!db.db.result.called);
1110 });
1111 }); // topicSet
1112
1113 describe('topicSetContent', function () {
1114 let data;
1115 beforeEach(function () {
1116 data = {
1117 content: 'content',
1118 contentType: 'text/plain',
1119 contentHash: 'abc123',
1120 };
1121 });
1122 it('success', async function() {
1123 const dbResult = {
1124 rowCount: 1,
1125 rows: [],
1126 duration: 10,
1127 };
1128 const expected = {
1129 changes: 1,
1130 lastInsertRowid: undefined,
1131 duration: 10,
1132 };
1133 sinon.stub(db.db, 'result').resolves(dbResult);
1134 const result = await db.topicSetContent(dbCtx, data);
1135 assert.deepStrictEqual(result, expected);
1136 });
1137 it('failure', async function () {
1138 const dbResult = {
1139 rowCount: 0,
1140 rows: [],
1141 duration: 10,
1142 };
1143 sinon.stub(db.db, 'result').resolves(dbResult);
1144 try {
1145 await db.topicSetContent(dbCtx, data);
1146 assert.fail(noExpectedException);
1147 } catch (e) {
1148 assert(e instanceof DBErrors.UnexpectedResult);
1149 }
1150 });
1151 }); // topicSetContent
1152
1153 describe('topicUpdate', function () {
1154 let data;
1155 beforeEach(function () {
1156 data = {
1157 leaseSecondsPreferred: 123,
1158 leaseSecondsMin: 100,
1159 leaseSecondsMax: 1000,
1160 publisherValidationUrl: null,
1161 contentHashAlgorithm: 'sha256',
1162 };
1163 });
1164 it('success', async function() {
1165 const dbResult = {
1166 rowCount: 1,
1167 rows: [],
1168 duration: 10,
1169 };
1170 sinon.stub(db.db, 'result').resolves(dbResult);
1171 await db.topicUpdate(dbCtx, data);
1172 });
1173 it('failure', async function () {
1174 const dbResult = {
1175 rowCount: 0,
1176 rows: [],
1177 duration: 10,
1178 };
1179 sinon.stub(db.db, 'result').resolves(dbResult);
1180 try {
1181 await db.topicUpdate(dbCtx, data);
1182 assert.fail(noExpectedException);
1183 } catch (e) {
1184 assert(e instanceof DBErrors.UnexpectedResult);
1185 }
1186 });
1187
1188 });
1189
1190 describe('verificationClaim', function () {
1191 it('success', async function() {
1192 const dbManyOrNone = [{ id: verificationId }];
1193 const expected = [verificationId];
1194 sinon.stub(db.db, 'manyOrNone').resolves(dbManyOrNone);
1195 const result = await db.verificationClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
1196 assert.deepStrictEqual(result, expected);
1197 });
1198 it('failure', async function () {
1199 const expected = new Error();
1200 sinon.stub(db.db, 'manyOrNone').throws(expected);
1201 try {
1202 await db.verificationClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
1203 assert.fail(noExpectedException);
1204 } catch (e) {
1205 assert.deepStrictEqual(e, expected);
1206 }
1207 });
1208 }); // verificationClaim
1209
1210 describe('verificationClaimById', function () {
1211 it('success', async function() {
1212 const dbResult = {
1213 rowCount: 1,
1214 rows: [ { id: verificationId } ],
1215 duration: 10,
1216 };
1217 const expected = {
1218 changes: 1,
1219 lastInsertRowid: verificationId,
1220 duration: 10,
1221 };
1222 sinon.stub(db.db, 'result').resolves(dbResult);
1223 const result = await db.verificationClaimById(dbCtx, verificationId, claimTimeoutSeconds, claimant);
1224 assert.deepStrictEqual(result, expected);
1225 });
1226 it('failure', async function () {
1227 const expected = new Error();
1228 sinon.stub(db.db, 'result').throws(expected);
1229 try {
1230 await db.verificationClaimById(dbCtx, verificationId, claimTimeoutSeconds, claimant);
1231 assert.fail(noExpectedException);
1232 } catch (e) {
1233 assert.deepStrictEqual(e, expected);
1234 }
1235 });
1236 }); // verificationClaimById
1237
1238 describe('verificationComplete', function () {
1239 it('success', async function() {
1240 const dbResult = {
1241 rowCount: 1,
1242 rows: [],
1243 duration: 10,
1244 };
1245 sinon.stub(db.db, 'result').resolves(dbResult);
1246 await db.verificationComplete(dbCtx, verificationId, callback, topicId);
1247 });
1248 it('failure', async function () {
1249 const dbResult = {
1250 rowCount: 0,
1251 rows: [],
1252 duration: 10,
1253 };
1254 sinon.stub(db.db, 'result').resolves(dbResult);
1255 try {
1256 await db.verificationComplete(dbCtx, verificationId, callback, topicId);
1257 assert.fail(noExpectedException);
1258 } catch (e) {
1259 assert(e instanceof DBErrors.UnexpectedResult);
1260 }
1261 });
1262 }); // verificationComplete
1263
1264 describe('verificationGetById', function () {
1265 it('success', async function() {
1266 const dbOneOrNone = { id: verificationId };
1267 const expected = { id: verificationId };
1268 sinon.stub(db.db, 'oneOrNone').resolves(dbOneOrNone);
1269 const result = await db.verificationGetById(dbCtx, verificationId);
1270 assert.deepStrictEqual(result, expected);
1271 });
1272 it('failure', async function () {
1273 const expected = new Error();
1274 sinon.stub(db.db, 'oneOrNone').throws(expected);
1275 try {
1276 await db.verificationGetById(dbCtx, verificationId);
1277 assert.fail(noExpectedException);
1278 } catch (e) {
1279 assert.deepStrictEqual(e, expected);
1280 }
1281 });
1282 }); // verificationGetById
1283
1284 describe('verificationIncomplete', function () {
1285 it('success', async function() {
1286 const dbOne = { attempts: 0 };
1287 const dbResult0 = {
1288 rowCount: 1,
1289 rows: [],
1290 duration: 10,
1291 };
1292 const dbResult1 = {
1293 rowCount: 1,
1294 rows: [],
1295 duration: 10,
1296 };
1297 sinon.stub(db.db, 'one').resolves(dbOne);
1298 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1299 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1300 });
1301 it('covers defaults', async function() {
1302 const dbOne = { attempts: 0 };
1303 const dbResult0 = {
1304 rowCount: 1,
1305 rows: [],
1306 duration: 10,
1307 };
1308 const dbResult1 = {
1309 rowCount: 1,
1310 rows: [],
1311 duration: 10,
1312 };
1313 sinon.stub(db.db, 'one').resolves(dbOne);
1314 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1315 await db.verificationIncomplete(dbCtx, verificationId);
1316 });
1317 it('failure', async function () {
1318 const dbOne = { attempts: 0 };
1319 const dbResult0 = {
1320 rowCount: 0,
1321 rows: [],
1322 duration: 10,
1323 };
1324 const dbResult1 = {
1325 rowCount: 1,
1326 rows: [],
1327 duration: 10,
1328 };
1329 sinon.stub(db.db, 'one').resolves(dbOne);
1330 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1331 try {
1332 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1333 assert.fail(noExpectedException);
1334 } catch (e) {
1335 assert(e instanceof DBErrors.UnexpectedResult);
1336 }
1337 });
1338 it('second failure', async function () {
1339 const dbOne = { attempts: 0 };
1340 const dbResult0 = {
1341 rowCount: 1,
1342 rows: [],
1343 duration: 10,
1344 };
1345 const dbResult1 = {
1346 rowCount: 0,
1347 rows: [],
1348 duration: 10,
1349 };
1350 sinon.stub(db.db, 'one').resolves(dbOne);
1351 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1352 try {
1353 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1354 assert.fail(noExpectedException);
1355 } catch (e) {
1356 assert(e instanceof DBErrors.UnexpectedResult);
1357 }
1358 });
1359 }); // verificationIncomplete
1360
1361 describe('verificationInsert', function () {
1362 let verification;
1363 beforeEach(function () {
1364 verification = {
1365 topicId,
1366 callback,
1367 mode: 'subscribe',
1368 isPublisherValidated: true,
1369 leaseSeconds: 86400,
1370 };
1371 });
1372 it('success', async function() {
1373 const dbResult = {
1374 rowCount: 1,
1375 rows: [{ id: verificationId }],
1376 duration: 10,
1377 };
1378 const expected = verificationId;
1379 sinon.stub(db.db, 'result').resolves(dbResult);
1380 const result = await db.verificationInsert(dbCtx, verification);
1381 assert.deepStrictEqual(result, expected);
1382 });
1383 it('failure', async function () {
1384 const dbResult = {
1385 rowCount: 0,
1386 rows: [],
1387 duration: 10,
1388 };
1389 sinon.stub(db.db, 'result').resolves(dbResult);
1390 try {
1391 await db.verificationInsert(dbCtx, verification);
1392 assert.fail(noExpectedException);
1393 } catch (e) {
1394 assert(e instanceof DBErrors.UnexpectedResult);
1395 }
1396 });
1397 it('fails validation', async function () {
1398 delete verification.leaseSeconds;
1399 try {
1400 await db.verificationInsert(dbCtx, verification);
1401 assert.fail(noExpectedException);
1402 } catch (e) {
1403 assert(e instanceof DBErrors.DataValidation);
1404 }
1405 });
1406 }); // verificationInsert
1407
1408 describe('verificationRelease', function () {
1409 it('success', async function() {
1410 const dbResult = {
1411 rowCount: 1,
1412 rows: [],
1413 duration: 10,
1414 };
1415 sinon.stub(db.db, 'result').resolves(dbResult);
1416 await db.verificationRelease(dbCtx, verificationId);
1417 });
1418 it('failure', async function () {
1419 const dbResult = {
1420 rowCount: 0,
1421 rows: [],
1422 duration: 10,
1423 };
1424 sinon.stub(db.db, 'result').resolves(dbResult);
1425 try {
1426 await db.verificationRelease(dbCtx, verificationId);
1427 assert.fail(noExpectedException);
1428 } catch (e) {
1429 assert(e instanceof DBErrors.UnexpectedResult);
1430 }
1431 });
1432 }); // verificationRelease
1433
1434 describe('verificationUpdate', function () {
1435 let data;
1436 beforeEach(function () {
1437 data = {
1438 mode: 'subscribe',
1439 isPublisherValidated: true,
1440 };
1441 });
1442 it('success', async function() {
1443 const dbResult = {
1444 rowCount: 1,
1445 rows: [],
1446 duration: 10,
1447 };
1448 sinon.stub(db.db, 'result').resolves(dbResult);
1449 await db.verificationUpdate(dbCtx, verificationId, data);
1450 });
1451 it('failure', async function () {
1452 const dbResult = {
1453 rowCount: 0,
1454 rows: [],
1455 duration: 10,
1456 }
1457 sinon.stub(db.db, 'result').resolves(dbResult);
1458 try {
1459 await db.verificationUpdate(dbCtx, verificationId, data);
1460 assert.fail(noExpectedException);
1461 } catch (e) {
1462 assert(e instanceof DBErrors.UnexpectedResult, e.name);
1463 }
1464 });
1465 it('fails validation', async function () {
1466 delete data.mode;
1467 try {
1468 await db.verificationUpdate(dbCtx, verificationId, data);
1469 assert.fail(noExpectedException);
1470 } catch (e) {
1471 assert(e instanceof DBErrors.DataValidation);
1472 }
1473 });
1474 }); // verificationUpdate
1475
1476 describe('verificationValidated', function () {
1477 it('success', async function() {
1478 const dbResult = {
1479 rowCount: 1,
1480 rows: [],
1481 duration: 10,
1482 }
1483 sinon.stub(db.db, 'result').resolves(dbResult);
1484 await db.verificationValidated(dbCtx, verificationId);
1485 });
1486 it('failure', async function () {
1487 const dbResult = {
1488 rowCount: 0,
1489 rows: [],
1490 duration: 10,
1491 }
1492 sinon.stub(db.db, 'result').resolves(dbResult);
1493 try {
1494 await db.verificationValidated(dbCtx, verificationId);
1495 assert.fail(noExpectedException);
1496 } catch (e) {
1497 assert(e instanceof DBErrors.UnexpectedResult);
1498 }
1499 });
1500 }); // verificationValidated
1501
1502 }); // DatabasePostgres