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