1 /* eslint-disable sonarjs/no-identical-functions */
3 /* eslint-disable sonarjs/no-duplicate-string */
6 /* This provides implementation coverage, stubbing parts of better-sqlite3. */
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/sqlite');
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('DatabaseSQLite', function () {
22 let dbCtx
, claimant
, claimTimeoutSeconds
, callback
, subscriptionId
, topicId
, verificationId
;
23 let topicUrl
, leaseSeconds
, secret
, httpRemoteAddr
, httpFrom
, retryDelays
, wanted
;
25 options
= new Config('test');
26 options
.db
.connectionString
= 'sqlite://:memory:';
27 db
= new DB(stubLogger
, options
);
29 beforeEach(function () {
32 claimant
= '19af19b8-6be3-4a6f-8946-65f5f1ccc5d7';
33 claimTimeoutSeconds
= 300;
34 subscriptionId
= 'fbaf8f19-ed9c-4a21-89ae-98b7005e3bf6';
35 topicUrl
= 'https://example.com/blog';
36 callback
= 'https://example.com/callback?id=123';
37 topicId
= 'c59d4bda-10ad-41d9-99df-4ce8bc331424';
38 verificationId
= '55cd7748-d2d5-11eb-b355-0025905f714a';
42 httpRemoteAddr
= '127.0.0.1';
43 httpFrom
= 'user@example.com';
46 afterEach(function () {
50 // Ensure all interface methods are implemented
51 describe('Implementation', function () {
52 it('implements interface', async
function () {
53 const results
= await Promise
.allSettled(DBStub
._implementation
.map(async (fn
) => {
55 // eslint-disable-next-line security/detect-object-injection
58 assert(!(e
instanceof DBErrors
.NotImplemented
), `${fn} not implemented`);
61 const failures
= results
.filter((x
) => x
.status
=== 'rejected');
62 assert(!failures
.length
, failures
.map((x
) => {
63 x
= x
.reason
.toString();
64 return x
.slice(x
.indexOf(': '));
69 describe('_currentSchema', function () {
70 it('covers', async
function () {
71 const version
= { major: 1, minor: 0, patch: 0 };
72 sinon
.stub(db
.db
, 'prepare').returns({
75 const result
= await db
._currentSchema();
76 assert
.deepStrictEqual(result
, version
);
80 describe('_closeConnection', function () {
81 it('success', async
function () {
82 sinon
.stub(db
.db
, 'close');
83 await db
._closeConnection();
84 assert(db
.db
.close
.called
);
86 it('failure', async
function () {
87 const expected
= new Error();
88 sinon
.stub(db
.db
, 'close').throws(expected
);
90 await db
._closeConnection();
91 assert
.fail(noExpectedException
);
93 assert
.deepStrictEqual(e
, expected
);
96 }); // _closeConnection
98 describe('_purgeTables', function () {
99 beforeEach(function () {
100 sinon
.stub(db
.db
, 'prepare').returns({
104 it('covers not really', async
function () {
105 await db
._purgeTables(false);
106 assert(!db
.db
.prepare
.called
);
108 it('success', async
function () {
109 await db
._purgeTables(true);
110 assert(db
.db
.prepare
.called
);
112 it('failure', async
function () {
113 const expected
= new Error();
114 db
.db
.prepare
.restore();
115 sinon
.stub(db
.db
, 'prepare').throws(expected
);
117 await db
._purgeTables(true);
118 assert
.fail(noExpectedException
);
120 assert
.deepStrictEqual(e
, expected
);
125 describe('_optimize', function () {
127 beforeEach(function () {
128 origOAC
= db
.optimizeAfterChanges
;
129 sinon
.stub(db
.statement
._optimize
, 'all');
130 sinon
.stub(db
.db
, 'pragma');
132 this.afterEach(function () {
133 db
.optimizeAfterChanges
= origOAC
;
135 it('covers', async
function () {
136 db
.optimizeAfterChanges
= 10;
137 db
.changesSinceLastOptimize
= BigInt(20);
138 await db
._optimize();
139 assert(db
.db
.pragma
.called
);
141 it('covers none', async
function () {
142 db
.optimizeAfterChanges
= 0;
143 await db
._optimize();
144 assert(!db
.db
.pragma
.called
);
146 it('covers not enough changes', async
function () {
147 db
.optimizeAfterChanges
= 10;
148 db
.changesSinceLastOptimize
= BigInt(5);
149 await db
._optimize();
150 assert(!db
.db
.pragma
.called
);
154 describe('_deOphidiate', function () {
155 it('covers non-array', function () {
162 const result
= DB
._deOphidiate(obj
);
163 assert
.deepStrictEqual(result
, expected
);
165 it('covers array', function () {
182 const result
= DB
._deOphidiate(rows
);
183 assert
.deepStrictEqual(result
, expected
);
187 describe('_topicDataToNative', function () {
188 it('covers', function () {
189 const now
= new Date();
190 const nowEpoch
= now
.getTime() / 1000;
195 lastPublish: nowEpoch
,
196 contentFetchNextAttempt: nowEpoch
,
197 contentUpdated: nowEpoch
,
198 url: 'https://example.com/',
205 contentFetchNextAttempt: now
,
209 const result
= DB
._topicDataToNative(topic
);
210 assert
.deepStrictEqual(result
, expected
);
212 it('covers empty', function () {
213 const topic
= undefined;
214 const result
= DB
._topicDataToNative(topic
);
215 assert
.deepStrictEqual(result
, topic
);
217 }); // _topicDataToNative
219 describe('healthCheck', function () {
221 beforeEach(function () {
224 afterEach(function () {
227 it('covers', function () {
230 it('covers failure', function () {
231 db
.db
= { open: false };
234 assert
.fail(noExpectedException
);
236 assert(e
instanceof DBErrors
.UnexpectedResult
);
241 describe('context', function () {
242 it('covers', async
function () {
243 await db
.context(common
.nop
);
247 describe('transaction', function () {
248 it('covers', async
function () {
249 await db
.transaction(db
.db
, common
.nop
);
251 it('covers no context', async
function () {
252 await db
.transaction(undefined, common
.nop
);
256 describe('authenticationSuccess', function () {
258 beforeEach(function () {
259 identifier
= 'username';
261 it('success', async
function() {
264 lastInsertRowid: undefined,
266 sinon
.stub(db
.statement
.authenticationSuccess
, 'run').returns(dbResult
);
267 await db
.authenticationSuccess(dbCtx
, identifier
);
269 it('failure', async
function () {
272 lastInsertRowid: undefined,
274 sinon
.stub(db
.statement
.authenticationSuccess
, 'run').returns(dbResult
);
276 await db
.authenticationSuccess(dbCtx
, identifier
);
277 assert
.fail(noExpectedException
);
279 assert(e
instanceof DBErrors
.UnexpectedResult
);
282 }); // authenticationSuccess
284 describe('authenticationGet', function () {
285 let identifier
, credential
;
286 beforeEach(function () {
287 identifier
= 'username';
288 credential
= '$z$foo';
290 it('success', async
function() {
295 sinon
.stub(db
.statement
.authenticationGet
, 'get').returns(expected
);
296 const result
= await db
.authenticationGet(dbCtx
, identifier
);
297 assert
.deepStrictEqual(result
, expected
);
299 it('failure', async
function () {
300 const expected
= new Error();
301 sinon
.stub(db
.statement
.authenticationGet
, 'get').throws(expected
);
303 await db
.authenticationGet(dbCtx
, identifier
);
304 assert
.fail(noExpectedException
);
306 assert
.deepStrictEqual(e
, expected
);
309 }); // authenticationGet
311 describe('authenticationUpsert', function () {
312 let identifier
, credential
;
313 beforeEach(function () {
314 identifier
= 'username';
315 credential
= '$z$foo';
317 it('success', async
function() {
320 lastInsertRowid: undefined,
322 sinon
.stub(db
.statement
.authenticationUpsert
, 'run').returns(dbResult
);
323 await db
.authenticationUpsert(dbCtx
, identifier
, credential
);
325 it('failure', async
function () {
328 lastInsertRowid: undefined,
330 sinon
.stub(db
.statement
.authenticationUpsert
, 'run').returns(dbResult
);
332 await db
.authenticationUpsert(dbCtx
, identifier
, credential
);
333 assert
.fail(noExpectedException
);
335 assert(e
instanceof DBErrors
.UnexpectedResult
);
338 }); // authenticationUpsert
340 describe('subscriptionsByTopicId', function () {
341 it('success', async
function () {
342 const expected
= { count: 3 };
343 sinon
.stub(db
.statement
.subscriptionsByTopicId
, 'all').returns(expected
);
344 const result
= await db
.subscriptionsByTopicId(dbCtx
, topicUrl
);
345 assert
.deepStrictEqual(result
, expected
);
347 it('failure', async
function () {
348 const expected
= new Error();
349 sinon
.stub(db
.statement
.subscriptionsByTopicId
, 'all').throws(expected
);
351 await db
.subscriptionsByTopicId(dbCtx
, topicUrl
);
352 assert
.fail(noExpectedException
);
354 assert
.deepStrictEqual(e
, expected
);
357 }); // subscriptionsByTopicId
359 describe('subscriptionCountByTopicUrl', function () {
360 it('success', async
function () {
361 const expected
= { count: 3 };
362 sinon
.stub(db
.statement
.subscriptionCountByTopicUrl
, 'get').returns(expected
);
363 const result
= await db
.subscriptionCountByTopicUrl(dbCtx
, topicUrl
);
364 assert
.deepStrictEqual(result
, expected
);
366 it('failure', async
function () {
367 const expected
= new Error();
368 sinon
.stub(db
.statement
.subscriptionCountByTopicUrl
, 'get').throws(expected
);
370 await db
.subscriptionCountByTopicUrl(dbCtx
, topicUrl
);
371 assert
.fail(noExpectedException
);
373 assert
.deepStrictEqual(e
, expected
);
376 }); // subscriptionCountByTopicUrl
378 describe('subscriptionDelete', function () {
379 it('success', async
function() {
382 lastInsertRowid: undefined,
386 lastInsertRowid: undefined,
388 sinon
.stub(db
.statement
.subscriptionDelete
, 'run').returns(dbResult
);
389 const result
= await db
.subscriptionDelete(dbCtx
, callback
, topicId
);
390 assert
.deepStrictEqual(result
, expected
);
392 it('failure', async
function () {
395 lastInsertRowid: undefined,
397 sinon
.stub(db
.statement
.subscriptionDelete
, 'run').returns(dbResult
);
399 await db
.subscriptionDelete(dbCtx
, callback
, topicId
);
400 assert
.fail(noExpectedException
);
402 assert(e
instanceof DBErrors
.UnexpectedResult
);
405 }); // subscriptionDelete
407 describe('subscriptionDeliveryClaim', function () {
408 it('success', async
function() {
409 const dbAllResult
= [
411 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
414 const dbRunResult
= {
416 lastInsertRowid: undefined,
418 const expected
= ['c2e254c5-aa6e-4a8f-b1a1-e474b07392bb'];
419 sinon
.stub(db
.statement
.subscriptionDeliveryNeeded
, 'all').returns(dbAllResult
);
420 sinon
.stub(db
.statement
.subscriptionDeliveryClaimById
, 'run').returns(dbRunResult
);
421 const result
= await db
.subscriptionDeliveryClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
422 assert
.deepStrictEqual(result
, expected
);
424 it('failure', async
function () {
425 const dbAllResult
= [
427 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
430 const dbRunResult
= {
432 lastInsertRowid: undefined,
434 sinon
.stub(db
.statement
.subscriptionDeliveryNeeded
, 'all').returns(dbAllResult
);
435 sinon
.stub(db
.statement
.subscriptionDeliveryClaimById
, 'run').returns(dbRunResult
);
437 await db
.subscriptionDeliveryClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
438 assert
.fail(noExpectedException
);
440 assert(e
instanceof DBErrors
.UnexpectedResult
);
443 }); // subscriptionDeliveryClaim
445 describe('subscriptionDeliveryClaimById', function () {
446 it('success', async
function() {
449 lastInsertRowid: undefined,
451 sinon
.stub(db
.statement
.subscriptionDeliveryClaimById
, 'run').returns(dbResult
);
452 const result
= await db
.subscriptionDeliveryClaimById(dbCtx
, subscriptionId
, claimTimeoutSeconds
, claimant
);
453 assert
.deepStrictEqual(result
, dbResult
);
455 it('failure', async
function () {
458 lastInsertRowid: undefined,
460 sinon
.stub(db
.statement
.subscriptionDeliveryClaimById
, 'run').returns(dbResult
);
462 await db
.subscriptionDeliveryClaimById(dbCtx
, subscriptionId
, claimTimeoutSeconds
, claimant
);
463 assert
.fail(noExpectedException
);
465 assert(e
instanceof DBErrors
.UnexpectedResult
);
468 }); // subscriptionDeliveryClaimById
470 describe('subscriptionDeliveryComplete', function () {
471 it('success', async
function() {
475 sinon
.stub(db
.statement
.subscriptionDeliverySuccess
, 'run').returns(dbResult
);
476 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult
);
477 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
);
479 it('failure', async
function () {
483 sinon
.stub(db
.statement
.subscriptionDeliverySuccess
, 'run').returns(dbResult
);
484 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult
);
486 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
);
487 assert
.fail(noExpectedException
);
489 assert(e
instanceof DBErrors
.UnexpectedResult
);
492 it('second failure', async
function () {
499 sinon
.stub(db
.statement
.subscriptionDeliverySuccess
, 'run').returns(dbResult0
);
500 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult1
);
502 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
);
503 assert
.fail(noExpectedException
);
505 assert(e
instanceof DBErrors
.UnexpectedResult
);
508 }); // subscriptionDeliveryComplete
510 describe('subscriptionDeliveryGone', function () {
511 it('success', async
function() {
515 sinon
.stub(db
.statement
.subscriptionDelete
, 'run').returns(dbResult
);
516 await db
.subscriptionDeliveryGone(dbCtx
, callback
, topicId
);
518 it('failure', async
function () {
522 sinon
.stub(db
.statement
.subscriptionDelete
, 'run').returns(dbResult
);
524 await db
.subscriptionDeliveryGone(dbCtx
, callback
, topicId
);
525 assert
.fail(noExpectedException
);
527 assert(e
instanceof DBErrors
.UnexpectedResult
);
530 }); // subscriptionDeliveryGone
532 describe('subscriptionDeliveryIncomplete', function () {
533 it('success', async
function() {
534 const dbGet
= { deliveryAttemptsSinceSuccess: 0 };
538 sinon
.stub(db
.statement
.subscriptionDeliveryAttempts
, 'get').returns(dbGet
);
539 sinon
.stub(db
.statement
.subscriptionDeliveryFailure
, 'run').returns(dbResult
);
540 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult
);
541 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
543 it('success covers default', async
function() {
544 const dbGet
= { deliveryAttemptsSinceSuccess: 0 };
548 sinon
.stub(db
.statement
.subscriptionDeliveryAttempts
, 'get').returns(dbGet
);
549 sinon
.stub(db
.statement
.subscriptionDeliveryFailure
, 'run').returns(dbResult
);
550 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult
);
551 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
);
553 it('failure', async
function () {
554 const dbGet
= { deliveryAttemptsSinceSuccess: 0 };
558 sinon
.stub(db
.statement
.subscriptionDeliveryAttempts
, 'get').returns(dbGet
);
559 sinon
.stub(db
.statement
.subscriptionDeliveryFailure
, 'run').returns(dbResult
);
560 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult
);
562 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
563 assert
.fail(noExpectedException
);
565 assert(e
instanceof DBErrors
.UnexpectedResult
);
568 it('second failure', async
function () {
569 const dbGet
= { deliveryAttemptsSinceSuccess: 0 };
576 sinon
.stub(db
.statement
.subscriptionDeliveryAttempts
, 'get').returns(dbGet
);
577 sinon
.stub(db
.statement
.subscriptionDeliveryFailure
, 'run').returns(dbResult0
);
578 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult1
);
580 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
581 assert
.fail(noExpectedException
);
583 assert(e
instanceof DBErrors
.UnexpectedResult
);
586 }); // subscriptionDeliveryIncomplete
588 describe('subscriptionGet', function () {
589 it('success', async
function() {
593 sinon
.stub(db
.statement
.subscriptionGet
, 'get').returns(expected
);
594 const result
= await db
.subscriptionGet(dbCtx
, callback
, topicId
);
595 assert
.deepStrictEqual(result
, expected
);
597 it('failure', async
function () {
598 const expected
= new Error();
599 sinon
.stub(db
.statement
.subscriptionGet
, 'get').throws(expected
);
601 await db
.subscriptionGet(dbCtx
, callback
, topicId
);
602 assert
.fail(noExpectedException
);
604 assert
.deepStrictEqual(e
, expected
);
607 }); // subscriptionGet
609 describe('subscriptionGetById', function () {
610 it('success', async
function() {
614 sinon
.stub(db
.statement
.subscriptionGetById
, 'get').returns(expected
);
615 const result
= await db
.subscriptionGetById(dbCtx
, subscriptionId
);
616 assert
.deepStrictEqual(result
, expected
);
618 it('failure', async
function () {
619 const expected
= new Error();
620 sinon
.stub(db
.statement
.subscriptionGetById
, 'get').throws(expected
);
622 await db
.subscriptionGetById(dbCtx
, subscriptionId
);
623 assert
.fail(noExpectedException
);
625 assert
.deepStrictEqual(e
, expected
);
628 }); // subscriptionGetById
630 describe('subscriptionUpdate', function () {
632 beforeEach(function () {
635 signatureAlgorithm: 'sha256',
638 it('success', async
function() {
641 lastInsertRowid: subscriptionId
,
643 sinon
.stub(db
.statement
.subscriptionUpdate
, 'run').returns(dbResult
);
644 await db
.subscriptionUpdate(dbCtx
, data
);
646 it('failure', async
function () {
650 sinon
.stub(db
.statement
.subscriptionUpdate
, 'run').returns(dbResult
);
652 await db
.subscriptionUpdate(dbCtx
, data
);
653 assert
.fail(noExpectedException
);
655 assert(e
instanceof DBErrors
.UnexpectedResult
, e
);
658 }); // subscriptionUpdate
660 describe('subscriptionUpsert', function () {
662 beforeEach(function () {
672 it('success', async
function() {
675 lastInsertRowid: subscriptionId
,
679 lastInsertRowid: subscriptionId
,
681 sinon
.stub(db
.statement
.subscriptionUpsert
, 'run').returns(dbResult
);
682 const result
= await db
.subscriptionUpsert(dbCtx
, data
);
683 assert
.deepStrictEqual(result
, expected
);
685 it('failure', async
function () {
689 sinon
.stub(db
.statement
.subscriptionUpsert
, 'run').returns(dbResult
);
691 await db
.subscriptionUpsert(dbCtx
, data
);
692 assert
.fail(noExpectedException
);
694 assert(e
instanceof DBErrors
.UnexpectedResult
);
697 }); // subscriptionUpsert
699 describe('topicDeleted', function () {
700 it('success', async
function () {
701 sinon
.stub(db
.statement
.topicDeleted
, 'run').returns({ changes: 1 });
702 await db
.topicDeleted(dbCtx
, { topicId
});
704 it('failure', async
function () {
705 sinon
.stub(db
.statement
.topicDeleted
, 'run').returns({ changes: 0 });
707 await db
.topicDeleted(dbCtx
, { topicId
});
708 assert
.fail(noExpectedException
);
710 assert(e
instanceof DBErrors
.UnexpectedResult
);
715 describe('topicFetchClaim', function () {
716 it('success', async
function() {
717 const dbAll
= [{ id: topicId
}];
721 const expected
= [topicId
];
722 sinon
.stub(db
.statement
.topicContentFetchNeeded
, 'all').returns(dbAll
);
723 sinon
.stub(db
.statement
.topicContentFetchClaimById
, 'run').returns(dbResult
);
724 const result
= await db
.topicFetchClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
725 assert
.deepStrictEqual(result
, expected
);
727 it('failure', async
function () {
728 const dbAll
= [{ id: topicId
}];
732 sinon
.stub(db
.statement
.topicContentFetchNeeded
, 'all').returns(dbAll
);
733 sinon
.stub(db
.statement
.topicContentFetchClaimById
, 'run').returns(dbResult
);
735 await db
.topicFetchClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
736 assert
.fail(noExpectedException
);
738 assert(e
instanceof DBErrors
.UnexpectedResult
);
741 }); // topicFetchClaim
743 describe('topicFetchClaimById', function () {
744 it('success', async
function() {
747 lastInsertRowid: undefined,
749 sinon
.stub(db
.statement
.topicContentFetchClaimById
, 'run').returns(expected
);
750 const result
= await db
.topicFetchClaimById(dbCtx
, topicId
, claimTimeoutSeconds
, claimant
);
751 assert
.deepStrictEqual(result
, expected
);
753 it('failure', async
function () {
756 lastInsertRowid: undefined,
758 sinon
.stub(db
.statement
.topicContentFetchClaimById
, 'run').returns(expected
);
760 await db
.topicFetchClaimById(dbCtx
, topicId
, claimTimeoutSeconds
, claimant
);
761 assert
.fail(noExpectedException
);
763 assert(e
instanceof DBErrors
.UnexpectedResult
);
766 }); // topicFetchClaimById
768 describe('topicFetchComplete', function () {
769 it('success', async
function() {
772 lastInsertRowid: undefined,
774 sinon
.stub(db
.statement
.topicAttemptsReset
, 'run').returns(dbResult
);
775 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult
);
776 await db
.topicFetchComplete(dbCtx
, topicId
);
778 it('failure', async
function () {
781 lastInsertRowid: undefined,
783 sinon
.stub(db
.statement
.topicAttemptsReset
, 'run').returns(dbResult
);
784 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult
);
786 await db
.topicFetchComplete(dbCtx
, topicId
);
787 assert
.fail(noExpectedException
);
789 assert(e
instanceof DBErrors
.UnexpectedResult
);
792 it('second failure', async
function () {
795 lastInsertRowid: undefined,
799 lastInsertRowid: undefined,
801 sinon
.stub(db
.statement
.topicAttemptsReset
, 'run').returns(dbResult0
);
802 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult1
);
804 await db
.topicFetchComplete(dbCtx
, topicId
);
805 assert
.fail(noExpectedException
);
807 assert(e
instanceof DBErrors
.UnexpectedResult
);
810 }); // topicFetchComplete
812 describe('topicFetchIncomplete', function () {
813 it('success', async
function() {
814 const dbGet
= { currentAttempt: 0 };
817 lastInsertRowid: undefined,
821 lastInsertRowid: undefined,
825 lastInsertRowid: undefined,
827 sinon
.stub(db
.statement
.topicAttempts
, 'get').returns(dbGet
);
828 sinon
.stub(db
.statement
.topicAttemptsIncrement
, 'run').returns(dbResult0
);
829 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult1
);
830 const result
= await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
831 assert
.deepStrictEqual(result
, expected
);
833 it('covers defaults', async
function() {
834 const dbGet
= { currentAttempt: 0 };
837 lastInsertRowid: undefined,
841 lastInsertRowid: undefined,
845 lastInsertRowid: undefined,
847 sinon
.stub(db
.statement
.topicAttempts
, 'get').returns(dbGet
);
848 sinon
.stub(db
.statement
.topicAttemptsIncrement
, 'run').returns(dbResult0
);
849 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult1
);
850 const result
= await db
.topicFetchIncomplete(dbCtx
, topicId
);
851 assert
.deepStrictEqual(result
, expected
);
853 it('failure', async
function () {
854 const dbGet
= { currentAttempt: 0 };
857 lastInsertRowid: undefined,
861 lastInsertRowid: undefined,
863 sinon
.stub(db
.statement
.topicAttempts
, 'get').returns(dbGet
);
864 sinon
.stub(db
.statement
.topicAttemptsIncrement
, 'run').returns(dbResult0
);
865 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult1
);
867 await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
868 assert
.fail(noExpectedException
);
870 assert(e
instanceof DBErrors
.UnexpectedResult
);
873 it('second failure', async
function () {
874 const dbGet
= { currentAttempt: 0 };
877 lastInsertRowid: undefined,
881 lastInsertRowid: undefined,
883 sinon
.stub(db
.statement
.topicAttempts
, 'get').returns(dbGet
);
884 sinon
.stub(db
.statement
.topicAttemptsIncrement
, 'run').returns(dbResult0
);
885 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult1
);
887 await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
888 assert
.fail(noExpectedException
);
890 assert(e
instanceof DBErrors
.UnexpectedResult
);
893 }); // topicFetchIncomplete
895 describe('topicFetchRequested', function () {
896 it('success', async
function() {
899 lastInsertRowid: undefined,
903 lastInsertRowid: undefined,
905 sinon
.stub(db
.statement
.topicContentFetchRequested
, 'run').returns(dbResult
);
906 const result
= await db
.topicFetchRequested(dbCtx
, topicId
);
907 assert
.deepStrictEqual(result
, expected
);
909 it('failure', async
function () {
912 lastInsertRowid: undefined,
914 sinon
.stub(db
.statement
.topicContentFetchRequested
, 'run').returns(dbResult
);
916 await db
.topicFetchRequested(dbCtx
, topicId
);
917 assert
.fail(noExpectedException
);
919 assert(e
instanceof DBErrors
.UnexpectedResult
);
922 }); // topicFetchRequested
924 describe('topicGetAll', function () {
925 it('success', async
function() {
926 const expected
= [{ id: topicId
}];
927 sinon
.stub(db
.statement
.topicGetInfoAll
, 'all').returns(expected
);
928 const result
= await db
.topicGetAll(dbCtx
);
929 assert
.deepStrictEqual(result
, expected
);
931 it('covers none', async
function() {
932 const expected
= undefined;
933 sinon
.stub(db
.statement
.topicGetInfoAll
, 'all').returns(expected
);
934 const result
= await db
.topicGetAll(dbCtx
);
935 assert
.deepStrictEqual(result
, expected
);
937 it('failure', async
function () {
938 const expected
= new Error();
939 sinon
.stub(db
.statement
.topicGetInfoAll
, 'all').throws(expected
);
941 await db
.topicGetAll(dbCtx
);
942 assert
.fail(noExpectedException
);
944 assert
.deepStrictEqual(e
, expected
);
949 describe('topicGetById', function () {
950 it('success', async
function() {
951 const expected
= { id: topicId
};
952 sinon
.stub(db
.statement
.topicGetById
, 'get').returns(expected
);
953 const result
= await db
.topicGetById(dbCtx
, topicId
);
954 assert
.deepStrictEqual(result
, expected
);
956 it('covers no defaults', async
function () {
957 const expected
= { id: topicId
};
958 sinon
.stub(db
.statement
.topicGetById
, 'get').returns(expected
);
959 const result
= await db
.topicGetById(dbCtx
, topicId
, false);
960 assert
.deepStrictEqual(result
, expected
);
962 it('covers default', async
function() {
963 const expected
= undefined;
964 sinon
.stub(db
.statement
.topicGetById
, 'get').returns(expected
);
965 const result
= await db
.topicGetById(dbCtx
, topicId
);
966 assert
.deepStrictEqual(result
, expected
);
968 it('failure', async
function () {
969 const expected
= new Error();
970 sinon
.stub(db
.statement
.topicGetById
, 'get').throws(expected
);
972 await db
.topicGetById(dbCtx
, topicId
);
973 assert
.fail(noExpectedException
);
975 assert
.deepStrictEqual(e
, expected
);
980 describe('topicGetByUrl', function () {
981 it('success', async
function() {
983 sinon
.stub(db
.statement
.topicGetByUrl
, 'get').returns(expected
);
984 const result
= await db
.topicGetByUrl(dbCtx
, topicUrl
);
985 assert
.deepStrictEqual(result
, expected
);
987 it('failure', async
function () {
988 const expected
= new Error();
989 sinon
.stub(db
.statement
.topicGetByUrl
, 'get').throws(expected
);
991 await db
.topicGetByUrl(dbCtx
, topicUrl
);
992 assert
.fail(noExpectedException
);
994 assert
.deepStrictEqual(e
, expected
);
999 describe('topicGetContentById', function () {
1000 it('success', async
function() {
1001 const expected
= { id: topicId
};
1002 sinon
.stub(db
.statement
.topicGetContentById
, 'get').returns(expected
);
1003 const result
= await db
.topicGetContentById(dbCtx
, topicId
);
1004 assert
.deepStrictEqual(result
, expected
);
1006 it('covers default', async
function() {
1007 const expected
= undefined;
1008 sinon
.stub(db
.statement
.topicGetContentById
, 'get').returns(expected
);
1009 const result
= await db
.topicGetContentById(dbCtx
, topicId
);
1010 assert
.deepStrictEqual(result
, expected
);
1012 it('failure', async
function () {
1013 const expected
= new Error();
1014 sinon
.stub(db
.statement
.topicGetContentById
, 'get').throws(expected
);
1016 await db
.topicGetContentById(dbCtx
, topicId
);
1017 assert
.fail(noExpectedException
);
1019 assert
.deepStrictEqual(e
, expected
);
1022 }); // topicGetContentById
1024 describe('topicSet', function () {
1026 beforeEach(function () {
1031 it('success', async
function() {
1034 lastInsertRowid: topicId
,
1038 lastInsertRowid: topicId
,
1040 sinon
.stub(db
.statement
.topicUpsert
, 'run').returns(dbResult
);
1041 const result
= await db
.topicSet(dbCtx
, data
);
1042 assert
.deepStrictEqual(result
, expected
);
1044 it('failure', async
function () {
1047 lastInsertRowid: undefined,
1049 sinon
.stub(db
.statement
.topicUpsert
, 'run').returns(dbResult
);
1051 await db
.topicSet(dbCtx
, data
);
1052 assert
.fail(noExpectedException
);
1054 assert(e
instanceof DBErrors
.UnexpectedResult
);
1057 it('fails invalid value', async
function () {
1058 sinon
.stub(db
.statement
.topicUpsert
, 'run');
1060 data
.leaseSecondsPreferred
= -100;
1061 await db
.topicSet(dbCtx
, data
);
1062 assert
.fail(noExpectedException
);
1064 assert(e
instanceof DBErrors
.DataValidation
);
1066 assert(!db
.statement
.topicUpsert
.run
.called
);
1068 it('fails invalid values', async
function () {
1069 sinon
.stub(db
.statement
.topicUpsert
, 'run');
1071 data
.leaseSecondsPreferred
= 10;
1072 data
.leaseSecondsMax
= 100;
1073 data
.leaseSecondsMin
= 50;
1074 await db
.topicSet(dbCtx
, data
);
1075 assert
.fail(noExpectedException
);
1077 assert(e
instanceof DBErrors
.DataValidation
);
1079 assert(!db
.statement
.topicUpsert
.run
.called
);
1083 describe('topicSetContent', function () {
1085 beforeEach(function () {
1088 contentType: 'text/plain',
1089 contentHash: 'abc123',
1092 it('success', async
function() {
1095 lastInsertRowid: undefined,
1099 lastInsertRowid: undefined,
1101 sinon
.stub(db
.statement
.topicSetContent
, 'run').returns(dbResult
);
1102 const result
= await db
.topicSetContent(dbCtx
, data
);
1103 assert
.deepStrictEqual(result
, expected
);
1105 it('failure', async
function () {
1108 lastInsertRowid: undefined,
1110 sinon
.stub(db
.statement
.topicSetContent
, 'run').returns(dbResult
);
1112 await db
.topicSetContent(dbCtx
, data
);
1113 assert
.fail(noExpectedException
);
1115 assert(e
instanceof DBErrors
.UnexpectedResult
);
1118 }); // topicSetContent
1120 describe('topicUpdate', function () {
1122 beforeEach(function () {
1125 leaseSecondsPreferred: 9999,
1126 leaseSecondsMax: 99999,
1127 leaseSecondsMin: 999,
1128 publisherValidationUrl: null,
1129 contentHashAlgorithm: 'sha256',
1132 it('success', async
function() {
1135 lastInsertRowid: topicId
,
1137 sinon
.stub(db
.statement
.topicUpdate
, 'run').returns(dbResult
);
1138 await db
.topicUpdate(dbCtx
, data
);
1140 it('failure', async
function () {
1143 lastInsertRowid: undefined,
1145 sinon
.stub(db
.statement
.topicUpdate
, 'run').returns(dbResult
);
1147 await db
.topicUpdate(dbCtx
, data
);
1148 assert
.fail(noExpectedException
);
1150 assert(e
instanceof DBErrors
.UnexpectedResult
, e
);
1153 it('fails invalid value', async
function () {
1154 sinon
.stub(db
.statement
.topicUpdate
, 'run');
1156 data
.leaseSecondsPreferred
= -100;
1157 await db
.topicUpdate(dbCtx
, data
);
1158 assert
.fail(noExpectedException
);
1160 assert(e
instanceof DBErrors
.DataValidation
, e
);
1162 assert(!db
.statement
.topicUpdate
.run
.called
);
1164 it('fails invalid values', async
function () {
1165 sinon
.stub(db
.statement
.topicUpdate
, 'run');
1167 data
.leaseSecondsPreferred
= 10;
1168 data
.leaseSecondsMax
= 100;
1169 data
.leaseSecondsMin
= 50;
1170 await db
.topicUpdate(dbCtx
, data
);
1171 assert
.fail(noExpectedException
);
1173 assert(e
instanceof DBErrors
.DataValidation
, e
);
1175 assert(!db
.statement
.topicUpdate
.run
.called
);
1179 describe('verificationClaim', function () {
1180 it('success', async
function() {
1181 const dbAll
= [{ id: verificationId
}];
1184 lastInsertRowid: undefined,
1186 const expected
= [verificationId
];
1187 sinon
.stub(db
.statement
.verificationNeeded
, 'all').returns(dbAll
);
1188 sinon
.stub(db
.statement
.verificationClaimById
, 'run').returns(dbRun
);
1189 const result
= await db
.verificationClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
1190 assert
.deepStrictEqual(result
, expected
);
1192 it('failure', async
function () {
1193 const dbAll
= [{ id: verificationId
}];
1196 lastInsertRowid: undefined,
1198 sinon
.stub(db
.statement
.verificationNeeded
, 'all').returns(dbAll
);
1199 sinon
.stub(db
.statement
.verificationClaimById
, 'run').returns(dbRun
);
1201 await db
.verificationClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
1202 assert
.fail(noExpectedException
);
1204 assert(e
instanceof DBErrors
.UnexpectedResult
);
1207 }); // verificationClaim
1209 describe('verificationClaimById', function () {
1210 it('success', async
function() {
1213 lastInsertRowid: undefined,
1215 sinon
.stub(db
.statement
.verificationClaimById
, 'run').returns(dbRun
);
1216 const result
= await db
.verificationClaimById(dbCtx
, verificationId
, claimTimeoutSeconds
, claimant
);
1217 assert
.deepStrictEqual(result
, dbRun
);
1219 it('failure', async
function () {
1222 lastInsertRowid: undefined,
1224 sinon
.stub(db
.statement
.verificationClaimById
, 'run').returns(dbRun
);
1226 await db
.verificationClaimById(dbCtx
, verificationId
, claimTimeoutSeconds
, claimant
);
1227 assert
.fail(noExpectedException
);
1229 assert(e
instanceof DBErrors
.UnexpectedResult
);
1232 }); // verificationClaimById
1234 describe('verificationComplete', function () {
1235 it('success', async
function() {
1238 lastInsertRowid: undefined,
1240 sinon
.stub(db
.statement
.verificationScrub
, 'run').returns(dbResult
);
1241 await db
.verificationComplete(dbCtx
, verificationId
, callback
, topicId
);
1243 it('failure', async
function () {
1246 lastInsertRowid: undefined,
1248 sinon
.stub(db
.statement
.verificationScrub
, 'run').returns(dbResult
);
1250 await db
.verificationComplete(dbCtx
, verificationId
, callback
, topicId
);
1251 assert
.fail(noExpectedException
);
1253 assert(e
instanceof DBErrors
.UnexpectedResult
);
1256 }); // verificationComplete
1258 describe('verificationGetById', function () {
1259 it('success', async
function() {
1260 const dbOneOrNone
= { id: verificationId
, isPublisherValidated: 1 };
1261 const expected
= { id: verificationId
, isPublisherValidated: true };
1262 sinon
.stub(db
.statement
.verificationGetById
, 'get').returns(dbOneOrNone
);
1263 const result
= await db
.verificationGetById(dbCtx
, verificationId
);
1264 assert
.deepStrictEqual(result
, expected
);
1266 it('failure', async
function () {
1267 const expected
= new Error();
1268 sinon
.stub(db
.statement
.verificationGetById
, 'get').throws(expected
);
1270 await db
.verificationGetById(dbCtx
, verificationId
);
1271 assert
.fail(noExpectedException
);
1273 assert
.deepStrictEqual(e
, expected
);
1276 }); // verificationGetById
1278 describe('verificationIncomplete', function () {
1279 it('success', async
function() {
1280 const dbOne
= { attempts: 0 };
1283 lastInsertRowid: undefined,
1287 lastInsertRowid: undefined,
1289 sinon
.stub(db
.statement
.verificationAttempts
, 'get').returns(dbOne
);
1290 sinon
.stub(db
.statement
.verificationAttemptsIncrement
, 'run').returns(dbResult0
);
1291 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult1
);
1292 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1294 it('covers defaults', async
function() {
1295 const dbOne
= { attempts: 0 };
1298 lastInsertRowid: undefined,
1302 lastInsertRowid: undefined,
1304 sinon
.stub(db
.statement
.verificationAttempts
, 'get').returns(dbOne
);
1305 sinon
.stub(db
.statement
.verificationAttemptsIncrement
, 'run').returns(dbResult0
);
1306 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult1
);
1307 await db
.verificationIncomplete(dbCtx
, verificationId
);
1309 it('failure', async
function () {
1310 const dbOne
= { attempts: 0 };
1313 lastInsertRowid: undefined,
1317 lastInsertRowid: undefined,
1319 sinon
.stub(db
.statement
.verificationAttempts
, 'get').returns(dbOne
);
1320 sinon
.stub(db
.statement
.verificationAttemptsIncrement
, 'run').returns(dbResult0
);
1321 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult1
);
1323 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1324 assert
.fail(noExpectedException
);
1326 assert(e
instanceof DBErrors
.UnexpectedResult
);
1329 it('second failure', async
function () {
1330 const dbOne
= { attempts: 0 };
1333 lastInsertRowid: undefined,
1337 lastInsertRowid: undefined,
1339 sinon
.stub(db
.statement
.verificationAttempts
, 'get').returns(dbOne
);
1340 sinon
.stub(db
.statement
.verificationAttemptsIncrement
, 'run').returns(dbResult0
);
1341 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult1
);
1343 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1344 assert
.fail(noExpectedException
);
1346 assert(e
instanceof DBErrors
.UnexpectedResult
);
1349 }); // verificationIncomplete
1351 describe('_verificationDataToEngine', function () {
1352 it('covers no data', function () {
1353 DB
._verificationDataToEngine();
1355 it('covers true', function () {
1357 isPublisherValidated: true,
1359 DB
._verificationDataToEngine(data
);
1360 assert
.strictEqual(data
.isPublisherValidated
, 1);
1362 it('covers false', function () {
1364 isPublisherValidated: false,
1366 DB
._verificationDataToEngine(data
);
1367 assert
.strictEqual(data
.isPublisherValidated
, 0);
1369 }) // _verificationDataToEngine
1371 describe('verificationInsert', function () {
1373 beforeEach(function () {
1378 isPublisherValidated: true,
1379 leaseSeconds: 86400,
1382 it('success', async
function() {
1385 lastInsertRowid: verificationId
,
1387 const expected
= verificationId
;
1388 sinon
.stub(db
.statement
.verificationInsert
, 'run').returns(dbResult
);
1389 const result
= await db
.verificationInsert(dbCtx
, verification
);
1390 assert
.deepStrictEqual(result
, expected
);
1392 it('failure', async
function () {
1395 lastInsertRowid: undefined,
1397 sinon
.stub(db
.statement
.verificationInsert
, 'run').returns(dbResult
);
1399 await db
.verificationInsert(dbCtx
, verification
);
1400 assert
.fail(noExpectedException
);
1402 assert(e
instanceof DBErrors
.UnexpectedResult
);
1405 it('fails validation', async
function () {
1406 delete verification
.leaseSeconds
;
1408 await db
.verificationInsert(dbCtx
, verification
);
1409 assert
.fail(noExpectedException
);
1411 assert(e
instanceof DBErrors
.DataValidation
);
1414 }); // verificationInsert
1416 describe('verificationRelease', function () {
1417 it('success', async
function() {
1420 lastInsertRowid: undefined,
1422 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult
);
1423 await db
.verificationRelease(dbCtx
, verificationId
);
1425 it('failure', async
function () {
1428 lastInsertRowid: undefined,
1430 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult
);
1432 await db
.verificationRelease(dbCtx
, verificationId
);
1433 assert
.fail(noExpectedException
);
1435 assert(e
instanceof DBErrors
.UnexpectedResult
);
1438 }); // verificationRelease
1440 describe('verificationUpdate', function () {
1442 beforeEach(function () {
1445 isPublisherValidated: true,
1448 it('success', async
function() {
1451 lastInsertRowid: undefined,
1453 sinon
.stub(db
.statement
.verificationUpdate
, 'run').returns(dbResult
);
1454 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1456 it('failure', async
function () {
1459 lastInsertRowid: undefined,
1461 sinon
.stub(db
.statement
.verificationUpdate
, 'run').returns(dbResult
);
1463 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1464 assert
.fail(noExpectedException
);
1466 assert(e
instanceof DBErrors
.UnexpectedResult
, e
.name
);
1469 it('fails validation', async
function () {
1472 await db
.verificationUpdate(dbCtx
, data
);
1473 assert
.fail(noExpectedException
);
1475 assert(e
instanceof DBErrors
.DataValidation
);
1478 }); // verificationUpdate
1480 describe('verificationValidated', function () {
1481 it('success', async
function() {
1484 lastInsertRowid: undefined,
1486 sinon
.stub(db
.statement
.verificationValidate
, 'run').returns(dbResult
);
1487 await db
.verificationValidated(dbCtx
, verificationId
);
1489 it('failure', async
function () {
1492 lastInsertRowid: undefined,
1494 sinon
.stub(db
.statement
.verificationValidate
, 'run').returns(dbResult
);
1496 await db
.verificationValidated(dbCtx
, verificationId
);
1497 assert
.fail(noExpectedException
);
1499 assert(e
instanceof DBErrors
.UnexpectedResult
);
1502 }); // verificationValidated
1504 }); // DatabasePostgres