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');
231 sinon
.stub(db
, '_currentSchema').resolves(db
.schemaVersionsSupported
.max
);
232 sinon
.stub(db
.db
, 'one').resolves(db
.schemaVersionsSupported
.max
);
233 await db
.initialize();
235 it('covers listener', async
function() {
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
);
246 describe('healthCheck', function () {
247 beforeEach(function () {
248 sinon
.stub(db
.db
, 'connect').resolves({
251 serverVersion: '0.0',
255 it('covers', async
function () {
256 const result
= await db
.healthCheck();
257 assert
.deepStrictEqual(result
, { serverVersion: '0.0' });
261 describe('_queryFileHelper', function () {
262 it('covers success', function () {
263 const _queryFile
= db
._queryFileHelper(pgpStub
);
266 it('covers failure', function () {
267 const err
= new Error();
268 pgpStub
.QueryFile
= class {
273 const _queryFile
= db
._queryFileHelper(pgpStub
);
276 assert
.fail(noExpectedException
);
278 assert
.strictEqual(e
, err
);
281 }); // _queryFileHelper
283 describe('_closeConnection', function () {
287 it('success', async
function () {
288 sinon
.stub(db
._pgp
, 'end');
289 await db
._closeConnection();
290 assert(db
._pgp
.end
.called
);
292 it('failure', async
function () {
293 const expected
= new Error();
294 sinon
.stub(db
._pgp
, 'end').throws(expected
);
296 await db
._closeConnection();
297 assert
.fail(noExpectedException
);
299 assert
.deepStrictEqual(e
, expected
);
302 it('covers listener', async
function () {
306 sinon
.stub(db
._pgp
, 'end');
307 await db
._closeConnection();
308 assert(db
._pgp
.end
.called
);
310 }); // _closeConnection
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
);
318 it('success', async
function () {
319 sinon
.stub(db
.db
, 'batch');
320 await db
._purgeTables(true);
321 assert(db
.db
.batch
.called
);
323 it('failure', async
function () {
324 const expected
= new Error();
325 sinon
.stub(db
.db
, 'tx').rejects(expected
);
327 await db
._purgeTables(true);
328 assert
.fail(noExpectedException
);
330 assert
.deepStrictEqual(e
, expected
);
335 describe('_topicChanged', function () {
336 beforeEach(function () {
337 db
.cache
= new Map();
338 sinon
.stub(db
.cache
, 'delete');
343 it('covers', function () {
344 db
._topicChanged('topic-id');
345 assert(db
.cache
.delete.called
);
347 it('ignores ping', function () {
348 db
._topicChanged('ping');
349 assert(!db
.cache
.delete.called
);
353 describe('_listenerEstablished', function () {
354 it('creates cache', function () {
356 db
._listenerEstablished();
357 assert(db
.cache
instanceof Map
);
359 }); // _listenerEstablished
361 describe('_listenerLost', function () {
362 it('removes cache', function () {
363 db
.cache
= new Map();
369 describe('_cacheGet', function () {
371 beforeEach(function () {
374 it('nothing if no cache', function () {
376 const result
= db
._cacheGet(key
);
377 assert
.strictEqual(result
, undefined);
379 it('nothing if no entry', function () {
380 db
.cache
= new Map();
381 const result
= db
._cacheGet(key
);
382 assert
.strictEqual(result
, undefined);
384 it('returns cached entry', function () {
385 db
.cache
= new Map();
389 db
._cacheSet(key
, expected
);
390 const result
= db
._cacheGet(key
);
391 assert
.deepStrictEqual(result
, expected
);
395 describe('_cacheSet', function () {
397 beforeEach(function () {
400 it('covers no cache', function () {
402 db
._cacheSet(key
, 'data');
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
);
413 describe('context', function () {
414 it('covers', async
function () {
415 await db
.context(common
.nop
);
419 describe('transaction', function () {
420 it('covers', async
function () {
421 await db
.transaction(db
.db
, common
.nop
);
425 describe('authenticationSuccess', function () {
427 beforeEach(function () {
428 identifier
= 'username';
430 it('success', async
function () {
436 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
437 await db
.authenticationSuccess(dbCtx
, identifier
);
439 it('failure', async
function() {
445 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
447 await db
.authenticationSuccess(dbCtx
, identifier
);
448 assert
.fail(noExpectedException
);
450 assert(e
instanceof DBErrors
.UnexpectedResult
);
453 }); // authenticationSuccess
455 describe('authenticationGet', function () {
456 let identifier
, credential
;
457 beforeEach(function () {
458 identifier
= 'username';
459 credential
= '$z$foo';
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
);
467 it('failure', async
function() {
468 const expected
= new Error('blah');
469 sinon
.stub(db
.db
, 'oneOrNone').rejects(expected
);
471 await db
.authenticationGet(dbCtx
, identifier
, credential
);
472 assert
.fail(noExpectedException
);
474 assert
.deepStrictEqual(e
, expected
);
477 }); // authenticationGet
479 describe('authenticationUpsert', function () {
480 let identifier
, credential
;
481 beforeEach(function () {
482 identifier
= 'username';
483 credential
= '$z$foo';
485 it('success', async
function () {
491 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
492 await db
.authenticationUpsert(dbCtx
, identifier
, credential
);
494 it('failure', async
function() {
495 credential
= undefined;
501 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
503 await db
.authenticationUpsert(dbCtx
, identifier
, credential
);
504 assert
.fail(noExpectedException
);
506 assert(e
instanceof DBErrors
.UnexpectedResult
);
509 }); // authenticationUpsert
511 describe('subscriptionsByTopicId', function () {
512 it('success', async
function () {
514 sinon
.stub(db
.db
, 'manyOrNone').resolves(expected
);
515 const result
= await db
.subscriptionsByTopicId(dbCtx
, topicUrl
);
516 assert
.deepStrictEqual(result
, expected
);
518 it('failure', async
function () {
519 const expected
= new Error();
520 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
522 await db
.subscriptionsByTopicId(dbCtx
, topicUrl
);
523 assert
.fail(noExpectedException
);
525 assert
.deepStrictEqual(e
, expected
);
528 }); // subscriptionsByTopicId
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
);
537 it('failure', async
function () {
538 const expected
= new Error();
539 sinon
.stub(db
.db
, 'one').throws(expected
);
541 await db
.subscriptionCountByTopicUrl(dbCtx
, topicUrl
);
542 assert
.fail(noExpectedException
);
544 assert
.deepStrictEqual(e
, expected
);
547 }); // subscriptionCountByTopicUrl
549 describe('subscriptionDelete', function () {
550 it('success', async
function() {
558 lastInsertRowid: undefined,
561 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
562 const result
= await db
.subscriptionDelete(dbCtx
, callback
, topicId
);
563 assert
.deepStrictEqual(result
, expected
);
565 it('failure', async
function () {
566 const expected
= new Error();
567 sinon
.stub(db
.db
, 'result').throws(expected
);
569 await db
.subscriptionDelete(dbCtx
, callback
, topicId
);
570 assert
.fail(noExpectedException
);
572 assert
.deepStrictEqual(e
, expected
);
575 }); // subscriptionDelete
577 describe('subscriptionDeleteExpired', function () {
578 it('success', async
function () {
586 lastInsertRowid: undefined,
589 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
590 const result
= await db
.subscriptionDeleteExpired(dbCtx
, topicId
);
591 assert
.deepStrictEqual(result
, expected
);
593 it('failure', async
function() {
594 const expected
= new Error();
595 sinon
.stub(db
.db
, 'result').rejects(expected
);
597 await db
.subscriptionDeleteExpired(dbCtx
, topicId
);
598 assert
.fail(noExpectedException
);
600 assert
.deepStrictEqual(e
, expected
);
605 describe('subscriptionDeliveryClaim', function () {
606 it('success', async
function() {
609 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
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
);
617 it('failure', async
function () {
618 const expected
= new Error();
619 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
621 await db
.subscriptionDeliveryClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
622 assert
.fail(noExpectedException
);
624 assert
.deepStrictEqual(e
, expected
);
627 }); // subscriptionDeliveryClaim
629 describe('subscriptionDeliveryClaimById', function () {
630 it('success', async
function() {
633 rows: [{ id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb' }],
638 lastInsertRowid: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
641 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
642 const result
= await db
.subscriptionDeliveryClaimById(dbCtx
, subscriptionId
, claimTimeoutSeconds
, claimant
);
643 assert
.deepStrictEqual(result
, expected
);
645 it('failure', async
function () {
651 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
653 await db
.subscriptionDeliveryClaimById(dbCtx
, callback
, topicId
);
654 assert
.fail(noExpectedException
);
656 assert(e
instanceof DBErrors
.UnexpectedResult
);
659 }); // subscriptionDeliveryClaimById
661 describe('subscriptionDeliveryComplete', function () {
662 it('success', async
function() {
666 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
667 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
);
669 it('failure', async
function () {
673 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult
);
675 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
);
676 assert
.fail(noExpectedException
);
678 assert(e
instanceof DBErrors
.UnexpectedResult
);
681 it('second failure', async
function () {
688 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
690 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
);
691 assert
.fail(noExpectedException
);
693 assert(e
instanceof DBErrors
.UnexpectedResult
);
696 }); // subscriptionDeliveryComplete
698 describe('subscriptionDeliveryGone', function () {
699 it('success', async
function() {
703 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
704 await db
.subscriptionDeliveryGone(dbCtx
, callback
, topicId
);
706 it('failure', async
function () {
710 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
712 await db
.subscriptionDeliveryGone(dbCtx
, callback
, topicId
);
713 assert
.fail(noExpectedException
);
715 assert(e
instanceof DBErrors
.UnexpectedResult
);
718 }); // subscriptionDeliveryGone
720 describe('subscriptionDeliveryIncomplete', function () {
721 it('success', async
function() {
722 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
726 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
727 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
728 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
730 it('success covers default', async
function() {
731 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
735 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
736 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
737 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
);
739 it('failure', async
function () {
740 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
744 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
745 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
747 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
748 assert
.fail(noExpectedException
);
750 assert(e
instanceof DBErrors
.UnexpectedResult
);
753 it('second failure', async
function () {
754 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
761 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
762 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
764 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
765 assert
.fail(noExpectedException
);
767 assert(e
instanceof DBErrors
.UnexpectedResult
);
770 }); // subscriptionDeliveryIncomplete
772 describe('subscriptionGet', function () {
773 it('success', async
function() {
777 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
778 const result
= await db
.subscriptionGet(dbCtx
, callback
, topicId
);
779 assert
.deepStrictEqual(result
, expected
);
781 it('failure', async
function () {
782 const expected
= new Error();
783 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
785 await db
.subscriptionGet(dbCtx
, callback
, topicId
);
786 assert
.fail(noExpectedException
);
788 assert
.deepStrictEqual(e
, expected
);
791 }); // subscriptionGet
793 describe('subscriptionGetById', function () {
794 it('success', async
function() {
798 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
799 const result
= await db
.subscriptionGetById(dbCtx
, subscriptionId
);
800 assert
.deepStrictEqual(result
, expected
);
802 it('failure', async
function () {
803 const expected
= new Error();
804 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
806 await db
.subscriptionGetById(dbCtx
, subscriptionId
);
807 assert
.fail(noExpectedException
);
809 assert
.deepStrictEqual(e
, expected
);
812 }); // subscriptionGetById
814 describe('subscriptionUpsert', function () {
816 beforeEach(function () {
826 it('success', async
function() {
829 rows: [{ id: subscriptionId
}],
834 lastInsertRowid: subscriptionId
,
837 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
838 const result
= await db
.subscriptionUpsert(dbCtx
, data
);
839 assert
.deepStrictEqual(result
, expected
);
841 it('failure', async
function () {
845 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
847 await db
.subscriptionUpsert(dbCtx
, data
);
848 assert
.fail(noExpectedException
);
850 assert(e
instanceof DBErrors
.UnexpectedResult
);
853 }); // subscriptionUpsert
855 describe('subscriptionUpdate', function () {
857 beforeEach(function () {
859 signatureAlgorithm: 'sha256',
862 it('success', async
function() {
868 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
869 await db
.subscriptionUpdate(dbCtx
, data
);
871 it('failure', async
function () {
875 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
877 await db
.subscriptionUpdate(dbCtx
, data
);
878 assert
.fail(noExpectedException
);
880 assert(e
instanceof DBErrors
.UnexpectedResult
);
883 }); // subscriptionUpdate
885 describe('topicDeleted', function () {
886 it('success', async
function() {
890 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
891 await db
.topicDeleted(dbCtx
, topicId
);
893 it('failure', async
function() {
897 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
899 await db
.topicDeleted(dbCtx
, topicId
);
900 assert
.fail(noExpectedException
);
902 assert(e
instanceof DBErrors
.UnexpectedResult
);
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
);
915 it('failure', async
function () {
916 const expected
= new Error();
917 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
919 await db
.topicFetchClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
920 assert
.fail(noExpectedException
);
922 assert
.deepStrictEqual(e
, expected
);
925 }); // topicFetchClaim
927 describe('topicFetchClaimById', function () {
928 it('success', async
function() {
936 lastInsertRowid: undefined,
939 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
940 const result
= await db
.topicFetchClaimById(dbCtx
, topicId
, claimTimeoutSeconds
, claimant
);
941 assert
.deepStrictEqual(result
, expected
);
943 it('failure', async
function () {
944 const expected
= new Error();
945 sinon
.stub(db
.db
, 'result').throws(expected
);
947 await db
.topicFetchClaimById(dbCtx
, topicId
, claimTimeoutSeconds
, claimant
);
948 assert
.fail(noExpectedException
);
950 assert
.deepStrictEqual(e
, expected
);
953 }); // topicFetchClaimById
955 describe('topicFetchComplete', function () {
956 it('success', async
function() {
962 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
963 await db
.topicFetchComplete(dbCtx
, topicId
);
965 it('failure', async
function () {
971 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
973 await db
.topicFetchComplete(dbCtx
, topicId
);
974 assert
.fail(noExpectedException
);
976 assert(e
instanceof DBErrors
.UnexpectedResult
);
979 it('second failure', async
function () {
990 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
992 await db
.topicFetchComplete(dbCtx
, topicId
);
993 assert
.fail(noExpectedException
);
995 assert(e
instanceof DBErrors
.UnexpectedResult
);
998 }); // topicFetchComplete
1000 describe('topicFetchIncomplete', function () {
1001 it('success', async
function() {
1002 const dbOne
= { currentAttempt: 0 };
1015 lastInsertRowid: undefined,
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
);
1023 it('covers defaults', async
function() {
1024 const dbOne
= { currentAttempt: 0 };
1037 lastInsertRowid: undefined,
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
);
1045 it('failure', async
function () {
1046 const dbOne
= { currentAttempt: 0 };
1057 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1058 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1060 await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
1061 assert
.fail(noExpectedException
);
1063 assert(e
instanceof DBErrors
.UnexpectedResult
);
1066 it('second failure', async
function () {
1067 const dbOne
= { currentAttempt: 0 };
1078 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1079 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1081 await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
1082 assert
.fail(noExpectedException
);
1084 assert(e
instanceof DBErrors
.UnexpectedResult
);
1087 }); // topicFetchIncomplete
1089 describe('topicFetchRequested', function () {
1090 it('success', async
function() {
1098 lastInsertRowid: undefined,
1101 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1102 const result
= await db
.topicFetchRequested(dbCtx
, topicId
);
1103 assert
.deepStrictEqual(result
, expected
);
1105 it('failure', async
function () {
1111 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1113 await db
.topicFetchRequested(dbCtx
, topicId
);
1114 assert
.fail(noExpectedException
);
1116 assert(e
instanceof DBErrors
.UnexpectedResult
);
1119 }); // topicFetchRequested
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
);
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
);
1134 it('failure', async
function () {
1135 const expected
= new Error();
1136 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
1138 await db
.topicGetAll(dbCtx
);
1139 assert
.fail(noExpectedException
);
1141 assert
.deepStrictEqual(e
, expected
);
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
);
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
);
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
);
1165 it('failure', async
function () {
1166 const expected
= new Error();
1167 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1169 await db
.topicGetById(dbCtx
, topicId
);
1170 assert
.fail(noExpectedException
);
1172 assert
.deepStrictEqual(e
, expected
);
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
);
1184 it('failure', async
function () {
1185 const expected
= new Error();
1186 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1188 await db
.topicGetByUrl(dbCtx
, topicUrl
);
1189 assert
.fail(noExpectedException
);
1191 assert
.deepStrictEqual(e
, expected
);
1194 }); // topicGetByUrl
1196 describe('topicGetContentById', function () {
1198 beforeEach(function () {
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
);
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
);
1216 it('failure', async
function () {
1217 const expected
= new Error();
1218 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1220 await db
.topicGetContentById(dbCtx
, topicId
);
1221 assert
.fail(noExpectedException
);
1223 assert
.deepStrictEqual(e
, expected
);
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
);
1233 it('covers cached entry', async
function() {
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
);
1243 }); // topicGetContentById
1245 describe('topicPendingDelete', function () {
1246 beforeEach(function () {
1247 sinon
.stub(db
.db
, 'one');
1248 sinon
.stub(db
.db
, 'result');
1250 it('success', async
function () {
1251 db
.db
.one
.onCall(0).resolves({
1254 }).onCall(1).resolves({
1262 db
.db
.result
.resolves(dbResult
);
1263 await db
.topicPendingDelete(dbCtx
, topicId
);
1264 assert(db
.db
.result
.called
);
1266 it('does not delete non-deleted topic', async
function () {
1267 db
.db
.one
.onCall(0).resolves({
1270 }).onCall(1).resolves({
1273 await db
.topicPendingDelete(dbCtx
, topicId
);
1274 assert(!db
.db
.result
.called
);
1276 it('does not delete topic with active subscriptions', async
function () {
1277 db
.db
.one
.onCall(0).resolves({
1280 }).onCall(1).resolves({
1283 await db
.topicPendingDelete(dbCtx
, topicId
);
1284 assert(!db
.db
.result
.called
);
1286 it('covers no deletion', async
function () {
1287 db
.db
.one
.onCall(0).resolves({
1290 }).onCall(1).resolves({
1298 db
.db
.result
.resolves(dbResult
);
1300 await db
.topicPendingDelete(dbCtx
, topicId
);
1301 assert
.fail(noExpectedException
);
1303 assert(e
instanceof DBErrors
.UnexpectedResult
);
1308 describe('topicSet', function () {
1310 beforeEach(function () {
1315 it('success', async
function() {
1318 rows: [{ id: topicId
}],
1323 lastInsertRowid: topicId
,
1326 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1327 const result
= await db
.topicSet(dbCtx
, data
);
1328 assert
.deepStrictEqual(result
, expected
);
1330 it('failure', async
function () {
1336 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1338 await db
.topicSet(dbCtx
, data
);
1339 assert
.fail(noExpectedException
);
1341 assert(e
instanceof DBErrors
.UnexpectedResult
);
1344 it('fails invalid value', async
function () {
1345 sinon
.stub(db
.db
, 'result');
1347 data
.leaseSecondsPreferred
= -100;
1348 await db
.topicSet(dbCtx
, data
);
1349 assert
.fail(noExpectedException
);
1351 assert(e
instanceof DBErrors
.DataValidation
);
1353 assert(!db
.db
.result
.called
);
1355 it('fails invalid values', async
function () {
1356 sinon
.stub(db
.db
, 'result');
1358 data
.leaseSecondsPreferred
= 10;
1359 data
.leaseSecondsMax
= 100;
1360 data
.leaseSecondsMin
= 50;
1361 await db
.topicSet(dbCtx
, data
);
1362 assert
.fail(noExpectedException
);
1364 assert(e
instanceof DBErrors
.DataValidation
);
1366 assert(!db
.db
.result
.called
);
1370 describe('topicSetContent', function () {
1372 beforeEach(function () {
1375 contentType: 'text/plain',
1376 contentHash: 'abc123',
1379 it('success', async
function() {
1387 lastInsertRowid: undefined,
1390 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1391 const result
= await db
.topicSetContent(dbCtx
, data
);
1392 assert
.deepStrictEqual(result
, expected
);
1394 it('failure', async
function () {
1400 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1402 await db
.topicSetContent(dbCtx
, data
);
1403 assert
.fail(noExpectedException
);
1405 assert(e
instanceof DBErrors
.UnexpectedResult
);
1408 }); // topicSetContent
1410 describe('topicUpdate', function () {
1412 beforeEach(function () {
1414 leaseSecondsPreferred: 123,
1415 leaseSecondsMin: 100,
1416 leaseSecondsMax: 1000,
1417 publisherValidationUrl: null,
1418 contentHashAlgorithm: 'sha256',
1421 it('success', async
function() {
1427 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1428 await db
.topicUpdate(dbCtx
, data
);
1430 it('failure', async
function () {
1436 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1438 await db
.topicUpdate(dbCtx
, data
);
1439 assert
.fail(noExpectedException
);
1441 assert(e
instanceof DBErrors
.UnexpectedResult
);
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
);
1455 it('failure', async
function () {
1456 const expected
= new Error();
1457 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
1459 await db
.verificationClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
1460 assert
.fail(noExpectedException
);
1462 assert
.deepStrictEqual(e
, expected
);
1465 }); // verificationClaim
1467 describe('verificationClaimById', function () {
1468 it('success', async
function() {
1471 rows: [ { id: verificationId
} ],
1476 lastInsertRowid: verificationId
,
1479 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1480 const result
= await db
.verificationClaimById(dbCtx
, verificationId
, claimTimeoutSeconds
, claimant
);
1481 assert
.deepStrictEqual(result
, expected
);
1483 it('failure', async
function () {
1484 const expected
= new Error();
1485 sinon
.stub(db
.db
, 'result').throws(expected
);
1487 await db
.verificationClaimById(dbCtx
, verificationId
, claimTimeoutSeconds
, claimant
);
1488 assert
.fail(noExpectedException
);
1490 assert
.deepStrictEqual(e
, expected
);
1493 }); // verificationClaimById
1495 describe('verificationComplete', function () {
1496 it('success', async
function() {
1502 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1503 await db
.verificationComplete(dbCtx
, verificationId
, callback
, topicId
);
1505 it('failure', async
function () {
1511 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1513 await db
.verificationComplete(dbCtx
, verificationId
, callback
, topicId
);
1514 assert
.fail(noExpectedException
);
1516 assert(e
instanceof DBErrors
.UnexpectedResult
);
1519 }); // verificationComplete
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
);
1529 it('failure', async
function () {
1530 const expected
= new Error();
1531 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1533 await db
.verificationGetById(dbCtx
, verificationId
);
1534 assert
.fail(noExpectedException
);
1536 assert
.deepStrictEqual(e
, expected
);
1539 }); // verificationGetById
1541 describe('verificationIncomplete', function () {
1542 it('success', async
function() {
1543 const dbOne
= { attempts: 0 };
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
);
1558 it('covers defaults', async
function() {
1559 const dbOne
= { attempts: 0 };
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
);
1574 it('failure', async
function () {
1575 const dbOne
= { attempts: 0 };
1586 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1587 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1589 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1590 assert
.fail(noExpectedException
);
1592 assert(e
instanceof DBErrors
.UnexpectedResult
);
1595 it('second failure', async
function () {
1596 const dbOne
= { attempts: 0 };
1607 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1608 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1610 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1611 assert
.fail(noExpectedException
);
1613 assert(e
instanceof DBErrors
.UnexpectedResult
);
1616 }); // verificationIncomplete
1618 describe('verificationInsert', function () {
1620 beforeEach(function () {
1625 isPublisherValidated: true,
1626 leaseSeconds: 86400,
1629 it('success', async
function() {
1632 rows: [{ id: verificationId
}],
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
);
1640 it('failure', async
function () {
1646 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1648 await db
.verificationInsert(dbCtx
, verification
);
1649 assert
.fail(noExpectedException
);
1651 assert(e
instanceof DBErrors
.UnexpectedResult
);
1654 it('fails validation', async
function () {
1655 delete verification
.leaseSeconds
;
1657 await db
.verificationInsert(dbCtx
, verification
);
1658 assert
.fail(noExpectedException
);
1660 assert(e
instanceof DBErrors
.DataValidation
);
1663 }); // verificationInsert
1665 describe('verificationRelease', function () {
1666 it('success', async
function() {
1672 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1673 await db
.verificationRelease(dbCtx
, verificationId
);
1675 it('failure', async
function () {
1681 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1683 await db
.verificationRelease(dbCtx
, verificationId
);
1684 assert
.fail(noExpectedException
);
1686 assert(e
instanceof DBErrors
.UnexpectedResult
);
1689 }); // verificationRelease
1691 describe('verificationUpdate', function () {
1693 beforeEach(function () {
1696 isPublisherValidated: true,
1699 it('success', async
function() {
1705 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1706 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1708 it('failure', async
function () {
1714 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1716 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1717 assert
.fail(noExpectedException
);
1719 assert(e
instanceof DBErrors
.UnexpectedResult
, e
.name
);
1722 it('fails validation', async
function () {
1725 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1726 assert
.fail(noExpectedException
);
1728 assert(e
instanceof DBErrors
.DataValidation
);
1731 }); // verificationUpdate
1733 describe('verificationValidated', function () {
1734 it('success', async
function() {
1740 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1741 await db
.verificationValidated(dbCtx
, verificationId
);
1743 it('failure', async
function () {
1749 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1751 await db
.verificationValidated(dbCtx
, verificationId
);
1752 assert
.fail(noExpectedException
);
1754 assert(e
instanceof DBErrors
.UnexpectedResult
);
1757 }); // verificationValidated
1759 }); // DatabasePostgres