From eab2371206c86d91d9026d21a549ca5173ffe5b8 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Tue, 12 Jan 2021 18:08:14 +0000 Subject: [PATCH] Server: Add test units for sessions route --- .eslintignore | 24 +++++++- .gitignore | 24 +++++++- packages/server/package-lock.json | 41 +++++++++++++ packages/server/package.json | 1 + .../server/src/routes/api/sessions.test.ts | 61 +++++++++++++++++++ packages/server/src/utils/requestUtils.ts | 5 ++ .../src/utils/testing/koa/FakeRequest.ts | 10 ++- .../src/utils/testing/koa/FakeResponse.ts | 9 +++ .../server/src/utils/testing/testUtils.ts | 24 ++++++-- 9 files changed, 188 insertions(+), 11 deletions(-) create mode 100644 packages/server/src/routes/api/sessions.test.ts diff --git a/.eslintignore b/.eslintignore index b7facce7d..a25e0ae81 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1529,9 +1529,15 @@ packages/server/src/db.js.map packages/server/src/middleware/notificationHandler.d.ts packages/server/src/middleware/notificationHandler.js packages/server/src/middleware/notificationHandler.js.map +packages/server/src/middleware/notificationHandler.test.d.ts +packages/server/src/middleware/notificationHandler.test.js +packages/server/src/middleware/notificationHandler.test.js.map packages/server/src/middleware/ownerHandler.d.ts packages/server/src/middleware/ownerHandler.js packages/server/src/middleware/ownerHandler.js.map +packages/server/src/middleware/ownerHandler.test.d.ts +packages/server/src/middleware/ownerHandler.test.js +packages/server/src/middleware/ownerHandler.test.js.map packages/server/src/middleware/routeHandler.d.ts packages/server/src/middleware/routeHandler.js packages/server/src/middleware/routeHandler.js.map @@ -1592,6 +1598,9 @@ packages/server/src/routes/api/index.js.map packages/server/src/routes/api/ping.d.ts packages/server/src/routes/api/ping.js packages/server/src/routes/api/ping.js.map +packages/server/src/routes/api/ping.test.d.ts +packages/server/src/routes/api/ping.test.js +packages/server/src/routes/api/ping.test.js.map packages/server/src/routes/api/sessions.d.ts packages/server/src/routes/api/sessions.js packages/server/src/routes/api/sessions.js.map @@ -1673,12 +1682,21 @@ packages/server/src/utils/routeUtils.js.map packages/server/src/utils/routeUtils.test.d.ts packages/server/src/utils/routeUtils.test.js packages/server/src/utils/routeUtils.test.js.map -packages/server/src/utils/testUtils.d.ts -packages/server/src/utils/testUtils.js -packages/server/src/utils/testUtils.js.map +packages/server/src/utils/testing/koa/FakeCookies.d.ts +packages/server/src/utils/testing/koa/FakeCookies.js +packages/server/src/utils/testing/koa/FakeCookies.js.map +packages/server/src/utils/testing/koa/FakeRequest.d.ts +packages/server/src/utils/testing/koa/FakeRequest.js +packages/server/src/utils/testing/koa/FakeRequest.js.map +packages/server/src/utils/testing/koa/FakeResponse.d.ts +packages/server/src/utils/testing/koa/FakeResponse.js +packages/server/src/utils/testing/koa/FakeResponse.js.map packages/server/src/utils/testing/testRouters.d.ts packages/server/src/utils/testing/testRouters.js packages/server/src/utils/testing/testRouters.js.map +packages/server/src/utils/testing/testUtils.d.ts +packages/server/src/utils/testing/testUtils.js +packages/server/src/utils/testing/testUtils.js.map packages/server/src/utils/time.d.ts packages/server/src/utils/time.js packages/server/src/utils/time.js.map diff --git a/.gitignore b/.gitignore index f98ce2686..1c72964e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1518,9 +1518,15 @@ packages/server/src/db.js.map packages/server/src/middleware/notificationHandler.d.ts packages/server/src/middleware/notificationHandler.js packages/server/src/middleware/notificationHandler.js.map +packages/server/src/middleware/notificationHandler.test.d.ts +packages/server/src/middleware/notificationHandler.test.js +packages/server/src/middleware/notificationHandler.test.js.map packages/server/src/middleware/ownerHandler.d.ts packages/server/src/middleware/ownerHandler.js packages/server/src/middleware/ownerHandler.js.map +packages/server/src/middleware/ownerHandler.test.d.ts +packages/server/src/middleware/ownerHandler.test.js +packages/server/src/middleware/ownerHandler.test.js.map packages/server/src/middleware/routeHandler.d.ts packages/server/src/middleware/routeHandler.js packages/server/src/middleware/routeHandler.js.map @@ -1581,6 +1587,9 @@ packages/server/src/routes/api/index.js.map packages/server/src/routes/api/ping.d.ts packages/server/src/routes/api/ping.js packages/server/src/routes/api/ping.js.map +packages/server/src/routes/api/ping.test.d.ts +packages/server/src/routes/api/ping.test.js +packages/server/src/routes/api/ping.test.js.map packages/server/src/routes/api/sessions.d.ts packages/server/src/routes/api/sessions.js packages/server/src/routes/api/sessions.js.map @@ -1662,12 +1671,21 @@ packages/server/src/utils/routeUtils.js.map packages/server/src/utils/routeUtils.test.d.ts packages/server/src/utils/routeUtils.test.js packages/server/src/utils/routeUtils.test.js.map -packages/server/src/utils/testUtils.d.ts -packages/server/src/utils/testUtils.js -packages/server/src/utils/testUtils.js.map +packages/server/src/utils/testing/koa/FakeCookies.d.ts +packages/server/src/utils/testing/koa/FakeCookies.js +packages/server/src/utils/testing/koa/FakeCookies.js.map +packages/server/src/utils/testing/koa/FakeRequest.d.ts +packages/server/src/utils/testing/koa/FakeRequest.js +packages/server/src/utils/testing/koa/FakeRequest.js.map +packages/server/src/utils/testing/koa/FakeResponse.d.ts +packages/server/src/utils/testing/koa/FakeResponse.js +packages/server/src/utils/testing/koa/FakeResponse.js.map packages/server/src/utils/testing/testRouters.d.ts packages/server/src/utils/testing/testRouters.js packages/server/src/utils/testing/testRouters.js.map +packages/server/src/utils/testing/testUtils.d.ts +packages/server/src/utils/testing/testUtils.js +packages/server/src/utils/testing/testUtils.js.map packages/server/src/utils/time.d.ts packages/server/src/utils/time.js packages/server/src/utils/time.js.map diff --git a/packages/server/package-lock.json b/packages/server/package-lock.json index c7b3b0bf4..4113125dc 100644 --- a/packages/server/package-lock.json +++ b/packages/server/package-lock.json @@ -5703,12 +5703,24 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -5729,6 +5741,12 @@ "to-regex": "^3.0.2" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", @@ -5903,6 +5921,23 @@ "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", "dev": true }, + "node-mocks-http": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.10.0.tgz", + "integrity": "sha512-dFnwFc2i8m1EKpN8F9Tn42rAkfopah2FbNN2qETh+cmIT71XRzwwNXeXUd58WWX7r3Wgk1r54vGcUR9DeExAOQ==", + "dev": true, + "requires": { + "accepts": "^1.3.7", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + } + }, "node-modules-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", @@ -6639,6 +6674,12 @@ "strict-uri-encode": "^2.0.0" } }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", diff --git a/packages/server/package.json b/packages/server/package.json index 4984b4f54..7484ca441 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -42,6 +42,7 @@ "@types/mustache": "^0.8.32", "@types/yargs": "^13.0.2", "jest": "^26.6.3", + "node-mocks-http": "^1.10.0", "source-map-support": "^0.5.13", "typescript": "^4.1.2" } diff --git a/packages/server/src/routes/api/sessions.test.ts b/packages/server/src/routes/api/sessions.test.ts new file mode 100644 index 000000000..1c859d580 --- /dev/null +++ b/packages/server/src/routes/api/sessions.test.ts @@ -0,0 +1,61 @@ +import { Session } from '../../db'; +import routeHandler from '../../middleware/routeHandler'; +import { beforeAllDb, afterAllDb, beforeEachDb, koaAppContext, createUserAndSession, models } from '../../utils/testing/testUtils'; + +describe('api_sessions', function() { + + beforeAll(async () => { + await beforeAllDb('api_sessions'); + }); + + afterAll(async () => { + await afterAllDb(); + }); + + beforeEach(async () => { + await beforeEachDb(); + }); + + test('should login user', async function() { + const { user } = await createUserAndSession(1, false); + + const context = await koaAppContext({ + request: { + method: 'POST', + url: '/api/sessions', + body: { + email: user.email, + password: '123456', + }, + }, + }); + + await routeHandler(context); + + expect(context.response.status).toBe(200); + expect(!!context.response.body.id).toBe(true); + + const session: Session = await models().session().load(context.response.body.id); + expect(session.user_id).toBe(user.id); + }); + + test('should not login user with wrong password', async function() { + const { user } = await createUserAndSession(1, false); + + const context = await koaAppContext({ + request: { + method: 'POST', + url: '/api/sessions', + body: { + email: user.email, + password: 'wrong', + }, + }, + }); + + await routeHandler(context); + + expect(context.response.status).toBe(403); + }); + +}); diff --git a/packages/server/src/utils/requestUtils.ts b/packages/server/src/utils/requestUtils.ts index eae556cf4..87280fae6 100644 --- a/packages/server/src/utils/requestUtils.ts +++ b/packages/server/src/utils/requestUtils.ts @@ -30,6 +30,11 @@ export async function bodyFields(req: any): Promise { throw new ErrorBadRequest(`Unsupported Content-Type: "${req.headers['content-type']}". Expected: "application/json"`); } + // It's not clear how to get mocked requests to be parsed successfully by + // formidable so we use this small hack. If it's mocked, we are running test + // units and the request body is already an object and can be returned. + if (req.__isMocked) return { ...req.body }; + const form = await formParse(req); return form.fields; } diff --git a/packages/server/src/utils/testing/koa/FakeRequest.ts b/packages/server/src/utils/testing/koa/FakeRequest.ts index f2909a468..62b15a57c 100644 --- a/packages/server/src/utils/testing/koa/FakeRequest.ts +++ b/packages/server/src/utils/testing/koa/FakeRequest.ts @@ -1,5 +1,13 @@ export default class FakeRequest { - public method: string = 'GET'; + private req_: any; + + public constructor(nodeRequest: any) { + this.req_ = nodeRequest; + } + + public get method(): string { + return this.req_.method || 'GET'; + } } diff --git a/packages/server/src/utils/testing/koa/FakeResponse.ts b/packages/server/src/utils/testing/koa/FakeResponse.ts index 26971efe5..e48a18763 100644 --- a/packages/server/src/utils/testing/koa/FakeResponse.ts +++ b/packages/server/src/utils/testing/koa/FakeResponse.ts @@ -2,5 +2,14 @@ export default class FakeResponse { public status: number = 200; public body: any = null; + private headers_: any = {}; + + public set(name: string, value: any) { + this.headers_[name] = value; + } + + public get(name: string): any { + return this.headers_[name]; + } } diff --git a/packages/server/src/utils/testing/testUtils.ts b/packages/server/src/utils/testing/testUtils.ts index 9809b44f7..d9c3deed9 100644 --- a/packages/server/src/utils/testing/testUtils.ts +++ b/packages/server/src/utils/testing/testUtils.ts @@ -10,6 +10,7 @@ import Logger from '@joplin/lib/Logger'; import FakeCookies from './koa/FakeCookies'; import FakeRequest from './koa/FakeRequest'; import FakeResponse from './koa/FakeResponse'; +import * as httpMocks from 'node-mocks-http'; // Takes into account the fact that this file will be inside the /dist directory // when it runs. @@ -43,9 +44,10 @@ export async function beforeEachDb() { } interface AppContextTestOptions { - path?: string; + // path?: string; owner?: User; sessionId?: string; + request?: any; } function initGlobalLogger() { @@ -57,12 +59,24 @@ export async function koaAppContext(options: AppContextTestOptions = null): Prom if (!db_) throw new Error('Database must be initialized first'); options = { - path: '/home', + // path: '/home', ...options, }; initGlobalLogger(); + const reqOptions = { + ...options.request, + }; + + if (!reqOptions.method) reqOptions.method = 'GET'; + if (!reqOptions.url) reqOptions.url = '/home'; + if (!reqOptions.headers) reqOptions.headers = {}; + if (!reqOptions.headers['content-type']) reqOptions.headers['content-type'] = 'application/json'; + + const req = httpMocks.createRequest(reqOptions); + req.__isMocked = true; + const appLogger = Logger.create('AppTest'); // Set type to "any" because the Koa context has many properties and we @@ -75,11 +89,13 @@ export async function koaAppContext(options: AppContextTestOptions = null): Prom appContext.controllers = controllers(); appContext.appLogger = () => appLogger; - appContext.path = options.path; + appContext.path = req.url; appContext.owner = options.owner; appContext.cookies = new FakeCookies(); - appContext.request = new FakeRequest(); + appContext.request = new FakeRequest(req); appContext.response = new FakeResponse(); + appContext.req = req; + appContext.method = req.method; if (options.sessionId) { appContext.cookies.set('sessionId', options.sessionId);