4a0ecd98516372ecc1adada38595c1ce4c63fa61
1 /* eslint-disable sonarjs/no-identical-functions */
3 /* eslint-disable sonarjs/no-duplicate-string */
6 /* This provides implementation coverage, stubbing pg-promise. */
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');
18 const noExpectedException
= 'did not receive expected exception';
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
;
27 result: () => ({ rows: [] }),
32 manyOrNone: common
.nop
,
33 oneOrNone: common
.nop
,
36 multiResult: common
.nop
,
39 stub
.tx
= (fn
) => fn(stub
);
40 stub
.txIf
= (fn
) => fn(stub
);
41 stub
.task
= (fn
) => fn(stub
);
47 pgpStub
.QueryFile
= class {};
48 pgpStub
.end
= common
.nop
,
49 options
= new Config('test');
50 db
= new DB(stubLogger
, options
, pgpStub
);
52 beforeEach(function () {
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';
65 httpRemoteAddr
= '127.0.0.1';
66 httpFrom
= 'user@example.com';
69 afterEach(function () {
73 it('covers listener', function () {
74 const listenerOptions
= new Config('test');
75 listenerOptions
.db
.cacheEnabled
= true;
76 const listenerDb
= new DB(stubLogger
, listenerOptions
, pgpStub
);
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
) => {
85 // eslint-disable-next-line security/detect-object-injection
88 assert(!(e
instanceof DBErrors
.NotImplemented
), `${fn} not implemented`);
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(': '));
99 describe('pgpInitOptions', function () {
100 describe('error', function () {
101 it('covers', function () {
104 db
.pgpInitOptions
.error(err
, event
);
105 assert(db
.logger
.error
.called
);
108 describe('query', function () {
109 it('covers', function () {
111 db
.pgpInitOptions
.query(event
);
112 assert(db
.logger
.debug
.called
);
114 it('covers NOTIFY', function () {
115 const event
= { query: 'NOTIFY thing' };
116 db
.pgpInitOptions
.query(event
);
117 assert(!db
.logger
.debug
.called
);
120 describe('receive', function () {
121 it('covers', function () {
124 column_one: 'one', // eslint-disable-line camelcase
125 column_two: 2, // eslint-disable-line camelcase
128 column_one: 'foo', // eslint-disable-line camelcase
129 column_two: 4, // eslint-disable-line camelcase
134 const expectedData
= [
144 db
.pgpInitOptions
.receive(data
, result
, event
)
145 assert(db
.logger
.debug
.called
);
146 assert
.deepStrictEqual(data
, expectedData
);
148 it('covers NOTIFY', function () {
151 column_one: 'one', // eslint-disable-line camelcase
152 column_two: 2, // eslint-disable-line camelcase
155 column_one: 'foo', // eslint-disable-line camelcase
156 column_two: 4, // eslint-disable-line camelcase
163 const expectedData
= [
173 db
.pgpInitOptions
.receive(data
, result
, event
)
174 assert(!db
.logger
.debug
.called
);
175 assert
.deepStrictEqual(data
, expectedData
);
178 }); // pgpInitOptions
180 describe('_initTables', function () {
181 beforeEach(function () {
182 sinon
.stub(db
.db
, 'oneOrNone');
183 sinon
.stub(db
.db
, 'multiResult');
184 sinon
.stub(db
, '_currentSchema');
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();
192 it('covers exists', async
function() {
193 db
.db
.oneOrNone
.resolves({});
194 db
._currentSchema
.resolves(db
.schemaVersionsSupported
.max
);
195 await db
._initTables();
199 describe('initialize', function () {
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);
208 it('fails low version', async
function () {
209 const version
= { major: 0, minor: 0, patch: 0 };
210 sinon
.stub(db
.db
, 'one').resolves(version
);
212 await db
.initialize(false);
213 assert
.fail(noExpectedException
);
215 assert(e
instanceof DBErrors
.MigrationNeeded
);
218 it('fails high version', async
function () {
219 const version
= { major: 100, minor: 100, patch: 100 };
220 sinon
.stub(db
.db
, 'one').resolves(version
);
222 await db
.initialize(false);
223 assert
.fail(noExpectedException
);
225 assert(e
instanceof DBErrors
.MigrationNeeded
);
228 it('covers migration', async
function() {
229 sinon
.stub(db
.db
, 'oneOrNone').resolves({});
230 sinon
.stub(db
.db
, 'multiResult').resolves({});
231 sinon
.stub(db
, '_currentSchema').resolves(db
.schemaVersionsSupported
.min
);
232 sinon
.stub(db
.db
, 'one').resolves(db
.schemaVersionsSupported
.max
);
233 await db
.initialize();
235 it('covers migration failure', async
function() {
236 const expected
= new Error('oh no');
237 sinon
.stub(db
.db
, 'oneOrNone').resolves({});
238 sinon
.stub(db
.db
, 'multiResult').rejects(expected
);
239 sinon
.stub(db
, '_currentSchema').resolves(db
.schemaVersionsSupported
.min
);
240 sinon
.stub(db
.db
, 'one').resolves(db
.schemaVersionsSupported
.max
);
242 await db
.initialize();
243 assert
.fail(noExpectedException
);
245 assert
.deepStrictEqual(e
, expected
);
248 it('covers listener', async
function() {
252 const version
= { major: 1, minor: 0, patch: 0 };
253 sinon
.stub(db
.db
, 'one').resolves(version
);
254 await db
.initialize(false);
255 assert(db
.listener
.start
.called
);
259 describe('healthCheck', function () {
260 beforeEach(function () {
261 sinon
.stub(db
.db
, 'connect').resolves({
264 serverVersion: '0.0',
268 it('covers', async
function () {
269 const result
= await db
.healthCheck();
270 assert
.deepStrictEqual(result
, { serverVersion: '0.0' });
274 describe('_queryFileHelper', function () {
275 it('covers success', function () {
276 const _queryFile
= db
._queryFileHelper(pgpStub
);
279 it('covers failure', function () {
280 const err
= new Error();
281 pgpStub
.QueryFile
= class {
286 const _queryFile
= db
._queryFileHelper(pgpStub
);
289 assert
.fail(noExpectedException
);
291 assert
.strictEqual(e
, err
);
294 }); // _queryFileHelper
296 describe('_closeConnection', function () {
300 it('success', async
function () {
301 sinon
.stub(db
._pgp
, 'end');
302 await db
._closeConnection();
303 assert(db
._pgp
.end
.called
);
305 it('failure', async
function () {
306 const expected
= new Error();
307 sinon
.stub(db
._pgp
, 'end').throws(expected
);
309 await db
._closeConnection();
310 assert
.fail(noExpectedException
);
312 assert
.deepStrictEqual(e
, expected
);
315 it('covers listener', async
function () {
319 sinon
.stub(db
._pgp
, 'end');
320 await db
._closeConnection();
321 assert(db
._pgp
.end
.called
);
323 }); // _closeConnection
325 describe('_purgeTables', function () {
326 it('covers not really', async
function () {
327 sinon
.stub(db
.db
, 'tx');
328 await db
._purgeTables(false);
329 assert(!db
.db
.tx
.called
);
331 it('success', async
function () {
332 sinon
.stub(db
.db
, 'batch');
333 await db
._purgeTables(true);
334 assert(db
.db
.batch
.called
);
336 it('failure', async
function () {
337 const expected
= new Error();
338 sinon
.stub(db
.db
, 'tx').rejects(expected
);
340 await db
._purgeTables(true);
341 assert
.fail(noExpectedException
);
343 assert
.deepStrictEqual(e
, expected
);
348 describe('_topicChanged', function () {
349 beforeEach(function () {
350 db
.cache
= new Map();
351 sinon
.stub(db
.cache
, 'delete');
356 it('covers', function () {
357 db
._topicChanged('topic-id');
358 assert(db
.cache
.delete.called
);
360 it('ignores ping', function () {
361 db
._topicChanged('ping');
362 assert(!db
.cache
.delete.called
);
366 describe('_listenerEstablished', function () {
367 it('creates cache', function () {
369 db
._listenerEstablished();
370 assert(db
.cache
instanceof Map
);
372 }); // _listenerEstablished
374 describe('_listenerLost', function () {
375 it('removes cache', function () {
376 db
.cache
= new Map();
382 describe('_cacheGet', function () {
384 beforeEach(function () {
387 it('nothing if no cache', function () {
389 const result
= db
._cacheGet(key
);
390 assert
.strictEqual(result
, undefined);
392 it('nothing if no entry', function () {
393 db
.cache
= new Map();
394 const result
= db
._cacheGet(key
);
395 assert
.strictEqual(result
, undefined);
397 it('returns cached entry', function () {
398 db
.cache
= new Map();
402 db
._cacheSet(key
, expected
);
403 const result
= db
._cacheGet(key
);
404 assert
.deepStrictEqual(result
, expected
);
408 describe('_cacheSet', function () {
410 beforeEach(function () {
413 it('covers no cache', function () {
415 db
._cacheSet(key
, 'data');
417 it('covers cache', function () {
418 db
.cache
= new Map();
419 const expected
= 'blah';
420 db
._cacheSet(key
, expected
);
421 const result
= db
._cacheGet(key
);
422 assert
.deepStrictEqual(result
, expected
);
426 describe('context', function () {
427 it('covers', async
function () {
428 await db
.context(common
.nop
);
432 describe('transaction', function () {
433 it('covers', async
function () {
434 await db
.transaction(db
.db
, common
.nop
);
438 describe('authenticationSuccess', function () {
440 beforeEach(function () {
441 identifier
= 'username';
443 it('success', async
function () {
449 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
450 await db
.authenticationSuccess(dbCtx
, identifier
);
452 it('failure', async
function() {
458 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
460 await db
.authenticationSuccess(dbCtx
, identifier
);
461 assert
.fail(noExpectedException
);
463 assert(e
instanceof DBErrors
.UnexpectedResult
);
466 }); // authenticationSuccess
468 describe('authenticationGet', function () {
469 let identifier
, credential
;
470 beforeEach(function () {
471 identifier
= 'username';
472 credential
= '$z$foo';
474 it('success', async
function () {
475 const dbResult
= { identifier
, credential
};
476 sinon
.stub(db
.db
, 'oneOrNone').resolves(dbResult
);
477 const result
= await db
.authenticationGet(dbCtx
, identifier
);
478 assert
.deepStrictEqual(result
, dbResult
);
480 it('failure', async
function() {
481 const expected
= new Error('blah');
482 sinon
.stub(db
.db
, 'oneOrNone').rejects(expected
);
484 await db
.authenticationGet(dbCtx
, identifier
, credential
);
485 assert
.fail(noExpectedException
);
487 assert
.deepStrictEqual(e
, expected
);
490 }); // authenticationGet
492 describe('authenticationUpsert', function () {
493 let identifier
, credential
;
494 beforeEach(function () {
495 identifier
= 'username';
496 credential
= '$z$foo';
498 it('success', async
function () {
504 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
505 await db
.authenticationUpsert(dbCtx
, identifier
, credential
);
507 it('failure', async
function() {
508 credential
= undefined;
514 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
516 await db
.authenticationUpsert(dbCtx
, identifier
, credential
);
517 assert
.fail(noExpectedException
);
519 assert(e
instanceof DBErrors
.UnexpectedResult
);
522 }); // authenticationUpsert
524 describe('subscriptionsByTopicId', function () {
525 it('success', async
function () {
527 sinon
.stub(db
.db
, 'manyOrNone').resolves(expected
);
528 const result
= await db
.subscriptionsByTopicId(dbCtx
, topicUrl
);
529 assert
.deepStrictEqual(result
, expected
);
531 it('failure', async
function () {
532 const expected
= new Error();
533 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
535 await db
.subscriptionsByTopicId(dbCtx
, topicUrl
);
536 assert
.fail(noExpectedException
);
538 assert
.deepStrictEqual(e
, expected
);
541 }); // subscriptionsByTopicId
543 describe('subscriptionCountByTopicUrl', function () {
544 it('success', async
function () {
545 const expected
= { count: 3 };
546 sinon
.stub(db
.db
, 'one').resolves(expected
);
547 const result
= await db
.subscriptionCountByTopicUrl(dbCtx
, topicUrl
);
548 assert
.deepStrictEqual(result
, expected
);
550 it('failure', async
function () {
551 const expected
= new Error();
552 sinon
.stub(db
.db
, 'one').throws(expected
);
554 await db
.subscriptionCountByTopicUrl(dbCtx
, topicUrl
);
555 assert
.fail(noExpectedException
);
557 assert
.deepStrictEqual(e
, expected
);
560 }); // subscriptionCountByTopicUrl
562 describe('subscriptionDelete', function () {
563 it('success', async
function() {
571 lastInsertRowid: undefined,
574 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
575 const result
= await db
.subscriptionDelete(dbCtx
, callback
, topicId
);
576 assert
.deepStrictEqual(result
, expected
);
578 it('failure', async
function () {
579 const expected
= new Error();
580 sinon
.stub(db
.db
, 'result').throws(expected
);
582 await db
.subscriptionDelete(dbCtx
, callback
, topicId
);
583 assert
.fail(noExpectedException
);
585 assert
.deepStrictEqual(e
, expected
);
588 }); // subscriptionDelete
590 describe('subscriptionDeleteExpired', function () {
591 it('success', async
function () {
599 lastInsertRowid: undefined,
602 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
603 const result
= await db
.subscriptionDeleteExpired(dbCtx
, topicId
);
604 assert
.deepStrictEqual(result
, expected
);
606 it('failure', async
function() {
607 const expected
= new Error();
608 sinon
.stub(db
.db
, 'result').rejects(expected
);
610 await db
.subscriptionDeleteExpired(dbCtx
, topicId
);
611 assert
.fail(noExpectedException
);
613 assert
.deepStrictEqual(e
, expected
);
618 describe('subscriptionDeliveryClaim', function () {
619 it('success', async
function() {
622 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
625 const expected
= ['c2e254c5-aa6e-4a8f-b1a1-e474b07392bb'];
626 sinon
.stub(db
.db
, 'manyOrNone').resolves(dbResult
);
627 const result
= await db
.subscriptionDeliveryClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
628 assert
.deepStrictEqual(result
, expected
);
630 it('failure', async
function () {
631 const expected
= new Error();
632 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
634 await db
.subscriptionDeliveryClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
635 assert
.fail(noExpectedException
);
637 assert
.deepStrictEqual(e
, expected
);
640 }); // subscriptionDeliveryClaim
642 describe('subscriptionDeliveryClaimById', function () {
643 it('success', async
function() {
646 rows: [{ id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb' }],
651 lastInsertRowid: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
654 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
655 const result
= await db
.subscriptionDeliveryClaimById(dbCtx
, subscriptionId
, claimTimeoutSeconds
, claimant
);
656 assert
.deepStrictEqual(result
, expected
);
658 it('failure', async
function () {
664 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
666 await db
.subscriptionDeliveryClaimById(dbCtx
, callback
, topicId
);
667 assert
.fail(noExpectedException
);
669 assert(e
instanceof DBErrors
.UnexpectedResult
);
672 }); // subscriptionDeliveryClaimById
674 describe('subscriptionDeliveryComplete', function () {
675 let topicContentUpdated
;
677 topicContentUpdated
= new Date();
679 it('success', async
function() {
683 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
684 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
, topicContentUpdated
);
686 it('failure', async
function () {
690 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult
);
692 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
, topicContentUpdated
);
693 assert
.fail(noExpectedException
);
695 assert(e
instanceof DBErrors
.UnexpectedResult
);
698 it('second failure', async
function () {
705 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
707 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
, topicContentUpdated
);
708 assert
.fail(noExpectedException
);
710 assert(e
instanceof DBErrors
.UnexpectedResult
);
713 }); // subscriptionDeliveryComplete
715 describe('subscriptionDeliveryGone', function () {
716 it('success', async
function() {
720 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
721 await db
.subscriptionDeliveryGone(dbCtx
, callback
, topicId
);
723 it('failure', async
function () {
727 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
729 await db
.subscriptionDeliveryGone(dbCtx
, callback
, topicId
);
730 assert
.fail(noExpectedException
);
732 assert(e
instanceof DBErrors
.UnexpectedResult
);
735 }); // subscriptionDeliveryGone
737 describe('subscriptionDeliveryIncomplete', function () {
738 it('success', async
function() {
739 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
743 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
744 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
745 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
747 it('success covers default', async
function() {
748 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
752 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
753 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
754 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
);
756 it('failure', async
function () {
757 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
761 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
762 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
764 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
765 assert
.fail(noExpectedException
);
767 assert(e
instanceof DBErrors
.UnexpectedResult
);
770 it('second failure', async
function () {
771 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
778 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
779 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
781 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
782 assert
.fail(noExpectedException
);
784 assert(e
instanceof DBErrors
.UnexpectedResult
);
787 }); // subscriptionDeliveryIncomplete
789 describe('subscriptionGet', function () {
790 it('success', async
function() {
794 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
795 const result
= await db
.subscriptionGet(dbCtx
, callback
, topicId
);
796 assert
.deepStrictEqual(result
, expected
);
798 it('failure', async
function () {
799 const expected
= new Error();
800 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
802 await db
.subscriptionGet(dbCtx
, callback
, topicId
);
803 assert
.fail(noExpectedException
);
805 assert
.deepStrictEqual(e
, expected
);
808 }); // subscriptionGet
810 describe('subscriptionGetById', function () {
811 it('success', async
function() {
815 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
816 const result
= await db
.subscriptionGetById(dbCtx
, subscriptionId
);
817 assert
.deepStrictEqual(result
, expected
);
819 it('failure', async
function () {
820 const expected
= new Error();
821 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
823 await db
.subscriptionGetById(dbCtx
, subscriptionId
);
824 assert
.fail(noExpectedException
);
826 assert
.deepStrictEqual(e
, expected
);
829 }); // subscriptionGetById
831 describe('subscriptionUpsert', function () {
833 beforeEach(function () {
843 it('success', async
function() {
846 rows: [{ id: subscriptionId
}],
851 lastInsertRowid: subscriptionId
,
854 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
855 const result
= await db
.subscriptionUpsert(dbCtx
, data
);
856 assert
.deepStrictEqual(result
, expected
);
858 it('failure', async
function () {
862 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
864 await db
.subscriptionUpsert(dbCtx
, data
);
865 assert
.fail(noExpectedException
);
867 assert(e
instanceof DBErrors
.UnexpectedResult
);
870 }); // subscriptionUpsert
872 describe('subscriptionUpdate', function () {
874 beforeEach(function () {
876 signatureAlgorithm: 'sha256',
879 it('success', async
function() {
885 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
886 await db
.subscriptionUpdate(dbCtx
, data
);
888 it('failure', async
function () {
892 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
894 await db
.subscriptionUpdate(dbCtx
, data
);
895 assert
.fail(noExpectedException
);
897 assert(e
instanceof DBErrors
.UnexpectedResult
);
900 }); // subscriptionUpdate
902 describe('topicDeleted', function () {
903 it('success', async
function() {
907 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
908 await db
.topicDeleted(dbCtx
, topicId
);
910 it('failure', async
function() {
914 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
916 await db
.topicDeleted(dbCtx
, topicId
);
917 assert
.fail(noExpectedException
);
919 assert(e
instanceof DBErrors
.UnexpectedResult
);
924 describe('topicFetchClaim', function () {
925 it('success', async
function() {
926 const dbResult
= [{ id: topicId
}];
927 const expected
= [topicId
];
928 sinon
.stub(db
.db
, 'manyOrNone').resolves(dbResult
);
929 const result
= await db
.topicFetchClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
930 assert
.deepStrictEqual(result
, expected
);
932 it('failure', async
function () {
933 const expected
= new Error();
934 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
936 await db
.topicFetchClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
937 assert
.fail(noExpectedException
);
939 assert
.deepStrictEqual(e
, expected
);
942 }); // topicFetchClaim
944 describe('topicFetchClaimById', function () {
945 it('success', async
function() {
953 lastInsertRowid: undefined,
956 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
957 const result
= await db
.topicFetchClaimById(dbCtx
, topicId
, claimTimeoutSeconds
, claimant
);
958 assert
.deepStrictEqual(result
, expected
);
960 it('failure', async
function () {
961 const expected
= new Error();
962 sinon
.stub(db
.db
, 'result').throws(expected
);
964 await db
.topicFetchClaimById(dbCtx
, topicId
, claimTimeoutSeconds
, claimant
);
965 assert
.fail(noExpectedException
);
967 assert
.deepStrictEqual(e
, expected
);
970 }); // topicFetchClaimById
972 describe('topicFetchComplete', function () {
973 it('success', async
function() {
979 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
980 await db
.topicFetchComplete(dbCtx
, topicId
);
982 it('failure', async
function () {
988 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
990 await db
.topicFetchComplete(dbCtx
, topicId
);
991 assert
.fail(noExpectedException
);
993 assert(e
instanceof DBErrors
.UnexpectedResult
);
996 it('second failure', async
function () {
1007 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1009 await db
.topicFetchComplete(dbCtx
, topicId
);
1010 assert
.fail(noExpectedException
);
1012 assert(e
instanceof DBErrors
.UnexpectedResult
);
1015 }); // topicFetchComplete
1017 describe('topicFetchIncomplete', function () {
1018 it('success', async
function() {
1019 const dbOne
= { currentAttempt: 0 };
1032 lastInsertRowid: undefined,
1035 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1036 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1037 const result
= await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
1038 assert
.deepStrictEqual(result
, expected
);
1040 it('covers defaults', async
function() {
1041 const dbOne
= { currentAttempt: 0 };
1054 lastInsertRowid: undefined,
1057 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1058 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1059 const result
= await db
.topicFetchIncomplete(dbCtx
, topicId
);
1060 assert
.deepStrictEqual(result
, expected
);
1062 it('failure', async
function () {
1063 const dbOne
= { currentAttempt: 0 };
1074 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1075 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1077 await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
1078 assert
.fail(noExpectedException
);
1080 assert(e
instanceof DBErrors
.UnexpectedResult
);
1083 it('second failure', async
function () {
1084 const dbOne
= { currentAttempt: 0 };
1095 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1096 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1098 await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
1099 assert
.fail(noExpectedException
);
1101 assert(e
instanceof DBErrors
.UnexpectedResult
);
1104 }); // topicFetchIncomplete
1106 describe('topicFetchRequested', function () {
1107 it('success', async
function() {
1115 lastInsertRowid: undefined,
1118 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1119 const result
= await db
.topicFetchRequested(dbCtx
, topicId
);
1120 assert
.deepStrictEqual(result
, expected
);
1122 it('failure', async
function () {
1128 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1130 await db
.topicFetchRequested(dbCtx
, topicId
);
1131 assert
.fail(noExpectedException
);
1133 assert(e
instanceof DBErrors
.UnexpectedResult
);
1136 }); // topicFetchRequested
1138 describe('topicGetAll', function () {
1139 it('success', async
function() {
1140 const expected
= [{ id: topicId
}];
1141 sinon
.stub(db
.db
, 'manyOrNone').resolves(expected
);
1142 const result
= await db
.topicGetAll(dbCtx
);
1143 assert
.deepStrictEqual(result
, expected
);
1145 it('covers default', async
function() {
1146 const expected
= undefined;
1147 sinon
.stub(db
.db
, 'manyOrNone').resolves(expected
);
1148 const result
= await db
.topicGetAll(dbCtx
);
1149 assert
.deepStrictEqual(result
, expected
);
1151 it('failure', async
function () {
1152 const expected
= new Error();
1153 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
1155 await db
.topicGetAll(dbCtx
);
1156 assert
.fail(noExpectedException
);
1158 assert
.deepStrictEqual(e
, expected
);
1163 describe('topicGetById', function () {
1164 it('success', async
function() {
1165 const expected
= { id: topicId
};
1166 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1167 const result
= await db
.topicGetById(dbCtx
, topicId
);
1168 assert
.deepStrictEqual(result
, expected
);
1170 it('covers none', async
function() {
1171 const expected
= undefined;
1172 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1173 const result
= await db
.topicGetById(dbCtx
, topicId
);
1174 assert
.deepStrictEqual(result
, expected
);
1176 it('covers no defaults', async
function () {
1177 const expected
= { id: topicId
};
1178 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1179 const result
= await db
.topicGetById(dbCtx
, topicId
, false);
1180 assert
.deepStrictEqual(result
, expected
);
1182 it('failure', async
function () {
1183 const expected
= new Error();
1184 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1186 await db
.topicGetById(dbCtx
, topicId
);
1187 assert
.fail(noExpectedException
);
1189 assert
.deepStrictEqual(e
, expected
);
1194 describe('topicGetByUrl', function () {
1195 it('success', async
function() {
1196 const expected
= [];
1197 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1198 const result
= await db
.topicGetByUrl(dbCtx
, topicUrl
);
1199 assert
.deepStrictEqual(result
, expected
);
1201 it('failure', async
function () {
1202 const expected
= new Error();
1203 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1205 await db
.topicGetByUrl(dbCtx
, topicUrl
);
1206 assert
.fail(noExpectedException
);
1208 assert
.deepStrictEqual(e
, expected
);
1211 }); // topicGetByUrl
1213 describe('topicGetContentById', function () {
1215 beforeEach(function () {
1221 it('success', async
function() {
1222 const expected
= topic
;
1223 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1224 const result
= await db
.topicGetContentById(dbCtx
, topicId
);
1225 assert
.deepStrictEqual(result
, expected
);
1227 it('covers default', async
function() {
1228 const expected
= undefined;
1229 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1230 const result
= await db
.topicGetContentById(dbCtx
, topicId
);
1231 assert
.deepStrictEqual(result
, expected
);
1233 it('failure', async
function () {
1234 const expected
= new Error();
1235 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1237 await db
.topicGetContentById(dbCtx
, topicId
);
1238 assert
.fail(noExpectedException
);
1240 assert
.deepStrictEqual(e
, expected
);
1243 it('caches success', async
function () {
1244 db
.cache
= new Map();
1245 const expected
= topic
;
1246 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1247 const result
= await db
.topicGetContentById(dbCtx
, topicId
);
1248 assert
.deepStrictEqual(result
, expected
);
1250 it('covers cached entry', async
function() {
1252 db
.cache
= new Map();
1253 const expected
= topic
;
1254 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1255 result
= await db
.topicGetContentById(dbCtx
, topicId
);
1256 assert
.deepStrictEqual(result
, expected
);
1257 result
= await db
.topicGetContentById(dbCtx
, topicId
);
1258 assert
.deepStrictEqual(result
, expected
);
1260 }); // topicGetContentById
1262 describe('topicPendingDelete', function () {
1263 beforeEach(function () {
1264 sinon
.stub(db
.db
, 'one');
1265 sinon
.stub(db
.db
, 'result');
1267 it('success', async
function () {
1268 db
.db
.one
.onCall(0).resolves({
1271 }).onCall(1).resolves({
1279 db
.db
.result
.resolves(dbResult
);
1280 await db
.topicPendingDelete(dbCtx
, topicId
);
1281 assert(db
.db
.result
.called
);
1283 it('does not delete non-deleted topic', async
function () {
1284 db
.db
.one
.onCall(0).resolves({
1287 }).onCall(1).resolves({
1290 await db
.topicPendingDelete(dbCtx
, topicId
);
1291 assert(!db
.db
.result
.called
);
1293 it('does not delete topic with active subscriptions', async
function () {
1294 db
.db
.one
.onCall(0).resolves({
1297 }).onCall(1).resolves({
1300 await db
.topicPendingDelete(dbCtx
, topicId
);
1301 assert(!db
.db
.result
.called
);
1303 it('covers no deletion', async
function () {
1304 db
.db
.one
.onCall(0).resolves({
1307 }).onCall(1).resolves({
1315 db
.db
.result
.resolves(dbResult
);
1317 await db
.topicPendingDelete(dbCtx
, topicId
);
1318 assert
.fail(noExpectedException
);
1320 assert(e
instanceof DBErrors
.UnexpectedResult
);
1325 describe('topicSet', function () {
1327 beforeEach(function () {
1332 it('success', async
function() {
1335 rows: [{ id: topicId
}],
1340 lastInsertRowid: topicId
,
1343 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1344 const result
= await db
.topicSet(dbCtx
, data
);
1345 assert
.deepStrictEqual(result
, expected
);
1347 it('failure', async
function () {
1353 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1355 await db
.topicSet(dbCtx
, data
);
1356 assert
.fail(noExpectedException
);
1358 assert(e
instanceof DBErrors
.UnexpectedResult
);
1361 it('fails invalid value', async
function () {
1362 sinon
.stub(db
.db
, 'result');
1364 data
.leaseSecondsPreferred
= -100;
1365 await db
.topicSet(dbCtx
, data
);
1366 assert
.fail(noExpectedException
);
1368 assert(e
instanceof DBErrors
.DataValidation
);
1370 assert(!db
.db
.result
.called
);
1372 it('fails invalid values', async
function () {
1373 sinon
.stub(db
.db
, 'result');
1375 data
.leaseSecondsPreferred
= 10;
1376 data
.leaseSecondsMax
= 100;
1377 data
.leaseSecondsMin
= 50;
1378 await db
.topicSet(dbCtx
, data
);
1379 assert
.fail(noExpectedException
);
1381 assert(e
instanceof DBErrors
.DataValidation
);
1383 assert(!db
.db
.result
.called
);
1387 describe('topicSetContent', function () {
1389 beforeEach(function () {
1392 contentType: 'text/plain',
1393 contentHash: 'abc123',
1396 it('success', async
function() {
1404 lastInsertRowid: undefined,
1407 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1408 const result
= await db
.topicSetContent(dbCtx
, data
);
1409 assert
.deepStrictEqual(result
, expected
);
1411 it('failure', async
function () {
1417 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1419 await db
.topicSetContent(dbCtx
, data
);
1420 assert
.fail(noExpectedException
);
1422 assert(e
instanceof DBErrors
.UnexpectedResult
);
1425 }); // topicSetContent
1427 describe('topicUpdate', function () {
1429 beforeEach(function () {
1431 leaseSecondsPreferred: 123,
1432 leaseSecondsMin: 100,
1433 leaseSecondsMax: 1000,
1434 publisherValidationUrl: null,
1435 contentHashAlgorithm: 'sha256',
1438 it('success', async
function() {
1444 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1445 await db
.topicUpdate(dbCtx
, data
);
1447 it('failure', async
function () {
1453 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1455 await db
.topicUpdate(dbCtx
, data
);
1456 assert
.fail(noExpectedException
);
1458 assert(e
instanceof DBErrors
.UnexpectedResult
);
1464 describe('verificationClaim', function () {
1465 it('success', async
function() {
1466 const dbManyOrNone
= [{ id: verificationId
}];
1467 const expected
= [verificationId
];
1468 sinon
.stub(db
.db
, 'manyOrNone').resolves(dbManyOrNone
);
1469 const result
= await db
.verificationClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
1470 assert
.deepStrictEqual(result
, expected
);
1472 it('failure', async
function () {
1473 const expected
= new Error();
1474 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
1476 await db
.verificationClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
1477 assert
.fail(noExpectedException
);
1479 assert
.deepStrictEqual(e
, expected
);
1482 }); // verificationClaim
1484 describe('verificationClaimById', function () {
1485 it('success', async
function() {
1488 rows: [ { id: verificationId
} ],
1493 lastInsertRowid: verificationId
,
1496 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1497 const result
= await db
.verificationClaimById(dbCtx
, verificationId
, claimTimeoutSeconds
, claimant
);
1498 assert
.deepStrictEqual(result
, expected
);
1500 it('failure', async
function () {
1501 const expected
= new Error();
1502 sinon
.stub(db
.db
, 'result').throws(expected
);
1504 await db
.verificationClaimById(dbCtx
, verificationId
, claimTimeoutSeconds
, claimant
);
1505 assert
.fail(noExpectedException
);
1507 assert
.deepStrictEqual(e
, expected
);
1510 }); // verificationClaimById
1512 describe('verificationComplete', function () {
1513 it('success', async
function() {
1519 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1520 await db
.verificationComplete(dbCtx
, verificationId
, callback
, topicId
);
1522 it('failure', async
function () {
1528 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1530 await db
.verificationComplete(dbCtx
, verificationId
, callback
, topicId
);
1531 assert
.fail(noExpectedException
);
1533 assert(e
instanceof DBErrors
.UnexpectedResult
);
1536 }); // verificationComplete
1538 describe('verificationGetById', function () {
1539 it('success', async
function() {
1540 const dbOneOrNone
= { id: verificationId
};
1541 const expected
= { id: verificationId
};
1542 sinon
.stub(db
.db
, 'oneOrNone').resolves(dbOneOrNone
);
1543 const result
= await db
.verificationGetById(dbCtx
, verificationId
);
1544 assert
.deepStrictEqual(result
, expected
);
1546 it('failure', async
function () {
1547 const expected
= new Error();
1548 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1550 await db
.verificationGetById(dbCtx
, verificationId
);
1551 assert
.fail(noExpectedException
);
1553 assert
.deepStrictEqual(e
, expected
);
1556 }); // verificationGetById
1558 describe('verificationIncomplete', function () {
1559 it('success', async
function() {
1560 const dbOne
= { attempts: 0 };
1571 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1572 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1573 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1575 it('covers defaults', async
function() {
1576 const dbOne
= { attempts: 0 };
1587 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1588 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1589 await db
.verificationIncomplete(dbCtx
, verificationId
);
1591 it('failure', async
function () {
1592 const dbOne
= { attempts: 0 };
1603 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1604 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1606 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1607 assert
.fail(noExpectedException
);
1609 assert(e
instanceof DBErrors
.UnexpectedResult
);
1612 it('second failure', async
function () {
1613 const dbOne
= { attempts: 0 };
1624 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1625 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1627 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1628 assert
.fail(noExpectedException
);
1630 assert(e
instanceof DBErrors
.UnexpectedResult
);
1633 }); // verificationIncomplete
1635 describe('verificationInsert', function () {
1637 beforeEach(function () {
1642 isPublisherValidated: true,
1643 leaseSeconds: 86400,
1646 it('success', async
function() {
1649 rows: [{ id: verificationId
}],
1652 const expected
= verificationId
;
1653 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1654 const result
= await db
.verificationInsert(dbCtx
, verification
);
1655 assert
.deepStrictEqual(result
, expected
);
1657 it('failure', async
function () {
1663 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1665 await db
.verificationInsert(dbCtx
, verification
);
1666 assert
.fail(noExpectedException
);
1668 assert(e
instanceof DBErrors
.UnexpectedResult
);
1671 it('fails validation', async
function () {
1672 delete verification
.leaseSeconds
;
1674 await db
.verificationInsert(dbCtx
, verification
);
1675 assert
.fail(noExpectedException
);
1677 assert(e
instanceof DBErrors
.DataValidation
);
1680 }); // verificationInsert
1682 describe('verificationRelease', function () {
1683 it('success', async
function() {
1689 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1690 await db
.verificationRelease(dbCtx
, verificationId
);
1692 it('failure', async
function () {
1698 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1700 await db
.verificationRelease(dbCtx
, verificationId
);
1701 assert
.fail(noExpectedException
);
1703 assert(e
instanceof DBErrors
.UnexpectedResult
);
1706 }); // verificationRelease
1708 describe('verificationUpdate', function () {
1710 beforeEach(function () {
1713 isPublisherValidated: true,
1716 it('success', async
function() {
1722 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1723 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1725 it('failure', async
function () {
1731 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1733 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1734 assert
.fail(noExpectedException
);
1736 assert(e
instanceof DBErrors
.UnexpectedResult
, e
.name
);
1739 it('fails validation', async
function () {
1742 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1743 assert
.fail(noExpectedException
);
1745 assert(e
instanceof DBErrors
.DataValidation
);
1748 }); // verificationUpdate
1750 describe('verificationValidated', function () {
1751 it('success', async
function() {
1757 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1758 await db
.verificationValidated(dbCtx
, verificationId
);
1760 it('failure', async
function () {
1766 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1768 await db
.verificationValidated(dbCtx
, verificationId
);
1769 assert
.fail(noExpectedException
);
1771 assert(e
instanceof DBErrors
.UnexpectedResult
);
1774 }); // verificationValidated
1776 }); // DatabasePostgres