support indieauth-metadata relation on profiles, per spec 20220212
[squeep-indieauth-helper] / test / lib / communication.js
1 /* eslint-env mocha */
2 /* eslint-disable capitalized-comments, sonarjs/no-duplicate-string */
3
4 'use strict';
5
6 const assert = require('assert');
7 const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
8
9 const Communication = require('../../lib/communication');
10
11 const stubLogger = require('../stub-logger');
12 const testData = require('../test-data/communication');
13
14 const noExpectedException = 'did not get expected exception';
15
16 describe('Communication', function () {
17 let communication, options;
18
19 beforeEach(function () {
20 options = {};
21 communication = new Communication(stubLogger, options);
22 stubLogger._reset();
23 sinon.stub(communication, 'axios');
24 });
25 afterEach(function () {
26 sinon.restore();
27 });
28
29 it('instantiates', function () {
30 assert(communication);
31 });
32
33 it('covers no config', function () {
34 communication = new Communication(stubLogger);
35 });
36
37 describe('Axios timing coverage', function () {
38 const request = {};
39 const response = {
40 config: request,
41 };
42 it('tags request', function () {
43 communication.axios.interceptors.request.handlers[0].fulfilled(request);
44 assert(request.startTimestampMs);
45 });
46 it('tags response', function () {
47 communication.axios.interceptors.response.handlers[0].fulfilled(response);
48 assert(response.elapsedTimeMs);
49 });
50 }); // Axios timing coverage
51
52 describe('_challengeFromVerifier', function () {
53 it('covers', function () {
54 const verifier = 'VGhpcyBpcyBhIHNlY3JldC4u';
55 const expected = 'O5W5A-1CAnrNGp2yHZtEql6rfHere4wJmzsyow7LLiY';
56 const result = Communication._challengeFromVerifier(verifier);
57 assert.strictEqual(result, expected);
58 });
59 }); // _challengeFromVerifier
60
61 describe('generatePKCE', function () {
62 it('covers', async function () {
63 const result = await Communication.generatePKCE();
64 assert(result.codeVerifier);
65 assert(result.codeChallenge);
66 assert(result.codeChallengeMethod);
67 assert.strictEqual(result.codeChallengeMethod, 'S256');
68 });
69 it('covers error', async function () {
70 try {
71 await Communication.generatePKCE(1);
72 assert.fail(noExpectedException);
73 } catch (e) {
74 assert(e instanceof RangeError);
75 }
76 });
77 }); // generatePKCE
78
79 describe('verifyChallenge', function () {
80 it('covers success', function () {
81 const method = 'S256';
82 const challenge = 'O5W5A-1CAnrNGp2yHZtEql6rfHere4wJmzsyow7LLiY';
83 const verifier = 'VGhpcyBpcyBhIHNlY3JldC4u';
84 const result = Communication.verifyChallenge(challenge, verifier, method);
85 assert.strictEqual(result, true);
86 });
87 it('also covers success', function () {
88 const method = 'SHA256';
89 const challenge = 'O5W5A-1CAnrNGp2yHZtEql6rfHere4wJmzsyow7LLiY';
90 const verifier = 'VGhpcyBpcyBhIHNlY3JldC4u';
91 const result = Communication.verifyChallenge(challenge, verifier, method);
92 assert.strictEqual(result, true);
93 });
94 it('covers failure', function () {
95 const method = 'S256';
96 const challenge = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
97 const verifier = 'VGhpcyBpcyBhIHNlY3JldC4u';
98 const result = Communication.verifyChallenge(challenge, verifier, method);
99 assert.strictEqual(result, false);
100 });
101 it('covers unhandled method', function () {
102 const method = 'MD5';
103 const challenge = 'xkfP7DUYDsnu07Kg6ogc8A';
104 const verifier = 'VGhpcyBpcyBhIHNlY3JldC4u';
105 try {
106 Communication.verifyChallenge(challenge, verifier, method);
107 assert.fail(noExpectedException);
108 } catch (e) {
109 assert(e.message.includes('unsupported'));
110 }
111 });
112 }); // verifyChallenge
113
114 describe('_userAgentString', function () {
115 it('has default behavior', function () {
116 const result = Communication._userAgentString();
117 assert(result);
118 assert(result.length > 30);
119 });
120 it('is settable', function () {
121 const result = Communication._userAgentString({
122 product: 'myClient',
123 version: '9.9.9',
124 implementation: 'custom',
125 });
126 assert(result);
127 assert.strictEqual(result, 'myClient/9.9.9 (custom)');
128 });
129 it('covers branches', function () {
130 const result = Communication._userAgentString({
131 product: 'myClient',
132 version: '9.9.9',
133 implementation: '',
134 });
135 assert(result);
136 assert.strictEqual(result, 'myClient/9.9.9');
137 });
138 }); // userAgentString
139
140 describe('Axios Configurations', function () {
141 let requestUrl, expectedUrl;
142 beforeEach(function () {
143 requestUrl = 'https://example.com/client_id';
144 expectedUrl = 'https://example.com/client_id';
145 });
146 it('_axiosConfig', function () {
147 const method = 'GET';
148 const contentType = 'text/plain';
149 const body = undefined;
150 const params = {
151 'extra_parameter': 'foobar',
152 };
153 const urlObj = new URL(requestUrl);
154 const expectedUrlObj = new URL(`${requestUrl}?extra_parameter=foobar`);
155 const expected = {
156 method,
157 url: 'https://example.com/client_id',
158 headers: {
159 'Content-Type': 'text/plain',
160 },
161 params: expectedUrlObj.searchParams,
162 responseType: 'text',
163 };
164 const result = Communication._axiosConfig(method, urlObj, body, params, {
165 'Content-Type': contentType,
166 });
167 delete result.transformResponse;
168 assert.deepStrictEqual(result, expected);
169 });
170 it('_axiosConfig covers defaults', function () {
171 const method = 'OPTIONS';
172 const urlObj = new URL(requestUrl);
173 const expectedUrlObj = new URL(requestUrl);
174 const expected = {
175 method,
176 url: expectedUrl,
177 headers: {},
178 params: expectedUrlObj.searchParams,
179 responseType: 'text',
180 };
181 const result = Communication._axiosConfig(method, urlObj);
182 delete result.transformResponse;
183 assert.deepStrictEqual(result, expected);
184 });
185 it('covers data', function () {
186 const method = 'POST';
187 const body = Buffer.from('some data');
188 const params = {};
189 const urlObj = new URL(requestUrl);
190 const expected = {
191 method,
192 url: 'https://example.com/client_id',
193 data: body,
194 headers: {},
195 params: urlObj.searchParams,
196 responseType: 'text',
197 };
198 const result = Communication._axiosConfig(method, urlObj, body, params, {});
199 delete result.transformResponse;
200 assert.deepStrictEqual(result, expected);
201
202 });
203 it('covers null response transform', function () {
204 const urlObj = new URL(requestUrl);
205 const result = Communication._axiosConfig('GET', urlObj, undefined, {}, {});
206 result.transformResponse[0]();
207 });
208 }); // Axios Configurations
209
210 describe('_baseUrlString', function () {
211 it('covers no path', function () {
212 const urlObj = new URL('https://example.com');
213 const expected = 'https://example.com/';
214 const result = Communication._baseUrlString(urlObj);
215 assert.strictEqual(result, expected);
216 });
217 it('covers paths', function () {
218 const urlObj = new URL('https://example.com/path/blah');
219 const expected = 'https://example.com/path/';
220 const result = Communication._baseUrlString(urlObj);
221 assert.strictEqual(result, expected);
222 });
223 }); // _baseUrlString
224
225 describe('_parseContentType', function () {
226 let contentTypeHeader, expected, result;
227 it('covers undefined', function () {
228 contentTypeHeader = undefined;
229 expected = {
230 mediaType: 'application/octet-stream',
231 params: {},
232 };
233 result = Communication._parseContentType(contentTypeHeader);
234 assert.deepStrictEqual(result, expected);
235 });
236 it('covers empty', function () {
237 contentTypeHeader = '';
238 expected = {
239 mediaType: 'application/octet-stream',
240 params: {},
241 };
242 result = Communication._parseContentType(contentTypeHeader);
243 assert.deepStrictEqual(result, expected);
244 });
245 it('covers extra parameters', function () {
246 contentTypeHeader = 'text/plain; CharSet="UTF-8"; WeirdParam';
247 expected = {
248 mediaType: 'text/plain',
249 params: {
250 'charset': 'UTF-8',
251 'weirdparam': undefined,
252 },
253 };
254 result = Communication._parseContentType(contentTypeHeader);
255 assert.deepStrictEqual(result, expected);
256 });
257 }); // parseContentType
258
259 describe('_mergeLinkHeader', function () {
260 let microformat, response, expected;
261 beforeEach(function () {
262 microformat = {};
263 response = {
264 headers: {
265 link: '<https://example.com/>; rel="self", <https://hub.example.com/>;rel="hub"',
266 },
267 data: {},
268 }
269 });
270 it('covers', function () {
271 expected = {
272 items: [],
273 rels: {
274 'hub': ['https://hub.example.com/'],
275 'self': ['https://example.com/'],
276 },
277 'rel-urls': {
278 'https://example.com/': {
279 rels: ['self'],
280 text: '',
281 },
282 'https://hub.example.com/': {
283 rels: ['hub'],
284 text: '',
285 },
286 },
287 };
288 communication._mergeLinkHeader(microformat, response);
289 assert.deepStrictEqual(microformat, expected);
290 });
291 it('covers existing', function () {
292 microformat = {
293 items: [],
294 rels: {
295 'preload': ['https://example.com/style'],
296 'hub': ['https://hub.example.com/'],
297 },
298 'rel-urls': {
299 'https://hub.example.com/': {
300 rels: ['hub'],
301 text: '',
302 },
303 'https://example.com/style': {
304 rels: ['preload'],
305 text: '',
306 },
307 },
308 };
309 expected = {
310 items: [],
311 rels: {
312 'preload': ['https://example.com/style'],
313 'hub': ['https://hub.example.com/', 'https://hub.example.com/'],
314 'self': ['https://example.com/'],
315 },
316 'rel-urls': {
317 'https://example.com/': {
318 rels: ['self'],
319 text: '',
320 },
321 'https://hub.example.com/': {
322 rels: ['hub', 'hub'],
323 text: '',
324 },
325 'https://example.com/style': {
326 rels: ['preload'],
327 text: '',
328 },
329 },
330 };
331 communication._mergeLinkHeader(microformat, response);
332 assert.deepStrictEqual(microformat, expected);
333 });
334 it('ignores bad header', function () {
335 response.headers.link = 'not really a link header';
336 expected = {
337 items: [],
338 rels: {},
339 'rel-urls': {},
340 };
341 communication._mergeLinkHeader(microformat, response);
342 assert.deepStrictEqual(microformat, expected);
343 });
344 }); // _mergeLinkHeader
345
346 describe('fetchMicroformat', function () {
347 let expected, response, result, urlObj;
348 beforeEach(function () {
349 expected = undefined;
350 result = undefined;
351 urlObj = new URL('https://thuza.ratfeathers.com/');
352 response = {
353 headers: Object.assign({}, testData.linkHeaders),
354 data: testData.hCardHtml,
355 };
356 });
357 it('covers', async function () {
358 response.data = testData.hCardHtml;
359 communication.axios.resolves(response);
360 expected = {
361 rels: {
362 'authorization_endpoint': ['https://ia.squeep.com/auth'],
363 'token_endpoint': ['https://ia.squeep.com/token'],
364 'canonical': ['https://thuza.ratfeathers.com/'],
365 'author': ['https://thuza.ratfeathers.com/'],
366 'me': ['https://thuza.ratfeathers.com/'],
367 'self': ['https://thuza.ratfeathers.com/'],
368 'hub': ['https://hub.squeep.com/'],
369 'preload': ['https://thuza.ratfeathers.com/image.png'],
370 },
371 'rel-urls': {
372 'https://hub.squeep.com/': {
373 rels: ['hub'],
374 text: '',
375 },
376 'https://ia.squeep.com/auth': {
377 rels: ['authorization_endpoint'],
378 text: '',
379 },
380 'https://ia.squeep.com/token': {
381 rels: ['token_endpoint'],
382 text: '',
383 },
384 'https://thuza.ratfeathers.com/': {
385 rels: ['self', 'canonical', 'author', 'me'],
386 text: 'Thuza',
387 },
388 'https://thuza.ratfeathers.com/image.png': {
389 rels: ['preload'],
390 text: '',
391 },
392 },
393 items: [{
394 properties: {
395 name: ['Thuza'],
396 photo: ['https://thuza.ratfeathers.com/image.png'],
397 url: ['https://thuza.ratfeathers.com/'],
398 },
399 type: ['h-card'],
400 }],
401 };
402
403 result = await communication.fetchMicroformat(urlObj);
404 assert.deepStrictEqual(result, expected);
405 });
406 it('covers axios error', async function () {
407 communication.axios.rejects(new Error('blah'));
408 expected = undefined;
409
410 result = await communication.fetchMicroformat(urlObj);
411
412 assert.deepStrictEqual(result, expected);
413 });
414 it('covers non-parsable content', async function () {
415 response.data = 'some bare text';
416 response.headers = {};
417 communication.axios.resolves(response);
418 expected = {
419 items: [],
420 rels: {},
421 'rel-urls': {},
422 };
423
424 result = await communication.fetchMicroformat(urlObj);
425
426 assert.deepStrictEqual(result, expected);
427 });
428 it('covers non-utf8 content', async function () {
429 response.headers['content-type'] = 'text/html; charset=ASCII';
430 communication.axios.resolves(response);
431 expected = {
432 rels: {
433 'authorization_endpoint': ['https://ia.squeep.com/auth'],
434 'token_endpoint': ['https://ia.squeep.com/token'],
435 'canonical': ['https://thuza.ratfeathers.com/'],
436 'author': ['https://thuza.ratfeathers.com/'],
437 'me': ['https://thuza.ratfeathers.com/'],
438 'self': ['https://thuza.ratfeathers.com/'],
439 'hub': ['https://hub.squeep.com/'],
440 'preload': ['https://thuza.ratfeathers.com/image.png'],
441 },
442 'rel-urls': {
443 'https://hub.squeep.com/': {
444 rels: ['hub'],
445 text: '',
446 },
447 'https://ia.squeep.com/auth': {
448 rels: ['authorization_endpoint'],
449 text: '',
450 },
451 'https://ia.squeep.com/token': {
452 rels: ['token_endpoint'],
453 text: '',
454 },
455 'https://thuza.ratfeathers.com/': {
456 rels: ['self', 'canonical', 'author', 'me'],
457 text: 'Thuza',
458 },
459 'https://thuza.ratfeathers.com/image.png': {
460 rels: ['preload'],
461 text: '',
462 },
463 },
464 items: [{
465 properties: {
466 name: ['Thuza'],
467 photo: ['https://thuza.ratfeathers.com/image.png'],
468 url: ['https://thuza.ratfeathers.com/'],
469 },
470 type: ['h-card'],
471 }],
472 };
473
474 result = await communication.fetchMicroformat(urlObj);
475
476 assert.deepStrictEqual(result, expected);
477 });
478 }); // fetchMicroformat
479
480 describe('fetchJSON', function () {
481 let expected, response, result, urlObj;
482 beforeEach(function () {
483 expected = undefined;
484 result = undefined;
485 urlObj = new URL('https://thuza.ratfeathers.com/');
486 response = {
487 headers: Object.assign({}, testData.linkHeaders),
488 data: testData.hCardHtml,
489 };
490 });
491 it('covers', async function () {
492 communication.axios.resolves(response);
493 expected = { foo: 'bar', baz: 123 };
494 response.data = JSON.stringify(expected);
495
496 result = await communication.fetchJSON(urlObj);
497 assert.deepStrictEqual(result, expected);
498 });
499 it('covers axios error', async function () {
500 communication.axios.rejects(new Error('blah'));
501 expected = undefined;
502
503 result = await communication.fetchJSON(urlObj);
504
505 assert.deepStrictEqual(result, expected);
506 });
507 it('covers non-parsable content', async function () {
508 response.data = 'some bare text';
509 response.headers = {};
510 communication.axios.resolves(response);
511 expected = undefined;
512
513 result = await communication.fetchJSON(urlObj);
514
515 assert.deepStrictEqual(result, expected);
516 });
517 }); // fetchJSON
518
519 describe('fetchClientIdentifier', function () {
520 let expected, response, result, urlObj;
521 beforeEach(function () {
522 expected = undefined;
523 result = undefined;
524 urlObj = new URL('https://thuza.ratfeathers.com/');
525 response = {
526 headers: {},
527 data: testData.multiMF2Html,
528 };
529 });
530 it('covers', async function () {
531 communication.axios.resolves(response);
532 expected = {
533 items: [{
534 properties: {
535 name: ['Also Some Client'],
536 url: ['https://thuza.ratfeathers.com/'],
537 },
538 type: ['h-app'],
539 }],
540 rels: {
541 'author': ['https://thuza.ratfeathers.com/'],
542 'authorization_endpoint': ['https://ia.squeep.com/auth'],
543 'canonical': ['https://thuza.ratfeathers.com/'],
544 'me': ['https://thuza.ratfeathers.com/'],
545 'token_endpoint': ['https://ia.squeep.com/token'],
546 },
547 };
548 result = await communication.fetchClientIdentifier(urlObj);
549 assert.deepStrictEqual(result, expected);
550 });
551 it('covers failed fetch', async function () {
552 communication.axios.rejects();
553 expected = undefined;
554 result = await communication.fetchClientIdentifier(urlObj);
555 assert.deepStrictEqual(result, expected);
556 });
557 it('covers no h-app data', async function () {
558 response.data = testData.noneMF2Html;
559 communication.axios.resolves(response);
560 expected = {
561 items: [],
562 rels: {},
563 };
564 result = await communication.fetchClientIdentifier(urlObj);
565 assert.deepStrictEqual(result, expected);
566 });
567 it('covers missing fields', async function () {
568 sinon.stub(communication, 'fetchMicroformat').resolves({});
569 expected = {
570 rels: {},
571 items: [],
572 };
573 result = await communication.fetchClientIdentifier(urlObj);
574 assert.deepStrictEqual(result, expected);
575 });
576 it('covers other missing fields', async function () {
577 sinon.stub(communication, 'fetchMicroformat').resolves({
578 items: [
579 {},
580 {
581 type: ['h-app'],
582 properties: {
583 url: ['https://example.com'],
584 },
585 },
586 ],
587 });
588 expected = {
589 rels: {},
590 items: [],
591 };
592 result = await communication.fetchClientIdentifier(urlObj);
593 assert.deepStrictEqual(result, expected);
594 });
595 }); // fetchClientIdentifier
596
597 describe('fetchProfile', function () {
598 let expected, response, result, urlObj;
599 beforeEach(function () {
600 expected = undefined;
601 result = undefined;
602 urlObj = new URL('https://thuza.ratfeathers.com/');
603 response = {
604 headers: {},
605 data: testData.hCardHtml,
606 };
607 sinon.stub(communication, 'fetchJSON');
608 });
609 describe('legacy without indieauth-metadata', function () {
610 it('covers', async function () {
611 communication.axios.resolves(response);
612 expected = {
613 name: 'Thuza',
614 photo: 'https://thuza.ratfeathers.com/image.png',
615 url: 'https://thuza.ratfeathers.com/',
616 email: undefined,
617 authorizationEndpoint: 'https://ia.squeep.com/auth',
618 tokenEndpoint: 'https://ia.squeep.com/token',
619 metadata: {
620 authorizationEndpoint: 'https://ia.squeep.com/auth',
621 tokenEndpoint: 'https://ia.squeep.com/token',
622 },
623 };
624 result = await communication.fetchProfile(urlObj);
625 assert.deepStrictEqual(result, expected);
626 });
627 it('covers multiple hCards', async function () {
628 response.data = testData.multiMF2Html;
629 communication.axios.resolves(response);
630 expected = {
631 email: undefined,
632 name: 'Thuza',
633 photo: 'https://thuza.ratfeathers.com/image.png',
634 url: 'https://thuza.ratfeathers.com/',
635 authorizationEndpoint: 'https://ia.squeep.com/auth',
636 tokenEndpoint: 'https://ia.squeep.com/token',
637 metadata: {
638 authorizationEndpoint: 'https://ia.squeep.com/auth',
639 tokenEndpoint: 'https://ia.squeep.com/token',
640 },
641 };
642 result = await communication.fetchProfile(urlObj);
643 assert.deepStrictEqual(result, expected);
644 });
645 it('covers failed fetch', async function () {
646 communication.axios.rejects();
647 expected = {
648 email: undefined,
649 name: undefined,
650 photo: undefined,
651 url: undefined,
652 metadata: {},
653 };
654 result = await communication.fetchProfile(urlObj);
655 assert.deepStrictEqual(result, expected);
656 });
657 });
658 it('covers', async function () {
659 response.data = testData.hCardMetadataHtml;
660 communication.axios.resolves(response);
661 communication.fetchJSON.resolves({
662 'issuer': 'https://ia.squeep.com/',
663 'authorization_endpoint': 'https://ia.squeep.com/auth',
664 'token_endpoint': 'https://ia.squeep.com/token',
665 'introspection_endpoint': 'https://ia.squeep.com/introspect',
666 'introspection_endpoint_auth_methods_supported': [ '' ],
667 'revocation_endpoint': 'https://ia.squeep.com/revoke',
668 'revocation_endpoint_auth_methods_supported': [ 'none' ],
669 'scopes_supported': [ 'profile', 'email' ],
670 'service_documentation': 'https://indieauth.spec.indieweb.org/',
671 'code_challenge_methods_supported': [ 'S256', 'SHA256' ],
672 'authorization_response_iss_parameter_supported': true,
673 'userinfo_endpoint': 'https://ia.squeep.com/userinfo',
674 });
675 expected = {
676 name: 'Thuza',
677 photo: 'https://thuza.ratfeathers.com/image.png',
678 url: 'https://thuza.ratfeathers.com/',
679 email: undefined,
680 metadata: {
681 authorizationEndpoint: 'https://ia.squeep.com/auth',
682 tokenEndpoint: 'https://ia.squeep.com/token',
683 issuer: 'https://ia.squeep.com/',
684 introspectionEndpoint: 'https://ia.squeep.com/introspect',
685 introspectionEndpointAuthMethodsSupported: [ '' ],
686 revocationEndpoint: 'https://ia.squeep.com/revoke',
687 revocationEndpointAuthMethodsSupported: [ 'none' ],
688 scopesSupported: [ 'profile', 'email' ],
689 serviceDocumentation: 'https://indieauth.spec.indieweb.org/',
690 codeChallengeMethodsSupported: [ 'S256', 'SHA256' ],
691 authorizationResponseIssParameterSupported: true,
692 userinfoEndpoint: 'https://ia.squeep.com/userinfo',
693 },
694 authorizationEndpoint: 'https://ia.squeep.com/auth',
695 tokenEndpoint: 'https://ia.squeep.com/token',
696 indieauthMetadata: 'https://ia.squeep.com/meta',
697 };
698
699 result = await communication.fetchProfile(urlObj);
700
701 assert.deepStrictEqual(result, expected);
702 });
703 it('covers metadata missing fields', async function () {
704 response.data = testData.hCardMetadataHtml;
705 communication.axios.resolves(response);
706 communication.fetchJSON.resolves({
707 'issuer': 'https://ia.squeep.com/',
708 });
709 expected = {
710 name: 'Thuza',
711 photo: 'https://thuza.ratfeathers.com/image.png',
712 url: 'https://thuza.ratfeathers.com/',
713 email: undefined,
714 metadata: {
715 issuer: 'https://ia.squeep.com/',
716 },
717 indieauthMetadata: 'https://ia.squeep.com/meta',
718 };
719
720 result = await communication.fetchProfile(urlObj);
721
722 assert.deepStrictEqual(result, expected);
723 });
724 it('covers metadata response failure', async function () {
725 const jsonError = new Error('oh no');
726 response.data = testData.hCardMetadataHtml;
727 communication.axios
728 .onCall(0).resolves(response)
729 .onCall(1).rejects(jsonError);
730 communication.fetchJSON.restore();
731 expected = {
732 name: 'Thuza',
733 photo: 'https://thuza.ratfeathers.com/image.png',
734 url: 'https://thuza.ratfeathers.com/',
735 email: undefined,
736 metadata: {},
737 indieauthMetadata: 'https://ia.squeep.com/meta',
738 };
739
740 result = await communication.fetchProfile(urlObj);
741
742 assert.deepStrictEqual(result, expected);
743 });
744 }); // fetchProfile
745
746 describe('redeemProfileCode', function () {
747 let expected, urlObj, code, codeVerifier, clientId, redirectURI;
748 this.beforeEach(function () {
749 urlObj = new URL('https://example.com/auth');
750 code = Buffer.allocUnsafe(42).toString('base64').replace('/', '_').replace('+', '-');
751 codeVerifier = Buffer.allocUnsafe(42).toString('base64').replace('/', '_').replace('+', '-');
752 clientId = 'https://example.com/';
753 redirectURI = 'https://example.com/_ia';
754 });
755 it('covers', async function () {
756 communication.axios.resolves({
757 data: '{"me":"https://profile.example.com/"}',
758 });
759 expected = {
760 me: 'https://profile.example.com/',
761 };
762
763 const result = await communication.redeemProfileCode(urlObj, code, codeVerifier, clientId, redirectURI);
764
765 assert.deepStrictEqual(result, expected);
766 });
767 it('covers failure', async function () {
768 communication.axios.resolves('Not a JSON payload.');
769
770 const result = await communication.redeemProfileCode(urlObj, code, codeVerifier, clientId, redirectURI);
771
772 assert.strictEqual(result, undefined);
773 });
774 });
775 }); // Communication