0f037d6fae91b6dd4eed3098626317c9797f9d79
[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('subscriptionDeleteExpired', function () {
578 it('success', async function () {
579 const dbResult = {
580 rowCount: 1,
581 rows: [],
582 duration: 10,
583 };
584 const expected = {
585 changes: 1,
586 lastInsertRowid: undefined,
587 duration: 10,
588 };
589 sinon.stub(db.db, 'result').resolves(dbResult);
590 const result = await db.subscriptionDeleteExpired(dbCtx, topicId);
591 assert.deepStrictEqual(result, expected);
592 });
593 it('failure', async function() {
594 const expected = new Error();
595 sinon.stub(db.db, 'result').rejects(expected);
596 try {
597 await db.subscriptionDeleteExpired(dbCtx, topicId);
598 assert.fail(noExpectedException);
599 } catch (e) {
600 assert.deepStrictEqual(e, expected);
601 }
602 });
603 });
604
605 describe('subscriptionDeliveryClaim', function () {
606 it('success', async function() {
607 const dbResult = [
608 {
609 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
610 },
611 ];
612 const expected = ['c2e254c5-aa6e-4a8f-b1a1-e474b07392bb'];
613 sinon.stub(db.db, 'manyOrNone').resolves(dbResult);
614 const result = await db.subscriptionDeliveryClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
615 assert.deepStrictEqual(result, expected);
616 });
617 it('failure', async function () {
618 const expected = new Error();
619 sinon.stub(db.db, 'manyOrNone').throws(expected);
620 try {
621 await db.subscriptionDeliveryClaim(dbCtx, wanted, claimTimeoutSeconds, claimant );
622 assert.fail(noExpectedException);
623 } catch (e) {
624 assert.deepStrictEqual(e, expected);
625 }
626 });
627 }); // subscriptionDeliveryClaim
628
629 describe('subscriptionDeliveryClaimById', function () {
630 it('success', async function() {
631 const dbResult = {
632 rowCount: 1,
633 rows: [{ id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb' }],
634 duration: 11,
635 };
636 const expected = {
637 changes: 1,
638 lastInsertRowid: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
639 duration: 11,
640 }
641 sinon.stub(db.db, 'result').resolves(dbResult);
642 const result = await db.subscriptionDeliveryClaimById(dbCtx, subscriptionId, claimTimeoutSeconds, claimant);
643 assert.deepStrictEqual(result, expected);
644 });
645 it('failure', async function () {
646 const dbResult = {
647 rowCount: 0,
648 rows: undefined,
649 duration: 11,
650 };
651 sinon.stub(db.db, 'result').resolves(dbResult);
652 try {
653 await db.subscriptionDeliveryClaimById(dbCtx, callback, topicId);
654 assert.fail(noExpectedException);
655 } catch (e) {
656 assert(e instanceof DBErrors.UnexpectedResult);
657 }
658 });
659 }); // subscriptionDeliveryClaimById
660
661 describe('subscriptionDeliveryComplete', function () {
662 it('success', async function() {
663 const dbResult = {
664 rowCount: 1,
665 };
666 sinon.stub(db.db, 'result').resolves(dbResult);
667 await db.subscriptionDeliveryComplete(dbCtx, callback, topicId);
668 });
669 it('failure', async function () {
670 const dbResult = {
671 rowCount: 0,
672 };
673 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult);
674 try {
675 await db.subscriptionDeliveryComplete(dbCtx, callback, topicId);
676 assert.fail(noExpectedException);
677 } catch (e) {
678 assert(e instanceof DBErrors.UnexpectedResult);
679 }
680 });
681 it('second failure', async function () {
682 const dbResult0 = {
683 rowCount: 1,
684 };
685 const dbResult1 = {
686 rowCount: 0,
687 };
688 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
689 try {
690 await db.subscriptionDeliveryComplete(dbCtx, callback, topicId);
691 assert.fail(noExpectedException);
692 } catch (e) {
693 assert(e instanceof DBErrors.UnexpectedResult);
694 }
695 });
696 }); // subscriptionDeliveryComplete
697
698 describe('subscriptionDeliveryGone', function () {
699 it('success', async function() {
700 const dbResult = {
701 rowCount: 1,
702 };
703 sinon.stub(db.db, 'result').resolves(dbResult);
704 await db.subscriptionDeliveryGone(dbCtx, callback, topicId);
705 });
706 it('failure', async function () {
707 const dbResult = {
708 rowCount: 0,
709 };
710 sinon.stub(db.db, 'result').resolves(dbResult);
711 try {
712 await db.subscriptionDeliveryGone(dbCtx, callback, topicId);
713 assert.fail(noExpectedException);
714 } catch (e) {
715 assert(e instanceof DBErrors.UnexpectedResult);
716 }
717 });
718 }); // subscriptionDeliveryGone
719
720 describe('subscriptionDeliveryIncomplete', function () {
721 it('success', async function() {
722 const dbOne = { deliveryAttemptsSinceSuccess: 0 };
723 const dbResult = {
724 rowCount: 1,
725 };
726 sinon.stub(db.db, 'one').resolves(dbOne);
727 sinon.stub(db.db, 'result').resolves(dbResult);
728 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId, retryDelays);
729 });
730 it('success covers default', async function() {
731 const dbOne = { deliveryAttemptsSinceSuccess: 0 };
732 const dbResult = {
733 rowCount: 1,
734 };
735 sinon.stub(db.db, 'one').resolves(dbOne);
736 sinon.stub(db.db, 'result').resolves(dbResult);
737 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId);
738 });
739 it('failure', async function () {
740 const dbOne = { deliveryAttemptsSinceSuccess: 0 };
741 const dbResult = {
742 rowCount: 0,
743 };
744 sinon.stub(db.db, 'one').resolves(dbOne);
745 sinon.stub(db.db, 'result').resolves(dbResult);
746 try {
747 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId, retryDelays);
748 assert.fail(noExpectedException);
749 } catch (e) {
750 assert(e instanceof DBErrors.UnexpectedResult);
751 }
752 });
753 it('second failure', async function () {
754 const dbOne = { deliveryAttemptsSinceSuccess: 0 };
755 const dbResult0 = {
756 rowCount: 1,
757 };
758 const dbResult1 = {
759 rowCount: 0,
760 };
761 sinon.stub(db.db, 'one').resolves(dbOne);
762 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
763 try {
764 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId, retryDelays);
765 assert.fail(noExpectedException);
766 } catch (e) {
767 assert(e instanceof DBErrors.UnexpectedResult);
768 }
769 });
770 }); // subscriptionDeliveryIncomplete
771
772 describe('subscriptionGet', function () {
773 it('success', async function() {
774 const expected = {
775 id: subscriptionId,
776 };
777 sinon.stub(db.db, 'oneOrNone').resolves(expected);
778 const result = await db.subscriptionGet(dbCtx, callback, topicId);
779 assert.deepStrictEqual(result, expected);
780 });
781 it('failure', async function () {
782 const expected = new Error();
783 sinon.stub(db.db, 'oneOrNone').throws(expected);
784 try {
785 await db.subscriptionGet(dbCtx, callback, topicId);
786 assert.fail(noExpectedException);
787 } catch (e) {
788 assert.deepStrictEqual(e, expected);
789 }
790 });
791 }); // subscriptionGet
792
793 describe('subscriptionGetById', function () {
794 it('success', async function() {
795 const expected = {
796 id: subscriptionId,
797 };
798 sinon.stub(db.db, 'oneOrNone').resolves(expected);
799 const result = await db.subscriptionGetById(dbCtx, subscriptionId);
800 assert.deepStrictEqual(result, expected);
801 });
802 it('failure', async function () {
803 const expected = new Error();
804 sinon.stub(db.db, 'oneOrNone').throws(expected);
805 try {
806 await db.subscriptionGetById(dbCtx, subscriptionId);
807 assert.fail(noExpectedException);
808 } catch (e) {
809 assert.deepStrictEqual(e, expected);
810 }
811 });
812 }); // subscriptionGetById
813
814 describe('subscriptionUpsert', function () {
815 let data;
816 beforeEach(function () {
817 data = {
818 callback,
819 topicId,
820 leaseSeconds,
821 secret,
822 httpRemoteAddr,
823 httpFrom,
824 };
825 });
826 it('success', async function() {
827 const dbResult = {
828 rowCount: 1,
829 rows: [{ id: subscriptionId }],
830 duration: 10,
831 };
832 const expected = {
833 changes: 1,
834 lastInsertRowid: subscriptionId,
835 duration: 10,
836 };
837 sinon.stub(db.db, 'result').resolves(dbResult);
838 const result = await db.subscriptionUpsert(dbCtx, data);
839 assert.deepStrictEqual(result, expected);
840 });
841 it('failure', async function () {
842 const dbResult = {
843 rowCount: 0,
844 };
845 sinon.stub(db.db, 'result').resolves(dbResult);
846 try {
847 await db.subscriptionUpsert(dbCtx, data);
848 assert.fail(noExpectedException);
849 } catch (e) {
850 assert(e instanceof DBErrors.UnexpectedResult);
851 }
852 });
853 }); // subscriptionUpsert
854
855 describe('subscriptionUpdate', function () {
856 let data;
857 beforeEach(function () {
858 data = {
859 signatureAlgorithm: 'sha256',
860 };
861 });
862 it('success', async function() {
863 const dbResult = {
864 rowCount: 1,
865 rows: [],
866 duration: 10,
867 };
868 sinon.stub(db.db, 'result').resolves(dbResult);
869 await db.subscriptionUpdate(dbCtx, data);
870 });
871 it('failure', async function () {
872 const dbResult = {
873 rowCount: 0,
874 };
875 sinon.stub(db.db, 'result').resolves(dbResult);
876 try {
877 await db.subscriptionUpdate(dbCtx, data);
878 assert.fail(noExpectedException);
879 } catch (e) {
880 assert(e instanceof DBErrors.UnexpectedResult);
881 }
882 });
883 }); // subscriptionUpdate
884
885 describe('topicDeleted', function () {
886 it('success', async function() {
887 const dbResult = {
888 rowCount: 1,
889 };
890 sinon.stub(db.db, 'result').resolves(dbResult);
891 await db.topicDeleted(dbCtx, topicId);
892 });
893 it('failure', async function() {
894 const dbResult = {
895 rowCount: 0,
896 };
897 sinon.stub(db.db, 'result').resolves(dbResult);
898 try {
899 await db.topicDeleted(dbCtx, topicId);
900 assert.fail(noExpectedException);
901 } catch (e) {
902 assert(e instanceof DBErrors.UnexpectedResult);
903 }
904 });
905 }); // topicDeleted
906
907 describe('topicFetchClaim', function () {
908 it('success', async function() {
909 const dbResult = [{ id: topicId }];
910 const expected = [topicId];
911 sinon.stub(db.db, 'manyOrNone').resolves(dbResult);
912 const result = await db.topicFetchClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
913 assert.deepStrictEqual(result, expected);
914 });
915 it('failure', async function () {
916 const expected = new Error();
917 sinon.stub(db.db, 'manyOrNone').throws(expected);
918 try {
919 await db.topicFetchClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
920 assert.fail(noExpectedException);
921 } catch (e) {
922 assert.deepStrictEqual(e, expected);
923 }
924 });
925 }); // topicFetchClaim
926
927 describe('topicFetchClaimById', function () {
928 it('success', async function() {
929 const dbResult = {
930 rowCount: 1,
931 rows: [],
932 duration: 10,
933 };
934 const expected = {
935 changes: 1,
936 lastInsertRowid: undefined,
937 duration: 10,
938 };
939 sinon.stub(db.db, 'result').resolves(dbResult);
940 const result = await db.topicFetchClaimById(dbCtx, topicId, claimTimeoutSeconds, claimant);
941 assert.deepStrictEqual(result, expected);
942 });
943 it('failure', async function () {
944 const expected = new Error();
945 sinon.stub(db.db, 'result').throws(expected);
946 try {
947 await db.topicFetchClaimById(dbCtx, topicId, claimTimeoutSeconds, claimant);
948 assert.fail(noExpectedException);
949 } catch (e) {
950 assert.deepStrictEqual(e, expected);
951 }
952 });
953 }); // topicFetchClaimById
954
955 describe('topicFetchComplete', function () {
956 it('success', async function() {
957 const dbResult = {
958 rowCount: 1,
959 rows: [],
960 duration: 10,
961 };
962 sinon.stub(db.db, 'result').resolves(dbResult);
963 await db.topicFetchComplete(dbCtx, topicId);
964 });
965 it('failure', async function () {
966 const dbResult = {
967 rowCount: 0,
968 rows: [],
969 duration: 10,
970 };
971 sinon.stub(db.db, 'result').resolves(dbResult);
972 try {
973 await db.topicFetchComplete(dbCtx, topicId);
974 assert.fail(noExpectedException);
975 } catch (e) {
976 assert(e instanceof DBErrors.UnexpectedResult);
977 }
978 });
979 it('second failure', async function () {
980 const dbResult0 = {
981 rowCount: 1,
982 rows: [],
983 duration: 10,
984 };
985 const dbResult1 = {
986 rowCount: 0,
987 rows: [],
988 duration: 10,
989 };
990 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
991 try {
992 await db.topicFetchComplete(dbCtx, topicId);
993 assert.fail(noExpectedException);
994 } catch (e) {
995 assert(e instanceof DBErrors.UnexpectedResult);
996 }
997 });
998 }); // topicFetchComplete
999
1000 describe('topicFetchIncomplete', function () {
1001 it('success', async function() {
1002 const dbOne = { currentAttempt: 0 };
1003 const dbResult0 = {
1004 rowCount: 1,
1005 rows: [],
1006 duration: 10,
1007 };
1008 const dbResult1 = {
1009 rowCount: 1,
1010 rows: [],
1011 duration: 10,
1012 }
1013 const expected = {
1014 changes: 1,
1015 lastInsertRowid: undefined,
1016 duration: 10,
1017 };
1018 sinon.stub(db.db, 'one').resolves(dbOne);
1019 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1020 const result = await db.topicFetchIncomplete(dbCtx, topicId, retryDelays);
1021 assert.deepStrictEqual(result, expected);
1022 });
1023 it('covers defaults', async function() {
1024 const dbOne = { currentAttempt: 0 };
1025 const dbResult0 = {
1026 rowCount: 1,
1027 rows: [],
1028 duration: 10,
1029 };
1030 const dbResult1 = {
1031 rowCount: 1,
1032 rows: [],
1033 duration: 10,
1034 }
1035 const expected = {
1036 changes: 1,
1037 lastInsertRowid: undefined,
1038 duration: 10,
1039 };
1040 sinon.stub(db.db, 'one').resolves(dbOne);
1041 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1042 const result = await db.topicFetchIncomplete(dbCtx, topicId);
1043 assert.deepStrictEqual(result, expected);
1044 });
1045 it('failure', async function () {
1046 const dbOne = { currentAttempt: 0 };
1047 const dbResult0 = {
1048 rowCount: 1,
1049 rows: [],
1050 duration: 10,
1051 };
1052 const dbResult1 = {
1053 rowCount: 0,
1054 rows: [],
1055 duration: 10,
1056 }
1057 sinon.stub(db.db, 'one').resolves(dbOne);
1058 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1059 try {
1060 await db.topicFetchIncomplete(dbCtx, topicId, retryDelays);
1061 assert.fail(noExpectedException);
1062 } catch (e) {
1063 assert(e instanceof DBErrors.UnexpectedResult);
1064 }
1065 });
1066 it('second failure', async function () {
1067 const dbOne = { currentAttempt: 0 };
1068 const dbResult0 = {
1069 rowCount: 0,
1070 rows: [],
1071 duration: 10,
1072 };
1073 const dbResult1 = {
1074 rowCount: 0,
1075 rows: [],
1076 duration: 10,
1077 }
1078 sinon.stub(db.db, 'one').resolves(dbOne);
1079 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1080 try {
1081 await db.topicFetchIncomplete(dbCtx, topicId, retryDelays);
1082 assert.fail(noExpectedException);
1083 } catch (e) {
1084 assert(e instanceof DBErrors.UnexpectedResult);
1085 }
1086 });
1087 }); // topicFetchIncomplete
1088
1089 describe('topicFetchRequested', function () {
1090 it('success', async function() {
1091 const dbResult = {
1092 rowCount: 1,
1093 rows: [],
1094 duration: 10,
1095 };
1096 const expected = {
1097 changes: 1,
1098 lastInsertRowid: undefined,
1099 duration: 10,
1100 };
1101 sinon.stub(db.db, 'result').resolves(dbResult);
1102 const result = await db.topicFetchRequested(dbCtx, topicId);
1103 assert.deepStrictEqual(result, expected);
1104 });
1105 it('failure', async function () {
1106 const dbResult = {
1107 rowCount: 0,
1108 rows: [],
1109 duration: 10,
1110 };
1111 sinon.stub(db.db, 'result').resolves(dbResult);
1112 try {
1113 await db.topicFetchRequested(dbCtx, topicId);
1114 assert.fail(noExpectedException);
1115 } catch (e) {
1116 assert(e instanceof DBErrors.UnexpectedResult);
1117 }
1118 });
1119 }); // topicFetchRequested
1120
1121 describe('topicGetAll', function () {
1122 it('success', async function() {
1123 const expected = [{ id: topicId }];
1124 sinon.stub(db.db, 'manyOrNone').resolves(expected);
1125 const result = await db.topicGetAll(dbCtx);
1126 assert.deepStrictEqual(result, expected);
1127 });
1128 it('covers default', async function() {
1129 const expected = undefined;
1130 sinon.stub(db.db, 'manyOrNone').resolves(expected);
1131 const result = await db.topicGetAll(dbCtx);
1132 assert.deepStrictEqual(result, expected);
1133 });
1134 it('failure', async function () {
1135 const expected = new Error();
1136 sinon.stub(db.db, 'manyOrNone').throws(expected);
1137 try {
1138 await db.topicGetAll(dbCtx);
1139 assert.fail(noExpectedException);
1140 } catch (e) {
1141 assert.deepStrictEqual(e, expected);
1142 }
1143 });
1144 }); // topicGetById
1145
1146 describe('topicGetById', function () {
1147 it('success', async function() {
1148 const expected = { id: topicId };
1149 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1150 const result = await db.topicGetById(dbCtx, topicId);
1151 assert.deepStrictEqual(result, expected);
1152 });
1153 it('covers none', async function() {
1154 const expected = undefined;
1155 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1156 const result = await db.topicGetById(dbCtx, topicId);
1157 assert.deepStrictEqual(result, expected);
1158 });
1159 it('covers no defaults', async function () {
1160 const expected = { id: topicId };
1161 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1162 const result = await db.topicGetById(dbCtx, topicId, false);
1163 assert.deepStrictEqual(result, expected);
1164 });
1165 it('failure', async function () {
1166 const expected = new Error();
1167 sinon.stub(db.db, 'oneOrNone').throws(expected);
1168 try {
1169 await db.topicGetById(dbCtx, topicId);
1170 assert.fail(noExpectedException);
1171 } catch (e) {
1172 assert.deepStrictEqual(e, expected);
1173 }
1174 });
1175 }); // topicGetById
1176
1177 describe('topicGetByUrl', function () {
1178 it('success', async function() {
1179 const expected = [];
1180 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1181 const result = await db.topicGetByUrl(dbCtx, topicUrl);
1182 assert.deepStrictEqual(result, expected);
1183 });
1184 it('failure', async function () {
1185 const expected = new Error();
1186 sinon.stub(db.db, 'oneOrNone').throws(expected);
1187 try {
1188 await db.topicGetByUrl(dbCtx, topicUrl);
1189 assert.fail(noExpectedException);
1190 } catch (e) {
1191 assert.deepStrictEqual(e, expected);
1192 }
1193 });
1194 }); // topicGetByUrl
1195
1196 describe('topicGetContentById', function () {
1197 let topic;
1198 beforeEach(function () {
1199 delete db.cache;
1200 topic = {
1201 id: topicId,
1202 };
1203 });
1204 it('success', async function() {
1205 const expected = topic;
1206 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1207 const result = await db.topicGetContentById(dbCtx, topicId);
1208 assert.deepStrictEqual(result, expected);
1209 });
1210 it('covers default', async function() {
1211 const expected = undefined;
1212 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1213 const result = await db.topicGetContentById(dbCtx, topicId);
1214 assert.deepStrictEqual(result, expected);
1215 });
1216 it('failure', async function () {
1217 const expected = new Error();
1218 sinon.stub(db.db, 'oneOrNone').throws(expected);
1219 try {
1220 await db.topicGetContentById(dbCtx, topicId);
1221 assert.fail(noExpectedException);
1222 } catch (e) {
1223 assert.deepStrictEqual(e, expected);
1224 }
1225 });
1226 it('caches success', async function () {
1227 db.cache = new Map();
1228 const expected = topic;
1229 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1230 const result = await db.topicGetContentById(dbCtx, topicId);
1231 assert.deepStrictEqual(result, expected);
1232 });
1233 it('covers cached entry', async function() {
1234 let result;
1235 db.cache = new Map();
1236 const expected = topic;
1237 sinon.stub(db.db, 'oneOrNone').resolves(expected);
1238 result = await db.topicGetContentById(dbCtx, topicId);
1239 assert.deepStrictEqual(result, expected);
1240 result = await db.topicGetContentById(dbCtx, topicId);
1241 assert.deepStrictEqual(result, expected);
1242 });
1243 }); // topicGetContentById
1244
1245 describe('topicPendingDelete', function () {
1246 beforeEach(function () {
1247 sinon.stub(db.db, 'one');
1248 sinon.stub(db.db, 'result');
1249 });
1250 it('success', async function () {
1251 db.db.one.onCall(0).resolves({
1252 id: topicId,
1253 isDeleted: true,
1254 }).onCall(1).resolves({
1255 count: 0,
1256 });
1257 const dbResult = {
1258 rowCount: 1,
1259 rows: [],
1260 duration: 10,
1261 };
1262 db.db.result.resolves(dbResult);
1263 await db.topicPendingDelete(dbCtx, topicId);
1264 assert(db.db.result.called);
1265 });
1266 it('does not delete non-deleted topic', async function () {
1267 db.db.one.onCall(0).resolves({
1268 id: topicId,
1269 isDeleted: false,
1270 }).onCall(1).resolves({
1271 count: 0,
1272 });
1273 await db.topicPendingDelete(dbCtx, topicId);
1274 assert(!db.db.result.called);
1275 });
1276 it('does not delete topic with active subscriptions', async function () {
1277 db.db.one.onCall(0).resolves({
1278 id: topicId,
1279 isDeleted: true,
1280 }).onCall(1).resolves({
1281 count: 10,
1282 });
1283 await db.topicPendingDelete(dbCtx, topicId);
1284 assert(!db.db.result.called);
1285 });
1286 it('covers no deletion', async function () {
1287 db.db.one.onCall(0).resolves({
1288 id: topicId,
1289 isDeleted: true,
1290 }).onCall(1).resolves({
1291 count: 0,
1292 });
1293 const dbResult = {
1294 rowCount: 0,
1295 rows: [],
1296 duration: 10,
1297 };
1298 db.db.result.resolves(dbResult);
1299 try {
1300 await db.topicPendingDelete(dbCtx, topicId);
1301 assert.fail(noExpectedException);
1302 } catch (e) {
1303 assert(e instanceof DBErrors.UnexpectedResult);
1304 }
1305 });
1306 });
1307
1308 describe('topicSet', function () {
1309 let data;
1310 beforeEach(function () {
1311 data = {
1312 url: topicUrl,
1313 };
1314 });
1315 it('success', async function() {
1316 const dbResult = {
1317 rowCount: 1,
1318 rows: [{ id: topicId }],
1319 duration: 10,
1320 };
1321 const expected = {
1322 changes: 1,
1323 lastInsertRowid: topicId,
1324 duration: 10,
1325 };
1326 sinon.stub(db.db, 'result').resolves(dbResult);
1327 const result = await db.topicSet(dbCtx, data);
1328 assert.deepStrictEqual(result, expected);
1329 });
1330 it('failure', async function () {
1331 const dbResult = {
1332 rowCount: 0,
1333 rows: [],
1334 duration: 10,
1335 };
1336 sinon.stub(db.db, 'result').resolves(dbResult);
1337 try {
1338 await db.topicSet(dbCtx, data);
1339 assert.fail(noExpectedException);
1340 } catch (e) {
1341 assert(e instanceof DBErrors.UnexpectedResult);
1342 }
1343 });
1344 it('fails invalid value', async function () {
1345 sinon.stub(db.db, 'result');
1346 try {
1347 data.leaseSecondsPreferred = -100;
1348 await db.topicSet(dbCtx, data);
1349 assert.fail(noExpectedException);
1350 } catch (e) {
1351 assert(e instanceof DBErrors.DataValidation);
1352 }
1353 assert(!db.db.result.called);
1354 });
1355 it('fails invalid values', async function () {
1356 sinon.stub(db.db, 'result');
1357 try {
1358 data.leaseSecondsPreferred = 10;
1359 data.leaseSecondsMax = 100;
1360 data.leaseSecondsMin = 50;
1361 await db.topicSet(dbCtx, data);
1362 assert.fail(noExpectedException);
1363 } catch (e) {
1364 assert(e instanceof DBErrors.DataValidation);
1365 }
1366 assert(!db.db.result.called);
1367 });
1368 }); // topicSet
1369
1370 describe('topicSetContent', function () {
1371 let data;
1372 beforeEach(function () {
1373 data = {
1374 content: 'content',
1375 contentType: 'text/plain',
1376 contentHash: 'abc123',
1377 };
1378 });
1379 it('success', async function() {
1380 const dbResult = {
1381 rowCount: 1,
1382 rows: [],
1383 duration: 10,
1384 };
1385 const expected = {
1386 changes: 1,
1387 lastInsertRowid: undefined,
1388 duration: 10,
1389 };
1390 sinon.stub(db.db, 'result').resolves(dbResult);
1391 const result = await db.topicSetContent(dbCtx, data);
1392 assert.deepStrictEqual(result, expected);
1393 });
1394 it('failure', async function () {
1395 const dbResult = {
1396 rowCount: 0,
1397 rows: [],
1398 duration: 10,
1399 };
1400 sinon.stub(db.db, 'result').resolves(dbResult);
1401 try {
1402 await db.topicSetContent(dbCtx, data);
1403 assert.fail(noExpectedException);
1404 } catch (e) {
1405 assert(e instanceof DBErrors.UnexpectedResult);
1406 }
1407 });
1408 }); // topicSetContent
1409
1410 describe('topicUpdate', function () {
1411 let data;
1412 beforeEach(function () {
1413 data = {
1414 leaseSecondsPreferred: 123,
1415 leaseSecondsMin: 100,
1416 leaseSecondsMax: 1000,
1417 publisherValidationUrl: null,
1418 contentHashAlgorithm: 'sha256',
1419 };
1420 });
1421 it('success', async function() {
1422 const dbResult = {
1423 rowCount: 1,
1424 rows: [],
1425 duration: 10,
1426 };
1427 sinon.stub(db.db, 'result').resolves(dbResult);
1428 await db.topicUpdate(dbCtx, data);
1429 });
1430 it('failure', async function () {
1431 const dbResult = {
1432 rowCount: 0,
1433 rows: [],
1434 duration: 10,
1435 };
1436 sinon.stub(db.db, 'result').resolves(dbResult);
1437 try {
1438 await db.topicUpdate(dbCtx, data);
1439 assert.fail(noExpectedException);
1440 } catch (e) {
1441 assert(e instanceof DBErrors.UnexpectedResult);
1442 }
1443 });
1444
1445 }); // topicUpdate
1446
1447 describe('verificationClaim', function () {
1448 it('success', async function() {
1449 const dbManyOrNone = [{ id: verificationId }];
1450 const expected = [verificationId];
1451 sinon.stub(db.db, 'manyOrNone').resolves(dbManyOrNone);
1452 const result = await db.verificationClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
1453 assert.deepStrictEqual(result, expected);
1454 });
1455 it('failure', async function () {
1456 const expected = new Error();
1457 sinon.stub(db.db, 'manyOrNone').throws(expected);
1458 try {
1459 await db.verificationClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
1460 assert.fail(noExpectedException);
1461 } catch (e) {
1462 assert.deepStrictEqual(e, expected);
1463 }
1464 });
1465 }); // verificationClaim
1466
1467 describe('verificationClaimById', function () {
1468 it('success', async function() {
1469 const dbResult = {
1470 rowCount: 1,
1471 rows: [ { id: verificationId } ],
1472 duration: 10,
1473 };
1474 const expected = {
1475 changes: 1,
1476 lastInsertRowid: verificationId,
1477 duration: 10,
1478 };
1479 sinon.stub(db.db, 'result').resolves(dbResult);
1480 const result = await db.verificationClaimById(dbCtx, verificationId, claimTimeoutSeconds, claimant);
1481 assert.deepStrictEqual(result, expected);
1482 });
1483 it('failure', async function () {
1484 const expected = new Error();
1485 sinon.stub(db.db, 'result').throws(expected);
1486 try {
1487 await db.verificationClaimById(dbCtx, verificationId, claimTimeoutSeconds, claimant);
1488 assert.fail(noExpectedException);
1489 } catch (e) {
1490 assert.deepStrictEqual(e, expected);
1491 }
1492 });
1493 }); // verificationClaimById
1494
1495 describe('verificationComplete', function () {
1496 it('success', async function() {
1497 const dbResult = {
1498 rowCount: 1,
1499 rows: [],
1500 duration: 10,
1501 };
1502 sinon.stub(db.db, 'result').resolves(dbResult);
1503 await db.verificationComplete(dbCtx, verificationId, callback, topicId);
1504 });
1505 it('failure', async function () {
1506 const dbResult = {
1507 rowCount: 0,
1508 rows: [],
1509 duration: 10,
1510 };
1511 sinon.stub(db.db, 'result').resolves(dbResult);
1512 try {
1513 await db.verificationComplete(dbCtx, verificationId, callback, topicId);
1514 assert.fail(noExpectedException);
1515 } catch (e) {
1516 assert(e instanceof DBErrors.UnexpectedResult);
1517 }
1518 });
1519 }); // verificationComplete
1520
1521 describe('verificationGetById', function () {
1522 it('success', async function() {
1523 const dbOneOrNone = { id: verificationId };
1524 const expected = { id: verificationId };
1525 sinon.stub(db.db, 'oneOrNone').resolves(dbOneOrNone);
1526 const result = await db.verificationGetById(dbCtx, verificationId);
1527 assert.deepStrictEqual(result, expected);
1528 });
1529 it('failure', async function () {
1530 const expected = new Error();
1531 sinon.stub(db.db, 'oneOrNone').throws(expected);
1532 try {
1533 await db.verificationGetById(dbCtx, verificationId);
1534 assert.fail(noExpectedException);
1535 } catch (e) {
1536 assert.deepStrictEqual(e, expected);
1537 }
1538 });
1539 }); // verificationGetById
1540
1541 describe('verificationIncomplete', function () {
1542 it('success', async function() {
1543 const dbOne = { attempts: 0 };
1544 const dbResult0 = {
1545 rowCount: 1,
1546 rows: [],
1547 duration: 10,
1548 };
1549 const dbResult1 = {
1550 rowCount: 1,
1551 rows: [],
1552 duration: 10,
1553 };
1554 sinon.stub(db.db, 'one').resolves(dbOne);
1555 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1556 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1557 });
1558 it('covers defaults', async function() {
1559 const dbOne = { attempts: 0 };
1560 const dbResult0 = {
1561 rowCount: 1,
1562 rows: [],
1563 duration: 10,
1564 };
1565 const dbResult1 = {
1566 rowCount: 1,
1567 rows: [],
1568 duration: 10,
1569 };
1570 sinon.stub(db.db, 'one').resolves(dbOne);
1571 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1572 await db.verificationIncomplete(dbCtx, verificationId);
1573 });
1574 it('failure', async function () {
1575 const dbOne = { attempts: 0 };
1576 const dbResult0 = {
1577 rowCount: 0,
1578 rows: [],
1579 duration: 10,
1580 };
1581 const dbResult1 = {
1582 rowCount: 1,
1583 rows: [],
1584 duration: 10,
1585 };
1586 sinon.stub(db.db, 'one').resolves(dbOne);
1587 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1588 try {
1589 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1590 assert.fail(noExpectedException);
1591 } catch (e) {
1592 assert(e instanceof DBErrors.UnexpectedResult);
1593 }
1594 });
1595 it('second failure', async function () {
1596 const dbOne = { attempts: 0 };
1597 const dbResult0 = {
1598 rowCount: 1,
1599 rows: [],
1600 duration: 10,
1601 };
1602 const dbResult1 = {
1603 rowCount: 0,
1604 rows: [],
1605 duration: 10,
1606 };
1607 sinon.stub(db.db, 'one').resolves(dbOne);
1608 sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1);
1609 try {
1610 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1611 assert.fail(noExpectedException);
1612 } catch (e) {
1613 assert(e instanceof DBErrors.UnexpectedResult);
1614 }
1615 });
1616 }); // verificationIncomplete
1617
1618 describe('verificationInsert', function () {
1619 let verification;
1620 beforeEach(function () {
1621 verification = {
1622 topicId,
1623 callback,
1624 mode: 'subscribe',
1625 isPublisherValidated: true,
1626 leaseSeconds: 86400,
1627 };
1628 });
1629 it('success', async function() {
1630 const dbResult = {
1631 rowCount: 1,
1632 rows: [{ id: verificationId }],
1633 duration: 10,
1634 };
1635 const expected = verificationId;
1636 sinon.stub(db.db, 'result').resolves(dbResult);
1637 const result = await db.verificationInsert(dbCtx, verification);
1638 assert.deepStrictEqual(result, expected);
1639 });
1640 it('failure', async function () {
1641 const dbResult = {
1642 rowCount: 0,
1643 rows: [],
1644 duration: 10,
1645 };
1646 sinon.stub(db.db, 'result').resolves(dbResult);
1647 try {
1648 await db.verificationInsert(dbCtx, verification);
1649 assert.fail(noExpectedException);
1650 } catch (e) {
1651 assert(e instanceof DBErrors.UnexpectedResult);
1652 }
1653 });
1654 it('fails validation', async function () {
1655 delete verification.leaseSeconds;
1656 try {
1657 await db.verificationInsert(dbCtx, verification);
1658 assert.fail(noExpectedException);
1659 } catch (e) {
1660 assert(e instanceof DBErrors.DataValidation);
1661 }
1662 });
1663 }); // verificationInsert
1664
1665 describe('verificationRelease', function () {
1666 it('success', async function() {
1667 const dbResult = {
1668 rowCount: 1,
1669 rows: [],
1670 duration: 10,
1671 };
1672 sinon.stub(db.db, 'result').resolves(dbResult);
1673 await db.verificationRelease(dbCtx, verificationId);
1674 });
1675 it('failure', async function () {
1676 const dbResult = {
1677 rowCount: 0,
1678 rows: [],
1679 duration: 10,
1680 };
1681 sinon.stub(db.db, 'result').resolves(dbResult);
1682 try {
1683 await db.verificationRelease(dbCtx, verificationId);
1684 assert.fail(noExpectedException);
1685 } catch (e) {
1686 assert(e instanceof DBErrors.UnexpectedResult);
1687 }
1688 });
1689 }); // verificationRelease
1690
1691 describe('verificationUpdate', function () {
1692 let data;
1693 beforeEach(function () {
1694 data = {
1695 mode: 'subscribe',
1696 isPublisherValidated: true,
1697 };
1698 });
1699 it('success', async function() {
1700 const dbResult = {
1701 rowCount: 1,
1702 rows: [],
1703 duration: 10,
1704 };
1705 sinon.stub(db.db, 'result').resolves(dbResult);
1706 await db.verificationUpdate(dbCtx, verificationId, data);
1707 });
1708 it('failure', async function () {
1709 const dbResult = {
1710 rowCount: 0,
1711 rows: [],
1712 duration: 10,
1713 }
1714 sinon.stub(db.db, 'result').resolves(dbResult);
1715 try {
1716 await db.verificationUpdate(dbCtx, verificationId, data);
1717 assert.fail(noExpectedException);
1718 } catch (e) {
1719 assert(e instanceof DBErrors.UnexpectedResult, e.name);
1720 }
1721 });
1722 it('fails validation', async function () {
1723 delete data.mode;
1724 try {
1725 await db.verificationUpdate(dbCtx, verificationId, data);
1726 assert.fail(noExpectedException);
1727 } catch (e) {
1728 assert(e instanceof DBErrors.DataValidation);
1729 }
1730 });
1731 }); // verificationUpdate
1732
1733 describe('verificationValidated', function () {
1734 it('success', async function() {
1735 const dbResult = {
1736 rowCount: 1,
1737 rows: [],
1738 duration: 10,
1739 }
1740 sinon.stub(db.db, 'result').resolves(dbResult);
1741 await db.verificationValidated(dbCtx, verificationId);
1742 });
1743 it('failure', async function () {
1744 const dbResult = {
1745 rowCount: 0,
1746 rows: [],
1747 duration: 10,
1748 }
1749 sinon.stub(db.db, 'result').resolves(dbResult);
1750 try {
1751 await db.verificationValidated(dbCtx, verificationId);
1752 assert.fail(noExpectedException);
1753 } catch (e) {
1754 assert(e instanceof DBErrors.UnexpectedResult);
1755 }
1756 });
1757 }); // verificationValidated
1758
1759 }); // DatabasePostgres