Skip to content

Commit

Permalink
User service: login, registration, graphql completion (#3)
Browse files Browse the repository at this point in the history
* user schema, abstract dynamo db class, user integration test

* completed integration test suite

* delete test cleanup

* user GQL service integration, start spec for user schema, service connection to gql middleware

* - dynamic config/environment support
- ddb abstract support of environments
- sls pseudo parameters for env support
- debugging cleanup
- tsconfig es2018 update (prep for type-graphql)

* mocha cli testing, actions

* testing workflow

* testing workflow

* adding package-lock

* workflow testing step

* join build/test jobs

* re-naming workflow

* user route/svc, login/mfa/oob methods, correct error handling, a0 envvars through ssm

* user service static method tests, http wrapper tests, updated env support for user svc

* rolling back router response type change

* user registration consolidation to graph endpoint, code cleanup, strict type checking on graph endpoint

* update content-type for graph requests to json

* - updated test cases to account for user svc updates
- user svc field requirement testing testing (email, sub, phone)
- user svc email field regex
- user svc sub format to follow A0 (database<type>|uuid)
- user svc phone number to support U.S. prefix/format only
- user schema write now requires (email, phone, sub)
- user schema marshaller createdAt correction
- user route query payload regex correction

* user svc validation refactor, PR feedback interation

* email field regex update, fix for missing phone field in payload, updated test cases

* no unused var fix

* - completed user registration flow
- refactored user authentication flow
- user mfa verification flow, post registration
  • Loading branch information
listenrightmeow authored Aug 13, 2020
1 parent 5ec5e46 commit 5a85115
Show file tree
Hide file tree
Showing 38 changed files with 810 additions and 221 deletions.
2 changes: 1 addition & 1 deletion __tests__/authorizers/auth0.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as jwt from 'jsonwebtoken';

import { A0 } from '@services/auth0';
import { authenticateToken, decodeToken, generatePolicy, getSigningKey, stripTokenFromHeader } from '@authorizers/auth0';
import { Responses as lang } from '@i18n/authorizer';
import { Authorizer as lang } from '@i18n/authorizer';

describe('auth0-authorizer', () => {
const sandbox = sinon.createSandbox();
Expand Down
39 changes: 0 additions & 39 deletions __tests__/helpers/axios.spec.ts

This file was deleted.

46 changes: 46 additions & 0 deletions __tests__/services/http.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* eslint-disable @typescript-eslint/no-var-requires */

const sinon = require('sinon');

import { expect } from 'chai';

import { httpWrapper as https } from '@services/http';

describe('http', () => {
const sandbox = sinon.createSandbox();

afterEach(function () {
sandbox.restore();
});

describe('get', () => {
it('promise should resolve', async () => {
let request;
const klass = new https('foo.bar');
sandbox.stub(klass, 'get').resolves(true);

try {
request = await klass.get('/baz');
console.log(request);
} catch (error) {
console.log(error);
} finally {
expect(request).to.equal(true);
}
});

it('promise should reject', async () => {
let request;
const klass = new https('foo.bar');
sandbox.stub(klass, 'get').rejects(false);

try {
request = await klass.get('/baz');
console.log(request);
} catch (error) {
console.log(error);
expect(request).to.have.throw;
}
});
});
})
97 changes: 78 additions & 19 deletions __tests__/services/user.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { expect } from 'chai';
import { User } from '@services/user';

describe.skip('user', () => {
let klass;
let payload;
let klass, payload;

const sandbox = sinon.createSandbox();

Expand All @@ -16,7 +15,7 @@ describe.skip('user', () => {
});

beforeEach(function(done) {
payload = { email: '[email protected]', sub: +new Date() };
payload = { email: '[email protected]', phone: '+19995551111' };
klass = new User();

const tableExists = klass.ensureTableExists();
Expand All @@ -29,9 +28,9 @@ describe.skip('user', () => {

describe('delete', () => {
it('should delete record', (done) => {
const payload = { email: '[email protected]', sub: +new Date() };
const payload = { email: '[email protected]', phone: '+19995551111' };
const put = klass.execute('put', payload);
const del = klass.execute('delete', payload)
const del = klass.execute('delete', { email: payload.email });

Promise.all([put, del]).then(results => {
expect(results[results.length - 1].email).to.equal(payload.email);
Expand All @@ -40,12 +39,10 @@ describe.skip('user', () => {
});

it('should return error', async () => {
const payload = { foo: '[email protected]', sub: +new Date() };

try {
await klass.execute('delete', payload);
await klass.execute('delete', { foo: 'bar@baz' });
} catch (error) {
expect(error.code).to.equal('ValidationException');
expect(error.message).to.equal('Field does not match requirements: [email, undefined]');
}
});
});
Expand All @@ -67,7 +64,7 @@ describe.skip('user', () => {
});

describe('get', () => {
it('should return record', async () => {
it('should return record by full payload', async () => {
let record;

try {
Expand All @@ -80,47 +77,109 @@ describe.skip('user', () => {
expect(record.email).to.equal(payload.email);
});

it('should return error', async () => {
it('should return record by email', async () => {
let record;
const p = { email: '[email protected]' };

try {
record = await klass.execute('get', p);
} catch (error) {
console.log(error);
return;
}

expect(record.email).to.equal(payload.email);
});

it('should return error due to no valid parameters', async () => {
const payload = { foo: 'bar' };

try {
await klass.execute('get', payload);
} catch (error) {
expect(error.code).to.equal('ValidationException');
expect(error.message).to.equal('Field does not match requirements: [email, undefined]');
}
});
});

describe('put', () => {
it('should write record', async () => {
let record;
const email = 'baz@bar.foo';
const sub = +new Date();
const email = 'foo@bar.baz';
const phone = '+19995551111';

try {
record = await klass.execute('put', { email: email, sub: sub });
record = await klass.execute('put', { email, phone });
} catch (error) {
console.log(error);
return;
console.error(error);
}

expect(record.email).to.equal(email);
});

it('should violate email format requirement', async () => {
let record;
const email = 'baz@bar';
const phone = '+19995551111';

try {
record = await klass.execute('put', { email, phone });
} catch (error) {
expect(error.message).to.equal(`Field does not match requirements: [email, ${email}]`);
}

console.log(record);
});

it('should violate phone format requirement', async () => {
const email = '[email protected]';
const phone = '+828282';

try {
await klass.execute('put', { email, phone });
} catch (error) {
console.log(error);
expect(error.message).to.equal(`Field does not match requirements: [phone, ${phone}]`);
}
});
});

describe('update', () => {
it('should update record', async () => {
let record;
const sub = +new Date();
const phone = '+10005551111';
const sub = 'test|0987654321';

try {
record = await klass.execute('update', { email: payload.email, sub: sub });
record = await klass.execute('update', { email: payload.email, phone, sub });
} catch (error) {
console.log(error);
return;
}

expect(record.sub).to.equal(sub.toString());
});

it('should violate phone requirement', async () => {
const sub = 'test|1234567890';

try {
await klass.execute('update', { email: payload.email, sub });
} catch (error) {
console.log(error);
expect(error.message).to.equal(`Field does not match requirements: [phone, undefined]`);
}
});

it('should violate sub requirement', async () => {
const phone = '+10005551111';

try {
await klass.execute('update', { email: payload.email, phone });
} catch (error) {
console.log(error);
expect(error.message).to.equal(`Field does not match requirements: [sub, undefined]`);
}
});
});
});
113 changes: 113 additions & 0 deletions __tests__/services/user.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const sinon = require('sinon');

import { expect } from 'chai';

import { httpWrapper as Http } from '@services/http';
import { User as user } from '@services/user';

describe('user', () => {
const sandbox = sinon.createSandbox();

afterEach(function () {
sandbox.restore();
});

describe('auth', () => {
it('promise should resolve', async () => {
let response;
const payload = { mfa_token: 'foo' };

sandbox.stub(Http.prototype, 'post').resolves(payload);

try {
response = await user.auth('[email protected]', 'qux');
} catch (error) {
console.log(error);
} finally {
expect(response).to.equal(payload.mfa_token);
}
});

it('promise should reject, but forward mfa_challenge', async () => {
let response;
const exception = {
error: 'mfa_required',
mfa_token: 'foo'
}

sandbox.stub(Http.prototype, 'post').rejects(exception);

try {
response = await user.auth('[email protected]', 'qux');
} catch (error) {
console.log(error);
} finally {
expect(response).to.equal(exception.mfa_token);
}
});

it('promise should reject', async () => {
const exception = { error: 'foo' };

sandbox.stub(Http.prototype, 'post').rejects(exception);

try {
await user.auth('[email protected]', 'qux');
} catch (error) {
expect(error).to.deep.equal(exception);
}
});
});

describe('oauth', () => {
it('promise should resolve', async () => {
let request;
sandbox.stub(Http.prototype, 'post').resolves(true);

try {
request = await user.oauth('foo', 'bar', 'baz');
} catch (error) {
console.log(error);
} finally {
expect(request).to.be.true;
}
});

it('promise should reject', async () => {
sandbox.stub(Http.prototype, 'post').rejects(false);

try {
await user.oauth('foo', 'bar', 'baz');
} catch (error) {
expect(error).to.be.throw;
}
});
});

describe('oob', () => {
it('promise should resolve', async () => {
let request;
sandbox.stub(Http.prototype, 'post').resolves({ oob_code: 'foo' });

try {
request = await user.oob('bar');
} catch (error) {
console.log(error);
} finally {
expect(request).to.equal('foo');
}
});

it('promise should reject', async () => {
let request;
sandbox.stub(Http.prototype, 'post').rejects(false);

try {
request = await user.oob('bar');
} catch (error) {
expect(request).to.be.throw;
}
});
});
});
Loading

0 comments on commit 5a85115

Please sign in to comment.