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