dev(api): add build options to test env (#59957)

This commit is contained in:
Shaun Hamilton
2025-04-24 19:03:49 +02:00
committed by GitHub
parent f9edfe3bbd
commit 46b910ee40
4 changed files with 77 additions and 75 deletions

View File

@@ -1,6 +1,6 @@
import request from 'supertest';
import { build } from './src/app';
import { build, buildOptions } from './src/app';
import { createUserInput } from './src/utils/create-user';
import { examJson } from './__mocks__/exam';
import { CSRF_COOKIE, CSRF_HEADER } from './src/plugins/csrf';
@@ -158,7 +158,11 @@ const indexData: IndexData[] = [
export function setupServer(): void {
let fastify: FastifyTestInstance;
beforeAll(async () => {
fastify = await build();
if (process.env.FCC_ENABLE_TEST_LOGGING !== 'true') {
// @ts-expect-error Disable logging by unsetting logger
buildOptions.logger = undefined;
}
fastify = await build(buildOptions);
await fastify.ready();
// Prisma does not support TTL indexes in the schema yet, so, to avoid

View File

@@ -70,6 +70,7 @@
"develop": "tsx watch --clear-screen=false src/server.ts",
"start": "FREECODECAMP_NODE_ENV=production node dist/server.js",
"test": "jest --force-exit",
"test-with-logging": "FCC_ENABLE_TEST_LOGGING=true pnpm run test",
"prisma": "dotenv -e ../.env prisma",
"postinstall": "prisma generate",
"generate-exams": "tsx tools/exam-environment/generate/index.ts",

View File

@@ -1,3 +1,5 @@
import { randomBytes } from 'crypto';
import { isEmpty } from 'lodash';
import fastifyAccepts from '@fastify/accepts';
import fastifySwagger from '@fastify/swagger';
import fastifySwaggerUI from '@fastify/swagger-ui';
@@ -9,6 +11,7 @@ import Fastify, {
FastifyBaseLogger,
FastifyHttpOptions,
FastifyInstance,
FastifyRequest,
RawReplyDefaultExpression,
RawRequestDefaultExpression,
RawServerDefault
@@ -42,7 +45,9 @@ import {
FCC_ENABLE_EXAM_ENVIRONMENT,
FCC_ENABLE_SENTRY_ROUTES,
GROWTHBOOK_FASTIFY_API_HOST,
GROWTHBOOK_FASTIFY_CLIENT_KEY
GROWTHBOOK_FASTIFY_CLIENT_KEY,
FREECODECAMP_NODE_ENV,
FCC_API_LOG_LEVEL
} from './utils/env';
import { isObjectID } from './utils/validation';
import {
@@ -78,6 +83,67 @@ ajv.addFormat('objectid', {
validate: (str: string) => isObjectID(str)
});
const requestSerializer = (req: FastifyRequest) => {
const method = req.method || 'METHOD not found';
const url = req.url || 'URL not found';
const headers = req.headers || 'HEADERS not found';
const xForwardedFor = Array.isArray(req.headers['x-forwarded-for'])
? req.headers['x-forwarded-for'][0]
: req.headers['x-forwarded-for'];
const ip =
xForwardedFor || req.headers['x-real-ip'] || req.ip || 'IP not found';
const query = isEmpty(req.query) ? 'QUERY not found' : req.query;
const hostname = req.hostname || 'HOSTNAME not found';
const remotePort = req.socket.remotePort || 'REMOTE_PORT not found';
return {
REQ_ID: req.id,
METHOD: method,
URL: url,
IP: ip,
HOSTNAME: hostname,
REMOTE_PORT: remotePort,
QUERY: query,
HEADERS: headers
};
};
const envToLogger = {
development: {
transport: {
target: 'pino-pretty',
options: {
singleLine: true,
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname'
}
},
level: FCC_API_LOG_LEVEL || 'info',
serializers: {
req: (req: FastifyRequest) => {
return {
method: req.method,
url: req.url
};
}
}
// No need to redact in development
},
production: {
level: FCC_API_LOG_LEVEL || 'info',
serializers: {
req: requestSerializer
},
redact: ['req.HEADERS.cookie']
}
};
export const buildOptions = {
logger: envToLogger[FREECODECAMP_NODE_ENV] ?? true,
genReqId: () => randomBytes(8).toString('hex'),
disableRequestLogging: true
};
/**
* Top-level wrapper to instantiate the API server. This is where all middleware and
* routes should be mounted.

View File

@@ -1,79 +1,10 @@
import './instrument';
import { randomBytes } from 'crypto';
import { FastifyRequest } from 'fastify';
import { isEmpty } from 'lodash';
import { build } from './app';
import {
FREECODECAMP_NODE_ENV,
FCC_API_LOG_LEVEL,
HOST,
PORT
} from './utils/env';
const requestSerializer = (req: FastifyRequest) => {
const method = req.method || 'METHOD not found';
const url = req.url || 'URL not found';
const headers = req.headers || 'HEADERS not found';
const xForwardedFor = Array.isArray(req.headers['x-forwarded-for'])
? req.headers['x-forwarded-for'][0]
: req.headers['x-forwarded-for'];
const ip =
xForwardedFor || req.headers['x-real-ip'] || req.ip || 'IP not found';
const query = isEmpty(req.query) ? 'QUERY not found' : req.query;
const hostname = req.hostname || 'HOSTNAME not found';
const remotePort = req.socket.remotePort || 'REMOTE_PORT not found';
return {
REQ_ID: req.id,
METHOD: method,
URL: url,
IP: ip,
HOSTNAME: hostname,
REMOTE_PORT: remotePort,
QUERY: query,
HEADERS: headers
};
};
const envToLogger = {
development: {
transport: {
target: 'pino-pretty',
options: {
singleLine: true,
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname'
}
},
level: FCC_API_LOG_LEVEL || 'info',
serializers: {
req: (req: FastifyRequest) => {
return {
method: req.method,
url: req.url
};
}
}
// No need to redact in development
},
production: {
level: FCC_API_LOG_LEVEL || 'info',
serializers: {
req: requestSerializer
},
redact: ['req.HEADERS.cookie']
}
};
import { build, buildOptions } from './app';
import { HOST, PORT } from './utils/env';
const start = async () => {
const fastify = await build({
logger: envToLogger[FREECODECAMP_NODE_ENV] ?? true,
genReqId: () => randomBytes(8).toString('hex'),
disableRequestLogging: true
});
const fastify = await build(buildOptions);
try {
const port = Number(PORT);
fastify.log.info(`Starting server on port ${port}`);