1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Server: Moved controller tests to route and model

This commit is contained in:
Laurent Cozic 2021-01-13 23:20:45 +00:00
parent 247bd9bfd9
commit f14ea46f0b
16 changed files with 365 additions and 262 deletions

View File

@ -1484,18 +1484,12 @@ packages/server/src/controllers/BaseController.js.map
packages/server/src/controllers/api/FileController.d.ts
packages/server/src/controllers/api/FileController.js
packages/server/src/controllers/api/FileController.js.map
packages/server/src/controllers/api/FileController.test.d.ts
packages/server/src/controllers/api/FileController.test.js
packages/server/src/controllers/api/FileController.test.js.map
packages/server/src/controllers/api/OAuthController.d.ts
packages/server/src/controllers/api/OAuthController.js
packages/server/src/controllers/api/OAuthController.js.map
packages/server/src/controllers/api/SessionController.d.ts
packages/server/src/controllers/api/SessionController.js
packages/server/src/controllers/api/SessionController.js.map
packages/server/src/controllers/api/SessionController.test.d.ts
packages/server/src/controllers/api/SessionController.test.js
packages/server/src/controllers/api/SessionController.test.js.map
packages/server/src/controllers/api/UserController.d.ts
packages/server/src/controllers/api/UserController.js
packages/server/src/controllers/api/UserController.js.map
@ -1592,6 +1586,9 @@ packages/server/src/models/utils/pagination.test.js.map
packages/server/src/routes/api/files.d.ts
packages/server/src/routes/api/files.js
packages/server/src/routes/api/files.js.map
packages/server/src/routes/api/files.test.d.ts
packages/server/src/routes/api/files.test.js
packages/server/src/routes/api/files.test.js.map
packages/server/src/routes/api/index.d.ts
packages/server/src/routes/api/index.js
packages/server/src/routes/api/index.js.map
@ -1604,6 +1601,9 @@ 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
packages/server/src/routes/api/sessions.test.d.ts
packages/server/src/routes/api/sessions.test.js
packages/server/src/routes/api/sessions.test.js.map
packages/server/src/routes/default.d.ts
packages/server/src/routes/default.js
packages/server/src/routes/default.js.map
@ -1631,6 +1631,9 @@ packages/server/src/routes/index/user.js.map
packages/server/src/routes/index/users.d.ts
packages/server/src/routes/index/users.js
packages/server/src/routes/index/users.js.map
packages/server/src/routes/index/users.test.d.ts
packages/server/src/routes/index/users.test.js
packages/server/src/routes/index/users.test.js.map
packages/server/src/routes/oauth2/authorize.d.ts
packages/server/src/routes/oauth2/authorize.js
packages/server/src/routes/oauth2/authorize.js.map
@ -1682,6 +1685,9 @@ 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/testing/apiUtils.d.ts
packages/server/src/utils/testing/apiUtils.js
packages/server/src/utils/testing/apiUtils.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

18
.gitignore vendored
View File

@ -1473,18 +1473,12 @@ packages/server/src/controllers/BaseController.js.map
packages/server/src/controllers/api/FileController.d.ts
packages/server/src/controllers/api/FileController.js
packages/server/src/controllers/api/FileController.js.map
packages/server/src/controllers/api/FileController.test.d.ts
packages/server/src/controllers/api/FileController.test.js
packages/server/src/controllers/api/FileController.test.js.map
packages/server/src/controllers/api/OAuthController.d.ts
packages/server/src/controllers/api/OAuthController.js
packages/server/src/controllers/api/OAuthController.js.map
packages/server/src/controllers/api/SessionController.d.ts
packages/server/src/controllers/api/SessionController.js
packages/server/src/controllers/api/SessionController.js.map
packages/server/src/controllers/api/SessionController.test.d.ts
packages/server/src/controllers/api/SessionController.test.js
packages/server/src/controllers/api/SessionController.test.js.map
packages/server/src/controllers/api/UserController.d.ts
packages/server/src/controllers/api/UserController.js
packages/server/src/controllers/api/UserController.js.map
@ -1581,6 +1575,9 @@ packages/server/src/models/utils/pagination.test.js.map
packages/server/src/routes/api/files.d.ts
packages/server/src/routes/api/files.js
packages/server/src/routes/api/files.js.map
packages/server/src/routes/api/files.test.d.ts
packages/server/src/routes/api/files.test.js
packages/server/src/routes/api/files.test.js.map
packages/server/src/routes/api/index.d.ts
packages/server/src/routes/api/index.js
packages/server/src/routes/api/index.js.map
@ -1593,6 +1590,9 @@ 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
packages/server/src/routes/api/sessions.test.d.ts
packages/server/src/routes/api/sessions.test.js
packages/server/src/routes/api/sessions.test.js.map
packages/server/src/routes/default.d.ts
packages/server/src/routes/default.js
packages/server/src/routes/default.js.map
@ -1620,6 +1620,9 @@ packages/server/src/routes/index/user.js.map
packages/server/src/routes/index/users.d.ts
packages/server/src/routes/index/users.js
packages/server/src/routes/index/users.js.map
packages/server/src/routes/index/users.test.d.ts
packages/server/src/routes/index/users.test.js
packages/server/src/routes/index/users.test.js.map
packages/server/src/routes/oauth2/authorize.d.ts
packages/server/src/routes/oauth2/authorize.js
packages/server/src/routes/oauth2/authorize.js.map
@ -1671,6 +1674,9 @@ 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/testing/apiUtils.d.ts
packages/server/src/utils/testing/apiUtils.js
packages/server/src/utils/testing/apiUtils.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

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "1.6.7",
"version": "1.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/server",
"version": "1.6.4",
"version": "1.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -1308,6 +1308,17 @@
"pretty-format": "^26.0.0"
}
},
"@types/jsdom": {
"version": "16.2.6",
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.6.tgz",
"integrity": "sha512-yQA+HxknGtW9AkRTNyiSH3OKW5V+WzO8OPTdne99XwJkYC+KYxfNIcoJjeiSqP3V00PUUpFP6Myoo9wdIu78DQ==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/parse5": "*",
"@types/tough-cookie": "*"
}
},
"@types/keygrip": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.1.tgz",
@ -1384,6 +1395,12 @@
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"@types/parse5": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.0.tgz",
"integrity": "sha512-oPwPSj4a1wu9rsXTEGIJz91ISU725t0BmSnUhb57sI+M8XEmvUop84lzuiYdq0Y5M6xLY8DBPg0C2xEQKLyvBA==",
"dev": true
},
"@types/prettier": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.5.tgz",
@ -1412,6 +1429,12 @@
"integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==",
"dev": true
},
"@types/tough-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz",
"integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==",
"dev": true
},
"@types/yargs": {
"version": "13.0.2",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.2.tgz",

View File

@ -37,11 +37,13 @@
"@rmp135/sql-ts": "^1.7.0",
"@types/fs-extra": "^8.0.0",
"@types/jest": "^26.0.15",
"@types/jsdom": "^16.2.6",
"@types/koa": "^2.0.49",
"@types/markdown-it": "^12.0.0",
"@types/mustache": "^0.8.32",
"@types/yargs": "^13.0.2",
"jest": "^26.6.3",
"jsdom": "^16.4.0",
"node-mocks-http": "^1.10.0",
"source-map-support": "^0.5.13",
"typescript": "^4.1.2"

View File

@ -1,192 +0,0 @@
import { models, controllers, createUserAndSession, checkThrowAsync, beforeAllDb, afterAllTests, beforeEachDb } from '../../utils/testing/testUtils';
import { File, User } from '../../db';
import { ErrorForbidden, ErrorUnprocessableEntity } from '../../utils/errors';
describe('UserController', function() {
beforeAll(async () => {
await beforeAllDb('UserController');
});
afterAll(async () => {
await afterAllTests();
});
beforeEach(async () => {
await beforeEachDb();
});
it('should create a new user along with his root file', async function() {
const { session } = await createUserAndSession(1, true);
const controller = controllers().apiUser();
const newUser = await controller.postUser(session.id, { email: 'test@example.com', password: '123456' });
expect(!!newUser).toBe(true);
expect(!!newUser.id).toBe(true);
expect(!!newUser.is_admin).toBe(false);
expect(!!newUser.email).toBe(true);
expect(!newUser.password).toBe(true);
const userModel = models().user({ userId: newUser.id });
const userFromModel: User = await userModel.load(newUser.id);
expect(!!userFromModel.password).toBe(true);
expect(userFromModel.password === '123456').toBe(false); // Password has been hashed
const fileModel = models().file({ userId: newUser.id });
const rootFile: File = await fileModel.userRootFile();
expect(!!rootFile).toBe(true);
expect(!!rootFile.id).toBe(true);
});
it('should not create anything, neither user, root file nor permissions, if user creation fail', async function() {
const { user, session } = await createUserAndSession(1, true);
const controller = controllers().apiUser();
const fileModel = models().file({ userId: user.id });
const permissionModel = models().permission();
const userModel = models().user({ userId: user.id });
await controller.postUser(session.id, { email: 'test@example.com', password: '123456' });
const beforeFileCount = (await fileModel.all()).length;
const beforeUserCount = (await userModel.all()).length;
const beforePermissionCount = (await permissionModel.all()).length;
expect(beforeFileCount).toBe(2);
expect(beforeUserCount).toBe(2);
let hasThrown = false;
try {
await controller.postUser(session.id, { email: 'test@example.com', password: '123456' });
} catch (error) {
hasThrown = true;
}
expect(hasThrown).toBe(true);
const afterFileCount = (await fileModel.all()).length;
const afterUserCount = (await userModel.all()).length;
const afterPermissionCount = (await permissionModel.all()).length;
expect(beforeFileCount).toBe(afterFileCount);
expect(beforeUserCount).toBe(afterUserCount);
expect(beforePermissionCount).toBe(afterPermissionCount);
});
it('should change user properties', async function() {
const { user, session } = await createUserAndSession(1, true);
const controller = controllers().apiUser();
const userModel = models().user({ userId: user.id });
await controller.patchUser(session.id, { id: user.id, email: 'test2@example.com' });
let modUser: User = await userModel.load(user.id);
expect(modUser.email).toBe('test2@example.com');
const previousPassword = modUser.password;
await controller.patchUser(session.id, { id: user.id, password: 'abcdefgh' });
modUser = await userModel.load(user.id);
expect(!!modUser.password).toBe(true);
expect(modUser.password === previousPassword).toBe(false);
});
it('should get a user', async function() {
const { user, session } = await createUserAndSession();
const controller = controllers().apiUser();
const gotUser = await controller.getUser(session.id, user.id);
expect(gotUser.id).toBe(user.id);
expect(gotUser.email).toBe(user.email);
});
it('should validate user objects', async function() {
const { user: admin, session: adminSession } = await createUserAndSession(1, true);
const { user: user1, session: userSession1 } = await createUserAndSession(2, false);
const { user: user2 } = await createUserAndSession(3, false);
let error = null;
const controller = controllers().apiUser();
// Non-admin user can't create a user
error = await checkThrowAsync(async () => await controller.postUser(userSession1.id, { email: 'newone@example.com', password: '1234546' }));
expect(error instanceof ErrorForbidden).toBe(true);
// Email must be set
error = await checkThrowAsync(async () => await controller.postUser(adminSession.id, { email: '', password: '1234546' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// Password must be set
error = await checkThrowAsync(async () => await controller.postUser(adminSession.id, { email: 'newone@example.com', password: '' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// ID must be set when updating a user
error = await checkThrowAsync(async () => await controller.patchUser(userSession1.id, { email: 'newone@example.com' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// non-admin user cannot modify another user
error = await checkThrowAsync(async () => await controller.patchUser(userSession1.id, { id: user2.id, email: 'newone@example.com' }));
expect(error instanceof ErrorForbidden).toBe(true);
// email must be set
error = await checkThrowAsync(async () => await controller.patchUser(userSession1.id, { id: user1.id, email: '' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// password must be set
error = await checkThrowAsync(async () => await controller.patchUser(userSession1.id, { id: user1.id, password: '' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// non-admin user cannot make a user an admin
error = await checkThrowAsync(async () => await controller.patchUser(userSession1.id, { id: user1.id, is_admin: 1 }));
expect(error instanceof ErrorForbidden).toBe(true);
// non-admin user cannot remove admin bit from themselves
error = await checkThrowAsync(async () => await controller.patchUser(adminSession.id, { id: admin.id, is_admin: 0 }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// there is already a user with this email
error = await checkThrowAsync(async () => await controller.patchUser(userSession1.id, { id: user1.id, email: user2.email }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// check that the email is valid
error = await checkThrowAsync(async () => await controller.patchUser(userSession1.id, { id: user1.id, email: 'ohno' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
});
it('should delete a user', async function() {
const { user: admin, session: adminSession } = await createUserAndSession(1, true);
const { user: user1, session: session1 } = await createUserAndSession(2, false);
const { user: user2, session: session2 } = await createUserAndSession(3, false);
const controller = controllers().apiUser();
const userModel = models().user({ userId: admin.id });
const allUsers: File[] = await userModel.all();
const beforeCount: number = allUsers.length;
// Can't delete someone else user
const error = await checkThrowAsync(async () => await controller.deleteUser(session1.id, user2.id));
expect(error instanceof ErrorForbidden).toBe(true);
expect((await userModel.all()).length).toBe(beforeCount);
// Admin can delete any user
await controller.deleteUser(adminSession.id, user1.id);
expect((await userModel.all()).length).toBe(beforeCount - 1);
const allFiles = await models().file().all() as File[];
expect(allFiles.length).toBe(2);
expect(!!allFiles.find(f => f.owner_id === admin.id)).toBe(true);
expect(!!allFiles.find(f => f.owner_id === user2.id)).toBe(true);
// Can delete own user
const fileModel = models().file({ userId: user2.id });
expect(!!(await fileModel.userRootFile())).toBe(true);
await controller.deleteUser(session2.id, user2.id);
expect((await userModel.all()).length).toBe(beforeCount - 2);
expect(!!(await fileModel.userRootFile())).toBe(false);
});
});

View File

@ -56,14 +56,14 @@ describe('notificationHandler', function() {
});
test('should not check admin password for non-admin', async function() {
const { user } = await createUserAndSession(1, false);
const { session } = await createUserAndSession(1, false);
await createUserAndSession(2, true, {
email: defaultAdminEmail,
password: defaultAdminPassword,
});
const context = await koaAppContext({ owner: user });
const context = await koaAppContext({ sessionId: session.id });
await notificationHandler(context, koaNext);
const notifications: Notification[] = await models().notification().all();

View File

@ -39,7 +39,7 @@ export default async function(ctx: AppContext) {
ctx.response.status = error.httpCode ? error.httpCode : 500;
const responseFormat = routeResponseFormat(match, ctx.path);
const responseFormat = routeResponseFormat(match, ctx);
if (responseFormat === RouteResponseFormat.Html) {
ctx.response.set('Content-Type', 'text/html');

View File

@ -0,0 +1,98 @@
import { createUserAndSession, beforeAllDb, afterAllTests, beforeEachDb, models, checkThrowAsync } from '../utils/testing/testUtils';
import { File } from '../db';
import { ErrorForbidden, ErrorUnprocessableEntity } from '../utils/errors';
describe('NotificationModel', function() {
beforeAll(async () => {
await beforeAllDb('NotificationModel');
});
afterAll(async () => {
await afterAllTests();
});
beforeEach(async () => {
await beforeEachDb();
});
test('should validate user objects', async function() {
const { user: admin } = await createUserAndSession(1, true);
const { user: user1 } = await createUserAndSession(2, false);
const { user: user2 } = await createUserAndSession(3, false);
let error = null;
// Non-admin user can't create a user
error = await checkThrowAsync(async () => await models().user({ userId: user1.id }).save({ email: 'newone@example.com', password: '1234546' }));
expect(error instanceof ErrorForbidden).toBe(true);
// Email must be set
error = await checkThrowAsync(async () => await models().user({ userId: admin.id }).save({ email: '', password: '1234546' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// Password must be set
error = await checkThrowAsync(async () => await models().user({ userId: admin.id }).save({ email: 'newone@example.com', password: '' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// non-admin user cannot modify another user
error = await checkThrowAsync(async () => await models().user({ userId: user1.id }).save({ id: user2.id, email: 'newone@example.com' }));
expect(error instanceof ErrorForbidden).toBe(true);
// email must be set
error = await checkThrowAsync(async () => await models().user({ userId: user1.id }).save({ id: user1.id, email: '' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// password must be set
error = await checkThrowAsync(async () => await models().user({ userId: user1.id }).save({ id: user1.id, password: '' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// non-admin user cannot make a user an admin
error = await checkThrowAsync(async () => await models().user({ userId: user1.id }).save({ id: user1.id, is_admin: 1 }));
expect(error instanceof ErrorForbidden).toBe(true);
// non-admin user cannot remove admin bit from themselves
error = await checkThrowAsync(async () => await models().user({ userId: admin.id }).save({ id: admin.id, is_admin: 0 }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// there is already a user with this email
error = await checkThrowAsync(async () => await models().user({ userId: user1.id }).save({ id: user1.id, email: user2.email }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
// check that the email is valid
error = await checkThrowAsync(async () => await models().user({ userId: user1.id }).save({ id: user1.id, email: 'ohno' }));
expect(error instanceof ErrorUnprocessableEntity).toBe(true);
});
test('should delete a user', async function() {
const { user: admin } = await createUserAndSession(1, true);
const { user: user1 } = await createUserAndSession(2, false);
const { user: user2 } = await createUserAndSession(3, false);
const userModel = models().user({ userId: admin.id });
const allUsers: File[] = await userModel.all();
const beforeCount: number = allUsers.length;
// Can't delete someone else user
const error = await checkThrowAsync(async () => await models().user({ userId: user1.id }).delete(user2.id));
expect(error instanceof ErrorForbidden).toBe(true);
expect((await userModel.all()).length).toBe(beforeCount);
// Admin can delete any user
await models().user({ userId: admin.id }).delete(user1.id);
expect((await userModel.all()).length).toBe(beforeCount - 1);
const allFiles = await models().file().all() as File[];
expect(allFiles.length).toBe(2);
expect(!!allFiles.find(f => f.owner_id === admin.id)).toBe(true);
expect(!!allFiles.find(f => f.owner_id === user2.id)).toBe(true);
// Can delete own user
const fileModel = models().file({ userId: user2.id });
expect(!!(await fileModel.userRootFile())).toBe(true);
await models().user({ userId: user2.id }).delete(user2.id);
expect((await userModel.all()).length).toBe(beforeCount - 2);
expect(!!(await fileModel.userRootFile())).toBe(false);
});
});

View File

@ -1,59 +1,61 @@
import { SubPath, Route, redirect } from '../../utils/routeUtils';
import { AppContext } from '../../utils/types';
import { contextSessionId, formParse } from '../../utils/requestUtils';
import { ErrorMethodNotAllowed, ErrorUnprocessableEntity } from '../../utils/errors';
import { User } from '../../db';
import { baseUrl } from '../../config';
// Not used??
function makeUser(isNew: boolean, fields: any): User {
const user: User = {
email: fields.email,
full_name: fields.full_name,
};
// import { SubPath, Route, redirect } from '../../utils/routeUtils';
// import { AppContext } from '../../utils/types';
// import { contextSessionId, formParse } from '../../utils/requestUtils';
// import { ErrorMethodNotAllowed, ErrorUnprocessableEntity } from '../../utils/errors';
// import { User } from '../../db';
// import { baseUrl } from '../../config';
if (fields.password) {
if (fields.password !== fields.password2) throw new ErrorUnprocessableEntity('Passwords do not match');
user.password = fields.password;
}
// function makeUser(isNew: boolean, fields: any): User {
// const user: User = {
// email: fields.email,
// full_name: fields.full_name,
// };
if (!isNew) user.id = fields.id;
// if (fields.password) {
// if (fields.password !== fields.password2) throw new ErrorUnprocessableEntity('Passwords do not match');
// user.password = fields.password;
// }
return user;
}
// if (!isNew) user.id = fields.id;
const route: Route = {
// return user;
// }
exec: async function(_path: SubPath, ctx: AppContext) {
const sessionId = contextSessionId(ctx);
// const route: Route = {
// if (ctx.method === 'GET') {
// return ctx.controllers.indexUser().getOne(sessionId);
// }
// exec: async function(_path: SubPath, ctx: AppContext) {
// const sessionId = contextSessionId(ctx);
if (ctx.method === 'POST') {
const user: User = {};
// // if (ctx.method === 'GET') {
// // return ctx.controllers.indexUser().getOne(sessionId);
// // }
try {
const body = await formParse(ctx.req);
const fields = body.fields;
const isNew = !!Number(fields.is_new);
const user = makeUser(isNew, fields);
// if (ctx.method === 'POST') {
// const user: User = {};
if (isNew) {
await ctx.controllers.apiUser().postUser(sessionId, user);
} else {
await ctx.controllers.apiUser().patchUser(sessionId, user);
}
// try {
// const body = await formParse(ctx.req);
// const fields = body.fields;
// const isNew = !!Number(fields.is_new);
// const user = makeUser(isNew, fields);
return redirect(ctx, `${baseUrl()}/users`);
} catch (error) {
return ctx.controllers.indexProfile().getIndex(sessionId, user, error);
}
}
// if (isNew) {
// await ctx.controllers.apiUser().postUser(sessionId, user);
// } else {
// await ctx.controllers.apiUser().patchUser(sessionId, user);
// }
throw new ErrorMethodNotAllowed();
},
// return redirect(ctx, `${baseUrl()}/users`);
// } catch (error) {
// return ctx.controllers.indexProfile().getIndex(sessionId, user, error);
// }
// }
};
// throw new ErrorMethodNotAllowed();
// },
export default route;
// };
// export default route;

View File

@ -0,0 +1,149 @@
import { File, User } from '../../db';
import routeHandler from '../../middleware/routeHandler';
import { checkContextError } from '../../utils/testing/apiUtils';
import { beforeAllDb, afterAllTests, beforeEachDb, koaAppContext, createUserAndSession, models, parseHtml } from '../../utils/testing/testUtils';
export async function postUser(sessionId: string, email: string, password: string): Promise<User> {
const context = await koaAppContext({
sessionId: sessionId,
request: {
method: 'POST',
url: '/users/new',
body: {
email: email,
password: password,
password2: password,
post_button: true,
},
},
});
await routeHandler(context);
checkContextError(context);
return context.response.body;
}
export async function patchUser(sessionId: string, user: any): Promise<User> {
const context = await koaAppContext({
sessionId: sessionId,
request: {
method: 'POST',
url: '/users',
body: {
...user,
post_button: true,
},
},
});
await routeHandler(context);
checkContextError(context);
return context.response.body;
}
export async function getUserHtml(sessionId: string, userId: string): Promise<string> {
const context = await koaAppContext({
sessionId: sessionId,
request: {
method: 'GET',
url: `/users/${userId}`,
},
});
await routeHandler(context);
checkContextError(context);
return context.response.body;
}
describe('index_users', function() {
beforeAll(async () => {
await beforeAllDb('index_users');
});
afterAll(async () => {
await afterAllTests();
});
beforeEach(async () => {
await beforeEachDb();
});
test('should create a new user along with his root file', async function() {
const { user: admin, session } = await createUserAndSession(1, true);
await postUser(session.id, 'test@example.com', '123456');
const newUser = await models().user({ userId: admin.id }).loadByEmail('test@example.com');
expect(!!newUser).toBe(true);
expect(!!newUser.id).toBe(true);
expect(!!newUser.is_admin).toBe(false);
expect(!!newUser.email).toBe(true);
const userModel = models().user({ userId: newUser.id });
const userFromModel: User = await userModel.load(newUser.id);
expect(!!userFromModel.password).toBe(true);
expect(userFromModel.password === '123456').toBe(false); // Password has been hashed
const fileModel = models().file({ userId: newUser.id });
const rootFile: File = await fileModel.userRootFile();
expect(!!rootFile).toBe(true);
expect(!!rootFile.id).toBe(true);
});
test('should not create anything, neither user, root file nor permissions, if user creation fail', async function() {
const { user, session } = await createUserAndSession(1, true);
const fileModel = models().file({ userId: user.id });
const permissionModel = models().permission();
const userModel = models().user({ userId: user.id });
await postUser(session.id, 'test@example.com', '123456');
const beforeFileCount = (await fileModel.all()).length;
const beforeUserCount = (await userModel.all()).length;
const beforePermissionCount = (await permissionModel.all()).length;
expect(beforeFileCount).toBe(2);
expect(beforeUserCount).toBe(2);
await postUser(session.id, 'test@example.com', '123456');
const afterFileCount = (await fileModel.all()).length;
const afterUserCount = (await userModel.all()).length;
const afterPermissionCount = (await permissionModel.all()).length;
expect(beforeFileCount).toBe(afterFileCount);
expect(beforeUserCount).toBe(afterUserCount);
expect(beforePermissionCount).toBe(afterPermissionCount);
});
test('should change user properties', async function() {
const { user, session } = await createUserAndSession(1, true);
const userModel = models().user({ userId: user.id });
await patchUser(session.id, { id: user.id, email: 'test2@example.com' });
let modUser: User = await userModel.load(user.id);
expect(modUser.email).toBe('test2@example.com');
const previousPassword = modUser.password;
await patchUser(session.id, { id: user.id, password: 'abcdefgh', password2: 'abcdefgh' });
modUser = await userModel.load(user.id);
expect(!!modUser.password).toBe(true);
expect(modUser.password === previousPassword).toBe(false);
});
test('should get a user', async function() {
const { user, session } = await createUserAndSession();
const userHtml = await getUserHtml(session.id, user.id);
const doc = parseHtml(userHtml);
// <input class="input" type="email" name="email" value="user1@localhost"/>
expect((doc.querySelector('input[name=email]') as any).value).toBe('user1@localhost');
});
});

View File

@ -6,10 +6,10 @@ import { User } from '../../db';
import { baseUrl } from '../../config';
function makeUser(isNew: boolean, fields: any): User {
const user: User = {
email: fields.email,
full_name: fields.full_name,
};
const user: User = {};
if ('email' in fields) user.email = fields.email;
if ('full_name' in fields) user.full_name = fields.full_name;
if (fields.password) {
if (fields.password !== fields.password2) throw new ErrorUnprocessableEntity('Passwords do not match');

View File

@ -8,7 +8,7 @@ import indexLogoutRoute from './index/logout';
import indexHomeRoute from './index/home';
import indexProfileRoute from './index/profile';
import indexUsersRoute from './index/users';
import indexUserRoute from './index/user';
// import indexUserRoute from './index/user';
import indexFilesRoute from './index/files';
import indexNotificationsRoute from './index/notifications';
import defaultRoute from './default';
@ -23,7 +23,7 @@ const routes: Routes = {
'home': indexHomeRoute,
'profile': indexProfileRoute,
'users': indexUsersRoute,
'user': indexUserRoute,
// 'user': indexUserRoute,
'files': indexFilesRoute,
'notifications': indexNotificationsRoute,

View File

@ -144,7 +144,10 @@ export function parseSubPath(p: string): SubPath {
return output;
}
export function routeResponseFormat(match: MatchedRoute, rawPath: string): RouteResponseFormat {
export function routeResponseFormat(match: MatchedRoute, context: AppContext): RouteResponseFormat {
// if (context.query && context.query.response_format === 'json') return RouteResponseFormat.Json;
const rawPath = context.path;
if (match && match.route.responseFormat) return match.route.responseFormat;
let path = rawPath;

View File

@ -14,7 +14,7 @@ import { PaginatedResults, Pagination, paginationToQueryParams } from '../../mod
import { AppContext } from '../types';
import { koaAppContext } from './testUtils';
function checkContextError(context: AppContext) {
export function checkContextError(context: AppContext) {
if (context.response.status >= 400) throw new Error(`Cannot create directory: ${JSON.stringify(context.response)}`);
}

View File

@ -13,6 +13,7 @@ import FakeResponse from './koa/FakeResponse';
import * as httpMocks from 'node-mocks-http';
import * as crypto from 'crypto';
import * as fs from 'fs-extra';
import * as jsdom from 'jsdom';
// Takes into account the fact that this file will be inside the /dist directory
// when it runs.
@ -157,6 +158,11 @@ export function controllers() {
return controllerFactory(models());
}
export function parseHtml(html: string): Document {
const dom = new jsdom.JSDOM(html);
return dom.window.document;
}
interface CreateUserAndSessionOptions {
email?: string;
password?: string;