UNPKG

@hoodie/account-server

Version:
201 lines (174 loc) 5.99 kB
var defaultsDeep = require('lodash/defaultsDeep') var Joi = require('joi') var _ = require('lodash') var nock = require('nock') var stubTransport = require('nodemailer-stub-transport') var test = require('tap').test var getServer = require('../../utils/get-server') var couchdbErrorTests = require('../../utils/couchdb-error-tests') var invalidTypeErrors = require('../../utils/invalid-type-errors.js') var transport = stubTransport() var routeOptions = { method: 'POST', url: '/requests', headers: { accept: 'application/vnd.api+json', 'content-type': 'application/vnd.api+json' }, payload: { data: { type: 'request', attributes: { type: 'passwordreset', username: 'pat@example.com' } } } } var mockCouchDbGetUserDoc = nock('http://localhost:5984') .get('/_users/org.couchdb.user%3Apat%40example.com') .query(true) function mockUserFound (docChange) { return mockCouchDbGetUserDoc .reply(200, _.merge({ _id: 'org.couchdb.user:pat@example.com', _rev: '1-234', password_scheme: 'pbkdf2', iterations: 10, type: 'user', name: 'pat@example.com', roles: ['id:userid123', 'mycustomrole'], derived_key: '4b5c9721ab77dd2faf06a36785fd0a30f0bf0d27', salt: 'salt123' }, docChange)) } test('POST /requests', function (group) { var sentEmails = [] transport.on('log', function (log) { if (log.type === 'envelope') { sentEmails.push(JSON.parse(log.message)) return } if (log.type === 'message' && !/^Content-Type:/.test(log.message)) { sentEmails[sentEmails.length - 1].body = log.message } }) group.beforeEach(function (next) { var self = this getServer({ notifications: { transport: transport, from: 'notifications@example.com' } }, function (error, server) { self.server = server next(error) }) }) group.test('user found', function (t) { var couchdb = mockUserFound() .put('/_users/org.couchdb.user%3Apat%40example.com', function (body) { var error = Joi.object({ _id: Joi.any().only('org.couchdb.user:pat@example.com').required(), _rev: Joi.any().only('1-234').required(), name: Joi.any().only('pat@example.com').required(), type: Joi.any().only('user').required(), salt: Joi.string().required(), derived_key: Joi.string().required(), iterations: Joi.any().only(10).required(), password_scheme: Joi.any().only('pbkdf2').required(), roles: Joi.array().items(Joi.string()) }).validate(body).error return error === null }) .query(true) .reply(201, { ok: true, id: 'org.couchdb.user:pat-doe', rev: '2-345' }) this.server.inject(routeOptions, function (response) { t.is(couchdb.pendingMocks()[0], undefined, 'all mocks satisfied') t.is(response.statusCode, 201, 'returns 201 status') t.ok(response.result.data.id, 'returns with id') t.is(response.result.data.attributes.username, 'pat@example.com', 'returns username attribute') t.ok(response.result.data.attributes.messageId, 'returns messageId attribute') t.is(sentEmails.length, 1, '1 email sent') var email = sentEmails.pop() t.is(email.from, 'notifications@example.com', 'sent from notifications@example.com') t.deepEqual(email.to, ['pat@example.com'], 'sent to pat@example.com') t.ok(/username: pat@example.com\npassword: [0-9a-f]{12}(\n|$)/.test(email.body), 'has new password') t.end() }) }) group.test('user not found', function (t) { var couchdb = mockCouchDbGetUserDoc .reply(404, { error: 'not_found', reason: 'missing' }) this.server.inject(routeOptions, function (response) { t.is(response.statusCode, 404, 'returns 404 status') t.is(sentEmails.length, 0, 'no email sent') t.is(couchdb.pendingMocks()[0], undefined, 'all mocks satisfied') t.end() }) }) group.test('username is not a valid email', function (t) { var options = defaultsDeep({ payload: { data: { attributes: { username: 'foo' } } } }, routeOptions) this.server.inject(options, function (response) { t.is(response.statusCode, 409, 'returns 409 status') t.is(response.result.errors.length, 1, 'returns one error') t.is(response.result.errors[0].title, 'Conflict', 'returns "Conflict" error') // TODO: fix Joi’s standard error messages. `.detail` currently is // child "data" fails because [child "attributes" fails because // [child "username" fails because ["username" must be a valid email]]] // t.is(response.result.errors[0].detail, 'username (foo) is invalid email address') t.end() }) }) couchdbErrorTests(group, mockCouchDbGetUserDoc, routeOptions) invalidTypeErrors(group, routeOptions, 'request') group.end() }) test('POST /requests?include=foobar', function (t) { getServer({ notifications: { transport: transport, from: 'notifications@example.com' } }, function (error, server) { t.error(error) var options = _.defaultsDeep({ url: '/requests?include=foobar' }, routeOptions) this.server.inject(options, function (response) { t.is(response.statusCode, 400, 'returns 400 status') t.deepEqual(response.result.errors[0].detail, '?include not allowed', 'returns error message') t.end() }) }) }) // test('POST /requests without notifications config', function (t) { // getServer({ // notifications: {} // }, function (error, server) { // if (error) { // t.error(error) // t.end() // } // // this.server.inject(routeOptions, function (response) { // t.is(response.statusCode, 503, 'returns 503 status') // t.end() // }) // }) // })