update dependencies and devDependencies, fix lint issues
[websub-hub] / test / src / db / sqlite.js
1 'use strict';
2
3 /* This provides implementation coverage, stubbing parts of better-sqlite3. */
4
5 const assert = require('node:assert');
6 const sinon = require('sinon');
7 const DBStub = require('../../stub-db');
8 const stubLogger = require('../../stub-logger');
9 const DB = require('../../../src/db/sqlite');
10 const DBErrors = require('../../../src/db/errors');
11 const common = require('../../../src/common');
12 const Config = require('../../../config');
13
14 const noExpectedException = 'did not receive expected exception';
15
16 describe('DatabaseSQLite', function () {
17 let db, options;
18 let dbCtx, claimant, claimTimeoutSeconds, callback, subscriptionId, topicId, verificationId;
19 let topicUrl, leaseSeconds, secret, httpRemoteAddr, httpFrom, retryDelays, wanted;
20 before(function () {
21 options = new Config('test');
22 options.db.connectionString = 'sqlite://:memory:';
23 db = new DB(stubLogger, options);
24 });
25 beforeEach(function () {
26 stubLogger._reset();
27 dbCtx = db.db;
28 claimant = '19af19b8-6be3-4a6f-8946-65f5f1ccc5d7';
29 claimTimeoutSeconds = 300;
30 subscriptionId = 'fbaf8f19-ed9c-4a21-89ae-98b7005e3bf6';
31 topicUrl = 'https://example.com/blog';
32 callback = 'https://example.com/callback?id=123';
33 topicId = 'c59d4bda-10ad-41d9-99df-4ce8bc331424';
34 verificationId = '55cd7748-d2d5-11eb-b355-0025905f714a';
35 retryDelays = [60];
36 leaseSeconds = 86400;
37 secret = 'secret';
38 httpRemoteAddr = '127.0.0.1';
39 httpFrom = 'user@example.com';
40 wanted = 5;
41 });
42 afterEach(function () {
43 sinon.restore();
44 });
45
46 // Ensure all interface methods are implemented
47 describe('Implementation', function () {
48 it('implements interface', async function () {
49 const results = await Promise.allSettled(DBStub._implementation.map(async (fn) => {
50 try {
51 // eslint-disable-next-line security/detect-object-injection
52 await db[fn](db.db);
53 } catch (e) {
54 assert(!(e instanceof DBErrors.NotImplemented), `${fn} not implemented`);
55 }
56 }));
57 const failures = results.filter((x) => x.status === 'rejected');
58 assert(!failures.length, failures.map((x) => {
59 x = x.reason.toString();
60 return x.slice(x.indexOf(': '));
61 }));
62 });
63 }); // Implementation
64
65 describe('_initTables', function () {
66 let preparedGet;
67 beforeEach(function () {
68 preparedGet = sinon.stub();
69 sinon.stub(db.db, 'prepare').returns({
70 pluck: () => ({
71 bind: () => ({
72 get: preparedGet,
73 }),
74 }),
75 });
76 sinon.stub(db, '_currentSchema').returns(db.schemaVersionsSupported.min);
77 sinon.stub(db.db, 'exec');
78 });
79 it('covers migration', async function() {
80 preparedGet.returns({});
81 await db._initTables();
82 assert(db.db.exec.called);
83 });
84 it('covers migration failure', async function() {
85 const expected = new Error('oh no');
86 preparedGet.returns({});
87 db.db.exec.throws(expected);
88 try {
89 await db._initTables();
90 assert.fail(noExpectedException);
91 } catch (e) {
92 assert.deepStrictEqual(e, expected);
93 }
94 });
95 }); // _initTables
96
97 describe('_currentSchema', function () {
98 it('covers', async function () {
99 const version = { major: 1, minor: 0, patch: 0 };
100 sinon.stub(db.db, 'prepare').returns({
101 get: () => version,
102 });
103 const result = await db._currentSchema();
104 assert.deepStrictEqual(result, version);
105 });
106 }); // _currentSchema
107
108 describe('_closeConnection', function () {
109 it('success', async function () {
110 sinon.stub(db.db, 'close');
111 await db._closeConnection();
112 assert(db.db.close.called);
113 });
114 it('failure', async function () {
115 const expected = new Error();
116 sinon.stub(db.db, 'close').throws(expected);
117 try {
118 await db._closeConnection();
119 assert.fail(noExpectedException);
120 } catch (e) {
121 assert.deepStrictEqual(e, expected);
122 }
123 });
124 }); // _closeConnection
125
126 describe('_purgeTables', function () {
127 beforeEach(function () {
128 sinon.stub(db.db, 'prepare').returns({
129 run: sinon.stub(),
130 });
131 });
132 it('covers not really', async function () {
133 await db._purgeTables(false);
134 assert(!db.db.prepare.called);
135 });
136 it('success', async function () {
137 await db._purgeTables(true);
138 assert(db.db.prepare.called);
139 });
140 it('failure', async function () {
141 const expected = new Error();
142 db.db.prepare.restore();
143 sinon.stub(db.db, 'prepare').throws(expected);
144 try {
145 await db._purgeTables(true);
146 assert.fail(noExpectedException);
147 } catch (e) {
148 assert.deepStrictEqual(e, expected);
149 }
150 });
151 }); // _purgeTables
152
153 describe('_optimize', function () {
154 let origOAC;
155 beforeEach(function () {
156 origOAC = db.optimizeAfterChanges;
157 sinon.stub(db.statement._optimize, 'all');
158 sinon.stub(db.db, 'pragma');
159 });
160 this.afterEach(function () {
161 db.optimizeAfterChanges = origOAC;
162 });
163 it('covers', async function () {
164 db.optimizeAfterChanges = 10;
165 db.changesSinceLastOptimize = BigInt(20);
166 await db._optimize();
167 assert(db.db.pragma.called);
168 });
169 it('covers none', async function () {
170 db.optimizeAfterChanges = 0;
171 await db._optimize();
172 assert(!db.db.pragma.called);
173 });
174 it('covers not enough changes', async function () {
175 db.optimizeAfterChanges = 10;
176 db.changesSinceLastOptimize = BigInt(5);
177 await db._optimize();
178 assert(!db.db.pragma.called);
179 });
180 }); // _optimize
181
182 describe('_deOphidiate', function () {
183 it('covers non-array', function () {
184 const obj = {
185 'snake_case': 1,
186 };
187 const expected = {
188 snakeCase: 1,
189 };
190 const result = DB._deOphidiate(obj);
191 assert.deepStrictEqual(result, expected);
192 });
193 it('covers array', function () {
194 const rows = [
195 {
196 'snek_field': 'foo',
197 },
198 {
199 'snek_field': 'bar',
200 },
201 ];
202 const expected = [
203 {
204 snekField: 'foo',
205 },
206 {
207 snekField: 'bar',
208 },
209 ];
210 const result = DB._deOphidiate(rows);
211 assert.deepStrictEqual(result, expected);
212 });
213 }); // _deOphidiate
214
215 describe('_topicDataToNative', function () {
216 it('covers', function () {
217 const now = new Date();
218 const nowEpoch = now.getTime() / 1000;
219 const topic = {
220 isActive: 1,
221 isDeleted: 0,
222 created: nowEpoch,
223 lastPublish: nowEpoch,
224 contentFetchNextAttempt: nowEpoch,
225 contentUpdated: nowEpoch,
226 url: 'https://example.com/',
227 };
228 const expected = {
229 isActive: true,
230 isDeleted: false,
231 created: now,
232 lastPublish: now,
233 contentFetchNextAttempt: now,
234 contentUpdated: now,
235 url: topic.url,
236 };
237 const result = DB._topicDataToNative(topic);
238 assert.deepStrictEqual(result, expected);
239 });
240 it('covers empty', function () {
241 const topic = undefined;
242 const result = DB._topicDataToNative(topic);
243 assert.deepStrictEqual(result, topic);
244 });
245 }); // _topicDataToNative
246
247 describe('healthCheck', function () {
248 let origDb;
249 beforeEach(function () {
250 origDb = db.db;
251 });
252 afterEach(function () {
253 db.db = origDb;
254 });
255 it('covers', function () {
256 db.healthCheck();
257 });
258 it('covers failure', function () {
259 db.db = { open: false };
260 try {
261 db.healthCheck();
262 assert.fail(noExpectedException);
263 } catch (e) {
264 assert(e instanceof DBErrors.UnexpectedResult);
265 }
266 });
267 }); // healthCheck
268
269 describe('context', function () {
270 it('covers', async function () {
271 await db.context(common.nop);
272 });
273 }); // context
274
275 describe('transaction', function () {
276 it('covers', async function () {
277 await db.transaction(db.db, common.nop);
278 });
279 it('covers no context', async function () {
280 await db.transaction(undefined, common.nop);
281 });
282 }); // transaction
283
284 describe('authenticationSuccess', function () {
285 let identifier;
286 beforeEach(function () {
287 identifier = 'username';
288 });
289 it('success', async function() {
290 const dbResult = {
291 changes: 1,
292 lastInsertRowid: undefined,
293 };
294 sinon.stub(db.statement.authenticationSuccess, 'run').returns(dbResult);
295 await db.authenticationSuccess(dbCtx, identifier);
296 });
297 it('failure', async function () {
298 const dbResult = {
299 changes: 0,
300 lastInsertRowid: undefined,
301 };
302 sinon.stub(db.statement.authenticationSuccess, 'run').returns(dbResult);
303 try {
304 await db.authenticationSuccess(dbCtx, identifier);
305 assert.fail(noExpectedException);
306 } catch (e) {
307 assert(e instanceof DBErrors.UnexpectedResult);
308 }
309 });
310 }); // authenticationSuccess
311
312 describe('authenticationGet', function () {
313 let identifier, credential;
314 beforeEach(function () {
315 identifier = 'username';
316 credential = '$z$foo';
317 });
318 it('success', async function() {
319 const expected = {
320 identifier,
321 credential,
322 };
323 sinon.stub(db.statement.authenticationGet, 'get').returns(expected);
324 const result = await db.authenticationGet(dbCtx, identifier);
325 assert.deepStrictEqual(result, expected);
326 });
327 it('failure', async function () {
328 const expected = new Error();
329 sinon.stub(db.statement.authenticationGet, 'get').throws(expected);
330 try {
331 await db.authenticationGet(dbCtx, identifier);
332 assert.fail(noExpectedException);
333 } catch (e) {
334 assert.deepStrictEqual(e, expected);
335 }
336 });
337 }); // authenticationGet
338
339 describe('authenticationUpsert', function () {
340 let identifier, credential;
341 beforeEach(function () {
342 identifier = 'username';
343 credential = '$z$foo';
344 });
345 it('success', async function() {
346 const dbResult = {
347 changes: 1,
348 lastInsertRowid: undefined,
349 };
350 sinon.stub(db.statement.authenticationUpsert, 'run').returns(dbResult);
351 await db.authenticationUpsert(dbCtx, identifier, credential);
352 });
353 it('failure', async function () {
354 const dbResult = {
355 changes: 0,
356 lastInsertRowid: undefined,
357 };
358 sinon.stub(db.statement.authenticationUpsert, 'run').returns(dbResult);
359 try {
360 await db.authenticationUpsert(dbCtx, identifier, credential);
361 assert.fail(noExpectedException);
362 } catch (e) {
363 assert(e instanceof DBErrors.UnexpectedResult);
364 }
365 });
366 }); // authenticationUpsert
367
368 describe('subscriptionsByTopicId', function () {
369 it('success', async function () {
370 const expected = [{ id: 3 }];
371 sinon.stub(db.statement.subscriptionsByTopicId, 'all').returns(expected);
372 const result = await db.subscriptionsByTopicId(dbCtx, topicUrl);
373 assert.deepStrictEqual(result, expected);
374 });
375 it('failure', async function () {
376 const expected = new Error();
377 sinon.stub(db.statement.subscriptionsByTopicId, 'all').throws(expected);
378 try {
379 await db.subscriptionsByTopicId(dbCtx, topicUrl);
380 assert.fail(noExpectedException);
381 } catch (e) {
382 assert.deepStrictEqual(e, expected);
383 }
384 });
385 }); // subscriptionsByTopicId
386
387 describe('subscriptionCountByTopicUrl', function () {
388 it('success', async function () {
389 const expected = { count: 3 };
390 sinon.stub(db.statement.subscriptionCountByTopicUrl, 'get').returns(expected);
391 const result = await db.subscriptionCountByTopicUrl(dbCtx, topicUrl);
392 assert.deepStrictEqual(result, expected);
393 });
394 it('failure', async function () {
395 const expected = new Error();
396 sinon.stub(db.statement.subscriptionCountByTopicUrl, 'get').throws(expected);
397 try {
398 await db.subscriptionCountByTopicUrl(dbCtx, topicUrl);
399 assert.fail(noExpectedException);
400 } catch (e) {
401 assert.deepStrictEqual(e, expected);
402 }
403 });
404 }); // subscriptionCountByTopicUrl
405
406 describe('subscriptionDelete', function () {
407 it('success', async function() {
408 const dbResult = {
409 changes: 1,
410 lastInsertRowid: undefined,
411 };
412 const expected = {
413 changes: 1,
414 lastInsertRowid: undefined,
415 };
416 sinon.stub(db.statement.subscriptionDelete, 'run').returns(dbResult);
417 const result = await db.subscriptionDelete(dbCtx, callback, topicId);
418 assert.deepStrictEqual(result, expected);
419 });
420 it('failure', async function () {
421 const dbResult = {
422 changes: 0,
423 lastInsertRowid: undefined,
424 };
425 sinon.stub(db.statement.subscriptionDelete, 'run').returns(dbResult);
426 try {
427 await db.subscriptionDelete(dbCtx, callback, topicId);
428 assert.fail(noExpectedException);
429 } catch (e) {
430 assert(e instanceof DBErrors.UnexpectedResult);
431 }
432 });
433 }); // subscriptionDelete
434
435 describe('subscriptionDeleteExpired', function () {
436 it('success', async function () {
437 const dbResult = {
438 changes: 1,
439 lastInsertRowid: undefined,
440 };
441 const expected = {
442 changes: 1,
443 lastInsertRowid: undefined,
444 };
445 sinon.stub(db.statement.subscriptionDeleteExpired, 'run').returns(dbResult);
446 const result = await db.subscriptionDeleteExpired(dbCtx, topicId);
447 assert.deepStrictEqual(result, expected);
448 });
449 it('failure', async function () {
450 const expected = new Error();
451 sinon.stub(db.statement.subscriptionDeleteExpired, 'run').throws(expected);
452 try {
453 await db.subscriptionDeleteExpired(dbCtx, topicId);
454 assert.fail(noExpectedException);
455 } catch (e) {
456 assert.deepStrictEqual(e, expected);
457 }
458 });
459 }); // subscriptionDeleteExpired
460
461 describe('subscriptionDeliveryClaim', function () {
462 it('success', async function () {
463 const dbAllResult = [
464 {
465 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
466 },
467 ];
468 const dbRunResult = {
469 changes: 1,
470 lastInsertRowid: undefined,
471 };
472 const expected = ['c2e254c5-aa6e-4a8f-b1a1-e474b07392bb'];
473 sinon.stub(db.statement.subscriptionDeliveryNeeded, 'all').returns(dbAllResult);
474 sinon.stub(db.statement.subscriptionDeliveryClaimById, 'run').returns(dbRunResult);
475 const result = await db.subscriptionDeliveryClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
476 assert.deepStrictEqual(result, expected);
477 });
478 it('failure', async function () {
479 const dbAllResult = [
480 {
481 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
482 },
483 ];
484 const dbRunResult = {
485 changes: 0,
486 lastInsertRowid: undefined,
487 };
488 sinon.stub(db.statement.subscriptionDeliveryNeeded, 'all').returns(dbAllResult);
489 sinon.stub(db.statement.subscriptionDeliveryClaimById, 'run').returns(dbRunResult);
490 try {
491 await db.subscriptionDeliveryClaim(dbCtx, wanted, claimTimeoutSeconds, claimant );
492 assert.fail(noExpectedException);
493 } catch (e) {
494 assert(e instanceof DBErrors.UnexpectedResult);
495 }
496 });
497 }); // subscriptionDeliveryClaim
498
499 describe('subscriptionDeliveryClaimById', function () {
500 it('success', async function() {
501 const dbResult = {
502 changes: 1,
503 lastInsertRowid: undefined,
504 };
505 sinon.stub(db.statement.subscriptionDeliveryClaimById, 'run').returns(dbResult);
506 const result = await db.subscriptionDeliveryClaimById(dbCtx, subscriptionId, claimTimeoutSeconds, claimant);
507 assert.deepStrictEqual(result, dbResult);
508 });
509 it('failure', async function () {
510 const dbResult = {
511 changes: 0,
512 lastInsertRowid: undefined,
513 };
514 sinon.stub(db.statement.subscriptionDeliveryClaimById, 'run').returns(dbResult);
515 try {
516 await db.subscriptionDeliveryClaimById(dbCtx, subscriptionId, claimTimeoutSeconds, claimant);
517 assert.fail(noExpectedException);
518 } catch (e) {
519 assert(e instanceof DBErrors.UnexpectedResult);
520 }
521 });
522 }); // subscriptionDeliveryClaimById
523
524 describe('subscriptionDeliveryComplete', function () {
525 let topicContentUpdated;
526 before(function () {
527 topicContentUpdated = new Date();
528 });
529 it('success', async function() {
530 const dbResult = {
531 changes: 1,
532 };
533 sinon.stub(db.statement.subscriptionDeliverySuccess, 'run').returns(dbResult);
534 sinon.stub(db.statement.subscriptionDeliveryDone, 'run').returns(dbResult);
535 await db.subscriptionDeliveryComplete(dbCtx, callback, topicId, topicContentUpdated);
536 });
537 it('failure', async function () {
538 const dbResult = {
539 changes: 0,
540 };
541 sinon.stub(db.statement.subscriptionDeliverySuccess, 'run').returns(dbResult);
542 sinon.stub(db.statement.subscriptionDeliveryDone, 'run').returns(dbResult);
543 try {
544 await db.subscriptionDeliveryComplete(dbCtx, callback, topicId, topicContentUpdated);
545 assert.fail(noExpectedException);
546 } catch (e) {
547 assert(e instanceof DBErrors.UnexpectedResult);
548 }
549 });
550 it('second failure', async function () {
551 const dbResult0 = {
552 changes: 1,
553 };
554 const dbResult1 = {
555 changes: 0,
556 };
557 sinon.stub(db.statement.subscriptionDeliverySuccess, 'run').returns(dbResult0);
558 sinon.stub(db.statement.subscriptionDeliveryDone, 'run').returns(dbResult1);
559 try {
560 await db.subscriptionDeliveryComplete(dbCtx, callback, topicId, topicContentUpdated);
561 assert.fail(noExpectedException);
562 } catch (e) {
563 assert(e instanceof DBErrors.UnexpectedResult);
564 }
565 });
566 }); // subscriptionDeliveryComplete
567
568 describe('subscriptionDeliveryGone', function () {
569 it('success', async function() {
570 const dbResult = {
571 changes: 1,
572 };
573 sinon.stub(db.statement.subscriptionDelete, 'run').returns(dbResult);
574 await db.subscriptionDeliveryGone(dbCtx, callback, topicId);
575 });
576 it('failure', async function () {
577 const dbResult = {
578 changes: 0,
579 };
580 sinon.stub(db.statement.subscriptionDelete, 'run').returns(dbResult);
581 try {
582 await db.subscriptionDeliveryGone(dbCtx, callback, topicId);
583 assert.fail(noExpectedException);
584 } catch (e) {
585 assert(e instanceof DBErrors.UnexpectedResult);
586 }
587 });
588 }); // subscriptionDeliveryGone
589
590 describe('subscriptionDeliveryIncomplete', function () {
591 it('success', async function() {
592 const dbGet = { deliveryAttemptsSinceSuccess: 0 };
593 const dbResult = {
594 changes: 1,
595 };
596 sinon.stub(db.statement.subscriptionDeliveryAttempts, 'get').returns(dbGet);
597 sinon.stub(db.statement.subscriptionDeliveryFailure, 'run').returns(dbResult);
598 sinon.stub(db.statement.subscriptionDeliveryDone, 'run').returns(dbResult);
599 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId, retryDelays);
600 });
601 it('success covers default', async function() {
602 const dbGet = { deliveryAttemptsSinceSuccess: 0 };
603 const dbResult = {
604 changes: 1,
605 };
606 sinon.stub(db.statement.subscriptionDeliveryAttempts, 'get').returns(dbGet);
607 sinon.stub(db.statement.subscriptionDeliveryFailure, 'run').returns(dbResult);
608 sinon.stub(db.statement.subscriptionDeliveryDone, 'run').returns(dbResult);
609 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId);
610 });
611 it('failure', async function () {
612 const dbGet = { deliveryAttemptsSinceSuccess: 0 };
613 const dbResult = {
614 changes: 0,
615 };
616 sinon.stub(db.statement.subscriptionDeliveryAttempts, 'get').returns(dbGet);
617 sinon.stub(db.statement.subscriptionDeliveryFailure, 'run').returns(dbResult);
618 sinon.stub(db.statement.subscriptionDeliveryDone, 'run').returns(dbResult);
619 try {
620 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId, retryDelays);
621 assert.fail(noExpectedException);
622 } catch (e) {
623 assert(e instanceof DBErrors.UnexpectedResult);
624 }
625 });
626 it('second failure', async function () {
627 const dbGet = { deliveryAttemptsSinceSuccess: 0 };
628 const dbResult0 = {
629 changes: 1,
630 };
631 const dbResult1 = {
632 changes: 0,
633 };
634 sinon.stub(db.statement.subscriptionDeliveryAttempts, 'get').returns(dbGet);
635 sinon.stub(db.statement.subscriptionDeliveryFailure, 'run').returns(dbResult0);
636 sinon.stub(db.statement.subscriptionDeliveryDone, 'run').returns(dbResult1);
637 try {
638 await db.subscriptionDeliveryIncomplete(dbCtx, callback, topicId, retryDelays);
639 assert.fail(noExpectedException);
640 } catch (e) {
641 assert(e instanceof DBErrors.UnexpectedResult);
642 }
643 });
644 }); // subscriptionDeliveryIncomplete
645
646 describe('subscriptionGet', function () {
647 it('success', async function() {
648 const expected = {
649 id: subscriptionId,
650 };
651 sinon.stub(db.statement.subscriptionGet, 'get').returns(expected);
652 const result = await db.subscriptionGet(dbCtx, callback, topicId);
653 assert.deepStrictEqual(result, expected);
654 });
655 it('failure', async function () {
656 const expected = new Error();
657 sinon.stub(db.statement.subscriptionGet, 'get').throws(expected);
658 try {
659 await db.subscriptionGet(dbCtx, callback, topicId);
660 assert.fail(noExpectedException);
661 } catch (e) {
662 assert.deepStrictEqual(e, expected);
663 }
664 });
665 }); // subscriptionGet
666
667 describe('subscriptionGetById', function () {
668 it('success', async function() {
669 const expected = {
670 id: subscriptionId,
671 };
672 sinon.stub(db.statement.subscriptionGetById, 'get').returns(expected);
673 const result = await db.subscriptionGetById(dbCtx, subscriptionId);
674 assert.deepStrictEqual(result, expected);
675 });
676 it('failure', async function () {
677 const expected = new Error();
678 sinon.stub(db.statement.subscriptionGetById, 'get').throws(expected);
679 try {
680 await db.subscriptionGetById(dbCtx, subscriptionId);
681 assert.fail(noExpectedException);
682 } catch (e) {
683 assert.deepStrictEqual(e, expected);
684 }
685 });
686 }); // subscriptionGetById
687
688 describe('subscriptionUpdate', function () {
689 let data;
690 beforeEach(function () {
691 data = {
692 subscriptionId,
693 signatureAlgorithm: 'sha256',
694 };
695 });
696 it('success', async function() {
697 const dbResult = {
698 changes: 1,
699 lastInsertRowid: subscriptionId,
700 };
701 sinon.stub(db.statement.subscriptionUpdate, 'run').returns(dbResult);
702 await db.subscriptionUpdate(dbCtx, data);
703 });
704 it('failure', async function () {
705 const dbResult = {
706 changes: 0,
707 };
708 sinon.stub(db.statement.subscriptionUpdate, 'run').returns(dbResult);
709 try {
710 await db.subscriptionUpdate(dbCtx, data);
711 assert.fail(noExpectedException);
712 } catch (e) {
713 assert(e instanceof DBErrors.UnexpectedResult, e);
714 }
715 });
716 }); // subscriptionUpdate
717
718 describe('subscriptionUpsert', function () {
719 let data;
720 beforeEach(function () {
721 data = {
722 callback,
723 topicId,
724 leaseSeconds,
725 secret,
726 httpRemoteAddr,
727 httpFrom,
728 };
729 });
730 it('success', async function() {
731 const dbResult = {
732 changes: 1,
733 lastInsertRowid: subscriptionId,
734 };
735 const expected = {
736 changes: 1,
737 lastInsertRowid: subscriptionId,
738 };
739 sinon.stub(db.statement.subscriptionUpsert, 'run').returns(dbResult);
740 const result = await db.subscriptionUpsert(dbCtx, data);
741 assert.deepStrictEqual(result, expected);
742 });
743 it('failure', async function () {
744 const dbResult = {
745 changes: 0,
746 };
747 sinon.stub(db.statement.subscriptionUpsert, 'run').returns(dbResult);
748 try {
749 await db.subscriptionUpsert(dbCtx, data);
750 assert.fail(noExpectedException);
751 } catch (e) {
752 assert(e instanceof DBErrors.UnexpectedResult);
753 }
754 });
755 }); // subscriptionUpsert
756
757 describe('topicDeleted', function () {
758 it('success', async function () {
759 sinon.stub(db.statement.topicDeleted, 'run').returns({ changes: 1 });
760 await db.topicDeleted(dbCtx, { topicId });
761 });
762 it('failure', async function () {
763 sinon.stub(db.statement.topicDeleted, 'run').returns({ changes: 0 });
764 try {
765 await db.topicDeleted(dbCtx, { topicId });
766 assert.fail(noExpectedException);
767 } catch (e) {
768 assert(e instanceof DBErrors.UnexpectedResult);
769 }
770 });
771 }); // topicDeleted
772
773 describe('topicFetchClaim', function () {
774 it('success', async function() {
775 const dbAll = [{ id: topicId }];
776 const dbResult = {
777 changes: 1,
778 };
779 const expected = [topicId];
780 sinon.stub(db.statement.topicContentFetchNeeded, 'all').returns(dbAll);
781 sinon.stub(db.statement.topicContentFetchClaimById, 'run').returns(dbResult);
782 const result = await db.topicFetchClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
783 assert.deepStrictEqual(result, expected);
784 });
785 it('failure', async function () {
786 const dbAll = [{ id: topicId }];
787 const dbResult = {
788 changes: 0,
789 };
790 sinon.stub(db.statement.topicContentFetchNeeded, 'all').returns(dbAll);
791 sinon.stub(db.statement.topicContentFetchClaimById, 'run').returns(dbResult);
792 try {
793 await db.topicFetchClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
794 assert.fail(noExpectedException);
795 } catch (e) {
796 assert(e instanceof DBErrors.UnexpectedResult);
797 }
798 });
799 }); // topicFetchClaim
800
801 describe('topicFetchClaimById', function () {
802 it('success', async function() {
803 const expected = {
804 changes: 1,
805 lastInsertRowid: undefined,
806 };
807 sinon.stub(db.statement.topicContentFetchClaimById, 'run').returns(expected);
808 const result = await db.topicFetchClaimById(dbCtx, topicId, claimTimeoutSeconds, claimant);
809 assert.deepStrictEqual(result, expected);
810 });
811 it('failure', async function () {
812 const expected = {
813 changes: 0,
814 lastInsertRowid: undefined,
815 };
816 sinon.stub(db.statement.topicContentFetchClaimById, 'run').returns(expected);
817 try {
818 await db.topicFetchClaimById(dbCtx, topicId, claimTimeoutSeconds, claimant);
819 assert.fail(noExpectedException);
820 } catch (e) {
821 assert(e instanceof DBErrors.UnexpectedResult);
822 }
823 });
824 }); // topicFetchClaimById
825
826 describe('topicFetchComplete', function () {
827 it('success', async function() {
828 const dbResult = {
829 changes: 1,
830 lastInsertRowid: undefined,
831 };
832 sinon.stub(db.statement.topicAttemptsReset, 'run').returns(dbResult);
833 sinon.stub(db.statement.topicContentFetchDone, 'run').returns(dbResult);
834 await db.topicFetchComplete(dbCtx, topicId);
835 });
836 it('failure', async function () {
837 const dbResult = {
838 changes: 0,
839 lastInsertRowid: undefined,
840 };
841 sinon.stub(db.statement.topicAttemptsReset, 'run').returns(dbResult);
842 sinon.stub(db.statement.topicContentFetchDone, 'run').returns(dbResult);
843 try {
844 await db.topicFetchComplete(dbCtx, topicId);
845 assert.fail(noExpectedException);
846 } catch (e) {
847 assert(e instanceof DBErrors.UnexpectedResult);
848 }
849 });
850 it('second failure', async function () {
851 const dbResult0 = {
852 changes: 1,
853 lastInsertRowid: undefined,
854 };
855 const dbResult1 = {
856 changes: 0,
857 lastInsertRowid: undefined,
858 };
859 sinon.stub(db.statement.topicAttemptsReset, 'run').returns(dbResult0);
860 sinon.stub(db.statement.topicContentFetchDone, 'run').returns(dbResult1);
861 try {
862 await db.topicFetchComplete(dbCtx, topicId);
863 assert.fail(noExpectedException);
864 } catch (e) {
865 assert(e instanceof DBErrors.UnexpectedResult);
866 }
867 });
868 }); // topicFetchComplete
869
870 describe('topicFetchIncomplete', function () {
871 it('success', async function() {
872 const dbGet = { currentAttempt: 0 };
873 const dbResult0 = {
874 changes: 1,
875 lastInsertRowid: undefined,
876 };
877 const dbResult1 = {
878 changes: 1,
879 lastInsertRowid: undefined,
880 };
881 const expected = {
882 changes: 1,
883 lastInsertRowid: undefined,
884 };
885 sinon.stub(db.statement.topicAttempts, 'get').returns(dbGet);
886 sinon.stub(db.statement.topicAttemptsIncrement, 'run').returns(dbResult0);
887 sinon.stub(db.statement.topicContentFetchDone, 'run').returns(dbResult1);
888 const result = await db.topicFetchIncomplete(dbCtx, topicId, retryDelays);
889 assert.deepStrictEqual(result, expected);
890 });
891 it('covers defaults', async function() {
892 const dbGet = { currentAttempt: 0 };
893 const dbResult0 = {
894 changes: 1,
895 lastInsertRowid: undefined,
896 };
897 const dbResult1 = {
898 changes: 1,
899 lastInsertRowid: undefined,
900 };
901 const expected = {
902 changes: 1,
903 lastInsertRowid: undefined,
904 };
905 sinon.stub(db.statement.topicAttempts, 'get').returns(dbGet);
906 sinon.stub(db.statement.topicAttemptsIncrement, 'run').returns(dbResult0);
907 sinon.stub(db.statement.topicContentFetchDone, 'run').returns(dbResult1);
908 const result = await db.topicFetchIncomplete(dbCtx, topicId);
909 assert.deepStrictEqual(result, expected);
910 });
911 it('failure', async function () {
912 const dbGet = { currentAttempt: 0 };
913 const dbResult0 = {
914 changes: 1,
915 lastInsertRowid: undefined,
916 };
917 const dbResult1 = {
918 changes: 0,
919 lastInsertRowid: undefined,
920 };
921 sinon.stub(db.statement.topicAttempts, 'get').returns(dbGet);
922 sinon.stub(db.statement.topicAttemptsIncrement, 'run').returns(dbResult0);
923 sinon.stub(db.statement.topicContentFetchDone, 'run').returns(dbResult1);
924 try {
925 await db.topicFetchIncomplete(dbCtx, topicId, retryDelays);
926 assert.fail(noExpectedException);
927 } catch (e) {
928 assert(e instanceof DBErrors.UnexpectedResult);
929 }
930 });
931 it('second failure', async function () {
932 const dbGet = { currentAttempt: 0 };
933 const dbResult0 = {
934 changes: 0,
935 lastInsertRowid: undefined,
936 };
937 const dbResult1 = {
938 changes: 0,
939 lastInsertRowid: undefined,
940 };
941 sinon.stub(db.statement.topicAttempts, 'get').returns(dbGet);
942 sinon.stub(db.statement.topicAttemptsIncrement, 'run').returns(dbResult0);
943 sinon.stub(db.statement.topicContentFetchDone, 'run').returns(dbResult1);
944 try {
945 await db.topicFetchIncomplete(dbCtx, topicId, retryDelays);
946 assert.fail(noExpectedException);
947 } catch (e) {
948 assert(e instanceof DBErrors.UnexpectedResult);
949 }
950 });
951 }); // topicFetchIncomplete
952
953 describe('topicFetchRequested', function () {
954 it('success', async function() {
955 const dbResult = {
956 changes: 1,
957 lastInsertRowid: undefined,
958 };
959 const expected = {
960 changes: 1,
961 lastInsertRowid: undefined,
962 };
963 sinon.stub(db.statement.topicContentFetchRequested, 'run').returns(dbResult);
964 const result = await db.topicFetchRequested(dbCtx, topicId);
965 assert.deepStrictEqual(result, expected);
966 });
967 it('failure', async function () {
968 const dbResult = {
969 changes: 0,
970 lastInsertRowid: undefined,
971 };
972 sinon.stub(db.statement.topicContentFetchRequested, 'run').returns(dbResult);
973 try {
974 await db.topicFetchRequested(dbCtx, topicId);
975 assert.fail(noExpectedException);
976 } catch (e) {
977 assert(e instanceof DBErrors.UnexpectedResult);
978 }
979 });
980 }); // topicFetchRequested
981
982 describe('topicGetAll', function () {
983 it('success', async function() {
984 const expected = [{ id: topicId }];
985 sinon.stub(db.statement.topicGetInfoAll, 'all').returns(expected);
986 const result = await db.topicGetAll(dbCtx);
987 assert.deepStrictEqual(result, expected);
988 });
989 it('covers none', async function() {
990 const expected = undefined;
991 sinon.stub(db.statement.topicGetInfoAll, 'all').returns(expected);
992 const result = await db.topicGetAll(dbCtx);
993 assert.deepStrictEqual(result, expected);
994 });
995 it('failure', async function () {
996 const expected = new Error();
997 sinon.stub(db.statement.topicGetInfoAll, 'all').throws(expected);
998 try {
999 await db.topicGetAll(dbCtx);
1000 assert.fail(noExpectedException);
1001 } catch (e) {
1002 assert.deepStrictEqual(e, expected);
1003 }
1004 });
1005 }); // topicGetById
1006
1007 describe('topicGetById', function () {
1008 it('success', async function() {
1009 const expected = { id: topicId };
1010 sinon.stub(db.statement.topicGetById, 'get').returns(expected);
1011 const result = await db.topicGetById(dbCtx, topicId);
1012 assert.deepStrictEqual(result, expected);
1013 });
1014 it('covers no defaults', async function () {
1015 const expected = { id: topicId };
1016 sinon.stub(db.statement.topicGetById, 'get').returns(expected);
1017 const result = await db.topicGetById(dbCtx, topicId, false);
1018 assert.deepStrictEqual(result, expected);
1019 });
1020 it('covers default', async function() {
1021 const expected = undefined;
1022 sinon.stub(db.statement.topicGetById, 'get').returns(expected);
1023 const result = await db.topicGetById(dbCtx, topicId);
1024 assert.deepStrictEqual(result, expected);
1025 });
1026 it('failure', async function () {
1027 const expected = new Error();
1028 sinon.stub(db.statement.topicGetById, 'get').throws(expected);
1029 try {
1030 await db.topicGetById(dbCtx, topicId);
1031 assert.fail(noExpectedException);
1032 } catch (e) {
1033 assert.deepStrictEqual(e, expected);
1034 }
1035 });
1036 }); // topicGetById
1037
1038 describe('topicGetByUrl', function () {
1039 it('success', async function() {
1040 const expected = [];
1041 sinon.stub(db.statement.topicGetByUrl, 'get').returns(expected);
1042 const result = await db.topicGetByUrl(dbCtx, topicUrl);
1043 assert.deepStrictEqual(result, expected);
1044 });
1045 it('failure', async function () {
1046 const expected = new Error();
1047 sinon.stub(db.statement.topicGetByUrl, 'get').throws(expected);
1048 try {
1049 await db.topicGetByUrl(dbCtx, topicUrl);
1050 assert.fail(noExpectedException);
1051 } catch (e) {
1052 assert.deepStrictEqual(e, expected);
1053 }
1054 });
1055 }); // topicGetByUrl
1056
1057 describe('topicGetContentById', function () {
1058 it('success', async function() {
1059 const expected = { id: topicId };
1060 sinon.stub(db.statement.topicGetContentById, 'get').returns(expected);
1061 const result = await db.topicGetContentById(dbCtx, topicId);
1062 assert.deepStrictEqual(result, expected);
1063 });
1064 it('covers default', async function() {
1065 const expected = undefined;
1066 sinon.stub(db.statement.topicGetContentById, 'get').returns(expected);
1067 const result = await db.topicGetContentById(dbCtx, topicId);
1068 assert.deepStrictEqual(result, expected);
1069 });
1070 it('failure', async function () {
1071 const expected = new Error();
1072 sinon.stub(db.statement.topicGetContentById, 'get').throws(expected);
1073 try {
1074 await db.topicGetContentById(dbCtx, topicId);
1075 assert.fail(noExpectedException);
1076 } catch (e) {
1077 assert.deepStrictEqual(e, expected);
1078 }
1079 });
1080 }); // topicGetContentById
1081
1082 describe('topicPendingDelete', function () {
1083 beforeEach(function () {
1084 sinon.stub(db.statement.topicGetById, 'get');
1085 sinon.stub(db.statement.subscriptionCountByTopicUrl, 'get');
1086 sinon.stub(db.statement.topicDeleteById, 'run');
1087 });
1088 it('success', async function () {
1089 db.statement.topicGetById.get.returns({
1090 id: topicId,
1091 isDeleted: true,
1092 });
1093 db.statement.subscriptionCountByTopicUrl.get.returns({
1094 count: 0,
1095 });
1096 db.statement.topicDeleteById.run.returns({
1097 changes: 1,
1098 });
1099 db.topicPendingDelete(dbCtx, topicId);
1100 assert(db.statement.topicDeleteById.run.called);
1101 });
1102 it('does not delete non-deleted topic', async function () {
1103 db.statement.topicGetById.get.returns({
1104 id: topicId,
1105 isDeleted: false,
1106 });
1107 db.statement.subscriptionCountByTopicUrl.get.returns({
1108 count: 0,
1109 });
1110 db.statement.topicDeleteById.run.returns({
1111 changes: 1,
1112 });
1113 db.topicPendingDelete(dbCtx, topicId);
1114 assert(!db.statement.topicDeleteById.run.called);
1115 });
1116 it('does not delete topic with active subscriptions', async function () {
1117 db.statement.topicGetById.get.returns({
1118 id: topicId,
1119 isDeleted: true,
1120 });
1121 db.statement.subscriptionCountByTopicUrl.get.returns({
1122 count: 10,
1123 });
1124 db.statement.topicDeleteById.run.returns({
1125 changes: 1,
1126 });
1127 db.topicPendingDelete(dbCtx, topicId);
1128 assert(!db.statement.topicDeleteById.run.called);
1129 });
1130 it('covers no deletion', async function () {
1131 db.statement.topicGetById.get.returns({
1132 id: topicId,
1133 isDeleted: true,
1134 });
1135 db.statement.subscriptionCountByTopicUrl.get.returns({
1136 count: 0,
1137 });
1138 db.statement.topicDeleteById.run.returns({
1139 changes: 0,
1140 });
1141 try {
1142 db.topicPendingDelete(dbCtx, topicId);
1143 assert.fail(noExpectedException);
1144
1145 } catch (e) {
1146 assert(e instanceof DBErrors.UnexpectedResult);
1147 }
1148 assert(db.statement.topicDeleteById.run.called);
1149 });
1150 }); // topicPendingDelete
1151
1152 describe('topicPublishHistory', function () {
1153 beforeEach(function () {
1154 sinon.stub(db.statement.topicPublishHistory, 'all');
1155 });
1156 it('success', function () {
1157 db.statement.topicPublishHistory.all.returns([
1158 { daysAgo: 1, contentUpdates: 1 },
1159 { daysAgo: 3, contentUpdates: 2 },
1160 ]);
1161 const result = db.topicPublishHistory(dbCtx, topicId, 7);
1162 const expected = [0, 1, 0, 2, 0, 0, 0];
1163 assert.deepStrictEqual(result, expected);
1164 });
1165 }); // topicPublishHistory
1166
1167 describe('topicSet', function () {
1168 let data;
1169 beforeEach(function () {
1170 data = {
1171 url: topicUrl,
1172 };
1173 });
1174 it('success', async function() {
1175 const dbResult = {
1176 changes: 1,
1177 lastInsertRowid: topicId,
1178 };
1179 const expected = {
1180 changes: 1,
1181 lastInsertRowid: topicId,
1182 };
1183 sinon.stub(db.statement.topicUpsert, 'run').returns(dbResult);
1184 const result = await db.topicSet(dbCtx, data);
1185 assert.deepStrictEqual(result, expected);
1186 });
1187 it('failure', async function () {
1188 const dbResult = {
1189 changes: 0,
1190 lastInsertRowid: undefined,
1191 };
1192 sinon.stub(db.statement.topicUpsert, 'run').returns(dbResult);
1193 try {
1194 await db.topicSet(dbCtx, data);
1195 assert.fail(noExpectedException);
1196 } catch (e) {
1197 assert(e instanceof DBErrors.UnexpectedResult);
1198 }
1199 });
1200 it('fails invalid value', async function () {
1201 sinon.stub(db.statement.topicUpsert, 'run');
1202 try {
1203 data.leaseSecondsPreferred = -100;
1204 await db.topicSet(dbCtx, data);
1205 assert.fail(noExpectedException);
1206 } catch (e) {
1207 assert(e instanceof DBErrors.DataValidation);
1208 }
1209 assert(!db.statement.topicUpsert.run.called);
1210 });
1211 it('fails invalid values', async function () {
1212 sinon.stub(db.statement.topicUpsert, 'run');
1213 try {
1214 data.leaseSecondsPreferred = 10;
1215 data.leaseSecondsMax = 100;
1216 data.leaseSecondsMin = 50;
1217 await db.topicSet(dbCtx, data);
1218 assert.fail(noExpectedException);
1219 } catch (e) {
1220 assert(e instanceof DBErrors.DataValidation);
1221 }
1222 assert(!db.statement.topicUpsert.run.called);
1223 });
1224 }); // topicSet
1225
1226 describe('topicSetContent', function () {
1227 let data;
1228 beforeEach(function () {
1229 data = {
1230 content: 'content',
1231 contentType: 'text/plain',
1232 contentHash: 'abc123',
1233 };
1234 sinon.stub(db.statement.topicSetContent, 'run');
1235 sinon.stub(db.statement.topicSetContentHistory, 'run');
1236 });
1237 it('success', async function() {
1238 const dbResult = {
1239 changes: 1,
1240 lastInsertRowid: undefined,
1241 };
1242 const expected = {
1243 changes: 1,
1244 lastInsertRowid: undefined,
1245 };
1246 db.statement.topicSetContent.run.returns(dbResult);
1247 db.statement.topicSetContentHistory.run.returns(dbResult);
1248 const result = await db.topicSetContent(dbCtx, data);
1249 assert.deepStrictEqual(result, expected);
1250 });
1251 it('failure', async function () {
1252 const dbResult = {
1253 changes: 0,
1254 lastInsertRowid: undefined,
1255 };
1256 db.statement.topicSetContent.run.returns(dbResult);
1257 try {
1258 await db.topicSetContent(dbCtx, data);
1259 assert.fail(noExpectedException);
1260 } catch (e) {
1261 assert(e instanceof DBErrors.UnexpectedResult);
1262 }
1263 });
1264 it('failure 2', async function () {
1265 const dbResultSuccess = {
1266 changes: 1,
1267 lastInsertRowid: undefined,
1268 };
1269 const dbResultFail = {
1270 changes: 0,
1271 lastInsertRowid: undefined,
1272 };
1273 db.statement.topicSetContent.run.returns(dbResultSuccess);
1274 db.statement.topicSetContentHistory.run.returns(dbResultFail);
1275 try {
1276 await db.topicSetContent(dbCtx, data);
1277 assert.fail(noExpectedException);
1278 } catch (e) {
1279 assert(e instanceof DBErrors.UnexpectedResult);
1280 }
1281 });
1282 }); // topicSetContent
1283
1284 describe('topicUpdate', function () {
1285 let data;
1286 beforeEach(function () {
1287 data = {
1288 topicId,
1289 leaseSecondsPreferred: 9999,
1290 leaseSecondsMax: 99999,
1291 leaseSecondsMin: 999,
1292 publisherValidationUrl: null,
1293 contentHashAlgorithm: 'sha256',
1294 };
1295 });
1296 it('success', async function() {
1297 const dbResult = {
1298 changes: 1,
1299 lastInsertRowid: topicId,
1300 };
1301 sinon.stub(db.statement.topicUpdate, 'run').returns(dbResult);
1302 await db.topicUpdate(dbCtx, data);
1303 });
1304 it('failure', async function () {
1305 const dbResult = {
1306 changes: 0,
1307 lastInsertRowid: undefined,
1308 };
1309 sinon.stub(db.statement.topicUpdate, 'run').returns(dbResult);
1310 try {
1311 await db.topicUpdate(dbCtx, data);
1312 assert.fail(noExpectedException);
1313 } catch (e) {
1314 assert(e instanceof DBErrors.UnexpectedResult, e);
1315 }
1316 });
1317 it('fails invalid value', async function () {
1318 sinon.stub(db.statement.topicUpdate, 'run');
1319 try {
1320 data.leaseSecondsPreferred = -100;
1321 await db.topicUpdate(dbCtx, data);
1322 assert.fail(noExpectedException);
1323 } catch (e) {
1324 assert(e instanceof DBErrors.DataValidation, e);
1325 }
1326 assert(!db.statement.topicUpdate.run.called);
1327 });
1328 it('fails invalid values', async function () {
1329 sinon.stub(db.statement.topicUpdate, 'run');
1330 try {
1331 data.leaseSecondsPreferred = 10;
1332 data.leaseSecondsMax = 100;
1333 data.leaseSecondsMin = 50;
1334 await db.topicUpdate(dbCtx, data);
1335 assert.fail(noExpectedException);
1336 } catch (e) {
1337 assert(e instanceof DBErrors.DataValidation, e);
1338 }
1339 assert(!db.statement.topicUpdate.run.called);
1340 });
1341 }); // topicUpdate
1342
1343 describe('verificationClaim', function () {
1344 it('success', async function() {
1345 const dbAll = [{ id: verificationId }];
1346 const dbRun = {
1347 changes: 1,
1348 lastInsertRowid: undefined,
1349 };
1350 const expected = [verificationId];
1351 sinon.stub(db.statement.verificationNeeded, 'all').returns(dbAll);
1352 sinon.stub(db.statement.verificationClaimById, 'run').returns(dbRun);
1353 const result = await db.verificationClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
1354 assert.deepStrictEqual(result, expected);
1355 });
1356 it('failure', async function () {
1357 const dbAll = [{ id: verificationId }];
1358 const dbRun = {
1359 changes: 0,
1360 lastInsertRowid: undefined,
1361 };
1362 sinon.stub(db.statement.verificationNeeded, 'all').returns(dbAll);
1363 sinon.stub(db.statement.verificationClaimById, 'run').returns(dbRun);
1364 try {
1365 await db.verificationClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
1366 assert.fail(noExpectedException);
1367 } catch (e) {
1368 assert(e instanceof DBErrors.UnexpectedResult);
1369 }
1370 });
1371 }); // verificationClaim
1372
1373 describe('verificationClaimById', function () {
1374 it('success', async function() {
1375 const dbRun = {
1376 changes: 1,
1377 lastInsertRowid: undefined,
1378 };
1379 sinon.stub(db.statement.verificationClaimById, 'run').returns(dbRun);
1380 const result = await db.verificationClaimById(dbCtx, verificationId, claimTimeoutSeconds, claimant);
1381 assert.deepStrictEqual(result, dbRun);
1382 });
1383 it('failure', async function () {
1384 const dbRun = {
1385 changes: 0,
1386 lastInsertRowid: undefined,
1387 };
1388 sinon.stub(db.statement.verificationClaimById, 'run').returns(dbRun);
1389 try {
1390 await db.verificationClaimById(dbCtx, verificationId, claimTimeoutSeconds, claimant);
1391 assert.fail(noExpectedException);
1392 } catch (e) {
1393 assert(e instanceof DBErrors.UnexpectedResult);
1394 }
1395 });
1396 }); // verificationClaimById
1397
1398 describe('verificationComplete', function () {
1399 it('success', async function() {
1400 const dbResult = {
1401 changes: 1,
1402 lastInsertRowid: undefined,
1403 };
1404 sinon.stub(db.statement.verificationScrub, 'run').returns(dbResult);
1405 await db.verificationComplete(dbCtx, verificationId, callback, topicId);
1406 });
1407 it('failure', async function () {
1408 const dbResult = {
1409 changes: 0,
1410 lastInsertRowid: undefined,
1411 };
1412 sinon.stub(db.statement.verificationScrub, 'run').returns(dbResult);
1413 try {
1414 await db.verificationComplete(dbCtx, verificationId, callback, topicId);
1415 assert.fail(noExpectedException);
1416 } catch (e) {
1417 assert(e instanceof DBErrors.UnexpectedResult);
1418 }
1419 });
1420 }); // verificationComplete
1421
1422 describe('verificationGetById', function () {
1423 it('success', async function() {
1424 const dbOneOrNone = { id: verificationId, isPublisherValidated: 1 };
1425 const expected = { id: verificationId, isPublisherValidated: true };
1426 sinon.stub(db.statement.verificationGetById, 'get').returns(dbOneOrNone);
1427 const result = await db.verificationGetById(dbCtx, verificationId);
1428 assert.deepStrictEqual(result, expected);
1429 });
1430 it('failure', async function () {
1431 const expected = new Error();
1432 sinon.stub(db.statement.verificationGetById, 'get').throws(expected);
1433 try {
1434 await db.verificationGetById(dbCtx, verificationId);
1435 assert.fail(noExpectedException);
1436 } catch (e) {
1437 assert.deepStrictEqual(e, expected);
1438 }
1439 });
1440 }); // verificationGetById
1441
1442 describe('verificationIncomplete', function () {
1443 it('success', async function() {
1444 const dbOne = { attempts: 0 };
1445 const dbResult0 = {
1446 changes: 1,
1447 lastInsertRowid: undefined,
1448 };
1449 const dbResult1 = {
1450 changes: 1,
1451 lastInsertRowid: undefined,
1452 };
1453 sinon.stub(db.statement.verificationAttempts, 'get').returns(dbOne);
1454 sinon.stub(db.statement.verificationAttemptsIncrement, 'run').returns(dbResult0);
1455 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult1);
1456 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1457 });
1458 it('covers defaults', async function() {
1459 const dbOne = { attempts: 0 };
1460 const dbResult0 = {
1461 changes: 1,
1462 lastInsertRowid: undefined,
1463 };
1464 const dbResult1 = {
1465 changes: 1,
1466 lastInsertRowid: undefined,
1467 };
1468 sinon.stub(db.statement.verificationAttempts, 'get').returns(dbOne);
1469 sinon.stub(db.statement.verificationAttemptsIncrement, 'run').returns(dbResult0);
1470 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult1);
1471 await db.verificationIncomplete(dbCtx, verificationId);
1472 });
1473 it('failure', async function () {
1474 const dbOne = { attempts: 0 };
1475 const dbResult0 = {
1476 changes: 0,
1477 lastInsertRowid: undefined,
1478 };
1479 const dbResult1 = {
1480 changes: 1,
1481 lastInsertRowid: undefined,
1482 };
1483 sinon.stub(db.statement.verificationAttempts, 'get').returns(dbOne);
1484 sinon.stub(db.statement.verificationAttemptsIncrement, 'run').returns(dbResult0);
1485 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult1);
1486 try {
1487 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1488 assert.fail(noExpectedException);
1489 } catch (e) {
1490 assert(e instanceof DBErrors.UnexpectedResult);
1491 }
1492 });
1493 it('second failure', async function () {
1494 const dbOne = { attempts: 0 };
1495 const dbResult0 = {
1496 changes: 1,
1497 lastInsertRowid: undefined,
1498 };
1499 const dbResult1 = {
1500 changes: 0,
1501 lastInsertRowid: undefined,
1502 };
1503 sinon.stub(db.statement.verificationAttempts, 'get').returns(dbOne);
1504 sinon.stub(db.statement.verificationAttemptsIncrement, 'run').returns(dbResult0);
1505 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult1);
1506 try {
1507 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1508 assert.fail(noExpectedException);
1509 } catch (e) {
1510 assert(e instanceof DBErrors.UnexpectedResult);
1511 }
1512 });
1513 }); // verificationIncomplete
1514
1515 describe('_verificationDataToEngine', function () {
1516 it('covers no data', function () {
1517 DB._verificationDataToEngine();
1518 });
1519 it('covers true', function () {
1520 const data = {
1521 isPublisherValidated: true,
1522 };
1523 DB._verificationDataToEngine(data);
1524 assert.strictEqual(data.isPublisherValidated, 1);
1525 });
1526 it('covers false', function () {
1527 const data = {
1528 isPublisherValidated: false,
1529 };
1530 DB._verificationDataToEngine(data);
1531 assert.strictEqual(data.isPublisherValidated, 0);
1532 });
1533 }); // _verificationDataToEngine
1534
1535 describe('verificationInsert', function () {
1536 let verification;
1537 beforeEach(function () {
1538 verification = {
1539 topicId,
1540 callback,
1541 mode: 'subscribe',
1542 isPublisherValidated: true,
1543 leaseSeconds: 86400,
1544 };
1545 });
1546 it('success', async function() {
1547 const dbResult = {
1548 changes: 1,
1549 lastInsertRowid: verificationId,
1550 };
1551 const expected = verificationId;
1552 sinon.stub(db.statement.verificationInsert, 'run').returns(dbResult);
1553 const result = await db.verificationInsert(dbCtx, verification);
1554 assert.deepStrictEqual(result, expected);
1555 });
1556 it('failure', async function () {
1557 const dbResult = {
1558 changes: 0,
1559 lastInsertRowid: undefined,
1560 };
1561 sinon.stub(db.statement.verificationInsert, 'run').returns(dbResult);
1562 try {
1563 await db.verificationInsert(dbCtx, verification);
1564 assert.fail(noExpectedException);
1565 } catch (e) {
1566 assert(e instanceof DBErrors.UnexpectedResult);
1567 }
1568 });
1569 it('fails validation', async function () {
1570 delete verification.leaseSeconds;
1571 try {
1572 await db.verificationInsert(dbCtx, verification);
1573 assert.fail(noExpectedException);
1574 } catch (e) {
1575 assert(e instanceof DBErrors.DataValidation);
1576 }
1577 });
1578 }); // verificationInsert
1579
1580 describe('verificationRelease', function () {
1581 it('success', async function() {
1582 const dbResult = {
1583 changes: 1,
1584 lastInsertRowid: undefined,
1585 };
1586 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult);
1587 await db.verificationRelease(dbCtx, verificationId);
1588 });
1589 it('failure', async function () {
1590 const dbResult = {
1591 changes: 0,
1592 lastInsertRowid: undefined,
1593 };
1594 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult);
1595 try {
1596 await db.verificationRelease(dbCtx, verificationId);
1597 assert.fail(noExpectedException);
1598 } catch (e) {
1599 assert(e instanceof DBErrors.UnexpectedResult);
1600 }
1601 });
1602 }); // verificationRelease
1603
1604 describe('verificationUpdate', function () {
1605 let data;
1606 beforeEach(function () {
1607 data = {
1608 mode: 'subscribe',
1609 isPublisherValidated: true,
1610 };
1611 });
1612 it('success', async function() {
1613 const dbResult = {
1614 changes: 1,
1615 lastInsertRowid: undefined,
1616 };
1617 sinon.stub(db.statement.verificationUpdate, 'run').returns(dbResult);
1618 await db.verificationUpdate(dbCtx, verificationId, data);
1619 });
1620 it('failure', async function () {
1621 const dbResult = {
1622 changes: 0,
1623 lastInsertRowid: undefined,
1624 };
1625 sinon.stub(db.statement.verificationUpdate, 'run').returns(dbResult);
1626 try {
1627 await db.verificationUpdate(dbCtx, verificationId, data);
1628 assert.fail(noExpectedException);
1629 } catch (e) {
1630 assert(e instanceof DBErrors.UnexpectedResult, e.name);
1631 }
1632 });
1633 it('fails validation', async function () {
1634 delete data.mode;
1635 try {
1636 await db.verificationUpdate(dbCtx, data);
1637 assert.fail(noExpectedException);
1638 } catch (e) {
1639 assert(e instanceof DBErrors.DataValidation);
1640 }
1641 });
1642 }); // verificationUpdate
1643
1644 describe('verificationValidated', function () {
1645 it('success', async function() {
1646 const dbResult = {
1647 changes: 1,
1648 lastInsertRowid: undefined,
1649 };
1650 sinon.stub(db.statement.verificationValidate, 'run').returns(dbResult);
1651 await db.verificationValidated(dbCtx, verificationId);
1652 });
1653 it('failure', async function () {
1654 const dbResult = {
1655 changes: 0,
1656 lastInsertRowid: undefined,
1657 };
1658 sinon.stub(db.statement.verificationValidate, 'run').returns(dbResult);
1659 try {
1660 await db.verificationValidated(dbCtx, verificationId);
1661 assert.fail(noExpectedException);
1662 } catch (e) {
1663 assert(e instanceof DBErrors.UnexpectedResult);
1664 }
1665 });
1666 }); // verificationValidated
1667
1668 }); // DatabaseSQLite