Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"ask": [],
"allow": [
"Bash(git add:*)",
"Bash(node dist/server.js)",
"Bash(node dist/adapters/express/index.js)",
"Bash(yarn install:*)",
"Bash(yarn dev)",
"Bash(yarn build)",
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
run: yarn build
- name: Start server in background
run: |
node dist/server.js &
node dist/adapters/express/index.js &
echo $! > server.pid
- name: Wait for server to be ready
run: |
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
**Development**:
- `yarn dev` - Start development server with hot reload using nodemon
- `yarn build` - Compile TypeScript to JavaScript in `dist/`
- `node dist/server.js` - Run the compiled application
- `node dist/adapters/express/index.js` - Run the compiled application

**Code Quality**:
- `yarn lint` - Check code with ESLint
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ COPY --from=build /app/dist ./dist
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production && yarn cache clean

CMD ["node", "dist/server.js"]
CMD ["node", "dist/adapters/express/index.js"]
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Built with **Node.js** and **Express**.
| `ALL` | `/base64/decode` | Decodes a Base64 string to its original format. |
| `ALL` | `/basic-auth` | Tests HTTP Basic Authentication by comparing credentials from Authorization header against query parameters. |
| `ALL` | `/error/timeout` | Simulates a timeout by never sending a response. |
| `ALL` | `/error/network` | Simulates a network error by closing the connection. |
| `ALL` | `/error/network` | Simulates a network error by closing the connection. Returns `400` in Lambda (socket destroy is unsupported). |
| `ALL` | `/error/malformed-json` | Returns malformed JSON response. |
| `ALL` | `/error/error` | Throws an unhandled exception to trigger Express error handler. |
| `ALL` | `/mirror` | Returns the request body as a response. |
Expand Down Expand Up @@ -97,7 +97,7 @@ Built with **Node.js** and **Express**.
4. Run the application:

```bash
node dist/server.js
node dist/adapters/express/index.js
```

---
Expand Down
19 changes: 12 additions & 7 deletions docs/DEVELOPMENT_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ This guide covers development-specific details for contributing to the HTTP Play

```
├── src
│ ├── app.ts # Main application setup
│ ├── env.ts # Environment variable handler
│ ├── logger.ts # Application logging configuration
│ ├── middlewares # Middlewares directory
│ ├── routes # API routes directory
│ ├── server.ts # Server startup and shutdown handling
│ ├── adapters # Runtime-specific entry points
│ │ ├── express # Express/Docker adapter
│ │ │ ├── app.ts # Express application setup
│ │ │ └── index.ts # Server startup and shutdown handling
│ │ └── lambda # AWS Lambda adapter
│ │ └── index.ts # Lambda handler (API Gateway HTTP API v2)
│ ├── core # Shared business logic
│ │ ├── env.ts # Environment variable handler
│ │ ├── logger.ts # Application logging configuration
│ │ ├── middlewares # Middlewares directory
│ │ └── routes # API routes directory
Comment on lines +9 to +19

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Keep the documented source layout in sync with the repo conventions.

This tree now tells contributors to put app/server code under src/adapters/express/ and shared modules under src/core/, but the current project conventions still point to src/app.ts, src/server.ts, src/env.ts, src/logger.ts, and src/routes/. Please resolve that mismatch in this PR so the repo has a single authoritative layout.

Based on learnings: "Applies to src/app.ts : Main Express application setup with middleware chain and route registration must be in src/app.ts", "Applies to src/routes/**/*.{ts,tsx} : All routes must be located in src/routes/ and export an Express Router", "Applies to src/logger.ts : Logger setup with development/production configurations must be in src/logger.ts", "Applies to src/server.ts : HTTP server startup with graceful shutdown handling must be in src/server.ts", and "Applies to src/env.ts : Configure environment variables in src/env.ts including ENABLE_SHUTDOWN, LOG_LEVEL, MAX_DELAY, NODE_ENV, ORIGIN, PORT, KEEP_ALIVE_TIMEOUT, HEADERS_TIMEOUT, and REQUEST_TIMEOUT".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/DEVELOPMENT_GUIDE.md` around lines 9 - 19, The docs tree in
DEVELOPMENT_GUIDE.md is inconsistent with the repository convention; update the
PR so the authoritative layout matches the existing codebase by reverting or
editing the docs to document the canonical files: ensure application setup lives
in src/app.ts (Express app with middleware and route registration), routes live
under src/routes/* exporting Express Router, logger configuration is described
in src/logger.ts (dev/prod configs), server startup and graceful shutdown are
documented for src/server.ts, and environment variables (ENABLE_SHUTDOWN,
LOG_LEVEL, MAX_DELAY, NODE_ENV, ORIGIN, PORT, KEEP_ALIVE_TIMEOUT,
HEADERS_TIMEOUT, REQUEST_TIMEOUT) are described in src/env.ts; reference these
filenames (src/app.ts, src/server.ts, src/env.ts, src/logger.ts, src/routes)
when making the doc changes so the guide and repo conventions are in sync.

│ └── utils # Utility functions directory
├── tests
│ ├── api # API tests directory
Expand Down Expand Up @@ -59,7 +64,7 @@ Running API Tests Locally:
1. Build and start the server:
```bash
yarn build
node dist/server.js
node dist/adapters/express/index.js
```

2. In another terminal, run the API tests:
Expand Down
2 changes: 1 addition & 1 deletion nodemon.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"watch": ["src"],
"ext": "ts",
"exec": "tsx ./src/server.ts",
"exec": "tsx ./src/adapters/express/index.ts",
"env": {
"LOG_LEVEL": "debug",
"KEEP_ALIVE_TIMEOUT": 5000,
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
"name": "http-playground-server",
"version": "0.0.1",
"type": "module",
"main": "dist/server.js",
"main": "dist/adapters/express/index.js",
"repository": "https://github.com/ryo8000/http-playground-server.git",
"author": "Ryo Hasegawa",
"license": "MIT",
"scripts": {
"build": "tsc",
"build:lambda:function": "yarn build && cp package.json dist/ && zip -r lambda-function.zip dist/",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The build:lambda:function script can be improved for efficiency and correctness.

  1. It includes the dist directory itself in the zip archive, which means your Lambda handler path will need to be dist/adapters/lambda/index.handler. It's generally better to zip the contents of the dist directory.
  2. It packages the Express adapter, which is not needed for the Lambda function, increasing the package size.

A simple improvement for the zip structure is to cd into the directory first.

Suggested change
"build:lambda:function": "yarn build && cp package.json dist/ && zip -r lambda-function.zip dist/",
"build:lambda:function": "yarn build && cp package.json dist/ && (cd dist && zip -r ../lambda-function.zip .)",

"build:lambda:layer": "mkdir -p lambda-layer/nodejs && cp -r node_modules lambda-layer/nodejs && cd lambda-layer && zip -r ../lambda-layer.zip . && cd .. && rm -rf lambda-layer",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The build:lambda:layer script copies the entire node_modules directory, which may include development dependencies, making the Lambda layer larger than necessary. Consider installing only production dependencies for the layer. Also, using cd ... && cd .. can be risky if a command fails; using a subshell (...) is safer.

Suggested change
"build:lambda:layer": "mkdir -p lambda-layer/nodejs && cp -r node_modules lambda-layer/nodejs && cd lambda-layer && zip -r ../lambda-layer.zip . && cd .. && rm -rf lambda-layer",
"build:lambda:layer": "mkdir -p lambda-layer/nodejs && cp -r node_modules lambda-layer/nodejs && (cd lambda-layer && zip -r ../lambda-layer.zip .) && rm -rf lambda-layer",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Package only runtime dependencies in the Lambda layer.

Copying the repo’s whole node_modules directory will pull Jest, ESLint, TypeScript, Newman, and the rest of the dev toolchain into the layer. That makes the artifact much larger than necessary and can turn deployment into a size problem. Build the layer from production dependencies only.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 12, The current "build:lambda:layer" npm script copies
the entire node_modules (including devDependencies) into the Lambda layer;
change it to install only production dependencies into the layer directory
instead. Update the script so it creates lambda-layer/nodejs and runs a
production install into that folder (for example using npm ci --only=production
--prefix lambda-layer/nodejs or npm install --production --prefix
lambda-layer/nodejs), then zip and clean up as before; ensure you remove the
plain cp -r node_modules step in the "build:lambda:layer" script so
devDependencies like Jest/ESLint/TypeScript are not included.

"dev": "nodemon",
"test": "jest --coverage",
"test:api": "newman run tests/api/api-test-collection.json -e tests/api/environment.json --reporters cli",
Expand All @@ -24,6 +26,7 @@
},
"devDependencies": {
"@eslint/js": "10.0.1",
"@types/aws-lambda": "8.10.149",
"@types/cookie-parser": "1.4.10",
"@types/cors": "2.8.19",
"@types/express": "5.0.6",
Expand Down
30 changes: 15 additions & 15 deletions src/app.ts → src/adapters/express/app.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import express from 'express';
import cookieParser from 'cookie-parser';
import cors from 'cors';
import { delayMiddleware } from './middlewares/delay.js';
import { loggerMiddleware } from './middlewares/logger.js';
import { base64Router } from './routes/base64.js';
import { basicAuthRouter } from './routes/basic-auth.js';
import { errorRouter } from './routes/error.js';
import { indexRouter } from './routes/index.js';
import { mirrorRouter } from './routes/mirror.js';
import { redirectRouter } from './routes/redirect.js';
import { requestRouter } from './routes/request.js';
import { shutdownRouter } from './routes/shutdown.js';
import { statusRouter } from './routes/status.js';
import { uuidRouter } from './routes/uuid.js';
import { HttpStatusCodes } from './utils/http.js';
import { environment } from './env.js';
import { log } from './logger.js';
import { delayMiddleware } from '../../core/middlewares/delay.js';
import { loggerMiddleware } from '../../core/middlewares/logger.js';
import { base64Router } from '../../core/routes/base64.js';
import { basicAuthRouter } from '../../core/routes/basic-auth.js';
import { errorRouter } from '../../core/routes/error.js';
import { indexRouter } from '../../core/routes/index.js';
import { mirrorRouter } from '../../core/routes/mirror.js';
import { redirectRouter } from '../../core/routes/redirect.js';
import { requestRouter } from '../../core/routes/request.js';
import { shutdownRouter } from '../../core/routes/shutdown.js';
import { statusRouter } from '../../core/routes/status.js';
import { uuidRouter } from '../../core/routes/uuid.js';
import { HttpStatusCodes } from '../../utils/http.js';
import { environment } from '../../core/env.js';
import { log } from '../../core/logger.js';

const app = express();

Expand Down
4 changes: 2 additions & 2 deletions src/server.ts → src/adapters/express/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { app } from './app.js';
import { environment } from './env.js';
import { log } from './logger.js';
import { environment } from '../../core/env.js';
import { log } from '../../core/logger.js';

const server = app.listen(environment.port, () => {
log.info(`Server is running on http://localhost:${environment.port}`);
Expand Down
137 changes: 137 additions & 0 deletions src/adapters/lambda/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import http from 'node:http';
import { createServer } from 'node:http';
import type {
APIGatewayProxyEventV2,
APIGatewayProxyStructuredResultV2,
Handler,
} from 'aws-lambda';
import { app } from '../express/app.js';

/** Hop-by-hop headers that must not be forwarded to the loopback request. */
const HOP_BY_HOP_HEADERS = new Set([
'connection',
'keep-alive',
'transfer-encoding',
'te',
'trailer',
'upgrade',
'proxy-authorization',
'proxy-authenticate',
]);

/**
* Starts the Express app on a random loopback port and resolves with the port number.
* Called once on cold start; subsequent invocations reuse the cached promise.
*/
const listeningPromise: Promise<number> = new Promise((resolve, reject) => {
const server = createServer(app);
server.listen(0, '127.0.0.1', () => {
const address = server.address();
if (address === null || typeof address === 'string') {
reject(new Error('Unexpected server address format'));
return;
}
resolve(address.port);
});
server.on('error', reject);
});
Comment on lines +26 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing RUNTIME environment variable initialization.

The /error/network route in src/core/routes/error.ts (lines 11-18) checks process.env['RUNTIME'] === 'lambda' to safely return a 400 response instead of calling req.socket.destroy(). However, this Lambda adapter never sets RUNTIME='lambda', so the socket destruction path will execute in Lambda invocations, which won't behave correctly since the socket lifecycle is managed by API Gateway.

🐛 Proposed fix to set RUNTIME before starting the server
+// Set runtime environment for Lambda-specific behavior in routes
+process.env['RUNTIME'] = 'lambda';
+
 /**
  * Starts the Express app on a random loopback port and resolves with the port number.
  * Called once on cold start; subsequent invocations reuse the cached promise.
  */
 const listeningPromise: Promise<number> = new Promise((resolve, reject) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/adapters/lambda/index.ts` around lines 26 - 37, The Lambda adapter never
sets the RUNTIME env var so requests hit the socket-destroy path; before
creating or starting the HTTP server (i.e., before the listeningPromise /
createServer(app) / server.listen call) set process.env['RUNTIME'] = 'lambda' so
the error route in src/core/routes/error.ts sees RUNTIME==='lambda' and returns
the safe 400 response instead of attempting req.socket.destroy(); ensure this
assignment runs synchronously prior to any server creation/listen logic.


/**
* Converts an API Gateway HTTP API v2 event into a loopback HTTP request
* against the locally running Express server, and maps the response back
* to an API Gateway proxy result.
*
* @param event - The API Gateway v2 proxy event.
* @returns A structured API Gateway proxy result.
*/
export const handler: Handler<APIGatewayProxyEventV2, APIGatewayProxyStructuredResultV2> = async (
event,
) => {
const port = await listeningPromise;

const method = event.requestContext.http.method;
const path = event.rawPath + (event.rawQueryString ? `?${event.rawQueryString}` : '');

const bodyBuffer: Buffer = event.body
? Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf-8')
: Buffer.alloc(0);

// Build forwarded headers, skipping hop-by-hop headers and recalculating content-length.
const forwardHeaders: Record<string, string> = {};
for (const [key, value] of Object.entries(event.headers ?? {})) {
const lower = key.toLowerCase();
if (!HOP_BY_HOP_HEADERS.has(lower) && value !== undefined) {
forwardHeaders[lower] = value;
}
}
if (bodyBuffer.length > 0) {
forwardHeaders['content-length'] = String(bodyBuffer.length);
}

try {
const { statusCode, headers, rawBody } = await new Promise<{
statusCode: number;
headers: Record<string, string | string[]>;
rawBody: Buffer;
}>((resolve, reject) => {
const req = http.request(
{
hostname: '127.0.0.1',
port,
method,
path,
headers: forwardHeaders,
},
(res) => {
const chunks: Buffer[] = [];
res.on('data', (chunk: Buffer) => chunks.push(chunk));
res.on('end', () => {
const responseHeaders: Record<string, string | string[]> = {};
for (const [key, value] of Object.entries(res.headers)) {
if (value !== undefined) {
responseHeaders[key] = value;
}
}
resolve({
statusCode: res.statusCode ?? 200,
headers: responseHeaders,
rawBody: Buffer.concat(chunks),
});
});
res.on('error', reject);
},
);
req.on('error', reject);
if (bodyBuffer.length > 0) {
req.write(bodyBuffer);
}
req.end();
});

// Extract Set-Cookie headers into the cookies array (API Gateway v2 requirement).
const cookies: string[] = [];
const responseHeaders: Record<string, string> = {};

for (const [key, value] of Object.entries(headers)) {
if (key.toLowerCase() === 'set-cookie') {
const cookieValues = Array.isArray(value) ? value : [value];
cookies.push(...cookieValues);
} else {
responseHeaders[key] = Array.isArray(value) ? value.join(', ') : value;
}
}

return {
statusCode,
headers: responseHeaders,
body: rawBody.toString('utf-8'),
...(cookies.length > 0 && { cookies }),
};
Comment on lines +124 to +129

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The response body is always converted to a utf-8 string. This will corrupt binary responses. The handler should check the Content-Type of the response and if it's binary, base64 encode the body and set isBase64Encoded: true in the response object. While the current application may not serve binary files, this would make the adapter more robust.

    const contentType = responseHeaders['content-type'] || '';
    const isText = /^(text\/|application\/json)/.test(contentType);
    return {
      statusCode,
      headers: responseHeaders,
      body: rawBody.toString(isText ? 'utf8' : 'base64'),
      isBase64Encoded: !isText,
      ...(cookies.length > 0 && { cookies }),
    };

} catch {
return {
statusCode: 500,
body: JSON.stringify({ error: { message: 'Internal server error' } }),
headers: { 'content-type': 'application/json' },
};
}
Comment on lines +130 to +136

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This catch block swallows the error object, which will make debugging very difficult in case of a failure. You should capture the error and log it for better observability.

  } catch (error) {
    console.error('Error in Lambda handler:', error);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: { message: 'Internal server error' } }),
      headers: { 'content-type': 'application/json' },
    };
  }

};
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/middlewares/delay.ts → src/core/middlewares/delay.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Request, Response, NextFunction } from 'express';
import { toSafeInteger } from '../utils/number.js';
import { toSafeInteger } from '../../utils/number.js';
import { environment } from '../env.js';

/**
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion src/routes/base64.ts → src/core/routes/base64.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Router } from 'express';
import { HttpStatusCodes } from '../utils/http.js';
import { HttpStatusCodes } from '../../utils/http.js';
import { log } from '../logger.js';

const base64Router = Router();
Expand Down
2 changes: 1 addition & 1 deletion src/routes/basic-auth.ts → src/core/routes/basic-auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Router, Response } from 'express';
import { HttpStatusCodes } from '../utils/http.js';
import { HttpStatusCodes } from '../../utils/http.js';

const basicAuthRouter = Router();

Expand Down
10 changes: 9 additions & 1 deletion src/routes/error.ts → src/core/routes/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ errorRouter.all('/timeout', () => {
return;
});

errorRouter.all('/network', (req) => {
errorRouter.all('/network', (req, res) => {
if (process.env['RUNTIME'] === 'lambda') {
res
.status(400)
.json({
error: { message: 'Network error simulation is not supported in this environment.' },
});
return;
Comment on lines +11 to +17

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The associated test for this logic in tests/ut/routes/error.test.ts expects a 500 status code, but the implementation here returns 400. The README.md also states it returns 400. A 400 Bad Request is more appropriate since the client is requesting an action not supported in the current environment. Please update the test to expect a 400 status code to align with the implementation and documentation.

}
// Intentionally destroy the connection
req.socket.destroy();
return;
Expand Down
2 changes: 1 addition & 1 deletion src/routes/index.ts → src/core/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Router } from 'express';
import { HttpStatusCodes } from '../utils/http.js';
import { HttpStatusCodes } from '../../utils/http.js';

const indexRouter = Router();

Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions src/routes/redirect.ts → src/core/routes/redirect.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Router } from 'express';
import { HttpStatusCodes, RedirectStatuses } from '../utils/http.js';
import { toSafeInteger } from '../utils/number.js';
import { HttpStatusCodes, RedirectStatuses } from '../../utils/http.js';
import { toSafeInteger } from '../../utils/number.js';

const redirectRouter = Router();

Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion src/routes/shutdown.ts → src/core/routes/shutdown.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Router } from 'express';
import { environment } from '../env.js';
import { HttpStatusCodes } from '../utils/http.js';
import { HttpStatusCodes } from '../../utils/http.js';

const shutdownRouter = Router();

Expand Down
4 changes: 2 additions & 2 deletions src/routes/status.ts → src/core/routes/status.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Router } from 'express';
import { HttpStatusCodes } from '../utils/http.js';
import { toSafeInteger } from '../utils/number.js';
import { HttpStatusCodes } from '../../utils/http.js';
import { toSafeInteger } from '../../utils/number.js';

const MIN_VALID_STATUS_CODE = 200;
const MAX_VALID_STATUS_CODE = 599;
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion tests/ut/app.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import request from 'supertest';
import { app } from '../../src/app.js';
import { app } from '../../src/adapters/express/app.js';

describe('App', () => {
describe('GET /', () => {
Expand Down
2 changes: 1 addition & 1 deletion tests/ut/env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('Environment configuration', () => {
});

const loadEnv = async () => {
const envModule = await import('../../src/env.js');
const envModule = await import('../../src/core/env.js');
return envModule.environment;
};

Expand Down
4 changes: 2 additions & 2 deletions tests/ut/middlewares/delay.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from 'express';
import { delayMiddleware } from '../../../src/middlewares/delay.js';
import { environment } from '../../../src/env.js';
import { delayMiddleware } from '../../../src/core/middlewares/delay.js';
import { environment } from '../../../src/core/env.js';

describe('delayMiddleware', () => {
let mockRequest: Partial<Request>;
Expand Down
Loading
Loading