2 /* eslint-disable capitalized-comments, sonarjs/no-duplicate-string */
6 const assert
= require('assert');
7 const sinon
= require('sinon'); // eslint-disable-line node/no-unpublished-require
9 const Communication
= require('../../src/communication');
10 const Config
= require('../../config');
11 const Errors
= require('../../src/errors');
13 const stubDb
= require('../stub-db');
14 const stubLogger
= require('../stub-logger');
16 const noExpectedException
= 'did not get expected exception';
18 describe('Communication', function () {
19 let communication
, options
;
21 beforeEach(function () {
22 options
= new Config('test');
23 communication
= new Communication(stubLogger
, stubDb
, options
);
27 afterEach(function () {
31 it('instantiates', function () {
32 assert(communication
);
35 it('covers config value', function () {
36 options
.dingus
.selfBaseUrl
= undefined;
37 communication
= new Communication(stubLogger
, stubDb
, options
);
40 describe('Axios timing coverage', function () {
45 it('tags request', function () {
46 communication
.axios
.interceptors
.request
.handlers
[0].fulfilled(request
);
47 assert(request
.startTimestampMs
);
49 it('tags response', function () {
50 communication
.axios
.interceptors
.response
.handlers
[0].fulfilled(response
);
51 assert(response
.elapsedTimeMs
);
53 }); // Axios timing coverage
55 describe('userAgentString', function () {
56 it('has default behavior', function () {
57 const result
= Communication
.userAgentString();
59 assert(result
.length
> 30);
61 it('is settable', function () {
62 const result
= Communication
.userAgentString({
65 implementation: 'custom',
68 assert
.strictEqual(result
, 'myhub/9.9.9 (custom)');
70 it('covers branches', function () {
71 const result
= Communication
.userAgentString({
77 assert
.strictEqual(result
, 'myhub/9.9.9');
81 describe('generateChallenge', function () {
82 it('generates a thing', async
function () {
83 const result
= await Communication
.generateChallenge();
85 assert(result
.length
);
89 describe('signature', function () {
90 let message
, secret
, algorithm
, expected
;
91 beforeEach(function () {
92 message
= 'Jackdaws love my big sphinx of quartz.';
93 secret
= 'secretsecret';
95 expected
= 'sha256=ee92148d9cd043cdfb8da7cf5ee1897abaafdb5ab840e85010abd4bf235fa31e';
97 it('signs a thing', function () {
98 const result
= Communication
.signature(message
, secret
, algorithm
);
99 assert
.strictEqual(result
, expected
);
103 describe('contentHash', function () {
104 let content
, algorithm
, expected
;
105 beforeEach(function () {
106 content
= 'Jived fox nymph grabs quick waltz.';
107 algorithm
= 'sha256';
108 expected
= '6e5e1a93bde78910b0d7c5fd8aba393294d4eca5d3fbf2bfd49100df3d5cc85d';
110 it('hashes', function () {
111 const result
= Communication
.contentHash(content
, algorithm
);
112 assert
.strictEqual(result
, expected
);
116 describe('Axios Configurations', function () {
117 let requestUrl
, expectedUrl
, topicUrl
;
118 beforeEach(function () {
119 requestUrl
= 'https://example.com/callback/?id=123';
120 expectedUrl
= 'https://example.com/callback/';
121 topicUrl
= 'http://example.com/blog/';
123 it('_axiosConfig', function () {
124 const method
= 'GET';
125 const contentType
= 'text/plain';
126 const body
= undefined;
128 'extra_parameter': 'foobar',
130 const expectedUrlObj
= new URL('https://example.com/callback/?id=123&extra_parameter=foobar');
133 url: 'https://example.com/callback/',
135 'Content-Type': 'text/plain',
137 params: expectedUrlObj
.searchParams
,
138 responseType: 'text',
140 const result
= Communication
._axiosConfig(method
, requestUrl
, body
, params
, {
141 'Content-Type': contentType
,
143 delete result
.transformResponse
;
144 assert
.deepStrictEqual(result
, expected
);
146 it('_axiosConfig covers defaults', function () {
147 const method
= 'OPTIONS';
148 const expectedUrlObj
= new URL(requestUrl
);
153 params: expectedUrlObj
.searchParams
,
154 responseType: 'text',
156 const result
= Communication
._axiosConfig(method
, requestUrl
);
157 delete result
.transformResponse
;
158 assert
.deepStrictEqual(result
, expected
);
160 it('covers null response transform', function () {
161 const result
= Communication
._axiosConfig('GET', 'https://example.com/', undefined, {}, {});
162 result
.transformResponse
[0]();
164 it('_intentVerifyAxiosConfig', function () {
165 const mode
= 'subscribe';
166 const leaseSeconds
= 864000;
167 const challenge
= 'abcxyz';
168 const expectedUrlObj
= new URL(`${requestUrl}&hub.mode=${mode}&hub.topic=${encodeURIComponent(topicUrl)}&hub.challenge=${challenge}&hub.lease_seconds=${leaseSeconds}`);
173 params: expectedUrlObj
.searchParams
,
174 responseType: 'text',
176 const result
= Communication
._intentVerifyAxiosConfig(requestUrl
, topicUrl
, mode
, leaseSeconds
, challenge
);
177 delete result
.transformResponse
;
178 assert
.deepStrictEqual(result
, expected
);
180 it('_intentDenyAxiosConfig', function () {
181 const reason
= 'something';
182 const expectedUrlObj
= new URL(`${requestUrl}&hub.mode=denied&hub.topic=${encodeURIComponent(topicUrl)}&hub.reason=${reason}`);
187 params: expectedUrlObj
.searchParams
,
188 responseType: 'text',
190 const result
= Communication
._intentDenyAxiosConfig(requestUrl
, topicUrl
, reason
);
191 delete result
.transformResponse
;
192 assert
.deepStrictEqual(result
, expected
);
194 it('_publisherValidationAxiosConfig', function () {
197 publisherValidationUrl: 'https://example.com/publisher/',
199 const verification
= {
200 callback: requestUrl
,
203 const expectedUrlObj
= new URL(topic
.publisherValidationUrl
);
206 url: topic
.publisherValidationUrl
,
208 callback: requestUrl
,
212 'Content-Type': 'application/json',
214 params: expectedUrlObj
.searchParams
,
215 responseType: 'text',
217 const result
= Communication
._publisherValidationAxiosConfig(topic
, verification
);
218 delete result
.transformResponse
;
219 assert
.deepStrictEqual(result
, expected
);
221 it('_topicFetchAxiosConfig', function () {
224 contentType: 'text/plain',
226 const expectedUrlObj
= new URL(topicUrl
);
230 params: expectedUrlObj
.searchParams
,
232 Accept: 'text/plain, */*;q=0.9',
234 responseType: 'text',
236 const result
= Communication
._topicFetchAxiosConfig(topic
);
237 delete result
.transformResponse
;
238 assert
.deepStrictEqual(result
, expected
);
240 }); // Axios Configurations
242 describe('verificationProcess', function () {
243 const challenge
= 'a_challenge';
244 let dbCtx
, callback
, requestId
, topicId
;
245 let topic
, verification
;
246 beforeEach(function () {
248 callback
= 'https://example.com/callback/?id=123';
249 requestId
= '7d37ea20-4ef7-417e-a08d-c0ba71269ab1';
250 topicId
= '234ec6fb-f1cd-4ac3-8ea9-29ed42ae0e21';
253 url: 'https://example.com/blog/',
260 isPublisherValidated: true,
261 leaseSeconds: 864000,
264 sinon
.stub(Communication
, 'generateChallenge').resolves(challenge
);
265 sinon
.stub(communication
, 'publisherValidate').resolves(true);
266 sinon
.stub(communication
, 'axios').resolves({
270 'content-type': 'text/plain',
275 communication
.db
.verificationGetById
.resolves(verification
);
276 communication
.db
.topicGetById
.resolves(topic
);
277 communication
.db
.verificationRelease
.resolves({});
278 communication
.db
.verificationUpdate
.resolves({});
279 communication
.db
.verificationIncomplete
.resolves({});
280 communication
.db
.verificationComplete
.resolves({});
283 it('errors on non-existent verification', async
function () {
284 communication
.db
.verificationGetById
.restore();
285 sinon
.stub(communication
.db
, 'verificationGetById').resolves();
288 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
289 assert
.fail(noExpectedException
);
291 assert(e
instanceof Errors
.InternalInconsistencyError
);
295 it('errors on non-existent topic', async
function () {
296 communication
.db
.topicGetById
.restore();
297 sinon
.stub(communication
.db
, 'topicGetById').resolves();
300 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
301 assert
.fail(noExpectedException
);
303 assert(e
instanceof Errors
.InternalInconsistencyError
);
307 it('skips inactive topic', async
function () {
308 communication
.db
.topicGetById
.restore();
309 topic
.isActive
= false;
310 sinon
.stub(communication
.db
, 'topicGetById').resolves(topic
);
312 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
314 assert(communication
.db
.verificationRelease
.called
);
315 assert(!communication
.axios
.called
);
318 it('denies subscription to deleted topic', async
function () {
319 communication
.db
.topicGetById
.restore();
320 topic
.isDeleted
= true;
321 sinon
.stub(communication
.db
, 'topicGetById').resolves(topic
);
323 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
325 assert(communication
.db
.verificationUpdate
.called
);
326 assert
.strictEqual(verification
.mode
, 'denied');
329 it('checks publisher validation if needed', async
function() {
330 communication
.db
.verificationGetById
.restore();
331 verification
.isPublisherValidated
= false;
332 sinon
.stub(communication
.db
, 'verificationGetById').resolves(verification
);
333 communication
.db
.topicGetById
.restore();
334 topic
.publisherValidationUrl
= 'https://example.com/publisher/';
335 sinon
.stub(communication
.db
, 'topicGetById').resolves(topic
);
337 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
339 assert(communication
.publisherValidate
.called
);
340 assert(communication
.db
.verificationComplete
.called
);
343 it('handles publisher validation failure', async
function() {
344 communication
.db
.verificationGetById
.restore();
345 verification
.isPublisherValidated
= false;
346 sinon
.stub(communication
.db
, 'verificationGetById').resolves(verification
);
347 communication
.db
.topicGetById
.restore();
348 topic
.publisherValidationUrl
= 'https://example.com/publisher/';
349 sinon
.stub(communication
.db
, 'topicGetById').resolves(topic
);
350 communication
.publisherValidate
.restore();
351 sinon
.stub(communication
, 'publisherValidate').resolves(false);
353 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
355 assert(communication
.publisherValidate
.called
);
356 assert(communication
.db
.verificationIncomplete
.called
);
359 it('handles request error', async
function () {
360 communication
.axios
.restore();
361 sinon
.stub(communication
, 'axios').throws(new Error());
363 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
365 assert(communication
.db
.verificationIncomplete
.called
);
368 it('handles 500 response', async
function () {
369 communication
.axios
.restore();
370 sinon
.stub(communication
, 'axios').resolves({
374 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
376 assert(communication
.db
.verificationIncomplete
.called
);
379 it('handles non-200 response', async
function () {
380 communication
.axios
.restore();
381 sinon
.stub(communication
, 'axios').resolves({
385 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
387 assert(communication
.db
.verificationComplete
.called
);
390 it('subscription succeeds', async
function () {
391 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
393 assert(communication
.db
.subscriptionUpsert
.called
);
394 assert(communication
.db
.verificationComplete
.called
);
397 it('unsubscription succeeds', async
function () {
398 communication
.db
.verificationGetById
.restore();
399 verification
.mode
= 'unsubscribe';
400 sinon
.stub(communication
.db
, 'verificationGetById').resolves(verification
);
402 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
404 assert(communication
.db
.subscriptionDelete
.called
);
405 assert(communication
.db
.verificationComplete
.called
);
408 it('unsubscription denial succeeds', async
function () {
409 communication
.db
.verificationGetById
.restore();
410 verification
.mode
= 'unsubscribe';
411 sinon
.stub(communication
.db
, 'verificationGetById').resolves(verification
);
412 communication
.axios
.restore();
413 sinon
.stub(communication
, 'axios').resolves({
417 'content-type': 'text/plain',
419 data: 'not the challenge',
422 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
424 assert(!communication
.db
.subscriptionDelete
.called
);
425 assert(communication
.db
.verificationComplete
.called
);
428 it('does not handle strange mode', async
function() {
429 communication
.db
.verificationGetById
.restore();
430 verification
.mode
= 'flarp';
431 sinon
.stub(communication
.db
, 'verificationGetById').resolves(verification
);
434 await communication
.verificationProcess(dbCtx
, callback
, topicId
, requestId
);
435 assert
.fail(noExpectedException
);
437 assert(e
instanceof Errors
.InternalInconsistencyError
);
440 }); // verificationProcess
442 describe('publisherValidate', function () {
443 let dbCtx
, topic
, verification
;
444 beforeEach(function () {
447 url: 'https://example.com/topic/',
448 publisherValidationUrl: 'https://example.com/pub_valid/',
451 callback: 'https://exmaple.com/callback/?id=123',
452 httpFrom: 'user@example.com',
453 httpRemoteAddr: '127.0.0.0',
456 sinon
.stub(communication
, 'axios').resolves({
460 'content-type': 'application/json',
464 communication
.db
.verificationIncomplete
.resolves();
465 communication
.db
.verificationUpdate
.resolves();
466 communication
.db
.verificationValidated
.resolves();
469 it('succeeds', async
function () {
470 const result
= await communication
.publisherValidate(dbCtx
, topic
, verification
);
472 assert(communication
.db
.verificationValidated
.called
);
473 assert
.strictEqual(result
, true);
476 it('succeeds with rejection', async
function () {
477 communication
.axios
.restore();
478 sinon
.stub(communication
, 'axios').resolves({
480 statusText: 'Bad Request',
482 'content-type': 'application/json',
486 const result
= await communication
.publisherValidate(dbCtx
, topic
, verification
);
488 assert(communication
.db
.verificationValidated
.called
);
489 assert(communication
.db
.verificationUpdate
.called
);
490 assert
.strictEqual(result
, true);
493 it('defers on request server error', async
function () {
494 communication
.axios
.restore();
495 sinon
.stub(communication
, 'axios').resolves({
497 statusText: 'Bad Gateway',
499 'content-type': 'text/plain',
503 const result
= await communication
.publisherValidate(dbCtx
, topic
, verification
);
505 assert
.strictEqual(result
, false);
508 it('handles request error', async
function () {
509 communication
.axios
.restore();
510 sinon
.stub(communication
, 'axios').throws(new Error());
512 const result
= await communication
.publisherValidate(dbCtx
, topic
, verification
);
514 assert
.strictEqual(result
, false);
517 }); // publisherValidate
519 describe('topicFetchProcess', function () {
520 let dbCtx
, topic
, requestId
, topicId
;
522 beforeEach(function () {
525 url: 'https://example.com/topic/',
527 contentHashAlgorithm: 'sha512',
529 requestId
= '7d37ea20-4ef7-417e-a08d-c0ba71269ab1';
530 topicId
= '234ec6fb-f1cd-4ac3-8ea9-29ed42ae0e21';
532 sinon
.stub(communication
, 'axios').resolves({
536 'content-type': 'text/plain',
537 link: '<https://example.com/hub/>; rel="hub"',
539 data: 'Jackdaws love my big sphinx of quartz.',
542 communication
.db
.topicGetById
.resolves(topic
);
545 it('requires topic exists', async
function () {
546 communication
.db
.topicGetById
.restore();
547 sinon
.stub(communication
.db
, 'topicGetById').resolves();
550 await communication
.topicFetchProcess(dbCtx
, topicId
, requestId
);
551 assert
.fail(noExpectedException
);
553 assert(e
instanceof Errors
.InternalInconsistencyError
);
557 it ('skips deleted topic', async
function () {
558 communication
.db
.topicGetById
.restore();
559 topic
.isDeleted
= true;
560 sinon
.stub(communication
.db
, 'topicGetById').resolves(topic
);
562 await communication
.topicFetchProcess(dbCtx
, topicId
, requestId
);
564 assert(!communication
.axios
.called
);
567 it('handles request error', async
function () {
568 communication
.axios
.restore();
569 sinon
.stub(communication
, 'axios').throws(new Error());
571 await communication
.topicFetchProcess(dbCtx
, topicId
, requestId
);
573 assert(communication
.db
.topicFetchIncomplete
.called
);
576 it('handles 500 response', async
function () {
577 communication
.axios
.restore();
578 sinon
.stub(communication
, 'axios').resolves({
580 statusText: 'Internal Server Error',
582 'content-type': 'text/plain',
586 await communication
.topicFetchProcess(dbCtx
, topicId
, requestId
);
588 assert(communication
.db
.topicFetchIncomplete
.called
);
591 it('handles bad response', async
function () {
592 communication
.axios
.restore();
593 sinon
.stub(communication
, 'axios').resolves({
595 statusText: 'Not Found',
597 'content-type': 'text/plain',
601 await communication
.topicFetchProcess(dbCtx
, topicId
, requestId
);
603 assert(communication
.db
.topicFetchIncomplete
.called
);
606 it('recognizes unchanged content', async
function () {
607 communication
.db
.topicGetById
.restore();
608 topic
.contentHash
= 'a630999c61738f3e066d79a1b299a295c5d0598c173e0904d04a707d43988e3e81660bfc1b1779377f4ec26f837d1bb31fa2b860c9ad2d37495d83de32647fea';
609 sinon
.stub(communication
.db
, 'topicGetById').resolves(topic
);
611 await communication
.topicFetchProcess(dbCtx
, topicId
, requestId
);
613 assert(communication
.db
.topicFetchComplete
.called
);
614 assert(!communication
.db
.topicSetContent
.called
);
617 it('updates content', async
function () {
618 await communication
.topicFetchProcess(dbCtx
, topicId
, requestId
);
620 assert(communication
.db
.topicFetchComplete
.called
);
621 assert(communication
.db
.topicSetContent
.called
);
624 it('updates content with lax link enforcement', async
function () {
625 communication
.axios
.restore();
626 sinon
.stub(communication
, 'axios').resolves({
630 'content-type': 'text/plain',
631 link: '<https://example.com/other/hub/>; rel="hub"',
633 data: 'Jackdaws love my big sphinx of quartz.',
636 communication
.options
.communication
.strictTopicHubLink
= false;
638 await communication
.topicFetchProcess(dbCtx
, topicId
, requestId
);
640 assert(communication
.db
.topicFetchComplete
.called
);
641 assert(communication
.db
.topicSetContent
.called
);
644 it('deletes topic when hub relation unsatisfied', async
function () {
645 communication
.axios
.restore();
646 sinon
.stub(communication
, 'axios').resolves({
650 'content-type': 'text/plain',
651 link: '<https://example.com/other/hub/>; rel="hub"',
653 data: 'Jackdaws love my big sphinx of quartz.',
656 await communication
.topicFetchProcess(dbCtx
, topicId
, requestId
);
658 assert(communication
.db
.topicFetchComplete
.called
);
659 assert(communication
.db
.topicDeleted
.called
);
661 }); // topicFetchProcess
663 describe('subscriptionDeliveryProcess', function () {
664 let dbCtx
, requestId
, topic
, topicId
, subscription
, subscriptionId
;
666 beforeEach(function () {
669 url: 'https://example.com/topic/',
671 contentHashAlgorithm: 'sha512',
672 content: 'Jackdaws love my big sphinx of quartz.',
674 requestId
= '7d37ea20-4ef7-417e-a08d-c0ba71269ab1';
675 topicId
= '234ec6fb-f1cd-4ac3-8ea9-29ed42ae0e21';
676 subscriptionId
= 'c5e6a3ac-dab8-11eb-b758-0025905f714a';
679 callback: 'https://example.com/callback/123',
680 secret: 'superdupersecret',
681 signatureAlgorithm: 'sha512',
684 sinon
.stub(communication
, 'axios').resolves({
688 'content-type': 'text/plain',
690 data: 'Jackdaws love my big sphinx of quartz.',
693 communication
.db
.topicGetContentById
.resolves(topic
);
694 communication
.db
.subscriptionGetById
.resolves(subscription
);
697 it('requires subscription to exist', async
function () {
698 communication
.db
.subscriptionGetById
.restore();
699 sinon
.stub(communication
.db
, 'subscriptionGetById').resolves();
701 await communication
.subscriptionDeliveryProcess(dbCtx
, subscriptionId
, requestId
);
702 assert
.fail(noExpectedException
);
704 assert(e
instanceof Errors
.InternalInconsistencyError
);
708 it('requires topic to exist', async
function () {
709 communication
.db
.topicGetContentById
.restore();
710 sinon
.stub(communication
.db
, 'topicGetContentById').resolves();
712 await communication
.subscriptionDeliveryProcess(dbCtx
, subscriptionId
, requestId
);
713 assert
.fail(noExpectedException
);
715 assert(e
instanceof Errors
.InternalInconsistencyError
);
719 it('succeeds', async
function () {
720 await communication
.subscriptionDeliveryProcess(dbCtx
, subscriptionId
, requestId
);
722 assert(communication
.db
.subscriptionDeliveryComplete
.called
);
725 it('handles request error', async
function () {
726 communication
.axios
.restore();
727 sinon
.stub(communication
, 'axios').throws();
729 await communication
.subscriptionDeliveryProcess(dbCtx
, subscriptionId
, requestId
);
731 assert(communication
.db
.subscriptionDeliveryIncomplete
.called
);
734 it('handles 5xx response', async
function () {
735 communication
.axios
.restore();
736 sinon
.stub(communication
, 'axios').resolves({
738 statusText: 'Internal Server Error',
740 'content-type': 'text/plain',
744 await communication
.subscriptionDeliveryProcess(dbCtx
, subscriptionId
, requestId
);
746 assert(communication
.db
.subscriptionDeliveryIncomplete
.called
);
749 it('handles 4xx response', async
function () {
750 communication
.axios
.restore();
751 sinon
.stub(communication
, 'axios').resolves({
753 statusText: 'Not Found',
755 'content-type': 'text/plain',
759 await communication
.subscriptionDeliveryProcess(dbCtx
, subscriptionId
, requestId
);
761 assert(communication
.db
.subscriptionDeliveryIncomplete
.called
);
764 it('handles 410 response', async
function () {
765 communication
.axios
.restore();
766 sinon
.stub(communication
, 'axios').resolves({
770 'content-type': 'text/plain',
774 await communication
.subscriptionDeliveryProcess(dbCtx
, subscriptionId
, requestId
);
776 assert(communication
.db
.subscriptionDeliveryGone
.called
);
779 it('unsubscribes when topic is deleted', async
function () {
780 topic
.isDeleted
= true;
781 communication
.db
.topicGetContentById
.restore();
782 sinon
.stub(communication
.db
, 'topicGetContentById').resolves(topic
);
784 await communication
.subscriptionDeliveryProcess(dbCtx
, subscriptionId
, requestId
);
786 assert(communication
.db
.verificationInsert
.called
);
787 assert(communication
.db
.subscriptionDeliveryComplete
.called
);
789 }); // subscriptionDeliveryProcess
791 describe('topicFetchClaimAndProcessById', function () {
792 let dbCtx
, topicId
, requestId
;
793 beforeEach(function () {
795 requestId
= '7d37ea20-4ef7-417e-a08d-c0ba71269ab1';
796 topicId
= '234ec6fb-f1cd-4ac3-8ea9-29ed42ae0e21';
797 sinon
.stub(communication
, 'topicFetchProcess');
799 it('covers claim', async
function () {
800 communication
.db
.topicFetchClaimById
.resolves({
803 await communication
.topicFetchClaimAndProcessById(dbCtx
, topicId
, requestId
);
804 assert(communication
.topicFetchProcess
.called
);
806 it('covers no claim', async
function () {
807 communication
.db
.topicFetchClaimById
.resolves({
810 await communication
.topicFetchClaimAndProcessById(dbCtx
, topicId
, requestId
);
811 assert(!communication
.topicFetchProcess
.called
);
813 }); // topicFetchClaimAndProcessById
815 describe('verificationClaimAndProcessById', function () {
816 let dbCtx
, verificationId
, requestId
;
817 beforeEach(function () {
819 verificationId
= '28488311-6652-42ea-9839-7bbc42b246cb';
820 requestId
= '7d37ea20-4ef7-417e-a08d-c0ba71269ab1';
821 sinon
.stub(communication
, 'verificationProcess');
823 it('covers claim', async
function () {
824 communication
.db
.verificationClaimById
.resolves({
827 await communication
.verificationClaimAndProcessById(dbCtx
, verificationId
, requestId
);
828 assert(communication
.verificationProcess
.called
);
830 it('covers no claim', async
function () {
831 communication
.db
.verificationClaimById
.resolves({
834 await communication
.verificationClaimAndProcessById(dbCtx
, verificationId
, requestId
);
835 assert(!communication
.verificationProcess
.called
);
837 }); // verificationClaimAndProcessById
839 describe('workFeed', function () {
841 beforeEach(function () {
842 sinon
.stub(communication
, 'topicFetchProcess');
843 sinon
.stub(communication
, 'verificationProcess');
844 sinon
.stub(communication
, 'subscriptionDeliveryProcess');
846 it('succeeds', async
function () {
847 const topicIds
= [ { id: '' }, { id: '' } ];
848 communication
.db
.topicFetchClaim
.resolves(topicIds
);
849 const verificationIds
= [ { id: '' }, { id: '' } ];
850 communication
.db
.verificationClaim
.resolves(verificationIds
);
851 const subscriptionIds
= [ { id: '' }, { id: '' } ];
852 communication
.db
.subscriptionDeliveryClaim
.resolves(subscriptionIds
);
853 const expectedLength
= [topicIds
, verificationIds
, subscriptionIds
].map((x
) => x
.length
).reduce((a
, b
) => a
+ b
, 0);
856 const result
= await communication
.workFeed(wanted
);
858 assert
.strictEqual(result
.length
, expectedLength
);
860 it('covers no wanted work', async
function () {
861 const result
= await communication
.workFeed(0);
862 assert
.strictEqual(result
.length
, 0);
863 assert(!communication
.db
.topicFetchClaim
.called
);
864 assert(!communication
.db
.verificationClaim
.called
);
865 assert(!communication
.db
.subscriptionDeliveryClaim
.called
);
867 it('deals with failure', async
function () {
868 const topicIds
= [ { id: '' }, { id: '' } ];
869 communication
.db
.topicFetchClaim
.resolves(topicIds
);
870 communication
.db
.verificationClaim
.throws();
871 const expectedLength
= topicIds
.length
;
874 const result
= await communication
.workFeed(wanted
);
876 assert
.strictEqual(result
.length
, expectedLength
);