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
= [{ id: 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('subscriptionDeleteExpired', function () {
408 it('success', async
function () {
411 lastInsertRowid: undefined,
415 lastInsertRowid: undefined,
417 sinon
.stub(db
.statement
.subscriptionDeleteExpired
, 'run').returns(dbResult
);
418 const result
= await db
.subscriptionDeleteExpired(dbCtx
, topicId
);
419 assert
.deepStrictEqual(result
, expected
);
421 it('failure', async
function () {
422 const expected
= new Error();
423 sinon
.stub(db
.statement
.subscriptionDeleteExpired
, 'run').throws(expected
);
425 await db
.subscriptionDeleteExpired(dbCtx
, topicId
);
426 assert
.fail(noExpectedException
);
428 assert
.deepStrictEqual(e
, expected
);
433 describe('subscriptionDeliveryClaim', function () {
434 it('success', async
function () {
435 const dbAllResult
= [
437 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
440 const dbRunResult
= {
442 lastInsertRowid: undefined,
444 const expected
= ['c2e254c5-aa6e-4a8f-b1a1-e474b07392bb'];
445 sinon
.stub(db
.statement
.subscriptionDeliveryNeeded
, 'all').returns(dbAllResult
);
446 sinon
.stub(db
.statement
.subscriptionDeliveryClaimById
, 'run').returns(dbRunResult
);
447 const result
= await db
.subscriptionDeliveryClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
448 assert
.deepStrictEqual(result
, expected
);
450 it('failure', async
function () {
451 const dbAllResult
= [
453 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
456 const dbRunResult
= {
458 lastInsertRowid: undefined,
460 sinon
.stub(db
.statement
.subscriptionDeliveryNeeded
, 'all').returns(dbAllResult
);
461 sinon
.stub(db
.statement
.subscriptionDeliveryClaimById
, 'run').returns(dbRunResult
);
463 await db
.subscriptionDeliveryClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
464 assert
.fail(noExpectedException
);
466 assert(e
instanceof DBErrors
.UnexpectedResult
);
469 }); // subscriptionDeliveryClaim
471 describe('subscriptionDeliveryClaimById', function () {
472 it('success', async
function() {
475 lastInsertRowid: undefined,
477 sinon
.stub(db
.statement
.subscriptionDeliveryClaimById
, 'run').returns(dbResult
);
478 const result
= await db
.subscriptionDeliveryClaimById(dbCtx
, subscriptionId
, claimTimeoutSeconds
, claimant
);
479 assert
.deepStrictEqual(result
, dbResult
);
481 it('failure', async
function () {
484 lastInsertRowid: undefined,
486 sinon
.stub(db
.statement
.subscriptionDeliveryClaimById
, 'run').returns(dbResult
);
488 await db
.subscriptionDeliveryClaimById(dbCtx
, subscriptionId
, claimTimeoutSeconds
, claimant
);
489 assert
.fail(noExpectedException
);
491 assert(e
instanceof DBErrors
.UnexpectedResult
);
494 }); // subscriptionDeliveryClaimById
496 describe('subscriptionDeliveryComplete', function () {
497 it('success', async
function() {
501 sinon
.stub(db
.statement
.subscriptionDeliverySuccess
, 'run').returns(dbResult
);
502 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult
);
503 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
);
505 it('failure', async
function () {
509 sinon
.stub(db
.statement
.subscriptionDeliverySuccess
, 'run').returns(dbResult
);
510 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult
);
512 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
);
513 assert
.fail(noExpectedException
);
515 assert(e
instanceof DBErrors
.UnexpectedResult
);
518 it('second failure', async
function () {
525 sinon
.stub(db
.statement
.subscriptionDeliverySuccess
, 'run').returns(dbResult0
);
526 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult1
);
528 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
);
529 assert
.fail(noExpectedException
);
531 assert(e
instanceof DBErrors
.UnexpectedResult
);
534 }); // subscriptionDeliveryComplete
536 describe('subscriptionDeliveryGone', function () {
537 it('success', async
function() {
541 sinon
.stub(db
.statement
.subscriptionDelete
, 'run').returns(dbResult
);
542 await db
.subscriptionDeliveryGone(dbCtx
, callback
, topicId
);
544 it('failure', async
function () {
548 sinon
.stub(db
.statement
.subscriptionDelete
, 'run').returns(dbResult
);
550 await db
.subscriptionDeliveryGone(dbCtx
, callback
, topicId
);
551 assert
.fail(noExpectedException
);
553 assert(e
instanceof DBErrors
.UnexpectedResult
);
556 }); // subscriptionDeliveryGone
558 describe('subscriptionDeliveryIncomplete', function () {
559 it('success', async
function() {
560 const dbGet
= { deliveryAttemptsSinceSuccess: 0 };
564 sinon
.stub(db
.statement
.subscriptionDeliveryAttempts
, 'get').returns(dbGet
);
565 sinon
.stub(db
.statement
.subscriptionDeliveryFailure
, 'run').returns(dbResult
);
566 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult
);
567 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
569 it('success covers default', async
function() {
570 const dbGet
= { deliveryAttemptsSinceSuccess: 0 };
574 sinon
.stub(db
.statement
.subscriptionDeliveryAttempts
, 'get').returns(dbGet
);
575 sinon
.stub(db
.statement
.subscriptionDeliveryFailure
, 'run').returns(dbResult
);
576 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult
);
577 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
);
579 it('failure', async
function () {
580 const dbGet
= { deliveryAttemptsSinceSuccess: 0 };
584 sinon
.stub(db
.statement
.subscriptionDeliveryAttempts
, 'get').returns(dbGet
);
585 sinon
.stub(db
.statement
.subscriptionDeliveryFailure
, 'run').returns(dbResult
);
586 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult
);
588 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
589 assert
.fail(noExpectedException
);
591 assert(e
instanceof DBErrors
.UnexpectedResult
);
594 it('second failure', async
function () {
595 const dbGet
= { deliveryAttemptsSinceSuccess: 0 };
602 sinon
.stub(db
.statement
.subscriptionDeliveryAttempts
, 'get').returns(dbGet
);
603 sinon
.stub(db
.statement
.subscriptionDeliveryFailure
, 'run').returns(dbResult0
);
604 sinon
.stub(db
.statement
.subscriptionDeliveryDone
, 'run').returns(dbResult1
);
606 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
607 assert
.fail(noExpectedException
);
609 assert(e
instanceof DBErrors
.UnexpectedResult
);
612 }); // subscriptionDeliveryIncomplete
614 describe('subscriptionGet', function () {
615 it('success', async
function() {
619 sinon
.stub(db
.statement
.subscriptionGet
, 'get').returns(expected
);
620 const result
= await db
.subscriptionGet(dbCtx
, callback
, topicId
);
621 assert
.deepStrictEqual(result
, expected
);
623 it('failure', async
function () {
624 const expected
= new Error();
625 sinon
.stub(db
.statement
.subscriptionGet
, 'get').throws(expected
);
627 await db
.subscriptionGet(dbCtx
, callback
, topicId
);
628 assert
.fail(noExpectedException
);
630 assert
.deepStrictEqual(e
, expected
);
633 }); // subscriptionGet
635 describe('subscriptionGetById', function () {
636 it('success', async
function() {
640 sinon
.stub(db
.statement
.subscriptionGetById
, 'get').returns(expected
);
641 const result
= await db
.subscriptionGetById(dbCtx
, subscriptionId
);
642 assert
.deepStrictEqual(result
, expected
);
644 it('failure', async
function () {
645 const expected
= new Error();
646 sinon
.stub(db
.statement
.subscriptionGetById
, 'get').throws(expected
);
648 await db
.subscriptionGetById(dbCtx
, subscriptionId
);
649 assert
.fail(noExpectedException
);
651 assert
.deepStrictEqual(e
, expected
);
654 }); // subscriptionGetById
656 describe('subscriptionUpdate', function () {
658 beforeEach(function () {
661 signatureAlgorithm: 'sha256',
664 it('success', async
function() {
667 lastInsertRowid: subscriptionId
,
669 sinon
.stub(db
.statement
.subscriptionUpdate
, 'run').returns(dbResult
);
670 await db
.subscriptionUpdate(dbCtx
, data
);
672 it('failure', async
function () {
676 sinon
.stub(db
.statement
.subscriptionUpdate
, 'run').returns(dbResult
);
678 await db
.subscriptionUpdate(dbCtx
, data
);
679 assert
.fail(noExpectedException
);
681 assert(e
instanceof DBErrors
.UnexpectedResult
, e
);
684 }); // subscriptionUpdate
686 describe('subscriptionUpsert', function () {
688 beforeEach(function () {
698 it('success', async
function() {
701 lastInsertRowid: subscriptionId
,
705 lastInsertRowid: subscriptionId
,
707 sinon
.stub(db
.statement
.subscriptionUpsert
, 'run').returns(dbResult
);
708 const result
= await db
.subscriptionUpsert(dbCtx
, data
);
709 assert
.deepStrictEqual(result
, expected
);
711 it('failure', async
function () {
715 sinon
.stub(db
.statement
.subscriptionUpsert
, 'run').returns(dbResult
);
717 await db
.subscriptionUpsert(dbCtx
, data
);
718 assert
.fail(noExpectedException
);
720 assert(e
instanceof DBErrors
.UnexpectedResult
);
723 }); // subscriptionUpsert
725 describe('topicDeleted', function () {
726 it('success', async
function () {
727 sinon
.stub(db
.statement
.topicDeleted
, 'run').returns({ changes: 1 });
728 await db
.topicDeleted(dbCtx
, { topicId
});
730 it('failure', async
function () {
731 sinon
.stub(db
.statement
.topicDeleted
, 'run').returns({ changes: 0 });
733 await db
.topicDeleted(dbCtx
, { topicId
});
734 assert
.fail(noExpectedException
);
736 assert(e
instanceof DBErrors
.UnexpectedResult
);
741 describe('topicFetchClaim', function () {
742 it('success', async
function() {
743 const dbAll
= [{ id: topicId
}];
747 const expected
= [topicId
];
748 sinon
.stub(db
.statement
.topicContentFetchNeeded
, 'all').returns(dbAll
);
749 sinon
.stub(db
.statement
.topicContentFetchClaimById
, 'run').returns(dbResult
);
750 const result
= await db
.topicFetchClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
751 assert
.deepStrictEqual(result
, expected
);
753 it('failure', async
function () {
754 const dbAll
= [{ id: topicId
}];
758 sinon
.stub(db
.statement
.topicContentFetchNeeded
, 'all').returns(dbAll
);
759 sinon
.stub(db
.statement
.topicContentFetchClaimById
, 'run').returns(dbResult
);
761 await db
.topicFetchClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
762 assert
.fail(noExpectedException
);
764 assert(e
instanceof DBErrors
.UnexpectedResult
);
767 }); // topicFetchClaim
769 describe('topicFetchClaimById', function () {
770 it('success', async
function() {
773 lastInsertRowid: undefined,
775 sinon
.stub(db
.statement
.topicContentFetchClaimById
, 'run').returns(expected
);
776 const result
= await db
.topicFetchClaimById(dbCtx
, topicId
, claimTimeoutSeconds
, claimant
);
777 assert
.deepStrictEqual(result
, expected
);
779 it('failure', async
function () {
782 lastInsertRowid: undefined,
784 sinon
.stub(db
.statement
.topicContentFetchClaimById
, 'run').returns(expected
);
786 await db
.topicFetchClaimById(dbCtx
, topicId
, claimTimeoutSeconds
, claimant
);
787 assert
.fail(noExpectedException
);
789 assert(e
instanceof DBErrors
.UnexpectedResult
);
792 }); // topicFetchClaimById
794 describe('topicFetchComplete', function () {
795 it('success', async
function() {
798 lastInsertRowid: undefined,
800 sinon
.stub(db
.statement
.topicAttemptsReset
, 'run').returns(dbResult
);
801 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult
);
802 await db
.topicFetchComplete(dbCtx
, topicId
);
804 it('failure', async
function () {
807 lastInsertRowid: undefined,
809 sinon
.stub(db
.statement
.topicAttemptsReset
, 'run').returns(dbResult
);
810 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult
);
812 await db
.topicFetchComplete(dbCtx
, topicId
);
813 assert
.fail(noExpectedException
);
815 assert(e
instanceof DBErrors
.UnexpectedResult
);
818 it('second failure', async
function () {
821 lastInsertRowid: undefined,
825 lastInsertRowid: undefined,
827 sinon
.stub(db
.statement
.topicAttemptsReset
, 'run').returns(dbResult0
);
828 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult1
);
830 await db
.topicFetchComplete(dbCtx
, topicId
);
831 assert
.fail(noExpectedException
);
833 assert(e
instanceof DBErrors
.UnexpectedResult
);
836 }); // topicFetchComplete
838 describe('topicFetchIncomplete', function () {
839 it('success', async
function() {
840 const dbGet
= { currentAttempt: 0 };
843 lastInsertRowid: undefined,
847 lastInsertRowid: undefined,
851 lastInsertRowid: undefined,
853 sinon
.stub(db
.statement
.topicAttempts
, 'get').returns(dbGet
);
854 sinon
.stub(db
.statement
.topicAttemptsIncrement
, 'run').returns(dbResult0
);
855 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult1
);
856 const result
= await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
857 assert
.deepStrictEqual(result
, expected
);
859 it('covers defaults', async
function() {
860 const dbGet
= { currentAttempt: 0 };
863 lastInsertRowid: undefined,
867 lastInsertRowid: undefined,
871 lastInsertRowid: undefined,
873 sinon
.stub(db
.statement
.topicAttempts
, 'get').returns(dbGet
);
874 sinon
.stub(db
.statement
.topicAttemptsIncrement
, 'run').returns(dbResult0
);
875 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult1
);
876 const result
= await db
.topicFetchIncomplete(dbCtx
, topicId
);
877 assert
.deepStrictEqual(result
, expected
);
879 it('failure', async
function () {
880 const dbGet
= { currentAttempt: 0 };
883 lastInsertRowid: undefined,
887 lastInsertRowid: undefined,
889 sinon
.stub(db
.statement
.topicAttempts
, 'get').returns(dbGet
);
890 sinon
.stub(db
.statement
.topicAttemptsIncrement
, 'run').returns(dbResult0
);
891 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult1
);
893 await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
894 assert
.fail(noExpectedException
);
896 assert(e
instanceof DBErrors
.UnexpectedResult
);
899 it('second failure', async
function () {
900 const dbGet
= { currentAttempt: 0 };
903 lastInsertRowid: undefined,
907 lastInsertRowid: undefined,
909 sinon
.stub(db
.statement
.topicAttempts
, 'get').returns(dbGet
);
910 sinon
.stub(db
.statement
.topicAttemptsIncrement
, 'run').returns(dbResult0
);
911 sinon
.stub(db
.statement
.topicContentFetchDone
, 'run').returns(dbResult1
);
913 await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
914 assert
.fail(noExpectedException
);
916 assert(e
instanceof DBErrors
.UnexpectedResult
);
919 }); // topicFetchIncomplete
921 describe('topicFetchRequested', function () {
922 it('success', async
function() {
925 lastInsertRowid: undefined,
929 lastInsertRowid: undefined,
931 sinon
.stub(db
.statement
.topicContentFetchRequested
, 'run').returns(dbResult
);
932 const result
= await db
.topicFetchRequested(dbCtx
, topicId
);
933 assert
.deepStrictEqual(result
, expected
);
935 it('failure', async
function () {
938 lastInsertRowid: undefined,
940 sinon
.stub(db
.statement
.topicContentFetchRequested
, 'run').returns(dbResult
);
942 await db
.topicFetchRequested(dbCtx
, topicId
);
943 assert
.fail(noExpectedException
);
945 assert(e
instanceof DBErrors
.UnexpectedResult
);
948 }); // topicFetchRequested
950 describe('topicGetAll', function () {
951 it('success', async
function() {
952 const expected
= [{ id: topicId
}];
953 sinon
.stub(db
.statement
.topicGetInfoAll
, 'all').returns(expected
);
954 const result
= await db
.topicGetAll(dbCtx
);
955 assert
.deepStrictEqual(result
, expected
);
957 it('covers none', async
function() {
958 const expected
= undefined;
959 sinon
.stub(db
.statement
.topicGetInfoAll
, 'all').returns(expected
);
960 const result
= await db
.topicGetAll(dbCtx
);
961 assert
.deepStrictEqual(result
, expected
);
963 it('failure', async
function () {
964 const expected
= new Error();
965 sinon
.stub(db
.statement
.topicGetInfoAll
, 'all').throws(expected
);
967 await db
.topicGetAll(dbCtx
);
968 assert
.fail(noExpectedException
);
970 assert
.deepStrictEqual(e
, expected
);
975 describe('topicGetById', function () {
976 it('success', async
function() {
977 const expected
= { id: topicId
};
978 sinon
.stub(db
.statement
.topicGetById
, 'get').returns(expected
);
979 const result
= await db
.topicGetById(dbCtx
, topicId
);
980 assert
.deepStrictEqual(result
, expected
);
982 it('covers no defaults', async
function () {
983 const expected
= { id: topicId
};
984 sinon
.stub(db
.statement
.topicGetById
, 'get').returns(expected
);
985 const result
= await db
.topicGetById(dbCtx
, topicId
, false);
986 assert
.deepStrictEqual(result
, expected
);
988 it('covers default', async
function() {
989 const expected
= undefined;
990 sinon
.stub(db
.statement
.topicGetById
, 'get').returns(expected
);
991 const result
= await db
.topicGetById(dbCtx
, topicId
);
992 assert
.deepStrictEqual(result
, expected
);
994 it('failure', async
function () {
995 const expected
= new Error();
996 sinon
.stub(db
.statement
.topicGetById
, 'get').throws(expected
);
998 await db
.topicGetById(dbCtx
, topicId
);
999 assert
.fail(noExpectedException
);
1001 assert
.deepStrictEqual(e
, expected
);
1006 describe('topicGetByUrl', function () {
1007 it('success', async
function() {
1008 const expected
= [];
1009 sinon
.stub(db
.statement
.topicGetByUrl
, 'get').returns(expected
);
1010 const result
= await db
.topicGetByUrl(dbCtx
, topicUrl
);
1011 assert
.deepStrictEqual(result
, expected
);
1013 it('failure', async
function () {
1014 const expected
= new Error();
1015 sinon
.stub(db
.statement
.topicGetByUrl
, 'get').throws(expected
);
1017 await db
.topicGetByUrl(dbCtx
, topicUrl
);
1018 assert
.fail(noExpectedException
);
1020 assert
.deepStrictEqual(e
, expected
);
1023 }); // topicGetByUrl
1025 describe('topicGetContentById', function () {
1026 it('success', async
function() {
1027 const expected
= { id: topicId
};
1028 sinon
.stub(db
.statement
.topicGetContentById
, 'get').returns(expected
);
1029 const result
= await db
.topicGetContentById(dbCtx
, topicId
);
1030 assert
.deepStrictEqual(result
, expected
);
1032 it('covers default', async
function() {
1033 const expected
= undefined;
1034 sinon
.stub(db
.statement
.topicGetContentById
, 'get').returns(expected
);
1035 const result
= await db
.topicGetContentById(dbCtx
, topicId
);
1036 assert
.deepStrictEqual(result
, expected
);
1038 it('failure', async
function () {
1039 const expected
= new Error();
1040 sinon
.stub(db
.statement
.topicGetContentById
, 'get').throws(expected
);
1042 await db
.topicGetContentById(dbCtx
, topicId
);
1043 assert
.fail(noExpectedException
);
1045 assert
.deepStrictEqual(e
, expected
);
1048 }); // topicGetContentById
1050 describe('topicPendingDelete', function () {
1051 beforeEach(function () {
1052 sinon
.stub(db
.statement
.topicGetById
, 'get');
1053 sinon
.stub(db
.statement
.subscriptionCountByTopicUrl
, 'get');
1054 sinon
.stub(db
.statement
.topicDeleteById
, 'run');
1056 it('success', async
function () {
1057 db
.statement
.topicGetById
.get.returns({
1061 db
.statement
.subscriptionCountByTopicUrl
.get.returns({
1064 db
.statement
.topicDeleteById
.run
.returns({
1067 db
.topicPendingDelete(dbCtx
, topicId
);
1068 assert(db
.statement
.topicDeleteById
.run
.called
);
1070 it('does not delete non-deleted topic', async
function () {
1071 db
.statement
.topicGetById
.get.returns({
1075 db
.statement
.subscriptionCountByTopicUrl
.get.returns({
1078 db
.statement
.topicDeleteById
.run
.returns({
1081 db
.topicPendingDelete(dbCtx
, topicId
);
1082 assert(!db
.statement
.topicDeleteById
.run
.called
);
1084 it('does not delete topic with active subscriptions', async
function () {
1085 db
.statement
.topicGetById
.get.returns({
1089 db
.statement
.subscriptionCountByTopicUrl
.get.returns({
1092 db
.statement
.topicDeleteById
.run
.returns({
1095 db
.topicPendingDelete(dbCtx
, topicId
);
1096 assert(!db
.statement
.topicDeleteById
.run
.called
);
1098 it('covers no deletion', async
function () {
1099 db
.statement
.topicGetById
.get.returns({
1103 db
.statement
.subscriptionCountByTopicUrl
.get.returns({
1106 db
.statement
.topicDeleteById
.run
.returns({
1110 db
.topicPendingDelete(dbCtx
, topicId
);
1111 assert
.fail(noExpectedException
);
1114 assert(e
instanceof DBErrors
.UnexpectedResult
);
1116 assert(db
.statement
.topicDeleteById
.run
.called
);
1120 describe('topicSet', function () {
1122 beforeEach(function () {
1127 it('success', async
function() {
1130 lastInsertRowid: topicId
,
1134 lastInsertRowid: topicId
,
1136 sinon
.stub(db
.statement
.topicUpsert
, 'run').returns(dbResult
);
1137 const result
= await db
.topicSet(dbCtx
, data
);
1138 assert
.deepStrictEqual(result
, expected
);
1140 it('failure', async
function () {
1143 lastInsertRowid: undefined,
1145 sinon
.stub(db
.statement
.topicUpsert
, 'run').returns(dbResult
);
1147 await db
.topicSet(dbCtx
, data
);
1148 assert
.fail(noExpectedException
);
1150 assert(e
instanceof DBErrors
.UnexpectedResult
);
1153 it('fails invalid value', async
function () {
1154 sinon
.stub(db
.statement
.topicUpsert
, 'run');
1156 data
.leaseSecondsPreferred
= -100;
1157 await db
.topicSet(dbCtx
, data
);
1158 assert
.fail(noExpectedException
);
1160 assert(e
instanceof DBErrors
.DataValidation
);
1162 assert(!db
.statement
.topicUpsert
.run
.called
);
1164 it('fails invalid values', async
function () {
1165 sinon
.stub(db
.statement
.topicUpsert
, 'run');
1167 data
.leaseSecondsPreferred
= 10;
1168 data
.leaseSecondsMax
= 100;
1169 data
.leaseSecondsMin
= 50;
1170 await db
.topicSet(dbCtx
, data
);
1171 assert
.fail(noExpectedException
);
1173 assert(e
instanceof DBErrors
.DataValidation
);
1175 assert(!db
.statement
.topicUpsert
.run
.called
);
1179 describe('topicSetContent', function () {
1181 beforeEach(function () {
1184 contentType: 'text/plain',
1185 contentHash: 'abc123',
1188 it('success', async
function() {
1191 lastInsertRowid: undefined,
1195 lastInsertRowid: undefined,
1197 sinon
.stub(db
.statement
.topicSetContent
, 'run').returns(dbResult
);
1198 const result
= await db
.topicSetContent(dbCtx
, data
);
1199 assert
.deepStrictEqual(result
, expected
);
1201 it('failure', async
function () {
1204 lastInsertRowid: undefined,
1206 sinon
.stub(db
.statement
.topicSetContent
, 'run').returns(dbResult
);
1208 await db
.topicSetContent(dbCtx
, data
);
1209 assert
.fail(noExpectedException
);
1211 assert(e
instanceof DBErrors
.UnexpectedResult
);
1214 }); // topicSetContent
1216 describe('topicUpdate', function () {
1218 beforeEach(function () {
1221 leaseSecondsPreferred: 9999,
1222 leaseSecondsMax: 99999,
1223 leaseSecondsMin: 999,
1224 publisherValidationUrl: null,
1225 contentHashAlgorithm: 'sha256',
1228 it('success', async
function() {
1231 lastInsertRowid: topicId
,
1233 sinon
.stub(db
.statement
.topicUpdate
, 'run').returns(dbResult
);
1234 await db
.topicUpdate(dbCtx
, data
);
1236 it('failure', async
function () {
1239 lastInsertRowid: undefined,
1241 sinon
.stub(db
.statement
.topicUpdate
, 'run').returns(dbResult
);
1243 await db
.topicUpdate(dbCtx
, data
);
1244 assert
.fail(noExpectedException
);
1246 assert(e
instanceof DBErrors
.UnexpectedResult
, e
);
1249 it('fails invalid value', async
function () {
1250 sinon
.stub(db
.statement
.topicUpdate
, 'run');
1252 data
.leaseSecondsPreferred
= -100;
1253 await db
.topicUpdate(dbCtx
, data
);
1254 assert
.fail(noExpectedException
);
1256 assert(e
instanceof DBErrors
.DataValidation
, e
);
1258 assert(!db
.statement
.topicUpdate
.run
.called
);
1260 it('fails invalid values', async
function () {
1261 sinon
.stub(db
.statement
.topicUpdate
, 'run');
1263 data
.leaseSecondsPreferred
= 10;
1264 data
.leaseSecondsMax
= 100;
1265 data
.leaseSecondsMin
= 50;
1266 await db
.topicUpdate(dbCtx
, data
);
1267 assert
.fail(noExpectedException
);
1269 assert(e
instanceof DBErrors
.DataValidation
, e
);
1271 assert(!db
.statement
.topicUpdate
.run
.called
);
1275 describe('verificationClaim', function () {
1276 it('success', async
function() {
1277 const dbAll
= [{ id: verificationId
}];
1280 lastInsertRowid: undefined,
1282 const expected
= [verificationId
];
1283 sinon
.stub(db
.statement
.verificationNeeded
, 'all').returns(dbAll
);
1284 sinon
.stub(db
.statement
.verificationClaimById
, 'run').returns(dbRun
);
1285 const result
= await db
.verificationClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
1286 assert
.deepStrictEqual(result
, expected
);
1288 it('failure', async
function () {
1289 const dbAll
= [{ id: verificationId
}];
1292 lastInsertRowid: undefined,
1294 sinon
.stub(db
.statement
.verificationNeeded
, 'all').returns(dbAll
);
1295 sinon
.stub(db
.statement
.verificationClaimById
, 'run').returns(dbRun
);
1297 await db
.verificationClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
1298 assert
.fail(noExpectedException
);
1300 assert(e
instanceof DBErrors
.UnexpectedResult
);
1303 }); // verificationClaim
1305 describe('verificationClaimById', function () {
1306 it('success', async
function() {
1309 lastInsertRowid: undefined,
1311 sinon
.stub(db
.statement
.verificationClaimById
, 'run').returns(dbRun
);
1312 const result
= await db
.verificationClaimById(dbCtx
, verificationId
, claimTimeoutSeconds
, claimant
);
1313 assert
.deepStrictEqual(result
, dbRun
);
1315 it('failure', async
function () {
1318 lastInsertRowid: undefined,
1320 sinon
.stub(db
.statement
.verificationClaimById
, 'run').returns(dbRun
);
1322 await db
.verificationClaimById(dbCtx
, verificationId
, claimTimeoutSeconds
, claimant
);
1323 assert
.fail(noExpectedException
);
1325 assert(e
instanceof DBErrors
.UnexpectedResult
);
1328 }); // verificationClaimById
1330 describe('verificationComplete', function () {
1331 it('success', async
function() {
1334 lastInsertRowid: undefined,
1336 sinon
.stub(db
.statement
.verificationScrub
, 'run').returns(dbResult
);
1337 await db
.verificationComplete(dbCtx
, verificationId
, callback
, topicId
);
1339 it('failure', async
function () {
1342 lastInsertRowid: undefined,
1344 sinon
.stub(db
.statement
.verificationScrub
, 'run').returns(dbResult
);
1346 await db
.verificationComplete(dbCtx
, verificationId
, callback
, topicId
);
1347 assert
.fail(noExpectedException
);
1349 assert(e
instanceof DBErrors
.UnexpectedResult
);
1352 }); // verificationComplete
1354 describe('verificationGetById', function () {
1355 it('success', async
function() {
1356 const dbOneOrNone
= { id: verificationId
, isPublisherValidated: 1 };
1357 const expected
= { id: verificationId
, isPublisherValidated: true };
1358 sinon
.stub(db
.statement
.verificationGetById
, 'get').returns(dbOneOrNone
);
1359 const result
= await db
.verificationGetById(dbCtx
, verificationId
);
1360 assert
.deepStrictEqual(result
, expected
);
1362 it('failure', async
function () {
1363 const expected
= new Error();
1364 sinon
.stub(db
.statement
.verificationGetById
, 'get').throws(expected
);
1366 await db
.verificationGetById(dbCtx
, verificationId
);
1367 assert
.fail(noExpectedException
);
1369 assert
.deepStrictEqual(e
, expected
);
1372 }); // verificationGetById
1374 describe('verificationIncomplete', function () {
1375 it('success', async
function() {
1376 const dbOne
= { attempts: 0 };
1379 lastInsertRowid: undefined,
1383 lastInsertRowid: undefined,
1385 sinon
.stub(db
.statement
.verificationAttempts
, 'get').returns(dbOne
);
1386 sinon
.stub(db
.statement
.verificationAttemptsIncrement
, 'run').returns(dbResult0
);
1387 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult1
);
1388 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1390 it('covers defaults', async
function() {
1391 const dbOne
= { attempts: 0 };
1394 lastInsertRowid: undefined,
1398 lastInsertRowid: undefined,
1400 sinon
.stub(db
.statement
.verificationAttempts
, 'get').returns(dbOne
);
1401 sinon
.stub(db
.statement
.verificationAttemptsIncrement
, 'run').returns(dbResult0
);
1402 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult1
);
1403 await db
.verificationIncomplete(dbCtx
, verificationId
);
1405 it('failure', async
function () {
1406 const dbOne
= { attempts: 0 };
1409 lastInsertRowid: undefined,
1413 lastInsertRowid: undefined,
1415 sinon
.stub(db
.statement
.verificationAttempts
, 'get').returns(dbOne
);
1416 sinon
.stub(db
.statement
.verificationAttemptsIncrement
, 'run').returns(dbResult0
);
1417 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult1
);
1419 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1420 assert
.fail(noExpectedException
);
1422 assert(e
instanceof DBErrors
.UnexpectedResult
);
1425 it('second failure', async
function () {
1426 const dbOne
= { attempts: 0 };
1429 lastInsertRowid: undefined,
1433 lastInsertRowid: undefined,
1435 sinon
.stub(db
.statement
.verificationAttempts
, 'get').returns(dbOne
);
1436 sinon
.stub(db
.statement
.verificationAttemptsIncrement
, 'run').returns(dbResult0
);
1437 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult1
);
1439 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1440 assert
.fail(noExpectedException
);
1442 assert(e
instanceof DBErrors
.UnexpectedResult
);
1445 }); // verificationIncomplete
1447 describe('_verificationDataToEngine', function () {
1448 it('covers no data', function () {
1449 DB
._verificationDataToEngine();
1451 it('covers true', function () {
1453 isPublisherValidated: true,
1455 DB
._verificationDataToEngine(data
);
1456 assert
.strictEqual(data
.isPublisherValidated
, 1);
1458 it('covers false', function () {
1460 isPublisherValidated: false,
1462 DB
._verificationDataToEngine(data
);
1463 assert
.strictEqual(data
.isPublisherValidated
, 0);
1465 }) // _verificationDataToEngine
1467 describe('verificationInsert', function () {
1469 beforeEach(function () {
1474 isPublisherValidated: true,
1475 leaseSeconds: 86400,
1478 it('success', async
function() {
1481 lastInsertRowid: verificationId
,
1483 const expected
= verificationId
;
1484 sinon
.stub(db
.statement
.verificationInsert
, 'run').returns(dbResult
);
1485 const result
= await db
.verificationInsert(dbCtx
, verification
);
1486 assert
.deepStrictEqual(result
, expected
);
1488 it('failure', async
function () {
1491 lastInsertRowid: undefined,
1493 sinon
.stub(db
.statement
.verificationInsert
, 'run').returns(dbResult
);
1495 await db
.verificationInsert(dbCtx
, verification
);
1496 assert
.fail(noExpectedException
);
1498 assert(e
instanceof DBErrors
.UnexpectedResult
);
1501 it('fails validation', async
function () {
1502 delete verification
.leaseSeconds
;
1504 await db
.verificationInsert(dbCtx
, verification
);
1505 assert
.fail(noExpectedException
);
1507 assert(e
instanceof DBErrors
.DataValidation
);
1510 }); // verificationInsert
1512 describe('verificationRelease', function () {
1513 it('success', async
function() {
1516 lastInsertRowid: undefined,
1518 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult
);
1519 await db
.verificationRelease(dbCtx
, verificationId
);
1521 it('failure', async
function () {
1524 lastInsertRowid: undefined,
1526 sinon
.stub(db
.statement
.verificationDone
, 'run').returns(dbResult
);
1528 await db
.verificationRelease(dbCtx
, verificationId
);
1529 assert
.fail(noExpectedException
);
1531 assert(e
instanceof DBErrors
.UnexpectedResult
);
1534 }); // verificationRelease
1536 describe('verificationUpdate', function () {
1538 beforeEach(function () {
1541 isPublisherValidated: true,
1544 it('success', async
function() {
1547 lastInsertRowid: undefined,
1549 sinon
.stub(db
.statement
.verificationUpdate
, 'run').returns(dbResult
);
1550 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1552 it('failure', async
function () {
1555 lastInsertRowid: undefined,
1557 sinon
.stub(db
.statement
.verificationUpdate
, 'run').returns(dbResult
);
1559 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1560 assert
.fail(noExpectedException
);
1562 assert(e
instanceof DBErrors
.UnexpectedResult
, e
.name
);
1565 it('fails validation', async
function () {
1568 await db
.verificationUpdate(dbCtx
, data
);
1569 assert
.fail(noExpectedException
);
1571 assert(e
instanceof DBErrors
.DataValidation
);
1574 }); // verificationUpdate
1576 describe('verificationValidated', function () {
1577 it('success', async
function() {
1580 lastInsertRowid: undefined,
1582 sinon
.stub(db
.statement
.verificationValidate
, 'run').returns(dbResult
);
1583 await db
.verificationValidated(dbCtx
, verificationId
);
1585 it('failure', async
function () {
1588 lastInsertRowid: undefined,
1590 sinon
.stub(db
.statement
.verificationValidate
, 'run').returns(dbResult
);
1592 await db
.verificationValidated(dbCtx
, verificationId
);
1593 assert
.fail(noExpectedException
);
1595 assert(e
instanceof DBErrors
.UnexpectedResult
);
1598 }); // verificationValidated
1600 }); // DatabaseSQLite