2024-02-19 05:24:14 +00:00
|
|
|
import { execSync } from 'child_process';
|
|
|
|
|
2023-04-26 07:02:12 +00:00
|
|
|
import request from 'supertest';
|
|
|
|
|
|
|
|
import { build } from './src/app';
|
2023-10-07 10:50:30 +00:00
|
|
|
import { createUserInput } from './src/utils/create-user';
|
2023-10-26 08:05:09 +00:00
|
|
|
import { examJson } from './__mocks__/exam';
|
2024-02-19 05:24:14 +00:00
|
|
|
import { MONGOHQ_URL } from './src/utils/env';
|
|
|
|
|
|
|
|
jest.mock('./src/utils/env', () => {
|
|
|
|
const createTestConnectionURL = (url: string, dbId: string) =>
|
|
|
|
url.replace(/(.*)(\?.*)/, `$1${dbId}$2`);
|
|
|
|
// There are other properties, and this type is too narrow, but we're only
|
|
|
|
// interested in MONGOHQ_URL here.
|
|
|
|
const actual: {
|
|
|
|
MONGOHQ_URL: string;
|
|
|
|
} = jest.requireActual('./src/utils/env');
|
|
|
|
return {
|
|
|
|
...actual,
|
|
|
|
MONGOHQ_URL: createTestConnectionURL(
|
|
|
|
actual.MONGOHQ_URL,
|
|
|
|
process.env.JEST_WORKER_ID!
|
|
|
|
)
|
|
|
|
};
|
|
|
|
});
|
2023-04-26 07:02:12 +00:00
|
|
|
|
2023-05-18 11:36:40 +00:00
|
|
|
type FastifyTestInstance = Awaited<ReturnType<typeof build>>;
|
|
|
|
|
2023-04-26 07:02:12 +00:00
|
|
|
declare global {
|
|
|
|
// eslint-disable-next-line no-var
|
2023-05-18 11:36:40 +00:00
|
|
|
var fastifyTestInstance: FastifyTestInstance;
|
2023-04-26 07:02:12 +00:00
|
|
|
}
|
|
|
|
|
2023-05-09 05:45:54 +00:00
|
|
|
type Options = {
|
|
|
|
sendCSRFToken: boolean;
|
|
|
|
};
|
|
|
|
|
|
|
|
const requests = {
|
|
|
|
GET: (resource: string) => request(fastifyTestInstance?.server).get(resource),
|
|
|
|
POST: (resource: string) =>
|
|
|
|
request(fastifyTestInstance?.server).post(resource),
|
2023-05-18 14:01:21 +00:00
|
|
|
PUT: (resource: string) => request(fastifyTestInstance?.server).put(resource),
|
|
|
|
DELETE: (resource: string) =>
|
|
|
|
request(fastifyTestInstance?.server).delete(resource)
|
2023-05-09 05:45:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export const getCsrfToken = (setCookies: string[]): string | undefined => {
|
|
|
|
const csrfSetCookie = setCookies.find(str => str.includes('csrf_token'));
|
|
|
|
const [csrfCookie] = csrfSetCookie?.split(';') ?? [];
|
|
|
|
const [_key, csrfToken] = csrfCookie?.split('=') ?? [];
|
|
|
|
|
|
|
|
return csrfToken;
|
|
|
|
};
|
|
|
|
|
2023-06-22 15:25:40 +00:00
|
|
|
export const ORIGIN = 'https://www.freecodecamp.org';
|
|
|
|
|
2023-05-09 05:45:54 +00:00
|
|
|
export function superRequest(
|
|
|
|
resource: string,
|
|
|
|
config: {
|
2023-05-18 14:01:21 +00:00
|
|
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
2023-05-09 05:45:54 +00:00
|
|
|
setCookies?: string[];
|
|
|
|
},
|
|
|
|
options?: Options
|
|
|
|
): request.Test {
|
|
|
|
const { method, setCookies } = config;
|
|
|
|
const { sendCSRFToken = true } = options ?? {};
|
|
|
|
|
2023-06-22 15:25:40 +00:00
|
|
|
const req = requests[method](resource).set('Origin', ORIGIN);
|
2023-05-09 05:45:54 +00:00
|
|
|
|
|
|
|
if (setCookies) {
|
|
|
|
void req.set('Cookie', setCookies);
|
|
|
|
}
|
|
|
|
|
|
|
|
const csrfToken = (setCookies && getCsrfToken(setCookies)) ?? '';
|
|
|
|
if (sendCSRFToken) {
|
|
|
|
void req.set('CSRF-Token', csrfToken);
|
|
|
|
}
|
|
|
|
return req;
|
2023-04-26 07:02:12 +00:00
|
|
|
}
|
|
|
|
|
2023-11-16 08:08:32 +00:00
|
|
|
export function createSuperRequest(config: {
|
|
|
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
|
|
setCookies?: string[];
|
|
|
|
}): (resource: string, options?: Options) => request.Test {
|
|
|
|
return (resource, options) => superRequest(resource, config, options);
|
|
|
|
}
|
|
|
|
|
2023-04-26 07:02:12 +00:00
|
|
|
export function setupServer(): void {
|
2023-05-18 11:36:40 +00:00
|
|
|
let fastify: FastifyTestInstance;
|
2023-04-26 07:02:12 +00:00
|
|
|
beforeAll(async () => {
|
|
|
|
fastify = await build();
|
|
|
|
await fastify.ready();
|
|
|
|
|
2024-02-19 05:24:14 +00:00
|
|
|
// Prisma does not support TTL indexes in the schema yet, so, to avoid
|
|
|
|
// conflicts with the TTL index in the sessions collection, we need to
|
|
|
|
// create it manually (before interacting with the db in any way)
|
|
|
|
await fastify.prisma.$runCommandRaw({
|
|
|
|
createIndexes: 'sessions',
|
|
|
|
indexes: [
|
|
|
|
{
|
|
|
|
key: { expires: 1 },
|
|
|
|
name: 'expires_1',
|
|
|
|
background: true,
|
|
|
|
expireAfterSeconds: 0
|
|
|
|
}
|
|
|
|
]
|
|
|
|
});
|
|
|
|
// push the schema to the test db to setup indexes, unique constraints, etc
|
|
|
|
execSync('pnpm prisma db push -- --skip-generate', {
|
|
|
|
env: {
|
|
|
|
...process.env,
|
|
|
|
MONGOHQ_URL
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-04-26 07:02:12 +00:00
|
|
|
global.fastifyTestInstance = fastify;
|
2023-10-03 22:00:21 +00:00
|
|
|
// allow a little time to setup the db
|
|
|
|
}, 10000);
|
2023-04-26 07:02:12 +00:00
|
|
|
|
|
|
|
afterAll(async () => {
|
2024-02-19 05:24:14 +00:00
|
|
|
await fastifyTestInstance.prisma.$runCommandRaw({ dropDatabase: 1 });
|
|
|
|
|
2023-04-26 07:02:12 +00:00
|
|
|
// Due to a prisma bug, this is not enough, we need to --force-exit jest:
|
|
|
|
// https://github.com/prisma/prisma/issues/18146
|
2023-05-18 11:36:40 +00:00
|
|
|
await fastifyTestInstance.close();
|
2023-04-26 07:02:12 +00:00
|
|
|
});
|
|
|
|
}
|
2023-07-31 14:08:05 +00:00
|
|
|
|
|
|
|
export const defaultUserId = '64c7810107dd4782d32baee7';
|
|
|
|
export const defaultUserEmail = 'foo@bar.com';
|
|
|
|
|
|
|
|
export async function devLogin(): Promise<string[]> {
|
|
|
|
await fastifyTestInstance.prisma.user.deleteMany({
|
|
|
|
where: { email: 'foo@bar.com' }
|
|
|
|
});
|
|
|
|
|
|
|
|
await fastifyTestInstance.prisma.user.create({
|
|
|
|
data: {
|
2023-10-07 10:50:30 +00:00
|
|
|
...createUserInput(defaultUserEmail),
|
|
|
|
id: defaultUserId
|
2023-07-31 14:08:05 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
const res = await superRequest('/auth/dev-callback', { method: 'GET' });
|
|
|
|
expect(res.status).toBe(200);
|
|
|
|
return res.get('Set-Cookie');
|
|
|
|
}
|
2023-10-26 08:05:09 +00:00
|
|
|
|
|
|
|
export async function seedExam(): Promise<void> {
|
|
|
|
const query = { where: { id: examJson.id } };
|
|
|
|
const testExamExists =
|
|
|
|
await fastifyTestInstance.prisma.exam.findUnique(query);
|
|
|
|
|
|
|
|
if (testExamExists) {
|
|
|
|
await fastifyTestInstance.prisma.exam.deleteMany(query);
|
|
|
|
}
|
|
|
|
|
|
|
|
await fastifyTestInstance.prisma.exam.create({
|
|
|
|
data: {
|
|
|
|
...examJson
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2023-11-03 09:00:55 +00:00
|
|
|
|
|
|
|
export function createFetchMock({ ok = true, body = {} } = {}) {
|
|
|
|
return jest.fn().mockResolvedValue(
|
|
|
|
Promise.resolve({
|
|
|
|
ok,
|
|
|
|
json: () => Promise.resolve(body)
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|