From f4faefbd7e95a775f17539eb9bb70fdadf588953 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 16 Apr 2020 15:03:16 -0400 Subject: [PATCH] extracts out tests into separate files to establish a better testing pattern --- demo/server.js | 6 +- jest.config.js | 6 + package.json | 8 +- .../requestHandlers/collections.spec.js} | 65 ++------- src/collections/requestHandlers/update.js | 1 + src/tests/credentials.js | 2 + src/tests/globalSetup.js | 26 ++++ src/tests/globalTeardown.js | 6 + src/tests/jest.config.integration.js | 8 -- src/tests/jest.config.js | 11 -- src/tests/unit/middleware.spec.js | 99 -------------- src/tests/unit/mongooseApiQuery.spec.js | 21 --- src/tests/unit/mongooseSchema.spec.js | 26 ---- src/tests/unit/paramParser.spec.js | 126 ------------------ src/tests/unit/testModels/IntlModel.js | 29 ---- src/tests/unit/testUtils.js | 7 - src/tests/unit/troubleshoot.spec.js | 7 - src/users/operations/index.js | 2 - src/users/operations/me.js | 0 src/users/operations/refresh.js | 55 ++++++++ src/users/requestHandlers/me.js | 12 +- src/users/requestHandlers/refresh.js | 41 ++---- src/users/users.spec.js | 88 ++++++++++++ toggle_mongo.cmd | 8 -- 24 files changed, 213 insertions(+), 447 deletions(-) create mode 100644 jest.config.js rename src/{tests/integration/api.spec.js => collections/requestHandlers/collections.spec.js} (65%) create mode 100644 src/tests/credentials.js create mode 100644 src/tests/globalSetup.js create mode 100644 src/tests/globalTeardown.js delete mode 100644 src/tests/jest.config.integration.js delete mode 100644 src/tests/jest.config.js delete mode 100644 src/tests/unit/middleware.spec.js delete mode 100644 src/tests/unit/mongooseApiQuery.spec.js delete mode 100644 src/tests/unit/mongooseSchema.spec.js delete mode 100644 src/tests/unit/paramParser.spec.js delete mode 100644 src/tests/unit/testModels/IntlModel.js delete mode 100644 src/tests/unit/testUtils.js delete mode 100644 src/tests/unit/troubleshoot.spec.js delete mode 100644 src/users/operations/me.js create mode 100644 src/users/users.spec.js delete mode 100644 toggle_mongo.cmd diff --git a/demo/server.js b/demo/server.js index 3019138aa..b213aba68 100644 --- a/demo/server.js +++ b/demo/server.js @@ -12,14 +12,12 @@ const payload = new Payload({ exports.payload = payload; exports.start = (cb) => { - expressApp.listen(config.port, () => { + const server = expressApp.listen(config.port, () => { console.log(`listening on ${config.port}...`); if (cb) cb(); }); -}; -exports.close = (cb) => { - if (expressApp) expressApp.close(cb); + return server; }; // when app.js is launched directly diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000..ec2ac38a4 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + verbose: true, + testEnvironment: 'node', + globalSetup: '/src/tests/globalSetup.js', + globalTeardown: '/src/tests/globalTeardown.js', +}; diff --git a/package.json b/package.json index 6b21fe86b..21e1149a5 100644 --- a/package.json +++ b/package.json @@ -3,15 +3,9 @@ "version": "1.0.0", "description": "", "main": "index.js", - "nodemonConfig": { - "ignore": [ - "src/*", - "demo/client/*" - ] - }, "scripts": { "test:unit": "cross-env NODE_ENV=test jest --config src/tests/jest.config.js", - "test:int": "cross-env NODE_ENV=test jest src/tests/integration/api.spec.js --forceExit --detectOpenHandles", + "test:int": "cross-env NODE_ENV=test jest --forceExit", "cov": "npm run core:build && node ./node_modules/jest/bin/jest.js src/tests --coverage", "dev": "nodemon demo/server.js", "server": "node demo/server.js", diff --git a/src/tests/integration/api.spec.js b/src/collections/requestHandlers/collections.spec.js similarity index 65% rename from src/tests/integration/api.spec.js rename to src/collections/requestHandlers/collections.spec.js index c9dae2669..5e8bde9db 100644 --- a/src/tests/integration/api.spec.js +++ b/src/collections/requestHandlers/collections.spec.js @@ -4,42 +4,19 @@ const faker = require('faker'); const server = require('../../../demo/server'); +const config = require('../../../demo/payload.config'); +const { email, password } = require('../../tests/credentials'); -describe('API', () => { - const url = 'http://localhost:3000'; - let token = null; - const email = 'test@test.com'; - const password = 'test123'; +const url = config.serverURL; - let localizedPostID; - const englishPostDesc = faker.lorem.lines(20); - const spanishPostDesc = faker.lorem.lines(20); +let token = null; - beforeAll((done) => { - server.start(done); - }); +let localizedPostID; +const englishPostDesc = faker.lorem.lines(20); +const spanishPostDesc = faker.lorem.lines(20); - it('should register a first user', async () => { - const response = await fetch(`${url}/api/first-register`, { - body: JSON.stringify({ - email, - password, - }), - headers: { - 'Content-Type': 'application/json', - }, - method: 'post', - }); - - const data = await response.json(); - - expect(response.status).toBe(201); - expect(data).toHaveProperty('email'); - expect(data).toHaveProperty('role'); - expect(data).toHaveProperty('createdAt'); - }); - - it('should login a user successfully', async () => { +describe('Collection CRUD', () => { + beforeAll(async () => { const response = await fetch(`${url}/api/login`, { body: JSON.stringify({ email, @@ -53,33 +30,9 @@ describe('API', () => { const data = await response.json(); - expect(response.status).toBe(200); - expect(data.token).not.toBeNull(); - ({ token } = data); }); - it('should allow a user to be created', async () => { - const response = await fetch(`${url}/api/users/register`, { - body: JSON.stringify({ - email: `${faker.name.firstName()}@test.com`, - password, - }), - headers: { - Authorization: `JWT ${token}`, - 'Content-Type': 'application/json', - }, - method: 'post', - }); - - const data = await response.json(); - - expect(response.status).toBe(201); - expect(data).toHaveProperty('email'); - expect(data).toHaveProperty('role'); - expect(data).toHaveProperty('createdAt'); - }); - it('should allow a post to be created in English', async () => { const response = await fetch(`${url}/api/posts`, { body: JSON.stringify({ diff --git a/src/collections/requestHandlers/update.js b/src/collections/requestHandlers/update.js index 45e2569b6..e3671fee1 100644 --- a/src/collections/requestHandlers/update.js +++ b/src/collections/requestHandlers/update.js @@ -23,6 +23,7 @@ const updateHandler = async (req, res) => { doc, }); } catch (err) { + console.log(err); return res.status(httpStatus.INTERNAL_SERVER_ERROR).json(formatErrorResponse(err, 'mongoose')); } }; diff --git a/src/tests/credentials.js b/src/tests/credentials.js new file mode 100644 index 000000000..de0cd02c8 --- /dev/null +++ b/src/tests/credentials.js @@ -0,0 +1,2 @@ +exports.email = 'test@test.com'; +exports.password = 'test123'; diff --git a/src/tests/globalSetup.js b/src/tests/globalSetup.js new file mode 100644 index 000000000..65b279c11 --- /dev/null +++ b/src/tests/globalSetup.js @@ -0,0 +1,26 @@ +const server = require('../../demo/server'); +const config = require('../../demo/payload.config'); +const { email, password } = require('./credentials'); + +const url = config.serverURL; +const usernameField = config.user.auth.useAsUsername; + +const globalSetup = async () => { + global.PAYLOAD_SERVER = await server.start(); + + const response = await fetch(`${url}/api/first-register`, { + body: JSON.stringify({ + [usernameField]: email, + password, + }), + headers: { + 'Content-Type': 'application/json', + }, + method: 'post', + }); + + const data = await response.json(); + global.AUTH_TOKEN = data.token; +}; + +module.exports = globalSetup; diff --git a/src/tests/globalTeardown.js b/src/tests/globalTeardown.js new file mode 100644 index 000000000..1e5ca2489 --- /dev/null +++ b/src/tests/globalTeardown.js @@ -0,0 +1,6 @@ +const globalTeardown = async () => { + const serverClosePromise = new Promise(resolve => global.PAYLOAD_SERVER.close(resolve)); + await serverClosePromise; +}; + +module.exports = globalTeardown; diff --git a/src/tests/jest.config.integration.js b/src/tests/jest.config.integration.js deleted file mode 100644 index 03ced90a8..000000000 --- a/src/tests/jest.config.integration.js +++ /dev/null @@ -1,8 +0,0 @@ -const defaults = require('./jest.config'); - -module.exports = { - ...defaults, - roots: [ - './integration' - ], -}; diff --git a/src/tests/jest.config.js b/src/tests/jest.config.js deleted file mode 100644 index 7d697827d..000000000 --- a/src/tests/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - verbose: true, - testURL: 'http://localhost/', - roots: [ - './unit' - ], - transform: { - '^.+\\.(j|t)s$': 'babel-jest' - }, - testEnvironment: 'node', -}; diff --git a/src/tests/unit/middleware.spec.js b/src/tests/unit/middleware.spec.js deleted file mode 100644 index 66e8d3fbe..000000000 --- a/src/tests/unit/middleware.spec.js +++ /dev/null @@ -1,99 +0,0 @@ -const mockExpress = require('jest-mock-express'); -const localizationMiddleware = require('../../localization/middleware'); - -let res = null; -let next = null; -describe('Payload Middleware', () => { - beforeEach(() => { - res = mockExpress.response(); - next = jest.fn(); - }); - - describe('Payload Locale Middleware', () => { - let req, localization; - - beforeEach(() => { - req = { - query: {}, - headers: {}, - body: {} - }; - localization = { - locales: ['en', 'es'], - defaultLocale: 'en' - }; - - }); - - it('Supports query params', () => { - req.query.locale = 'es'; - - localizationMiddleware(localization)(req, res, next); - - expect(next).toHaveBeenCalledTimes(1); - expect(req.locale).toEqual(req.query.locale); - }); - - it('Supports query param fallback to default', () => { - req.query.locale = 'pt'; - - localizationMiddleware(localization)(req, res, next); - - expect(next).toHaveBeenCalledTimes(1); - expect(req.locale).toEqual(localization.defaultLocale); - }); - - it('Supports accept-language header', () => { - req.headers['accept-language'] = 'es,fr;'; - - localizationMiddleware(localization)(req, res, next); - - expect(next).toHaveBeenCalledTimes(1); - expect(req.locale).toEqual('es'); - }); - - it('Supports accept-language header fallback', () => { - req.query.locale = 'pt'; - req.headers['accept-language'] = 'fr;'; - - localizationMiddleware(localization)(req, res, next); - - expect(next).toHaveBeenCalledTimes(1); - expect(req.locale).toEqual(localization.defaultLocale); - }); - - it('Query param takes precedence over header', () => { - req.query.locale = 'es'; - req.headers['accept-language'] = 'en;'; - - localizationMiddleware(localization)(req, res, next); - - expect(next).toHaveBeenCalledTimes(1); - expect(req.locale).toEqual('es'); - }); - - it('Supports default locale', () => { - localizationMiddleware(localization)(req, res, next); - - expect(next).toHaveBeenCalledTimes(1); - expect(req.locale).toEqual(localization.defaultLocale); - }); - - it('Supports locale all', () => { - req.query.locale = '*'; - localizationMiddleware(localization)(req, res, next); - - expect(next).toHaveBeenCalledTimes(1); - expect(req.locale).toBeUndefined(); - }); - - it('Supports locale in body on post', () => { - req.body = {locale: 'es'}; - req.method = 'post'; - localizationMiddleware(localization)(req, res, next); - - expect(next).toHaveBeenCalledTimes(1); - expect(req.locale).toEqual('es'); - }); - }); -}); diff --git a/src/tests/unit/mongooseApiQuery.spec.js b/src/tests/unit/mongooseApiQuery.spec.js deleted file mode 100644 index e15569b0b..000000000 --- a/src/tests/unit/mongooseApiQuery.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -import mongoose from 'mongoose'; -const Schema = mongoose.Schema; -import mongooseApiQuery from '../../mongoose/buildQuery.plugin'; - -const TestUserSchema = new Schema({ - name: {type: String}, - description: {type: String}, - married: {type: Boolean}, - age: {type: Number} - } -); - -TestUserSchema.plugin(mongooseApiQuery); -const TestUser = mongoose.model('TestUser', TestUserSchema); - -describe('mongooseApiQuery', () => { - - it('Should not blow up', () => { - expect(mongooseApiQuery).not.toBeNull(); - }); -}); diff --git a/src/tests/unit/mongooseSchema.spec.js b/src/tests/unit/mongooseSchema.spec.js deleted file mode 100644 index 9f3c0afb8..000000000 --- a/src/tests/unit/mongooseSchema.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -import SchemaLoader from '../../mongoose/schema/schemaLoader'; -import config from '../../../demo/payload.config'; - -let schemaLoader; - -describe('schemaLoader', () => { - - beforeAll(async () => { - schemaLoader = new SchemaLoader(config); - console.log('before done'); - }); - - it('load collections', async () => { - expect(schemaLoader.collections).not.toBeNull(); - }); - - it('load globals', async () => { - expect(schemaLoader.globalModel).not.toBeNull(); - expect(schemaLoader.globals).not.toBeNull(); - }); - - it('load blocks', async () => { - expect(schemaLoader.blockSchema).not.toBeNull(); - expect(schemaLoader.contentBlocks).not.toBeNull(); - }); -}); diff --git a/src/tests/unit/paramParser.spec.js b/src/tests/unit/paramParser.spec.js deleted file mode 100644 index e9d29058e..000000000 --- a/src/tests/unit/paramParser.spec.js +++ /dev/null @@ -1,126 +0,0 @@ -/* eslint-disable camelcase */ -import mongoose from 'mongoose'; - -const Schema = mongoose.Schema; -import { intlModel } from './testModels/IntlModel'; -import { paramParser } from '../../mongoose/buildQuery.plugin'; - -const AuthorSchema = new Schema({ - name: String, - publish_count: Number -}); - -const PageSchema = new Schema({ - title: { type: String, unique: true }, - author: AuthorSchema, - content: { type: String }, - metaTitle: String, - likes: { type: Number } -}); -const Page = mongoose.model('Page', PageSchema); - -describe('Param Parser', () => { - - describe('Parameter Parsing', () => { - it('No params', () => { - let parsed = paramParser(Page, {}); - expect(parsed.searchParams).toEqual({}); - }); - - it('Property Equals - Object property', () => { - let parsed = paramParser(Page, { title: 'This is my title' }); - expect(parsed.searchParams).toEqual({ title: 'This is my title' }); - }); - - it('Property Equals - String property', () => { - let parsed = paramParser(Page, { metaTitle: 'This is my title' }); - expect(parsed.searchParams).toEqual({ metaTitle: 'This is my title' }); - }); - - it('Multiple params', () => { - let parsed = paramParser(Page, { title: 'This is my title', metaTitle: 'this-is-my-title' }); - expect(parsed.searchParams).toEqual({ title: 'This is my title', metaTitle: 'this-is-my-title' }); - }); - - it('Greater than or equal', () => { - let parsed = paramParser(Page, { likes: '{gte}50' }); - expect(parsed.searchParams).toEqual({ likes: { '$gte': '50' } }); - }); - - it('Greater than, less than', () => { - let parsed = paramParser(Page, { likes: '{gte}50{lt}100' }); - expect(parsed.searchParams).toEqual({ likes: { '$gte': '50', '$lt': '100' } }); - }); - - it('Like', () => { - let parsed = paramParser(Page, { title: '{like}This' }); - expect(parsed.searchParams).toEqual({ title: { '$regex': 'This', '$options': '-i' } }); - }); - - describe('SubSchemas', () => { - it('Parse subschema for String', () => { - let parsed = paramParser(Page, { 'author.name': 'Jane' }); - expect(parsed.searchParams).toEqual({ 'author.name': 'Jane' }) - }); - - it('Parse subschema for Number', () => { - let parsed = paramParser(Page, { 'author.publish_count': '7' }); - expect(parsed.searchParams).toEqual({ 'author.publish_count': '7' }) - }) - }); - - describe('Locale handling', () => { - it('should handle intl string property', () => { - let parsed = paramParser(intlModel, { title: 'This is my title' }, 'en'); - expect(parsed.searchParams).toEqual({ 'title.en': 'This is my title'}); - }); - it('should handle intl string property', () => { - let parsed = paramParser(intlModel, { title: 'This is my title' }, 'en'); - expect(parsed.searchParams).toEqual({ 'title.en': 'This is my title'}); - }); - }); - }); - - describe('Include', () => { - it('Include Single', () => { - let parsed = paramParser(Page, { include: 'SomeId' }); - expect(parsed.searchParams).toEqual({ _id: 'SomeId' }); - }); - - it('Include Multiple', () => { - let parsed = paramParser(Page, { include: 'SomeId,SomeSecondId' }); - expect(parsed.searchParams) - .toEqual({ '$or': [{ _id: 'SomeId' }, { _id: 'SomeSecondId' }] }); - }); - }); - - describe('Exclude', () => { - it('Exclude Single', () => { - let parsed = paramParser(Page, { exclude: 'SomeId' }); - expect(parsed.searchParams).toEqual({ _id: { '$ne': 'SomeId' } }); - }); - - it('Exclude Multiple', () => { - let parsed = paramParser(Page, { exclude: 'SomeId,SomeSecondId' }); - expect(parsed.searchParams) - .toEqual({ '$and': [{ _id: { '$ne': 'SomeId' } }, { _id: { '$ne': 'SomeSecondId' } }] }); - }); - }); - - describe('Ordering/Sorting', () => { - it('Order by ascending (default)', () => { - let parsed = paramParser(Page, { sort_by: 'title' }); - expect(parsed).toEqual({ searchParams: {}, sort: { title: 1 } }); - }); - - it('Order by ascending', () => { - let parsed = paramParser(Page, { sort_by: 'title,asc' }); - expect(parsed).toEqual({ searchParams: {}, sort: { title: 1 } }); - }); - - it('Order by descending', () => { - let parsed = paramParser(Page, { sort_by: 'title,desc' }); - expect(parsed).toEqual({ searchParams: {}, sort: { title: 'desc' } }); - }) - }); -}); diff --git a/src/tests/unit/testModels/IntlModel.js b/src/tests/unit/testModels/IntlModel.js deleted file mode 100644 index 172d3a8af..000000000 --- a/src/tests/unit/testModels/IntlModel.js +++ /dev/null @@ -1,29 +0,0 @@ -import mongoose from 'mongoose'; -import { schemaBaseFields } from '../../../mongoose/schema/schemaBaseFields'; -import paginate from '../../../mongoose/paginate.plugin'; -import buildQueryPlugin from '../../../mongoose/buildQuery.plugin'; -import localizationPlugin from '../../../localization/localization.plugin'; - -const IntlSchema = new mongoose.Schema({ - ...schemaBaseFields, - title: { type: String, localized: true, unique: true }, - content: { type: String, localized: true }, - metaTitle: String, - metaDesc: String - }, - { timestamps: true } -); - -IntlSchema.plugin(paginate); -IntlSchema.plugin(buildQueryPlugin); -IntlSchema.plugin(localizationPlugin, { - locales: [ - 'en', - 'es' - ], - defaultLocale: 'en', - fallback: true -}); - -const intlModel = mongoose.model('IntlModel', IntlSchema); -export { intlModel }; diff --git a/src/tests/unit/testUtils.js b/src/tests/unit/testUtils.js deleted file mode 100644 index 8ecf80285..000000000 --- a/src/tests/unit/testUtils.js +++ /dev/null @@ -1,7 +0,0 @@ -import express from 'express'; - -export function getConfiguredExpress() { - let expressApp = express(); - expressApp.set('view engine', 'pug'); - return expressApp; -} diff --git a/src/tests/unit/troubleshoot.spec.js b/src/tests/unit/troubleshoot.spec.js deleted file mode 100644 index a64c8f41f..000000000 --- a/src/tests/unit/troubleshoot.spec.js +++ /dev/null @@ -1,7 +0,0 @@ -describe('troubleshooting', () => { - describe('plugins', () => { - it('should return all records', () => { - - }); - }); -}); diff --git a/src/users/operations/index.js b/src/users/operations/index.js index 9a9c5a100..473f28cc5 100644 --- a/src/users/operations/index.js +++ b/src/users/operations/index.js @@ -1,6 +1,5 @@ const checkIfInitialized = require('./checkIfInitialized'); const login = require('./login'); -const me = require('./me'); const refresh = require('./refresh'); const register = require('./register'); const init = require('./init'); @@ -10,7 +9,6 @@ const resetPassword = require('./resetPassword'); module.exports = { checkIfInitialized, login, - me, refresh, init, register, diff --git a/src/users/operations/me.js b/src/users/operations/me.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/users/operations/refresh.js b/src/users/operations/refresh.js index e69de29bb..307ccd77c 100644 --- a/src/users/operations/refresh.js +++ b/src/users/operations/refresh.js @@ -0,0 +1,55 @@ +const jwt = require('jsonwebtoken'); + +const refresh = async (args) => { + try { + // Await validation here + + let options = { + config: args.config, + api: args.api, + authorization: args.authorization, + }; + + // ///////////////////////////////////// + // 1. Execute before refresh hook + // ///////////////////////////////////// + + const beforeRefreshHook = args.config.user && args.config.user.hooks && args.config.user.hooks.beforeRefresh; + + if (typeof beforeRefreshHook === 'function') { + options = await beforeRefreshHook(options); + } + + // ///////////////////////////////////// + // 2. Perform refresh + // ///////////////////////////////////// + + const secret = options.config.user.auth.secretKey; + const opts = {}; + opts.expiresIn = options.config.user.auth.tokenExpiration; + + const token = options.authorization.replace('JWT ', ''); + jwt.verify(token, secret, {}); + const refreshedToken = jwt.sign(token, secret); + + // ///////////////////////////////////// + // 3. Execute after login hook + // ///////////////////////////////////// + + const afterRefreshHook = args.config.user && args.config.user.hooks && args.config.user.hooks.afterRefresh; + + if (typeof afterRefreshHook === 'function') { + await afterRefreshHook(options, refreshedToken); + } + + // ///////////////////////////////////// + // 4. Return refreshed token + // ///////////////////////////////////// + + return refreshedToken; + } catch (error) { + throw error; + } +}; + +module.exports = refresh; diff --git a/src/users/requestHandlers/me.js b/src/users/requestHandlers/me.js index 87d05596a..c461d38b3 100644 --- a/src/users/requestHandlers/me.js +++ b/src/users/requestHandlers/me.js @@ -1,11 +1,5 @@ -/** - * Returns User if user session is still open - * @param req - * @param res - * @returns {*} - */ -const me = (req, res) => { - return res.status(200).send(req.user); +const meHandler = async (req, res) => { + return res.status(200).json(req.user); }; -module.exports = me; +module.exports = meHandler; diff --git a/src/users/requestHandlers/refresh.js b/src/users/requestHandlers/refresh.js index e012436e5..f35591b72 100644 --- a/src/users/requestHandlers/refresh.js +++ b/src/users/requestHandlers/refresh.js @@ -1,35 +1,22 @@ -const jwt = require('jsonwebtoken'); const httpStatus = require('http-status'); -const { Forbidden, APIError } = require('../../errors'); const formatErrorResponse = require('../../express/responses/formatError'); +const { refresh } = require('../operations'); -/** - * Refresh an expired or soon to be expired auth token - * @param req - * @param res - * @param next - */ -const refresh = config => (req, res, next) => { - const secret = config.user.auth.secretKey; - const opts = {}; - opts.expiresIn = config.user.auth.tokenExpiration; - +const refreshHandler = config => async (req, res) => { try { - const token = req.headers.authorization.replace('JWT ', ''); - jwt.verify(token, secret, {}); - const refreshedToken = jwt.sign(token, secret); - res.status(200) - .json({ - message: 'Token Refresh Successful', - refreshedToken, - }); - } catch (e) { - if (e.status && e.status === 401) { - return res.status(httpStatus.FORBIDDEN).send(formatErrorResponse(new Forbidden())); - } + const refreshedToken = await refresh({ + config, + api: 'REST', + authorization: req.headers.authorization, + }); - return res.status(httpStatus.INTERNAL_SERVER_ERROR).send(formatErrorResponse(new APIError())); + res.status(200).json({ + message: 'Token refresh successful', + refreshedToken, + }); + } catch (error) { + return res.status(error.status || httpStatus.INTERNAL_SERVER_ERROR).json(formatErrorResponse(error)); } }; -module.exports = refresh; +module.exports = refreshHandler; diff --git a/src/users/users.spec.js b/src/users/users.spec.js new file mode 100644 index 000000000..f23a79689 --- /dev/null +++ b/src/users/users.spec.js @@ -0,0 +1,88 @@ +require('isomorphic-fetch'); +const faker = require('faker'); +const { email, password } = require('../tests/credentials'); + +/** + * @jest-environment node + */ + +const config = require('../../demo/payload.config'); + +describe('Users REST API', () => { + const url = config.serverURL; + const usernameField = config.user.auth.useAsUsername; + + let token = null; + + it('should prevent registering a first user', async () => { + const response = await fetch(`${url}/api/first-register`, { + body: JSON.stringify({ + [usernameField]: 'thisuser@shouldbeprevented.com', + password: 'get-out', + }), + headers: { + 'Content-Type': 'application/json', + }, + method: 'post', + }); + + const data = await response.json(); + + expect(response.status).toBe(403); + }); + + it('should login a user successfully', async () => { + const response = await fetch(`${url}/api/login`, { + body: JSON.stringify({ + [usernameField]: email, + password, + }), + headers: { + 'Content-Type': 'application/json', + }, + method: 'post', + }); + + const data = await response.json(); + + expect(response.status).toBe(200); + expect(data.token).not.toBeNull(); + + ({ token } = data); + }); + + it('should return a logged in user from /me', async () => { + const response = await fetch(`${url}/api/me`, { + method: 'post', + headers: { + Authorization: `JWT ${token}`, + }, + }); + + const data = await response.json(); + + expect(response.status).toBe(200); + expect(data[usernameField]).not.toBeNull(); + }); + + it('should allow a user to be created', async () => { + const response = await fetch(`${url}/api/users/register`, { + body: JSON.stringify({ + [usernameField]: `${faker.name.firstName()}@test.com`, + password, + }), + headers: { + Authorization: `JWT ${token}`, + 'Content-Type': 'application/json', + }, + method: 'post', + }); + + const data = await response.json(); + + expect(response.status).toBe(201); + expect(data).toHaveProperty(usernameField); + expect(data).toHaveProperty('role'); + expect(data).toHaveProperty('createdAt'); + }); +}); diff --git a/toggle_mongo.cmd b/toggle_mongo.cmd deleted file mode 100644 index aabf2c441..000000000 --- a/toggle_mongo.cmd +++ /dev/null @@ -1,8 +0,0 @@ -@ECHO OFF - -sc query "MongoDB" | findstr "RUNNING" -if errorlevel 1 ( - net start "MongoDB Server" -) else ( - net stop "MongoDB Server" -)