Skip to content

Commit

Permalink
test(backend): add a case for not finding a preprint
Browse files Browse the repository at this point in the history
This change adds a test case for when a preprint cannot be found.

The test case is straightforward but requires changes to be able to support it. As there's a strong coupling between the code and the ORM (e.g. through `getFields`), we can't replace the repositories with in-memory versions. Worse, some of the code requires Postgres SQL, rather than the generic type we can use across different databases.

As a result, we have to try and use an actual Postgres instance. Doing so makes the tests slower and harder to develop. (Docker solves the problem for CI, but Jest tests should be isolated enough that they can be run locally, such as in the developer's IDE.)

To try and reduce the complexity, this uses pg-mem (https://github.com/oguimbal/pg-mem), an in-memory emulation of Postgres. I've not used this before, and the readme clearly labels it as experimental, but it's worked for what I've tried so far. It has a nice rollback feature, which allows resetting the database before each test. There's probably going to be pitfuls, but it seems to be worth continuing.

I had to change the codebase to allow passing in pg-mem's customised MikroORM. Unfortunately, there isn't a clean separation between the ORM and its database connection; I'd be much happier just passing in the latter to the app, but this will have to do for now.

Refs #388, #391, #419
  • Loading branch information
thewilkybarkid committed Oct 28, 2021
1 parent f814632 commit 2c6e835
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 22 deletions.
92 changes: 92 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
"nodemon": "^2.0.6",
"npm-watch": "^0.9.0",
"parcel": "2.0.0-beta.2",
"pg-mem": "^2.1.7",
"prettier": "^2.4.1",
"rimraf": "^3.0.2",
"supertest": "^6.1.6",
Expand Down
15 changes: 0 additions & 15 deletions src/backend/db.ts

This file was deleted.

5 changes: 4 additions & 1 deletion src/backend/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { MikroORM } from '@mikro-orm/core';
import { createServer } from 'http';
import config from './config';
import ormConfig from './mikro-orm.config';
import configServer from './server';

/**
Expand All @@ -12,7 +14,8 @@ async function bootstrap() {
* await sequelize.authenticate()
*/
config.parse(process.argv);
return createServer(await configServer(config)).listen(config.port);
const orm = await MikroORM.init(ormConfig);
return createServer(await configServer(orm, config)).listen(config.port);
}

bootstrap()
Expand Down
5 changes: 3 additions & 2 deletions src/backend/scripts/dbInit.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { dbWrapper } from '../db';
import { MikroORM } from '@mikro-orm/core';
import dbConfig from '../mikro-orm.config';
import { groupModelWrapper } from '../models';

async function main() {
try {
const [db] = await dbWrapper();
const db = await MikroORM.init(dbConfig);
const groups = groupModelWrapper(db);
const adminGroup = groups.create({ name: 'admins' });
console.log('Created admins group:', adminGroup);
Expand Down
6 changes: 3 additions & 3 deletions src/backend/server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Node modules
import { MikroORM, RequestContext } from '@mikro-orm/core';
import { RequestListener } from 'http';
import path from 'path';

Expand All @@ -25,7 +26,6 @@ import serialize from 'serialize-javascript';

// Our modules
import { createError } from './utils/http-errors';
import { dbWrapper } from './db';

// Our middlewares
import authWrapper from './middleware/auth.js'; // authorization/user roles
Expand Down Expand Up @@ -88,6 +88,7 @@ const startTime = Symbol.for('request-received.startTime');
type Config = Record<string, string>; // TODO add correct type

export default async function configServer(
db: MikroORM,
config: Config,
): Promise<RequestListener> {
// Initialize our application server
Expand All @@ -113,8 +114,7 @@ export default async function configServer(
server.use(xRequestId());

// Initialize database
const [db, dbMiddleware] = await dbWrapper();
server.use(dbMiddleware);
server.use((_, next) => RequestContext.createAsync(db.em, next));

// Setup auth handlers
const userModel = userModelWrapper(db);
Expand Down
19 changes: 19 additions & 0 deletions test/backend/preprints.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { StatusCodes } from 'http-status-codes';
import request from 'supertest';
import { createServer } from '../setup';

describe('preprints', () => {
it('returns a 404 when not found', async () => {
const response = await request(await createServer()).get(
'/api/v2/preprints/doi-10.5555-12345678',
);

expect(response.status).toBe(StatusCodes.NOT_FOUND);
expect(response.type).toBe('application/json');
expect(response.body).toStrictEqual({
message: 'That preprint with ID doi-10.5555-12345678 does not exist.',
status: 'HTTP 404 Error.',
statusCode: 404,
});
});
});
25 changes: 24 additions & 1 deletion test/setup.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
import { MikroORM } from '@mikro-orm/core';
import { PostgreSqlDriver } from '@mikro-orm/postgresql';
import { RequestListener } from 'http';
import { IBackup, newDb } from 'pg-mem';
import dbConfig from '../src/backend/mikro-orm.config';
import configServer from '../src/backend/server';

let orm: MikroORM<PostgreSqlDriver>;
let backup: IBackup;

global.beforeAll(async () => {
const db = newDb();
orm = await db.adapters.createMikroOrm(dbConfig);
const generator = orm.getSchemaGenerator();

await generator.createSchema();

backup = db.backup();
});

global.afterAll(async () => {
await orm.close(true);
});

export async function createServer(config = {}): Promise<RequestListener> {
return await configServer({
backup.restore();

return await configServer(orm, {
logLevel: 'off',
orcidCallbackUrl: 'http://localhost/',
orcidClientId: 'orcid-client-id',
Expand Down

0 comments on commit 2c6e835

Please sign in to comment.