From c70f023fe0dc8084a7bd5d7a29426f58788a994c Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Tue, 12 Jan 2021 16:49:39 +0000 Subject: [PATCH] Server: Added test units for notification handler --- .../controllers/api/FileController.test.ts | 2 +- .../controllers/api/SessionController.test.ts | 2 +- .../controllers/api/UserController.test.ts | 2 +- .../middleware/notificationHandler.test.ts | 73 ++++++++++++++++++ .../server/src/models/ChangeModel.test.ts | 2 +- packages/server/src/models/FileModel.test.ts | 2 +- .../src/models/NotificationModel.test.ts | 2 +- .../src/models/utils/pagination.test.ts | 2 +- .../src/utils/{ => testing}/testUtils.ts | 76 ++++++++++++++++--- 9 files changed, 144 insertions(+), 19 deletions(-) create mode 100644 packages/server/src/middleware/notificationHandler.test.ts rename packages/server/src/utils/{ => testing}/testUtils.ts (59%) diff --git a/packages/server/src/controllers/api/FileController.test.ts b/packages/server/src/controllers/api/FileController.test.ts index d114be27f..cf9a824ac 100644 --- a/packages/server/src/controllers/api/FileController.test.ts +++ b/packages/server/src/controllers/api/FileController.test.ts @@ -1,4 +1,4 @@ -import { testAssetDir, createUserAndSession, createUser, checkThrowAsync, beforeAllDb, afterAllDb, beforeEachDb, models, controllers } from '../../utils/testUtils'; +import { testAssetDir, createUserAndSession, createUser, checkThrowAsync, beforeAllDb, afterAllDb, beforeEachDb, models, controllers } from '../../utils/testing/testUtils'; import * as fs from 'fs-extra'; import { ChangeType, File } from '../../db'; import { ErrorConflict, ErrorForbidden, ErrorNotFound, ErrorUnprocessableEntity } from '../../utils/errors'; diff --git a/packages/server/src/controllers/api/SessionController.test.ts b/packages/server/src/controllers/api/SessionController.test.ts index fb2364dde..01801d2b5 100644 --- a/packages/server/src/controllers/api/SessionController.test.ts +++ b/packages/server/src/controllers/api/SessionController.test.ts @@ -1,4 +1,4 @@ -import { createUser, checkThrowAsync, beforeAllDb, afterAllDb, beforeEachDb, controllers } from '../../utils/testUtils'; +import { createUser, checkThrowAsync, beforeAllDb, afterAllDb, beforeEachDb, controllers } from '../../utils/testing/testUtils'; import { ErrorForbidden } from '../../utils/errors'; describe('SessionController', function() { diff --git a/packages/server/src/controllers/api/UserController.test.ts b/packages/server/src/controllers/api/UserController.test.ts index 5406a0573..d00d46b5b 100644 --- a/packages/server/src/controllers/api/UserController.test.ts +++ b/packages/server/src/controllers/api/UserController.test.ts @@ -1,4 +1,4 @@ -import { models, controllers, createUserAndSession, checkThrowAsync, beforeAllDb, afterAllDb, beforeEachDb } from '../../utils/testUtils'; +import { models, controllers, createUserAndSession, checkThrowAsync, beforeAllDb, afterAllDb, beforeEachDb } from '../../utils/testing/testUtils'; import { File, User } from '../../db'; import { ErrorForbidden, ErrorUnprocessableEntity } from '../../utils/errors'; diff --git a/packages/server/src/middleware/notificationHandler.test.ts b/packages/server/src/middleware/notificationHandler.test.ts new file mode 100644 index 000000000..146d30d3e --- /dev/null +++ b/packages/server/src/middleware/notificationHandler.test.ts @@ -0,0 +1,73 @@ +import { createUserAndSession, beforeAllDb, afterAllDb, beforeEachDb, models, koaAppContext, koaNext } from '../utils/testing/testUtils'; +import { defaultAdminEmail, defaultAdminPassword, Notification } from '../db'; +import notificationHandler from './notificationHandler'; + +describe('notificationHandler', function() { + + beforeAll(async () => { + await beforeAllDb('notificationHandler'); + }); + + afterAll(async () => { + await afterAllDb(); + }); + + beforeEach(async () => { + await beforeEachDb(); + }); + + test('should check admin password', async function() { + const { user } = await createUserAndSession(1, true); + + const admin = await models().user({ userId: user.id }).save({ + email: defaultAdminEmail, + password: defaultAdminPassword, + is_admin: 1, + }); + + { + const context = await koaAppContext({ owner: user }); + await notificationHandler(context, koaNext); + + const notifications: Notification[] = await models().notification().all(); + expect(notifications.length).toBe(1); + expect(notifications[0].key).toBe('change_admin_password'); + expect(notifications[0].read).toBe(0); + + expect(context.notifications.length).toBe(1); + } + + { + await models().user({ userId: admin.id }).save({ + id: admin.id, + password: 'changed!', + }); + + const context = await koaAppContext({ owner: user }); + await notificationHandler(context, koaNext); + + const notifications: Notification[] = await models().notification().all(); + expect(notifications.length).toBe(1); + expect(notifications[0].key).toBe('change_admin_password'); + expect(notifications[0].read).toBe(1); + + expect(context.notifications.length).toBe(0); + } + }); + + test('should not check admin password for non-admin', async function() { + const { user } = await createUserAndSession(1, false); + + await createUserAndSession(2, true, { + email: defaultAdminEmail, + password: defaultAdminPassword, + }); + + const context = await koaAppContext({ owner: user }); + await notificationHandler(context, koaNext); + + const notifications: Notification[] = await models().notification().all(); + expect(notifications.length).toBe(0); + }); + +}); diff --git a/packages/server/src/models/ChangeModel.test.ts b/packages/server/src/models/ChangeModel.test.ts index e0f363b64..8f9d08d9e 100644 --- a/packages/server/src/models/ChangeModel.test.ts +++ b/packages/server/src/models/ChangeModel.test.ts @@ -1,4 +1,4 @@ -import { createUserAndSession, beforeAllDb, afterAllDb, beforeEachDb, models, expectThrow } from '../utils/testUtils'; +import { createUserAndSession, beforeAllDb, afterAllDb, beforeEachDb, models, expectThrow } from '../utils/testing/testUtils'; import { ChangeType, File } from '../db'; import FileModel from './FileModel'; import { msleep } from '../utils/time'; diff --git a/packages/server/src/models/FileModel.test.ts b/packages/server/src/models/FileModel.test.ts index 1f9d4222d..cab1d5508 100644 --- a/packages/server/src/models/FileModel.test.ts +++ b/packages/server/src/models/FileModel.test.ts @@ -1,4 +1,4 @@ -import { createUserAndSession, beforeAllDb, afterAllDb, beforeEachDb, models, createFileTree } from '../utils/testUtils'; +import { createUserAndSession, beforeAllDb, afterAllDb, beforeEachDb, models, createFileTree } from '../utils/testing/testUtils'; import { File } from '../db'; describe('FileModel', function() { diff --git a/packages/server/src/models/NotificationModel.test.ts b/packages/server/src/models/NotificationModel.test.ts index 8bf9ef134..cf0d465e4 100644 --- a/packages/server/src/models/NotificationModel.test.ts +++ b/packages/server/src/models/NotificationModel.test.ts @@ -1,4 +1,4 @@ -import { createUserAndSession, beforeAllDb, afterAllDb, beforeEachDb, models, expectThrow } from '../utils/testUtils'; +import { createUserAndSession, beforeAllDb, afterAllDb, beforeEachDb, models, expectThrow } from '../utils/testing/testUtils'; import { Notification, NotificationLevel } from '../db'; describe('NotificationModel', function() { diff --git a/packages/server/src/models/utils/pagination.test.ts b/packages/server/src/models/utils/pagination.test.ts index ab4069e3d..22204a78f 100644 --- a/packages/server/src/models/utils/pagination.test.ts +++ b/packages/server/src/models/utils/pagination.test.ts @@ -1,4 +1,4 @@ -import { expectThrow } from '../../utils/testUtils'; +import { expectThrow } from '../../utils/testing/testUtils'; import { defaultPagination, Pagination, createPaginationLinks, requestPagination } from './pagination'; describe('pagination', function() { diff --git a/packages/server/src/utils/testUtils.ts b/packages/server/src/utils/testing/testUtils.ts similarity index 59% rename from packages/server/src/utils/testUtils.ts rename to packages/server/src/utils/testing/testUtils.ts index bdc8452a8..72ac73524 100644 --- a/packages/server/src/utils/testUtils.ts +++ b/packages/server/src/utils/testing/testUtils.ts @@ -1,11 +1,12 @@ -import { User, Session, DbConnection, connectDb, disconnectDb, File, truncateTables } from '../db'; -import { createDb } from '../tools/dbTools'; -import modelFactory from '../models/factory'; -import controllerFactory from '../controllers/factory'; -import baseConfig from '../config-tests'; -import { Config } from './types'; -import { initConfig } from '../config'; -import FileModel from '../models/FileModel'; +import { User, Session, DbConnection, connectDb, disconnectDb, File, truncateTables } from '../../db'; +import { createDb } from '../../tools/dbTools'; +import modelFactory from '../../models/factory'; +import controllerFactory from '../../controllers/factory'; +import baseConfig from '../../config-tests'; +import { AppContext, Config, Env } from '../types'; +import { initConfig } from '../../config'; +import FileModel from '../../models/FileModel'; +import Logger from '@joplin/lib/Logger'; // Takes into account the fact that this file will be inside the /dist directory // when it runs. @@ -38,6 +39,47 @@ export async function beforeEachDb() { await truncateTables(db_); } +interface AppContextTestOptions { + path?: string; + owner?: User; +} + +let koaAppContextUserAndSession_: UserAndSession = null; + +export async function koaAppContextUserAndSession(): Promise { + if (koaAppContextUserAndSession_) return koaAppContextUserAndSession_; + koaAppContextUserAndSession_ = await createUserAndSession(1, false); + return koaAppContextUserAndSession_; +} + +export async function koaAppContext(options: AppContextTestOptions = null): Promise { + if (!db_) throw new Error('Database must be initialized first'); + + options = { + path: '/home', + ...options, + }; + + const appLogger = Logger.create('AppTest'); + + const appContext: any = {}; + + appContext.env = Env.Dev; + appContext.db = db_; + appContext.models = models(); + appContext.controllers = controllers(); + appContext.appLogger = () => appLogger; + + appContext.path = options.path; + appContext.owner = options.owner; + + return appContext as AppContext; +} + +export function koaNext(): Promise { + return Promise.resolve(); +} + export const testAssetDir = `${packageRootDir}/assets/tests`; interface UserAndSession { @@ -61,12 +103,22 @@ export function controllers() { return controllerFactory(models()); } -export const createUserAndSession = async function(index: number = 1, isAdmin: boolean = false): Promise { +interface CreateUserAndSessionOptions { + email?: string; + password?: string; +} + +export const createUserAndSession = async function(index: number = 1, isAdmin: boolean = false, options: CreateUserAndSessionOptions = null): Promise { const sessionController = controllers().apiSession(); - const email: string = `user${index}@localhost`; - const user = await models().user().save({ email: email, password: '123456', is_admin: isAdmin ? 1 : 0 }, { skipValidation: true }); - const session = await sessionController.authenticate(email, '123456'); + options = { + email: `user${index}@localhost`, + password: '123456', + ...options, + }; + + const user = await models().user().save({ email: options.email, password: options.password, is_admin: isAdmin ? 1 : 0 }, { skipValidation: true }); + const session = await sessionController.authenticate(options.email, options.password); return { user: user,