db migration 1.0.2, now stores and indexes date of content delivered to subscriber...
[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 });
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 });
1155
1156 describe('topicSet', function () {
1157 let data;
1158 beforeEach(function () {
1159 data = {
1160 url: topicUrl,
1161 };
1162 });
1163 it('success', async function() {
1164 const dbResult = {
1165 changes: 1,
1166 lastInsertRowid: topicId,
1167 };
1168 const expected = {
1169 changes: 1,
1170 lastInsertRowid: topicId,
1171 };
1172 sinon.stub(db.statement.topicUpsert, 'run').returns(dbResult);
1173 const result = await db.topicSet(dbCtx, data);
1174 assert.deepStrictEqual(result, expected);
1175 });
1176 it('failure', async function () {
1177 const dbResult = {
1178 changes: 0,
1179 lastInsertRowid: undefined,
1180 };
1181 sinon.stub(db.statement.topicUpsert, 'run').returns(dbResult);
1182 try {
1183 await db.topicSet(dbCtx, data);
1184 assert.fail(noExpectedException);
1185 } catch (e) {
1186 assert(e instanceof DBErrors.UnexpectedResult);
1187 }
1188 });
1189 it('fails invalid value', async function () {
1190 sinon.stub(db.statement.topicUpsert, 'run');
1191 try {
1192 data.leaseSecondsPreferred = -100;
1193 await db.topicSet(dbCtx, data);
1194 assert.fail(noExpectedException);
1195 } catch (e) {
1196 assert(e instanceof DBErrors.DataValidation);
1197 }
1198 assert(!db.statement.topicUpsert.run.called);
1199 });
1200 it('fails invalid values', async function () {
1201 sinon.stub(db.statement.topicUpsert, 'run');
1202 try {
1203 data.leaseSecondsPreferred = 10;
1204 data.leaseSecondsMax = 100;
1205 data.leaseSecondsMin = 50;
1206 await db.topicSet(dbCtx, data);
1207 assert.fail(noExpectedException);
1208 } catch (e) {
1209 assert(e instanceof DBErrors.DataValidation);
1210 }
1211 assert(!db.statement.topicUpsert.run.called);
1212 });
1213 }); // topicSet
1214
1215 describe('topicSetContent', function () {
1216 let data;
1217 beforeEach(function () {
1218 data = {
1219 content: 'content',
1220 contentType: 'text/plain',
1221 contentHash: 'abc123',
1222 };
1223 });
1224 it('success', async function() {
1225 const dbResult = {
1226 changes: 1,
1227 lastInsertRowid: undefined,
1228 };
1229 const expected = {
1230 changes: 1,
1231 lastInsertRowid: undefined,
1232 };
1233 sinon.stub(db.statement.topicSetContent, 'run').returns(dbResult);
1234 const result = await db.topicSetContent(dbCtx, data);
1235 assert.deepStrictEqual(result, expected);
1236 });
1237 it('failure', async function () {
1238 const dbResult = {
1239 changes: 0,
1240 lastInsertRowid: undefined,
1241 };
1242 sinon.stub(db.statement.topicSetContent, 'run').returns(dbResult);
1243 try {
1244 await db.topicSetContent(dbCtx, data);
1245 assert.fail(noExpectedException);
1246 } catch (e) {
1247 assert(e instanceof DBErrors.UnexpectedResult);
1248 }
1249 });
1250 }); // topicSetContent
1251
1252 describe('topicUpdate', function () {
1253 let data;
1254 beforeEach(function () {
1255 data = {
1256 topicId,
1257 leaseSecondsPreferred: 9999,
1258 leaseSecondsMax: 99999,
1259 leaseSecondsMin: 999,
1260 publisherValidationUrl: null,
1261 contentHashAlgorithm: 'sha256',
1262 };
1263 });
1264 it('success', async function() {
1265 const dbResult = {
1266 changes: 1,
1267 lastInsertRowid: topicId,
1268 };
1269 sinon.stub(db.statement.topicUpdate, 'run').returns(dbResult);
1270 await db.topicUpdate(dbCtx, data);
1271 });
1272 it('failure', async function () {
1273 const dbResult = {
1274 changes: 0,
1275 lastInsertRowid: undefined,
1276 };
1277 sinon.stub(db.statement.topicUpdate, 'run').returns(dbResult);
1278 try {
1279 await db.topicUpdate(dbCtx, data);
1280 assert.fail(noExpectedException);
1281 } catch (e) {
1282 assert(e instanceof DBErrors.UnexpectedResult, e);
1283 }
1284 });
1285 it('fails invalid value', async function () {
1286 sinon.stub(db.statement.topicUpdate, 'run');
1287 try {
1288 data.leaseSecondsPreferred = -100;
1289 await db.topicUpdate(dbCtx, data);
1290 assert.fail(noExpectedException);
1291 } catch (e) {
1292 assert(e instanceof DBErrors.DataValidation, e);
1293 }
1294 assert(!db.statement.topicUpdate.run.called);
1295 });
1296 it('fails invalid values', async function () {
1297 sinon.stub(db.statement.topicUpdate, 'run');
1298 try {
1299 data.leaseSecondsPreferred = 10;
1300 data.leaseSecondsMax = 100;
1301 data.leaseSecondsMin = 50;
1302 await db.topicUpdate(dbCtx, data);
1303 assert.fail(noExpectedException);
1304 } catch (e) {
1305 assert(e instanceof DBErrors.DataValidation, e);
1306 }
1307 assert(!db.statement.topicUpdate.run.called);
1308 });
1309 }); // topicUpdate
1310
1311 describe('verificationClaim', function () {
1312 it('success', async function() {
1313 const dbAll = [{ id: verificationId }];
1314 const dbRun = {
1315 changes: 1,
1316 lastInsertRowid: undefined,
1317 };
1318 const expected = [verificationId];
1319 sinon.stub(db.statement.verificationNeeded, 'all').returns(dbAll);
1320 sinon.stub(db.statement.verificationClaimById, 'run').returns(dbRun);
1321 const result = await db.verificationClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
1322 assert.deepStrictEqual(result, expected);
1323 });
1324 it('failure', async function () {
1325 const dbAll = [{ id: verificationId }];
1326 const dbRun = {
1327 changes: 0,
1328 lastInsertRowid: undefined,
1329 };
1330 sinon.stub(db.statement.verificationNeeded, 'all').returns(dbAll);
1331 sinon.stub(db.statement.verificationClaimById, 'run').returns(dbRun);
1332 try {
1333 await db.verificationClaim(dbCtx, wanted, claimTimeoutSeconds, claimant);
1334 assert.fail(noExpectedException);
1335 } catch (e) {
1336 assert(e instanceof DBErrors.UnexpectedResult);
1337 }
1338 });
1339 }); // verificationClaim
1340
1341 describe('verificationClaimById', function () {
1342 it('success', async function() {
1343 const dbRun = {
1344 changes: 1,
1345 lastInsertRowid: undefined,
1346 };
1347 sinon.stub(db.statement.verificationClaimById, 'run').returns(dbRun);
1348 const result = await db.verificationClaimById(dbCtx, verificationId, claimTimeoutSeconds, claimant);
1349 assert.deepStrictEqual(result, dbRun);
1350 });
1351 it('failure', async function () {
1352 const dbRun = {
1353 changes: 0,
1354 lastInsertRowid: undefined,
1355 };
1356 sinon.stub(db.statement.verificationClaimById, 'run').returns(dbRun);
1357 try {
1358 await db.verificationClaimById(dbCtx, verificationId, claimTimeoutSeconds, claimant);
1359 assert.fail(noExpectedException);
1360 } catch (e) {
1361 assert(e instanceof DBErrors.UnexpectedResult);
1362 }
1363 });
1364 }); // verificationClaimById
1365
1366 describe('verificationComplete', function () {
1367 it('success', async function() {
1368 const dbResult = {
1369 changes: 1,
1370 lastInsertRowid: undefined,
1371 };
1372 sinon.stub(db.statement.verificationScrub, 'run').returns(dbResult);
1373 await db.verificationComplete(dbCtx, verificationId, callback, topicId);
1374 });
1375 it('failure', async function () {
1376 const dbResult = {
1377 changes: 0,
1378 lastInsertRowid: undefined,
1379 };
1380 sinon.stub(db.statement.verificationScrub, 'run').returns(dbResult);
1381 try {
1382 await db.verificationComplete(dbCtx, verificationId, callback, topicId);
1383 assert.fail(noExpectedException);
1384 } catch (e) {
1385 assert(e instanceof DBErrors.UnexpectedResult);
1386 }
1387 });
1388 }); // verificationComplete
1389
1390 describe('verificationGetById', function () {
1391 it('success', async function() {
1392 const dbOneOrNone = { id: verificationId, isPublisherValidated: 1 };
1393 const expected = { id: verificationId, isPublisherValidated: true };
1394 sinon.stub(db.statement.verificationGetById, 'get').returns(dbOneOrNone);
1395 const result = await db.verificationGetById(dbCtx, verificationId);
1396 assert.deepStrictEqual(result, expected);
1397 });
1398 it('failure', async function () {
1399 const expected = new Error();
1400 sinon.stub(db.statement.verificationGetById, 'get').throws(expected);
1401 try {
1402 await db.verificationGetById(dbCtx, verificationId);
1403 assert.fail(noExpectedException);
1404 } catch (e) {
1405 assert.deepStrictEqual(e, expected);
1406 }
1407 });
1408 }); // verificationGetById
1409
1410 describe('verificationIncomplete', function () {
1411 it('success', async function() {
1412 const dbOne = { attempts: 0 };
1413 const dbResult0 = {
1414 changes: 1,
1415 lastInsertRowid: undefined,
1416 };
1417 const dbResult1 = {
1418 changes: 1,
1419 lastInsertRowid: undefined,
1420 };
1421 sinon.stub(db.statement.verificationAttempts, 'get').returns(dbOne);
1422 sinon.stub(db.statement.verificationAttemptsIncrement, 'run').returns(dbResult0);
1423 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult1);
1424 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1425 });
1426 it('covers defaults', async function() {
1427 const dbOne = { attempts: 0 };
1428 const dbResult0 = {
1429 changes: 1,
1430 lastInsertRowid: undefined,
1431 };
1432 const dbResult1 = {
1433 changes: 1,
1434 lastInsertRowid: undefined,
1435 };
1436 sinon.stub(db.statement.verificationAttempts, 'get').returns(dbOne);
1437 sinon.stub(db.statement.verificationAttemptsIncrement, 'run').returns(dbResult0);
1438 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult1);
1439 await db.verificationIncomplete(dbCtx, verificationId);
1440 });
1441 it('failure', async function () {
1442 const dbOne = { attempts: 0 };
1443 const dbResult0 = {
1444 changes: 0,
1445 lastInsertRowid: undefined,
1446 };
1447 const dbResult1 = {
1448 changes: 1,
1449 lastInsertRowid: undefined,
1450 };
1451 sinon.stub(db.statement.verificationAttempts, 'get').returns(dbOne);
1452 sinon.stub(db.statement.verificationAttemptsIncrement, 'run').returns(dbResult0);
1453 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult1);
1454 try {
1455 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1456 assert.fail(noExpectedException);
1457 } catch (e) {
1458 assert(e instanceof DBErrors.UnexpectedResult);
1459 }
1460 });
1461 it('second failure', async function () {
1462 const dbOne = { attempts: 0 };
1463 const dbResult0 = {
1464 changes: 1,
1465 lastInsertRowid: undefined,
1466 };
1467 const dbResult1 = {
1468 changes: 0,
1469 lastInsertRowid: undefined,
1470 };
1471 sinon.stub(db.statement.verificationAttempts, 'get').returns(dbOne);
1472 sinon.stub(db.statement.verificationAttemptsIncrement, 'run').returns(dbResult0);
1473 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult1);
1474 try {
1475 await db.verificationIncomplete(dbCtx, verificationId, retryDelays);
1476 assert.fail(noExpectedException);
1477 } catch (e) {
1478 assert(e instanceof DBErrors.UnexpectedResult);
1479 }
1480 });
1481 }); // verificationIncomplete
1482
1483 describe('_verificationDataToEngine', function () {
1484 it('covers no data', function () {
1485 DB._verificationDataToEngine();
1486 });
1487 it('covers true', function () {
1488 const data = {
1489 isPublisherValidated: true,
1490 };
1491 DB._verificationDataToEngine(data);
1492 assert.strictEqual(data.isPublisherValidated, 1);
1493 });
1494 it('covers false', function () {
1495 const data = {
1496 isPublisherValidated: false,
1497 };
1498 DB._verificationDataToEngine(data);
1499 assert.strictEqual(data.isPublisherValidated, 0);
1500 });
1501 }) // _verificationDataToEngine
1502
1503 describe('verificationInsert', function () {
1504 let verification;
1505 beforeEach(function () {
1506 verification = {
1507 topicId,
1508 callback,
1509 mode: 'subscribe',
1510 isPublisherValidated: true,
1511 leaseSeconds: 86400,
1512 };
1513 });
1514 it('success', async function() {
1515 const dbResult = {
1516 changes: 1,
1517 lastInsertRowid: verificationId,
1518 };
1519 const expected = verificationId;
1520 sinon.stub(db.statement.verificationInsert, 'run').returns(dbResult);
1521 const result = await db.verificationInsert(dbCtx, verification);
1522 assert.deepStrictEqual(result, expected);
1523 });
1524 it('failure', async function () {
1525 const dbResult = {
1526 changes: 0,
1527 lastInsertRowid: undefined,
1528 };
1529 sinon.stub(db.statement.verificationInsert, 'run').returns(dbResult);
1530 try {
1531 await db.verificationInsert(dbCtx, verification);
1532 assert.fail(noExpectedException);
1533 } catch (e) {
1534 assert(e instanceof DBErrors.UnexpectedResult);
1535 }
1536 });
1537 it('fails validation', async function () {
1538 delete verification.leaseSeconds;
1539 try {
1540 await db.verificationInsert(dbCtx, verification);
1541 assert.fail(noExpectedException);
1542 } catch (e) {
1543 assert(e instanceof DBErrors.DataValidation);
1544 }
1545 });
1546 }); // verificationInsert
1547
1548 describe('verificationRelease', function () {
1549 it('success', async function() {
1550 const dbResult = {
1551 changes: 1,
1552 lastInsertRowid: undefined,
1553 };
1554 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult);
1555 await db.verificationRelease(dbCtx, verificationId);
1556 });
1557 it('failure', async function () {
1558 const dbResult = {
1559 changes: 0,
1560 lastInsertRowid: undefined,
1561 };
1562 sinon.stub(db.statement.verificationDone, 'run').returns(dbResult);
1563 try {
1564 await db.verificationRelease(dbCtx, verificationId);
1565 assert.fail(noExpectedException);
1566 } catch (e) {
1567 assert(e instanceof DBErrors.UnexpectedResult);
1568 }
1569 });
1570 }); // verificationRelease
1571
1572 describe('verificationUpdate', function () {
1573 let data;
1574 beforeEach(function () {
1575 data = {
1576 mode: 'subscribe',
1577 isPublisherValidated: true,
1578 };
1579 });
1580 it('success', async function() {
1581 const dbResult = {
1582 changes: 1,
1583 lastInsertRowid: undefined,
1584 };
1585 sinon.stub(db.statement.verificationUpdate, 'run').returns(dbResult);
1586 await db.verificationUpdate(dbCtx, verificationId, data);
1587 });
1588 it('failure', async function () {
1589 const dbResult = {
1590 changes: 0,
1591 lastInsertRowid: undefined,
1592 }
1593 sinon.stub(db.statement.verificationUpdate, 'run').returns(dbResult);
1594 try {
1595 await db.verificationUpdate(dbCtx, verificationId, data);
1596 assert.fail(noExpectedException);
1597 } catch (e) {
1598 assert(e instanceof DBErrors.UnexpectedResult, e.name);
1599 }
1600 });
1601 it('fails validation', async function () {
1602 delete data.mode;
1603 try {
1604 await db.verificationUpdate(dbCtx, data);
1605 assert.fail(noExpectedException);
1606 } catch (e) {
1607 assert(e instanceof DBErrors.DataValidation);
1608 }
1609 });
1610 }); // verificationUpdate
1611
1612 describe('verificationValidated', function () {
1613 it('success', async function() {
1614 const dbResult = {
1615 changes: 1,
1616 lastInsertRowid: undefined,
1617 }
1618 sinon.stub(db.statement.verificationValidate, 'run').returns(dbResult);
1619 await db.verificationValidated(dbCtx, verificationId);
1620 });
1621 it('failure', async function () {
1622 const dbResult = {
1623 changes: 0,
1624 lastInsertRowid: undefined,
1625 }
1626 sinon.stub(db.statement.verificationValidate, 'run').returns(dbResult);
1627 try {
1628 await db.verificationValidated(dbCtx, verificationId);
1629 assert.fail(noExpectedException);
1630 } catch (e) {
1631 assert(e instanceof DBErrors.UnexpectedResult);
1632 }
1633 });
1634 }); // verificationValidated
1635
1636 }); // DatabaseSQLite