update dependencies and devDependencies, fix lint issues
[squeep-indie-auther] / test / src / manager.js
index 7336e1fb7989ad716543ead8daed5017ce93bc44..54e867f38ea0afc3bb52396ddc0babbdb66b0033 100644 (file)
@@ -1,10 +1,7 @@
-/* eslint-env mocha */
-/* eslint-disable capitalized-comments, sonarjs/no-duplicate-string, sonarjs/no-identical-functions */
-
 'use strict';
 
 const assert = require('assert');
-const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
+const sinon = require('sinon');
 
 const Manager = require('../../src/manager');
 const Config = require('../../config');
@@ -49,10 +46,14 @@ describe('Manager', function () {
     sinon.stub(manager.communication, 'fetchProfile');
     sinon.stub(manager.communication, 'fetchClientIdentifier');
     sinon.stub(manager.communication, 'deliverTicket');
-    sinon.stub(dns, 'lookupAsync').resolves([{ family: 4, address: '10.11.12.13' }]);
+    sinon.stub(manager.communication, 'redeemTicket');
+    sinon.stub(dns.promises, 'lookup').resolves([{ family: 4, address: '10.11.12.13' }]);
     sinon.stub(manager.queuePublisher, 'connect');
     sinon.stub(manager.queuePublisher, 'establishAMQPPlumbing');
     sinon.stub(manager.queuePublisher, 'publish');
+    sinon.stub(manager.queueConsumer, 'connect');
+    sinon.stub(manager.queueConsumer, 'establishAMQPPlumbing');
+    sinon.stub(manager.queueConsumer, 'consume');
   });
 
   afterEach(function () {
@@ -425,7 +426,8 @@ describe('Manager', function () {
       assert.strictEqual(res.statusCode, 302);
       assert.strictEqual(ctx.session.error, 'invalid_request');
       assert.strictEqual(ctx.session.errorDescriptions.length, 1);
-    });  }); // getAuthorization
+    });
+  }); // getAuthorization
 
   describe('_setError', function () {
     it('covers', function () {
@@ -899,7 +901,7 @@ describe('Manager', function () {
       assert.deepStrictEqual(result, []);
     });
     it('filters invalid scopes', function () {
-      ctx.parsedBody['accepted_scopes'] = ['read', 'email'];
+      ctx.parsedBody['accepted_scopes[]'] = ['read', 'email'];
       ctx.parsedBody['ad_hoc_scopes'] = 'bad"scope  create ';
       const result = manager._parseConsentScopes(ctx);
       assert.deepStrictEqual(result, ['read', 'create']);
@@ -1008,12 +1010,12 @@ describe('Manager', function () {
       assert(ctx.session.error);
     });
     it('removes email scope without profile', async function () {
-      ctx.parsedBody['accepted_scopes'] = ['email', 'create'];
+      ctx.parsedBody['accepted_scopes[]'] = ['email', 'create'];
       await manager.postConsent(res, ctx);
       assert(!ctx.session.acceptedScopes.includes('email'));
     });
     it('merges valid ad-hoc scopes', async function () {
-      ctx.parsedBody['accepted_scopes'] = ['email', 'create'];
+      ctx.parsedBody['accepted_scopes[]'] = ['email', 'create'];
       ctx.parsedBody['ad_hoc_scopes'] = '  my:scope  "badScope';
       await manager.postConsent(res, ctx);
       assert(ctx.session.acceptedScopes.includes('my:scope'));
@@ -1553,13 +1555,13 @@ describe('Manager', function () {
       manager.mysteryBox.unpack.resolves({});
       req.getHeader.returns('Bearer XXX');
       await manager._checkTokenValidationRequest(dbCtx, req, ctx);
-      assert(ctx.session.error)
+      assert(ctx.session.error);
     });
     it('covers no token', async function () {
       manager.mysteryBox.unpack.resolves({ c: 'xxx' });
       req.getHeader.returns('Bearer XXX');
       await manager._checkTokenValidationRequest(dbCtx, req, ctx);
-      assert(ctx.session.error)
+      assert(ctx.session.error);
     });
     it('covers db error', async function () {
       manager.mysteryBox.unpack.resolves({ c: 'xxx' });
@@ -2047,7 +2049,7 @@ describe('Manager', function () {
     describe('save-scopes action', function () {
       beforeEach(function () {
         ctx.parsedBody['action'] = 'save-scopes';
-        ctx.parsedBody['scopes-https://profile/example.com/'] = ['scope1', 'scope2'];
+        ctx.parsedBody['scopes-https://profile/example.com/[]'] = ['scope1', 'scope2'];
       });
       it('covers saving scopes', async function () {
         await manager.postAdmin(res, ctx);
@@ -2205,7 +2207,7 @@ describe('Manager', function () {
   describe('postAdminTicket', function () {
     beforeEach(function () {
       ctx.parsedBody['action'] = 'proffer-ticket';
-      ctx.parsedBody['scopes'] = ['read', 'role:private'];
+      ctx.parsedBody['scopes[]'] = ['read', 'role:private'];
       ctx.parsedBody['adhoc'] = 'adhoc_scope';
       ctx.parsedBody['profile'] = 'https://profile.example.com/';
       ctx.parsedBody['resource'] = 'https://profile.example.com/feed';
@@ -2229,7 +2231,7 @@ describe('Manager', function () {
       ctx.parsedBody['profile'] = 'bad url';
       ctx.parsedBody['resource'] = 'bad url';
       ctx.parsedBody['subject'] = 'bad url';
-      ctx.parsedBody['scopes'] = ['fl"hrgl', 'email'];
+      ctx.parsedBody['scopes[]'] = ['fl"hrgl', 'email'];
       await manager.postAdminTicket(res, ctx);
       assert(res.end.called);
       assert.strictEqual(ctx.errors.length, 5);
@@ -2276,6 +2278,7 @@ describe('Manager', function () {
         ticket: 'ticket123',
         resource: 'https://blog.example.com/',
         subject: 'https://otheruser.example.com/',
+        iss: 'https://ia.example.com/',
       };
     });
     it('accepts a ticket for a known profile', async function () {
@@ -2300,12 +2303,81 @@ describe('Manager', function () {
     it('covers no ticket queue', async function () {
       delete options.queues.amqp.url;
       manager = new Manager(logger, stubDb, options);
-
       await assert.rejects(() => manager.postTicket(req, res, ctx), ResponseError);
     });
+    it('covers no issuer', async function () {
+      delete ctx.parsedBody.iss;
+      manager.db.profileIsValid.resolves(true);
+      await manager.postTicket(req, res, ctx);
+      assert(res.end.called);
+      assert.strictEqual(res.statusCode, 202);
+    });
+    it('covers bad issuer', async function () {
+      ctx.parsedBody.iss = 'not a url';
+      manager.db.profileIsValid.resolves(true);
+      await manager.postTicket(req, res, ctx);
+      assert(res.end.called);
+      assert.strictEqual(res.statusCode, 202);
+    });
 
   }); // postTicket
 
+  describe('queuedTicketProcessor', function () {
+    let channel, content;
+    const message = () => ({
+      content: Buffer.from(JSON.stringify(content)),
+    });
+    beforeEach(function () {
+      channel = {
+        ack: sinon.stub(),
+      };
+      content = {
+        ticket: 'XXXticketXXX',
+        resource: 'https://blog.example.com/',
+        subject: 'https://otheruser.exmaple.com/',
+        iss: 'https://ia.example.com/',
+        epochMs: Date.now(),
+      };
+    });
+    it('redeems a ticket', async function () {
+      await manager.queuedTicketProcessor(channel, message());
+      assert(manager.queuePublisher.publish.called);
+      assert(channel.ack.called);
+    });
+    it('redeems a ticket, missing issuer', async function () {
+      delete content.iss;
+      await manager.queuedTicketProcessor(channel, message());
+      assert(manager.queuePublisher.publish.called);
+      assert(channel.ack.called);
+    });
+    it('covers bad message', async function () {
+      await manager.queuedTicketProcessor(channel, { content: 'diddly' });
+      assert(channel.ack.called);
+    });
+    it('covers bad issuer', async function () {
+      content.iss = 'not a url';
+      await manager.queuedTicketProcessor(channel, message());
+      assert(manager.queuePublisher.publish.called);
+    });
+    it('covers bad resource', async function () {
+      content.resource = 'not a url';
+      await manager.queuedTicketProcessor(channel, message());
+      assert(manager.communication.redeemTicket.notCalled);
+      assert(manager.queuePublisher.publish.notCalled);
+      assert(channel.ack.called);
+    });
+    it('covers failed redemption', async function () {
+      const expectedException = new Error('oh no');
+      manager.communication.redeemTicket.rejects(expectedException);
+      assert.rejects(() => manager.queuedTicketProcessor(channel, message()), expectedException);
+    });
+    it('covers failed publish', async function () {
+      const expectedException = new Error('oh no');
+      manager.queuePublisher.publish.rejects(expectedException);
+      assert.rejects(() => manager.queuedTicketProcessor(channel, message()), expectedException);
+    });
+  }); // queuedTicketProcessor
+
   describe('getAdminMaintenance', function () {
     it('covers information', async function () {
       await manager.getAdminMaintenance(res, ctx);
@@ -2320,4 +2392,4 @@ describe('Manager', function () {
     });
   }); // getAdminMaintenance
 
-}); // Manager
\ No newline at end of file
+}); // Manager