3 /* This provides implementation coverage, stubbing pg-promise. */
5 const assert
= require('node:assert');
6 const sinon
= require('sinon');
7 const DBStub
= require('../../stub-db');
8 const stubLogger
= require('../../stub-logger');
9 const DB
= require('../../../src/db/postgres');
10 const DBErrors
= require('../../../src/db/errors');
11 const common
= require('../../../src/common');
12 const Config
= require('../../../config');
14 const noExpectedException
= 'did not receive expected exception';
16 describe('DatabasePostgres', function () {
17 let db
, options
, pgpStub
;
18 let dbCtx
, claimant
, claimTimeoutSeconds
, callback
, subscriptionId
, topicId
, verificationId
;
19 let topicUrl
, leaseSeconds
, secret
, httpRemoteAddr
, httpFrom
, retryDelays
, wanted
;
23 result: () => ({ rows: [] }),
28 manyOrNone: common
.nop
,
29 oneOrNone: common
.nop
,
32 multiResult: common
.nop
,
35 stub
.tx
= (fn
) => fn(stub
);
36 stub
.txIf
= (fn
) => fn(stub
);
37 stub
.task
= (fn
) => fn(stub
);
43 pgpStub
.QueryFile
= class {};
44 pgpStub
.end
= common
.nop
,
45 options
= new Config('test');
46 db
= new DB(stubLogger
, options
, pgpStub
);
48 beforeEach(function () {
51 claimant
= '19af19b8-6be3-4a6f-8946-65f5f1ccc5d7';
52 claimTimeoutSeconds
= 300;
53 subscriptionId
= 'fbaf8f19-ed9c-4a21-89ae-98b7005e3bf6';
54 topicUrl
= 'https://example.com/blog';
55 callback
= 'https://example.com/callback?id=123';
56 topicId
= 'c59d4bda-10ad-41d9-99df-4ce8bc331424';
57 verificationId
= '55cd7748-d2d5-11eb-b355-0025905f714a';
61 httpRemoteAddr
= '127.0.0.1';
62 httpFrom
= 'user@example.com';
65 afterEach(function () {
69 it('covers listener', function () {
70 const listenerOptions
= new Config('test');
71 listenerOptions
.db
.cacheEnabled
= true;
72 const listenerDb
= new DB(stubLogger
, listenerOptions
, pgpStub
);
76 // Ensure all interface methods are implemented
77 describe('Implementation', function () {
78 it('implements interface', async
function () {
79 const results
= await Promise
.allSettled(DBStub
._implementation
.map(async (fn
) => {
81 // eslint-disable-next-line security/detect-object-injection
84 assert(!(e
instanceof DBErrors
.NotImplemented
), `${fn} not implemented`);
87 const failures
= results
.filter((x
) => x
.status
=== 'rejected');
88 assert(!failures
.length
, failures
.map((x
) => {
89 x
= x
.reason
.toString();
90 return x
.slice(x
.indexOf(': '));
95 describe('pgpInitOptions', function () {
96 describe('error', function () {
97 it('covers', function () {
100 db
.pgpInitOptions
.error(err
, event
);
101 assert(db
.logger
.error
.called
);
104 describe('query', function () {
105 it('covers', function () {
107 db
.pgpInitOptions
.query(event
);
108 assert(db
.logger
.debug
.called
);
110 it('covers NOTIFY', function () {
111 const event
= { query: 'NOTIFY thing' };
112 db
.pgpInitOptions
.query(event
);
113 assert(!db
.logger
.debug
.called
);
116 describe('receive', function () {
117 it('covers', function () {
120 column_one: 'one', // eslint-disable-line camelcase
121 column_two: 2, // eslint-disable-line camelcase
124 column_one: 'foo', // eslint-disable-line camelcase
125 column_two: 4, // eslint-disable-line camelcase
130 const expectedData
= [
140 db
.pgpInitOptions
.receive({ data
, result
, ctx: event
});
141 assert(db
.logger
.debug
.called
);
142 assert
.deepStrictEqual(data
, expectedData
);
144 it('covers NOTIFY', function () {
147 column_one: 'one', // eslint-disable-line camelcase
148 column_two: 2, // eslint-disable-line camelcase
151 column_one: 'foo', // eslint-disable-line camelcase
152 column_two: 4, // eslint-disable-line camelcase
159 const expectedData
= [
169 db
.pgpInitOptions
.receive({ data
, result
, ctx: event
});
170 assert(!db
.logger
.debug
.called
);
171 assert
.deepStrictEqual(data
, expectedData
);
174 }); // pgpInitOptions
176 describe('_initTables', function () {
177 beforeEach(function () {
178 sinon
.stub(db
.db
, 'oneOrNone');
179 sinon
.stub(db
.db
, 'multiResult');
180 sinon
.stub(db
, '_currentSchema');
183 it('covers apply', async
function() {
184 db
.db
.oneOrNone
.onCall(0).resolves(null).onCall(1).resolves({});
185 db
._currentSchema
.resolves({ major: 0, minor: 0, patch: 0 });
186 await db
._initTables();
188 it('covers exists', async
function() {
189 db
.db
.oneOrNone
.resolves({});
190 db
._currentSchema
.resolves(db
.schemaVersionsSupported
.max
);
191 await db
._initTables();
195 describe('initialize', function () {
199 it('passes supported version', async
function () {
200 const version
= { major: 1, minor: 0, patch: 0 };
201 sinon
.stub(db
.db
, 'one').resolves(version
);
202 await db
.initialize(false);
204 it('fails low version', async
function () {
205 const version
= { major: 0, minor: 0, patch: 0 };
206 sinon
.stub(db
.db
, 'one').resolves(version
);
208 await db
.initialize(false);
209 assert
.fail(noExpectedException
);
211 assert(e
instanceof DBErrors
.MigrationNeeded
);
214 it('fails high version', async
function () {
215 const version
= { major: 100, minor: 100, patch: 100 };
216 sinon
.stub(db
.db
, 'one').resolves(version
);
218 await db
.initialize(false);
219 assert
.fail(noExpectedException
);
221 assert(e
instanceof DBErrors
.MigrationNeeded
);
224 it('covers migration', async
function() {
225 sinon
.stub(db
.db
, 'oneOrNone').resolves({});
226 sinon
.stub(db
.db
, 'multiResult').resolves({});
227 sinon
.stub(db
, '_currentSchema').resolves(db
.schemaVersionsSupported
.min
);
228 sinon
.stub(db
.db
, 'one').resolves(db
.schemaVersionsSupported
.max
);
229 await db
.initialize();
231 it('covers migration failure', async
function() {
232 const expected
= new Error('oh no');
233 sinon
.stub(db
.db
, 'oneOrNone').resolves({});
234 sinon
.stub(db
.db
, 'multiResult').rejects(expected
);
235 sinon
.stub(db
, '_currentSchema').resolves(db
.schemaVersionsSupported
.min
);
236 sinon
.stub(db
.db
, 'one').resolves(db
.schemaVersionsSupported
.max
);
238 await db
.initialize();
239 assert
.fail(noExpectedException
);
241 assert
.deepStrictEqual(e
, expected
);
244 it('covers listener', async
function() {
248 const version
= { major: 1, minor: 0, patch: 0 };
249 sinon
.stub(db
.db
, 'one').resolves(version
);
250 await db
.initialize(false);
251 assert(db
.listener
.start
.called
);
255 describe('healthCheck', function () {
256 beforeEach(function () {
257 sinon
.stub(db
.db
, 'connect').resolves({
260 serverVersion: '0.0',
264 it('covers', async
function () {
265 const result
= await db
.healthCheck();
266 assert
.deepStrictEqual(result
, { serverVersion: '0.0' });
270 describe('_queryFileHelper', function () {
271 it('covers success', function () {
272 const _queryFile
= db
._queryFileHelper(pgpStub
);
275 it('covers failure', function () {
276 const err
= new Error();
277 pgpStub
.QueryFile
= class {
282 const _queryFile
= db
._queryFileHelper(pgpStub
);
285 assert
.fail(noExpectedException
);
287 assert
.strictEqual(e
, err
);
290 }); // _queryFileHelper
292 describe('_closeConnection', function () {
296 it('success', async
function () {
297 sinon
.stub(db
._pgp
, 'end');
298 await db
._closeConnection();
299 assert(db
._pgp
.end
.called
);
301 it('failure', async
function () {
302 const expected
= new Error();
303 sinon
.stub(db
._pgp
, 'end').throws(expected
);
305 await db
._closeConnection();
306 assert
.fail(noExpectedException
);
308 assert
.deepStrictEqual(e
, expected
);
311 it('covers listener', async
function () {
315 sinon
.stub(db
._pgp
, 'end');
316 await db
._closeConnection();
317 assert(db
._pgp
.end
.called
);
319 }); // _closeConnection
321 describe('_purgeTables', function () {
322 it('covers not really', async
function () {
323 sinon
.stub(db
.db
, 'tx');
324 await db
._purgeTables(false);
325 assert(!db
.db
.tx
.called
);
327 it('success', async
function () {
328 sinon
.stub(db
.db
, 'batch');
329 await db
._purgeTables(true);
330 assert(db
.db
.batch
.called
);
332 it('failure', async
function () {
333 const expected
= new Error();
334 sinon
.stub(db
.db
, 'tx').rejects(expected
);
336 await db
._purgeTables(true);
337 assert
.fail(noExpectedException
);
339 assert
.deepStrictEqual(e
, expected
);
344 describe('_topicChanged', function () {
345 beforeEach(function () {
346 db
.cache
= new Map();
347 sinon
.stub(db
.cache
, 'delete');
352 it('covers', function () {
353 db
._topicChanged('topic-id');
354 assert(db
.cache
.delete.called
);
356 it('ignores ping', function () {
357 db
._topicChanged('ping');
358 assert(!db
.cache
.delete.called
);
362 describe('_listenerEstablished', function () {
363 it('creates cache', function () {
365 db
._listenerEstablished();
366 assert(db
.cache
instanceof Map
);
368 }); // _listenerEstablished
370 describe('_listenerLost', function () {
371 it('removes cache', function () {
372 db
.cache
= new Map();
378 describe('_cacheGet', function () {
380 beforeEach(function () {
383 it('nothing if no cache', function () {
385 const result
= db
._cacheGet(key
);
386 assert
.strictEqual(result
, undefined);
388 it('nothing if no entry', function () {
389 db
.cache
= new Map();
390 const result
= db
._cacheGet(key
);
391 assert
.strictEqual(result
, undefined);
393 it('returns cached entry', function () {
394 db
.cache
= new Map();
398 db
._cacheSet(key
, expected
);
399 const result
= db
._cacheGet(key
);
400 assert
.deepStrictEqual(result
, expected
);
404 describe('_cacheSet', function () {
406 beforeEach(function () {
409 it('covers no cache', function () {
411 db
._cacheSet(key
, 'data');
413 it('covers cache', function () {
414 db
.cache
= new Map();
415 const expected
= 'blah';
416 db
._cacheSet(key
, expected
);
417 const result
= db
._cacheGet(key
);
418 assert
.deepStrictEqual(result
, expected
);
422 describe('context', function () {
423 it('covers', async
function () {
424 await db
.context(common
.nop
);
428 describe('transaction', function () {
429 it('covers', async
function () {
430 await db
.transaction(db
.db
, common
.nop
);
434 describe('authenticationSuccess', function () {
436 beforeEach(function () {
437 identifier
= 'username';
439 it('success', async
function () {
445 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
446 await db
.authenticationSuccess(dbCtx
, identifier
);
448 it('failure', async
function() {
454 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
456 await db
.authenticationSuccess(dbCtx
, identifier
);
457 assert
.fail(noExpectedException
);
459 assert(e
instanceof DBErrors
.UnexpectedResult
);
462 }); // authenticationSuccess
464 describe('authenticationGet', function () {
465 let identifier
, credential
;
466 beforeEach(function () {
467 identifier
= 'username';
468 credential
= '$z$foo';
470 it('success', async
function () {
471 const dbResult
= { identifier
, credential
};
472 sinon
.stub(db
.db
, 'oneOrNone').resolves(dbResult
);
473 const result
= await db
.authenticationGet(dbCtx
, identifier
);
474 assert
.deepStrictEqual(result
, dbResult
);
476 it('failure', async
function() {
477 const expected
= new Error('blah');
478 sinon
.stub(db
.db
, 'oneOrNone').rejects(expected
);
480 await db
.authenticationGet(dbCtx
, identifier
, credential
);
481 assert
.fail(noExpectedException
);
483 assert
.deepStrictEqual(e
, expected
);
486 }); // authenticationGet
488 describe('authenticationUpsert', function () {
489 let identifier
, credential
, otpKey
;
490 beforeEach(function () {
491 identifier
= 'username';
492 credential
= '$z$foo';
493 otpKey
= '12345678901234567890123456789012';
495 it('success', async
function () {
501 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
502 await db
.authenticationUpsert(dbCtx
, identifier
, credential
, otpKey
);
504 it('failure', async
function() {
505 credential
= undefined;
511 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
513 await db
.authenticationUpsert(dbCtx
, identifier
, credential
, otpKey
);
514 assert
.fail(noExpectedException
);
516 assert(e
instanceof DBErrors
.UnexpectedResult
);
519 }); // authenticationUpsert
521 describe('authenticationUpdateCredential', function () {
522 let identifier
, credential
;
523 beforeEach(function () {
524 identifier
= 'username';
526 it('success', async
function () {
532 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
533 await db
.authenticationUpdateCredential(dbCtx
, identifier
, credential
);
535 it('failure', async
function() {
536 credential
= undefined;
542 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
544 await db
.authenticationUpdateCredential(dbCtx
, identifier
, credential
);
545 assert
.fail(noExpectedException
);
547 assert(e
instanceof DBErrors
.UnexpectedResult
);
550 }); // authenticationUpdateCredential
552 describe('authenticationUpdateOTPKey', function () {
553 let identifier
, otpKey
;
554 beforeEach(function () {
555 identifier
= 'username';
556 otpKey
= '12345678901234567890123456789012';
558 it('success', async
function () {
564 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
565 await db
.authenticationUpdateOTPKey(dbCtx
, identifier
, otpKey
);
567 it('failure', async
function() {
573 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
575 await db
.authenticationUpdateOTPKey(dbCtx
, identifier
, otpKey
);
576 assert
.fail(noExpectedException
);
578 assert(e
instanceof DBErrors
.UnexpectedResult
);
581 }); // authenticationUpdateOTPKey
583 describe('subscriptionsByTopicId', function () {
584 it('success', async
function () {
586 sinon
.stub(db
.db
, 'manyOrNone').resolves(expected
);
587 const result
= await db
.subscriptionsByTopicId(dbCtx
, topicUrl
);
588 assert
.deepStrictEqual(result
, expected
);
590 it('failure', async
function () {
591 const expected
= new Error();
592 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
594 await db
.subscriptionsByTopicId(dbCtx
, topicUrl
);
595 assert
.fail(noExpectedException
);
597 assert
.deepStrictEqual(e
, expected
);
600 }); // subscriptionsByTopicId
602 describe('subscriptionCountByTopicUrl', function () {
603 it('success', async
function () {
604 const expected
= { count: 3 };
605 sinon
.stub(db
.db
, 'one').resolves(expected
);
606 const result
= await db
.subscriptionCountByTopicUrl(dbCtx
, topicUrl
);
607 assert
.deepStrictEqual(result
, expected
);
609 it('failure', async
function () {
610 const expected
= new Error();
611 sinon
.stub(db
.db
, 'one').throws(expected
);
613 await db
.subscriptionCountByTopicUrl(dbCtx
, topicUrl
);
614 assert
.fail(noExpectedException
);
616 assert
.deepStrictEqual(e
, expected
);
619 }); // subscriptionCountByTopicUrl
621 describe('subscriptionDelete', function () {
622 it('success', async
function() {
630 lastInsertRowid: undefined,
633 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
634 const result
= await db
.subscriptionDelete(dbCtx
, callback
, topicId
);
635 assert
.deepStrictEqual(result
, expected
);
637 it('failure', async
function () {
638 const expected
= new Error();
639 sinon
.stub(db
.db
, 'result').throws(expected
);
641 await db
.subscriptionDelete(dbCtx
, callback
, topicId
);
642 assert
.fail(noExpectedException
);
644 assert
.deepStrictEqual(e
, expected
);
647 }); // subscriptionDelete
649 describe('subscriptionDeleteExpired', function () {
650 it('success', async
function () {
658 lastInsertRowid: undefined,
661 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
662 const result
= await db
.subscriptionDeleteExpired(dbCtx
, topicId
);
663 assert
.deepStrictEqual(result
, expected
);
665 it('failure', async
function() {
666 const expected
= new Error();
667 sinon
.stub(db
.db
, 'result').rejects(expected
);
669 await db
.subscriptionDeleteExpired(dbCtx
, topicId
);
670 assert
.fail(noExpectedException
);
672 assert
.deepStrictEqual(e
, expected
);
677 describe('subscriptionDeliveryClaim', function () {
678 it('success', async
function() {
681 id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
684 const expected
= ['c2e254c5-aa6e-4a8f-b1a1-e474b07392bb'];
685 sinon
.stub(db
.db
, 'manyOrNone').resolves(dbResult
);
686 const result
= await db
.subscriptionDeliveryClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
687 assert
.deepStrictEqual(result
, expected
);
689 it('failure', async
function () {
690 const expected
= new Error();
691 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
693 await db
.subscriptionDeliveryClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
694 assert
.fail(noExpectedException
);
696 assert
.deepStrictEqual(e
, expected
);
699 }); // subscriptionDeliveryClaim
701 describe('subscriptionDeliveryClaimById', function () {
702 it('success', async
function() {
705 rows: [{ id: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb' }],
710 lastInsertRowid: 'c2e254c5-aa6e-4a8f-b1a1-e474b07392bb',
713 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
714 const result
= await db
.subscriptionDeliveryClaimById(dbCtx
, subscriptionId
, claimTimeoutSeconds
, claimant
);
715 assert
.deepStrictEqual(result
, expected
);
717 it('failure', async
function () {
723 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
725 await db
.subscriptionDeliveryClaimById(dbCtx
, callback
, topicId
);
726 assert
.fail(noExpectedException
);
728 assert(e
instanceof DBErrors
.UnexpectedResult
);
731 }); // subscriptionDeliveryClaimById
733 describe('subscriptionDeliveryComplete', function () {
734 let topicContentUpdated
;
736 topicContentUpdated
= new Date();
738 it('success', async
function() {
742 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
743 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
, topicContentUpdated
);
745 it('failure', async
function () {
749 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult
);
751 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
, topicContentUpdated
);
752 assert
.fail(noExpectedException
);
754 assert(e
instanceof DBErrors
.UnexpectedResult
);
757 it('second failure', async
function () {
764 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
766 await db
.subscriptionDeliveryComplete(dbCtx
, callback
, topicId
, topicContentUpdated
);
767 assert
.fail(noExpectedException
);
769 assert(e
instanceof DBErrors
.UnexpectedResult
);
772 }); // subscriptionDeliveryComplete
774 describe('subscriptionDeliveryGone', function () {
775 it('success', async
function() {
779 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
780 await db
.subscriptionDeliveryGone(dbCtx
, callback
, topicId
);
782 it('failure', async
function () {
786 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
788 await db
.subscriptionDeliveryGone(dbCtx
, callback
, topicId
);
789 assert
.fail(noExpectedException
);
791 assert(e
instanceof DBErrors
.UnexpectedResult
);
794 }); // subscriptionDeliveryGone
796 describe('subscriptionDeliveryIncomplete', function () {
797 it('success', async
function() {
798 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
802 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
803 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
804 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
806 it('success covers default', async
function() {
807 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
811 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
812 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
813 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
);
815 it('failure', async
function () {
816 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
820 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
821 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
823 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
824 assert
.fail(noExpectedException
);
826 assert(e
instanceof DBErrors
.UnexpectedResult
);
829 it('second failure', async
function () {
830 const dbOne
= { deliveryAttemptsSinceSuccess: 0 };
837 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
838 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
840 await db
.subscriptionDeliveryIncomplete(dbCtx
, callback
, topicId
, retryDelays
);
841 assert
.fail(noExpectedException
);
843 assert(e
instanceof DBErrors
.UnexpectedResult
);
846 }); // subscriptionDeliveryIncomplete
848 describe('subscriptionGet', function () {
849 it('success', async
function() {
853 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
854 const result
= await db
.subscriptionGet(dbCtx
, callback
, topicId
);
855 assert
.deepStrictEqual(result
, expected
);
857 it('failure', async
function () {
858 const expected
= new Error();
859 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
861 await db
.subscriptionGet(dbCtx
, callback
, topicId
);
862 assert
.fail(noExpectedException
);
864 assert
.deepStrictEqual(e
, expected
);
867 }); // subscriptionGet
869 describe('subscriptionGetById', function () {
870 it('success', async
function() {
874 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
875 const result
= await db
.subscriptionGetById(dbCtx
, subscriptionId
);
876 assert
.deepStrictEqual(result
, expected
);
878 it('failure', async
function () {
879 const expected
= new Error();
880 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
882 await db
.subscriptionGetById(dbCtx
, subscriptionId
);
883 assert
.fail(noExpectedException
);
885 assert
.deepStrictEqual(e
, expected
);
888 }); // subscriptionGetById
890 describe('subscriptionUpsert', function () {
892 beforeEach(function () {
902 it('success', async
function() {
905 rows: [{ id: subscriptionId
}],
910 lastInsertRowid: subscriptionId
,
913 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
914 const result
= await db
.subscriptionUpsert(dbCtx
, data
);
915 assert
.deepStrictEqual(result
, expected
);
917 it('failure', async
function () {
921 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
923 await db
.subscriptionUpsert(dbCtx
, data
);
924 assert
.fail(noExpectedException
);
926 assert(e
instanceof DBErrors
.UnexpectedResult
);
929 }); // subscriptionUpsert
931 describe('subscriptionUpdate', function () {
933 beforeEach(function () {
935 signatureAlgorithm: 'sha256',
938 it('success', async
function() {
944 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
945 await db
.subscriptionUpdate(dbCtx
, data
);
947 it('failure', async
function () {
951 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
953 await db
.subscriptionUpdate(dbCtx
, data
);
954 assert
.fail(noExpectedException
);
956 assert(e
instanceof DBErrors
.UnexpectedResult
);
959 }); // subscriptionUpdate
961 describe('topicDeleted', function () {
962 it('success', async
function() {
966 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
967 await db
.topicDeleted(dbCtx
, topicId
);
969 it('failure', async
function() {
973 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
975 await db
.topicDeleted(dbCtx
, topicId
);
976 assert
.fail(noExpectedException
);
978 assert(e
instanceof DBErrors
.UnexpectedResult
);
983 describe('topicFetchClaim', function () {
984 it('success', async
function() {
985 const dbResult
= [{ id: topicId
}];
986 const expected
= [topicId
];
987 sinon
.stub(db
.db
, 'manyOrNone').resolves(dbResult
);
988 const result
= await db
.topicFetchClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
989 assert
.deepStrictEqual(result
, expected
);
991 it('failure', async
function () {
992 const expected
= new Error();
993 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
995 await db
.topicFetchClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
996 assert
.fail(noExpectedException
);
998 assert
.deepStrictEqual(e
, expected
);
1001 }); // topicFetchClaim
1003 describe('topicFetchClaimById', function () {
1004 it('success', async
function() {
1012 lastInsertRowid: undefined,
1015 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1016 const result
= await db
.topicFetchClaimById(dbCtx
, topicId
, claimTimeoutSeconds
, claimant
);
1017 assert
.deepStrictEqual(result
, expected
);
1019 it('failure', async
function () {
1020 const expected
= new Error();
1021 sinon
.stub(db
.db
, 'result').throws(expected
);
1023 await db
.topicFetchClaimById(dbCtx
, topicId
, claimTimeoutSeconds
, claimant
);
1024 assert
.fail(noExpectedException
);
1026 assert
.deepStrictEqual(e
, expected
);
1029 }); // topicFetchClaimById
1031 describe('topicFetchComplete', function () {
1032 it('success', async
function() {
1038 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1039 await db
.topicFetchComplete(dbCtx
, topicId
);
1041 it('failure', async
function () {
1047 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1049 await db
.topicFetchComplete(dbCtx
, topicId
);
1050 assert
.fail(noExpectedException
);
1052 assert(e
instanceof DBErrors
.UnexpectedResult
);
1055 it('second failure', async
function () {
1066 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1068 await db
.topicFetchComplete(dbCtx
, topicId
);
1069 assert
.fail(noExpectedException
);
1071 assert(e
instanceof DBErrors
.UnexpectedResult
);
1074 }); // topicFetchComplete
1076 describe('topicFetchIncomplete', function () {
1077 it('success', async
function() {
1078 const dbOne
= { currentAttempt: 0 };
1091 lastInsertRowid: undefined,
1094 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1095 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1096 const result
= await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
1097 assert
.deepStrictEqual(result
, expected
);
1099 it('covers defaults', async
function() {
1100 const dbOne
= { currentAttempt: 0 };
1113 lastInsertRowid: undefined,
1116 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1117 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1118 const result
= await db
.topicFetchIncomplete(dbCtx
, topicId
);
1119 assert
.deepStrictEqual(result
, expected
);
1121 it('failure', async
function () {
1122 const dbOne
= { currentAttempt: 0 };
1133 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1134 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1136 await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
1137 assert
.fail(noExpectedException
);
1139 assert(e
instanceof DBErrors
.UnexpectedResult
);
1142 it('second failure', async
function () {
1143 const dbOne
= { currentAttempt: 0 };
1154 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1155 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1157 await db
.topicFetchIncomplete(dbCtx
, topicId
, retryDelays
);
1158 assert
.fail(noExpectedException
);
1160 assert(e
instanceof DBErrors
.UnexpectedResult
);
1163 }); // topicFetchIncomplete
1165 describe('topicFetchRequested', function () {
1166 it('success', async
function() {
1174 lastInsertRowid: undefined,
1177 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1178 const result
= await db
.topicFetchRequested(dbCtx
, topicId
);
1179 assert
.deepStrictEqual(result
, expected
);
1181 it('failure', async
function () {
1187 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1189 await db
.topicFetchRequested(dbCtx
, topicId
);
1190 assert
.fail(noExpectedException
);
1192 assert(e
instanceof DBErrors
.UnexpectedResult
);
1195 }); // topicFetchRequested
1197 describe('topicGetAll', function () {
1198 it('success', async
function() {
1199 const expected
= [{ id: topicId
}];
1200 sinon
.stub(db
.db
, 'manyOrNone').resolves(expected
);
1201 const result
= await db
.topicGetAll(dbCtx
);
1202 assert
.deepStrictEqual(result
, expected
);
1204 it('covers default', async
function() {
1205 const expected
= undefined;
1206 sinon
.stub(db
.db
, 'manyOrNone').resolves(expected
);
1207 const result
= await db
.topicGetAll(dbCtx
);
1208 assert
.deepStrictEqual(result
, expected
);
1210 it('failure', async
function () {
1211 const expected
= new Error();
1212 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
1214 await db
.topicGetAll(dbCtx
);
1215 assert
.fail(noExpectedException
);
1217 assert
.deepStrictEqual(e
, expected
);
1222 describe('topicGetById', function () {
1223 it('success', async
function() {
1224 const expected
= { id: topicId
};
1225 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1226 const result
= await db
.topicGetById(dbCtx
, topicId
);
1227 assert
.deepStrictEqual(result
, expected
);
1229 it('covers none', async
function() {
1230 const expected
= undefined;
1231 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1232 const result
= await db
.topicGetById(dbCtx
, topicId
);
1233 assert
.deepStrictEqual(result
, expected
);
1235 it('covers no defaults', async
function () {
1236 const expected
= { id: topicId
};
1237 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1238 const result
= await db
.topicGetById(dbCtx
, topicId
, false);
1239 assert
.deepStrictEqual(result
, expected
);
1241 it('failure', async
function () {
1242 const expected
= new Error();
1243 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1245 await db
.topicGetById(dbCtx
, topicId
);
1246 assert
.fail(noExpectedException
);
1248 assert
.deepStrictEqual(e
, expected
);
1253 describe('topicGetByUrl', function () {
1254 it('success', async
function() {
1255 const expected
= { id: topicId
};
1256 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1257 const result
= await db
.topicGetByUrl(dbCtx
, topicUrl
);
1258 assert
.deepStrictEqual(result
, expected
);
1260 it('success, no default', async
function() {
1261 const expected
= { id: topicId
};
1262 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1263 const result
= await db
.topicGetByUrl(dbCtx
, topicUrl
, false);
1264 assert
.deepStrictEqual(result
, expected
);
1266 it('failure', async
function () {
1267 const expected
= new Error();
1268 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1270 await db
.topicGetByUrl(dbCtx
, topicUrl
);
1271 assert
.fail(noExpectedException
);
1273 assert
.deepStrictEqual(e
, expected
);
1276 }); // topicGetByUrl
1278 describe('topicGetContentById', function () {
1280 beforeEach(function () {
1286 it('success', async
function() {
1287 const expected
= topic
;
1288 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1289 const result
= await db
.topicGetContentById(dbCtx
, topicId
);
1290 assert
.deepStrictEqual(result
, expected
);
1292 it('covers default', async
function() {
1293 const expected
= undefined;
1294 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1295 const result
= await db
.topicGetContentById(dbCtx
, topicId
);
1296 assert
.deepStrictEqual(result
, expected
);
1298 it('failure', async
function () {
1299 const expected
= new Error();
1300 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1302 await db
.topicGetContentById(dbCtx
, topicId
);
1303 assert
.fail(noExpectedException
);
1305 assert
.deepStrictEqual(e
, expected
);
1308 it('caches success', async
function () {
1309 db
.cache
= new Map();
1310 const expected
= topic
;
1311 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1312 const result
= await db
.topicGetContentById(dbCtx
, topicId
);
1313 assert
.deepStrictEqual(result
, expected
);
1315 it('covers cached entry', async
function() {
1317 db
.cache
= new Map();
1318 const expected
= topic
;
1319 sinon
.stub(db
.db
, 'oneOrNone').resolves(expected
);
1320 result
= await db
.topicGetContentById(dbCtx
, topicId
);
1321 assert
.deepStrictEqual(result
, expected
);
1322 result
= await db
.topicGetContentById(dbCtx
, topicId
);
1323 assert
.deepStrictEqual(result
, expected
);
1325 }); // topicGetContentById
1327 describe('topicPendingDelete', function () {
1328 beforeEach(function () {
1329 sinon
.stub(db
.db
, 'one');
1330 sinon
.stub(db
.db
, 'result');
1332 it('success', async
function () {
1333 db
.db
.one
.onCall(0).resolves({
1336 }).onCall(1).resolves({
1344 db
.db
.result
.resolves(dbResult
);
1345 await db
.topicPendingDelete(dbCtx
, topicId
);
1346 assert(db
.db
.result
.called
);
1348 it('does not delete non-deleted topic', async
function () {
1349 db
.db
.one
.onCall(0).resolves({
1352 }).onCall(1).resolves({
1355 await db
.topicPendingDelete(dbCtx
, topicId
);
1356 assert(!db
.db
.result
.called
);
1358 it('does not delete topic with active subscriptions', async
function () {
1359 db
.db
.one
.onCall(0).resolves({
1362 }).onCall(1).resolves({
1365 await db
.topicPendingDelete(dbCtx
, topicId
);
1366 assert(!db
.db
.result
.called
);
1368 it('covers no deletion', async
function () {
1369 db
.db
.one
.onCall(0).resolves({
1372 }).onCall(1).resolves({
1380 db
.db
.result
.resolves(dbResult
);
1382 await db
.topicPendingDelete(dbCtx
, topicId
);
1383 assert
.fail(noExpectedException
);
1385 assert(e
instanceof DBErrors
.UnexpectedResult
);
1390 describe('topicPublishHistory', function () {
1391 beforeEach(function () {
1392 sinon
.stub(db
.db
, 'manyOrNone');
1394 it('success', async
function () {
1395 db
.db
.manyOrNone
.returns([
1396 { daysAgo: 1, contentUpdates: 1 },
1397 { daysAgo: 3, contentUpdates: 2 },
1399 const result
= await db
.topicPublishHistory(dbCtx
, topicId
, 7);
1400 const expected
= [0, 1, 0, 2, 0, 0, 0];
1401 assert
.deepStrictEqual(result
, expected
);
1403 }); // topicPublishHistory
1405 describe('topicSet', function () {
1407 beforeEach(function () {
1412 it('success', async
function() {
1415 rows: [{ id: topicId
}],
1420 lastInsertRowid: topicId
,
1423 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1424 const result
= await db
.topicSet(dbCtx
, data
);
1425 assert
.deepStrictEqual(result
, expected
);
1427 it('failure', async
function () {
1433 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1435 await db
.topicSet(dbCtx
, data
);
1436 assert
.fail(noExpectedException
);
1438 assert(e
instanceof DBErrors
.UnexpectedResult
);
1441 it('fails invalid value', async
function () {
1442 sinon
.stub(db
.db
, 'result');
1444 data
.leaseSecondsPreferred
= -100;
1445 await db
.topicSet(dbCtx
, data
);
1446 assert
.fail(noExpectedException
);
1448 assert(e
instanceof DBErrors
.DataValidation
);
1450 assert(!db
.db
.result
.called
);
1452 it('fails invalid values', async
function () {
1453 sinon
.stub(db
.db
, 'result');
1455 data
.leaseSecondsPreferred
= 10;
1456 data
.leaseSecondsMax
= 100;
1457 data
.leaseSecondsMin
= 50;
1458 await db
.topicSet(dbCtx
, data
);
1459 assert
.fail(noExpectedException
);
1461 assert(e
instanceof DBErrors
.DataValidation
);
1463 assert(!db
.db
.result
.called
);
1467 describe('topicSetContent', function () {
1469 beforeEach(function () {
1472 contentType: 'text/plain',
1473 contentHash: 'abc123',
1475 sinon
.stub(db
.db
, 'result');
1477 it('success', async
function() {
1485 lastInsertRowid: undefined,
1488 db
.db
.result
.resolves(dbResult
);
1489 const result
= await db
.topicSetContent(dbCtx
, data
);
1490 assert
.deepStrictEqual(result
, expected
);
1492 it('failure', async
function () {
1498 db
.db
.result
.resolves(dbResult
);
1500 await db
.topicSetContent(dbCtx
, data
);
1501 assert
.fail(noExpectedException
);
1503 assert(e
instanceof DBErrors
.UnexpectedResult
);
1506 it('failure 2', async
function () {
1507 const dbResultSuccess
= {
1512 const dbResultFail
= {
1518 .onCall(0).resolves(dbResultSuccess
)
1519 .onCall(1).resolves(dbResultFail
);
1521 await db
.topicSetContent(dbCtx
, data
);
1522 assert
.fail(noExpectedException
);
1524 assert(e
instanceof DBErrors
.UnexpectedResult
);
1527 }); // topicSetContent
1529 describe('topicUpdate', function () {
1531 beforeEach(function () {
1533 leaseSecondsPreferred: 123,
1534 leaseSecondsMin: 100,
1535 leaseSecondsMax: 1000,
1536 publisherValidationUrl: null,
1537 contentHashAlgorithm: 'sha256',
1540 it('success', async
function() {
1546 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1547 await db
.topicUpdate(dbCtx
, data
);
1549 it('failure', async
function () {
1555 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1557 await db
.topicUpdate(dbCtx
, data
);
1558 assert
.fail(noExpectedException
);
1560 assert(e
instanceof DBErrors
.UnexpectedResult
);
1566 describe('verificationClaim', function () {
1567 it('success', async
function() {
1568 const dbManyOrNone
= [{ id: verificationId
}];
1569 const expected
= [verificationId
];
1570 sinon
.stub(db
.db
, 'manyOrNone').resolves(dbManyOrNone
);
1571 const result
= await db
.verificationClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
1572 assert
.deepStrictEqual(result
, expected
);
1574 it('failure', async
function () {
1575 const expected
= new Error();
1576 sinon
.stub(db
.db
, 'manyOrNone').throws(expected
);
1578 await db
.verificationClaim(dbCtx
, wanted
, claimTimeoutSeconds
, claimant
);
1579 assert
.fail(noExpectedException
);
1581 assert
.deepStrictEqual(e
, expected
);
1584 }); // verificationClaim
1586 describe('verificationClaimById', function () {
1587 it('success', async
function() {
1590 rows: [ { id: verificationId
} ],
1595 lastInsertRowid: verificationId
,
1598 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1599 const result
= await db
.verificationClaimById(dbCtx
, verificationId
, claimTimeoutSeconds
, claimant
);
1600 assert
.deepStrictEqual(result
, expected
);
1602 it('failure', async
function () {
1603 const expected
= new Error();
1604 sinon
.stub(db
.db
, 'result').throws(expected
);
1606 await db
.verificationClaimById(dbCtx
, verificationId
, claimTimeoutSeconds
, claimant
);
1607 assert
.fail(noExpectedException
);
1609 assert
.deepStrictEqual(e
, expected
);
1612 }); // verificationClaimById
1614 describe('verificationComplete', function () {
1615 it('success', async
function() {
1621 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1622 await db
.verificationComplete(dbCtx
, verificationId
, callback
, topicId
);
1624 it('failure', async
function () {
1630 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1632 await db
.verificationComplete(dbCtx
, verificationId
, callback
, topicId
);
1633 assert
.fail(noExpectedException
);
1635 assert(e
instanceof DBErrors
.UnexpectedResult
);
1638 }); // verificationComplete
1640 describe('verificationGetById', function () {
1641 it('success', async
function() {
1642 const dbOneOrNone
= { id: verificationId
};
1643 const expected
= { id: verificationId
};
1644 sinon
.stub(db
.db
, 'oneOrNone').resolves(dbOneOrNone
);
1645 const result
= await db
.verificationGetById(dbCtx
, verificationId
);
1646 assert
.deepStrictEqual(result
, expected
);
1648 it('failure', async
function () {
1649 const expected
= new Error();
1650 sinon
.stub(db
.db
, 'oneOrNone').throws(expected
);
1652 await db
.verificationGetById(dbCtx
, verificationId
);
1653 assert
.fail(noExpectedException
);
1655 assert
.deepStrictEqual(e
, expected
);
1658 }); // verificationGetById
1660 describe('verificationIncomplete', function () {
1661 it('success', async
function() {
1662 const dbOne
= { attempts: 0 };
1673 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1674 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1675 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1677 it('covers defaults', async
function() {
1678 const dbOne
= { attempts: 0 };
1689 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1690 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1691 await db
.verificationIncomplete(dbCtx
, verificationId
);
1693 it('failure', async
function () {
1694 const dbOne
= { attempts: 0 };
1705 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1706 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1708 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1709 assert
.fail(noExpectedException
);
1711 assert(e
instanceof DBErrors
.UnexpectedResult
);
1714 it('second failure', async
function () {
1715 const dbOne
= { attempts: 0 };
1726 sinon
.stub(db
.db
, 'one').resolves(dbOne
);
1727 sinon
.stub(db
.db
, 'result').onCall(0).resolves(dbResult0
).onCall(1).resolves(dbResult1
);
1729 await db
.verificationIncomplete(dbCtx
, verificationId
, retryDelays
);
1730 assert
.fail(noExpectedException
);
1732 assert(e
instanceof DBErrors
.UnexpectedResult
);
1735 }); // verificationIncomplete
1737 describe('verificationInsert', function () {
1739 beforeEach(function () {
1744 isPublisherValidated: true,
1745 leaseSeconds: 86400,
1748 it('success', async
function() {
1751 rows: [{ id: verificationId
}],
1754 const expected
= verificationId
;
1755 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1756 const result
= await db
.verificationInsert(dbCtx
, verification
);
1757 assert
.deepStrictEqual(result
, expected
);
1759 it('failure', async
function () {
1765 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1767 await db
.verificationInsert(dbCtx
, verification
);
1768 assert
.fail(noExpectedException
);
1770 assert(e
instanceof DBErrors
.UnexpectedResult
);
1773 it('fails validation', async
function () {
1774 delete verification
.leaseSeconds
;
1776 await db
.verificationInsert(dbCtx
, verification
);
1777 assert
.fail(noExpectedException
);
1779 assert(e
instanceof DBErrors
.DataValidation
);
1782 }); // verificationInsert
1784 describe('verificationRelease', function () {
1785 it('success', async
function() {
1791 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1792 await db
.verificationRelease(dbCtx
, verificationId
);
1794 it('failure', async
function () {
1800 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1802 await db
.verificationRelease(dbCtx
, verificationId
);
1803 assert
.fail(noExpectedException
);
1805 assert(e
instanceof DBErrors
.UnexpectedResult
);
1808 }); // verificationRelease
1810 describe('verificationUpdate', function () {
1812 beforeEach(function () {
1815 isPublisherValidated: true,
1818 it('success', async
function() {
1824 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1825 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1827 it('failure', async
function () {
1833 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1835 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1836 assert
.fail(noExpectedException
);
1838 assert(e
instanceof DBErrors
.UnexpectedResult
, e
.name
);
1841 it('fails validation', async
function () {
1844 await db
.verificationUpdate(dbCtx
, verificationId
, data
);
1845 assert
.fail(noExpectedException
);
1847 assert(e
instanceof DBErrors
.DataValidation
);
1850 }); // verificationUpdate
1852 describe('verificationValidated', function () {
1853 it('success', async
function() {
1859 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1860 await db
.verificationValidated(dbCtx
, verificationId
);
1862 it('failure', async
function () {
1868 sinon
.stub(db
.db
, 'result').resolves(dbResult
);
1870 await db
.verificationValidated(dbCtx
, verificationId
);
1871 assert
.fail(noExpectedException
);
1873 assert(e
instanceof DBErrors
.UnexpectedResult
);
1876 }); // verificationValidated
1878 }); // DatabasePostgres