(frontend) switch to vitest and enhance testability

Migrated from jest to vitest for server/y-provider, gaining faster runs,
esm-native support and cleaner mocking.

Signed-off-by: Stephan Meijer <me@stephanmeijer.com>
This commit is contained in:
Stephan Meijer
2025-07-02 16:39:46 +02:00
parent 58bf5071c2
commit f3c9c41b86
18 changed files with 684 additions and 333 deletions

View File

@@ -1 +0,0 @@
module.exports = {};

View File

@@ -1,66 +1,64 @@
import request from 'supertest';
import { describe, expect, test, vi } from 'vitest';
const port = 5555;
const origin = 'http://localhost:3000';
jest.mock('../src/env', () => {
vi.mock('../src/env', async (importOriginal) => {
return {
PORT: port,
COLLABORATION_SERVER_ORIGIN: origin,
...(await importOriginal()),
PORT: 5555,
COLLABORATION_SERVER_ORIGIN: 'http://localhost:3000',
COLLABORATION_SERVER_SECRET: 'test-secret-api-key',
};
});
console.error = jest.fn();
console.error = vi.fn();
import { hocusPocusServer } from '@/servers/hocusPocusServer';
import { initServer } from '../src/servers/appServer';
const { app, server } = initServer();
import { COLLABORATION_SERVER_ORIGIN as origin } from '@/env';
import { hocuspocusServer, initApp } from '@/servers';
describe('Server Tests', () => {
afterAll(() => {
server.close();
});
test('POST /collaboration/api/reset-connections?room=[ROOM_ID] with incorrect API key should return 403', async () => {
const response = await request(app as any)
const app = initApp();
const response = await request(app)
.post('/collaboration/api/reset-connections/?room=test-room')
.set('Origin', origin)
.set('Authorization', 'wrong-api-key');
expect(response.status).toBe(403);
expect(response.body.error).toBe('Forbidden: Invalid API Key');
expect(response.body).toStrictEqual({
error: 'Forbidden: Invalid API Key',
});
});
test('POST /collaboration/api/reset-connections?room=[ROOM_ID] failed if room not indicated', async () => {
const response = await request(app as any)
const app = initApp();
const response = await request(app)
.post('/collaboration/api/reset-connections/')
.set('Origin', origin)
.set('Authorization', 'test-secret-api-key')
.send({ document_id: 'test-document' });
expect(response.status).toBe(400);
expect(response.body.error).toBe('Room name not provided');
expect(response.body).toStrictEqual({ error: 'Room name not provided' });
});
test('POST /collaboration/api/reset-connections?room=[ROOM_ID] with correct API key should reset connections', async () => {
// eslint-disable-next-line jest/unbound-method
const { closeConnections } = hocusPocusServer;
const mockHandleConnection = jest.fn();
(hocusPocusServer.closeConnections as jest.Mock) = mockHandleConnection;
const closeConnectionsMock = vi
.spyOn(hocuspocusServer, 'closeConnections')
.mockResolvedValue();
const response = await request(app as any)
const app = initApp();
const response = await request(app)
.post('/collaboration/api/reset-connections?room=test-room')
.set('Origin', origin)
.set('Authorization', 'test-secret-api-key');
expect(response.status).toBe(200);
expect(response.body.message).toBe('Connections reset');
expect(response.body).toStrictEqual({ message: 'Connections reset' });
expect(mockHandleConnection).toHaveBeenCalled();
mockHandleConnection.mockClear();
hocusPocusServer.closeConnections = closeConnections;
// eslint-disable-next-line jest/unbound-method
expect(closeConnectionsMock).toHaveBeenCalledOnce();
});
});

View File

@@ -1,66 +1,93 @@
import { Hocuspocus } from '@hocuspocus/server';
import request from 'supertest';
import { describe, expect, test, vi } from 'vitest';
import { mock } from 'vitest-mock-extended';
const port = 5556;
const origin = 'http://localhost:3000';
jest.mock('../src/env', () => {
vi.mock('../src/env', async (importOriginal) => {
return {
PORT: port,
COLLABORATION_SERVER_ORIGIN: origin,
...(await importOriginal()),
COLLABORATION_SERVER_ORIGIN: 'http://localhost:3000',
Y_PROVIDER_API_KEY: 'yprovider-api-key',
};
});
import { initServer } from '../src/servers/appServer';
import { initApp } from '@/servers';
console.error = jest.fn();
const { app, server } = initServer();
import {
Y_PROVIDER_API_KEY as apiKey,
COLLABORATION_SERVER_ORIGIN as origin,
} from '../src/env';
console.error = vi.fn();
const mockOpts = {
fallbackMockImplementation: () => {
throw new Error('Unexpected call.');
},
};
describe('Server Tests', () => {
afterAll(() => {
server.close();
});
test('POST /api/convert with incorrect API key should responds with 403', async () => {
const hocuspocus = mock<Hocuspocus>({}, mockOpts);
const app = initApp(hocuspocus);
test('POST /api/convert with incorrect API key should return 403', async () => {
const response = await request(app as any)
const response = await request(app)
.post('/api/convert')
.set('Origin', origin)
.set('Authorization', 'wrong-api-key');
expect(response.status).toBe(403);
expect(response.body.error).toBe('Forbidden: Invalid API Key');
expect(response.body).toStrictEqual({
error: 'Forbidden: Invalid API Key',
});
});
test('POST /api/convert with a Bearer token', async () => {
const response = await request(app as any)
const hocuspocus = mock<Hocuspocus>({}, mockOpts);
const app = initApp(hocuspocus);
const response = await request(app)
.post('/api/convert')
.set('Origin', origin)
.set('Authorization', 'Bearer test-secret-api-key');
// Warning: Changing the authorization header to Bearer token format will break backend compatibility with this microservice.
expect(response.status).toBe(403);
expect(response.body).toStrictEqual({
error: 'Forbidden: Invalid API Key',
});
});
test('POST /api/convert with missing body param content', async () => {
const response = await request(app as any)
const hocuspocus = mock<Hocuspocus>({}, mockOpts);
const app = initApp(hocuspocus);
const response = await request(app)
.post('/api/convert')
.set('Origin', origin)
.set('Authorization', 'yprovider-api-key');
.set('Authorization', apiKey);
expect(response.status).toBe(400);
expect(response.body.error).toBe('Invalid request: missing content');
expect(response.body).toStrictEqual({
error: 'Invalid request: missing content',
});
});
test('POST /api/convert with body param content being an empty string', async () => {
const response = await request(app as any)
const hocuspocus = mock<Hocuspocus>({}, mockOpts);
const app = initApp(hocuspocus);
const response = await request(app)
.post('/api/convert')
.set('Origin', origin)
.set('Authorization', 'yprovider-api-key')
.set('Authorization', apiKey)
.send({
content: '',
});
expect(response.status).toBe(400);
expect(response.body.error).toBe('Invalid request: missing content');
expect(response.body).toStrictEqual({
error: 'Invalid request: missing content',
});
});
});

View File

@@ -1,56 +1,62 @@
import { Server } from 'node:net';
import {
HocuspocusProvider,
HocuspocusProviderWebsocket,
} from '@hocuspocus/provider';
import { v1 as uuidv1, v4 as uuidv4 } from 'uuid';
import {
afterAll,
beforeAll,
beforeEach,
describe,
expect,
test,
vi,
} from 'vitest';
import WebSocket from 'ws';
const port = 5559;
const portWS = 6666;
const origin = 'http://localhost:3000';
jest.mock('../src/env', () => {
vi.mock('../src/env', async (importOriginal) => {
return {
PORT: port,
COLLABORATION_SERVER_ORIGIN: origin,
...(await importOriginal()),
PORT: 5559,
COLLABORATION_SERVER_ORIGIN: 'http://localhost:3000',
COLLABORATION_SERVER_SECRET: 'test-secret-api-key',
COLLABORATION_BACKEND_BASE_URL: 'http://app-dev:8000',
COLLABORATION_LOGGING: 'true',
};
});
console.error = jest.fn();
console.log = jest.fn();
const mockDocFetch = jest.fn();
jest.mock('@/api/getDoc', () => ({
fetchDocument: mockDocFetch,
vi.mock('../src/api/collaborationBackend', () => ({
fetchCurrentUser: vi.fn(),
fetchDocument: vi.fn(),
}));
const mockGetMe = jest.fn();
jest.mock('@/api/getMe', () => ({
getMe: mockGetMe,
}));
console.error = vi.fn();
console.log = vi.fn();
import { hocusPocusServer } from '@/servers/hocusPocusServer';
import { promiseDone } from '../src/helpers';
import { initServer } from '../src/servers/appServer';
const { server } = initServer();
import * as CollaborationBackend from '@/api/collaborationBackend';
import { COLLABORATION_SERVER_ORIGIN as origin, PORT as port } from '@/env';
import { promiseDone } from '@/helpers';
import { hocuspocusServer, initApp } from '@/servers';
describe('Server Tests', () => {
beforeAll(async () => {
await hocusPocusServer.configure({ port: portWS }).listen();
let server: Server;
beforeEach(() => {
vi.restoreAllMocks();
});
afterEach(() => {
jest.clearAllMocks();
beforeAll(async () => {
server = initApp().listen(port);
await hocuspocusServer.configure({ port: portWS }).listen();
});
afterAll(() => {
void hocuspocusServer.destroy();
server.close();
void hocusPocusServer.destroy();
});
test('WebSocket connection with bad origin should be closed', () => {
@@ -209,7 +215,9 @@ describe('Server Tests', () => {
test('WebSocket connection fails if user can not access document', () => {
const { promise, done } = promiseDone();
mockDocFetch.mockRejectedValue('');
const fetchDocumentMock = vi
.spyOn(CollaborationBackend, 'fetchDocument')
.mockRejectedValue(new Error('some error'));
const room = uuidv4();
const wsHocus = new HocuspocusProviderWebsocket({
@@ -233,7 +241,10 @@ describe('Server Tests', () => {
wsHocus.stopConnectionAttempt();
expect(data.event.reason).toBe('Forbidden');
expect(mockDocFetch).toHaveBeenCalledTimes(1);
expect(fetchDocumentMock).toHaveBeenCalledExactlyOnceWith(
room,
expect.any(Object),
);
wsHocus.webSocket?.close();
wsHocus.disconnect();
provider.destroy();
@@ -249,11 +260,10 @@ describe('Server Tests', () => {
const { promise, done } = promiseDone();
const room = uuidv4();
mockDocFetch.mockResolvedValue({
abilities: {
retrieve: false,
},
});
const fetchDocumentMock = vi
.spyOn(CollaborationBackend, 'fetchDocument')
.mockResolvedValue({ abilities: { retrieve: false } } as any);
const wsHocus = new HocuspocusProviderWebsocket({
url: `ws://localhost:${portWS}/?room=${room}`,
@@ -278,7 +288,10 @@ describe('Server Tests', () => {
wsHocus.stopConnectionAttempt();
expect(data.event.reason).toBe('Forbidden');
expect(mockDocFetch).toHaveBeenCalledTimes(1);
expect(fetchDocumentMock).toHaveBeenCalledExactlyOnceWith(
room,
expect.any(Object),
);
wsHocus.webSocket?.close();
wsHocus.disconnect();
provider.destroy();
@@ -294,12 +307,11 @@ describe('Server Tests', () => {
test(`WebSocket connection ${canEdit ? 'can' : 'can not'} edit document`, () => {
const { promise, done } = promiseDone();
mockDocFetch.mockResolvedValue({
abilities: {
retrieve: true,
update: canEdit,
},
});
const fetchDocumentMock = vi
.spyOn(CollaborationBackend, 'fetchDocument')
.mockResolvedValue({
abilities: { retrieve: true, update: canEdit },
} as any);
const room = uuidv4();
const wsHocus = new HocuspocusProviderWebsocket({
@@ -313,7 +325,7 @@ describe('Server Tests', () => {
broadcast: false,
quiet: true,
onConnect: () => {
void hocusPocusServer
void hocuspocusServer
.openDirectConnection(room)
.then((connection) => {
connection.document?.getConnections().forEach((connection) => {
@@ -324,6 +336,12 @@ describe('Server Tests', () => {
provider.destroy();
wsHocus.destroy();
expect(fetchDocumentMock).toHaveBeenCalledWith(
room,
expect.any(Object),
);
done();
});
},
@@ -336,16 +354,15 @@ describe('Server Tests', () => {
test('Add request header x-user-id if found', () => {
const { promise, done } = promiseDone();
mockDocFetch.mockResolvedValue({
abilities: {
retrieve: true,
update: true,
},
});
const fetchDocumentMock = vi
.spyOn(CollaborationBackend, 'fetchDocument')
.mockResolvedValue({
abilities: { retrieve: true, update: true },
} as any);
mockGetMe.mockResolvedValue({
id: 'test-user-id',
});
const fetchCurrentUserMock = vi
.spyOn(CollaborationBackend, 'fetchCurrentUser')
.mockResolvedValue({ id: 'test-user-id' } as any);
const room = uuidv4();
const wsHocus = new HocuspocusProviderWebsocket({
@@ -359,7 +376,7 @@ describe('Server Tests', () => {
broadcast: false,
quiet: true,
onConnect: () => {
void hocusPocusServer.openDirectConnection(room).then((connection) => {
void hocuspocusServer.openDirectConnection(room).then((connection) => {
connection.document?.getConnections().forEach((connection) => {
expect(connection.context.userId).toBe('test-user-id');
});
@@ -367,6 +384,14 @@ describe('Server Tests', () => {
void connection.disconnect();
provider.destroy();
wsHocus.destroy();
expect(fetchDocumentMock).toHaveBeenCalledWith(
room,
expect.any(Object),
);
expect(fetchCurrentUserMock).toHaveBeenCalled();
done();
});
},

View File

@@ -1,45 +1,42 @@
import request from 'supertest';
import { describe, expect, it, test, vi } from 'vitest';
import { routes } from '@/routes';
import { initApp } from '@/servers';
const port = 5557;
const origin = 'http://localhost:3000';
jest.mock('../src/env', () => {
vi.mock('../src/env', async (importOriginal) => {
return {
PORT: port,
COLLABORATION_SERVER_ORIGIN: origin,
...(await importOriginal()),
COLLABORATION_SERVER_ORIGIN: 'http://localhost:3000',
};
});
console.error = jest.fn();
import { initServer } from '../src/servers/appServer';
const { app, server } = initServer();
console.error = vi.fn();
describe('Server Tests', () => {
afterAll(() => {
server.close();
});
test('Ping Pong', async () => {
const response = await request(app as any).get('/ping');
const app = initApp();
const response = await request(app).get('/ping');
expect(response.status).toBe(200);
expect(response.body.message).toBe('pong');
expect(response.body).toStrictEqual({ message: 'pong' });
});
['/collaboration/api/anything/', '/', '/anything'].forEach((path) => {
test(`"${path}" endpoint should be forbidden`, async () => {
const response = await request(app as any).post(path);
const app = initApp();
const response = await request(app).post(path);
expect(response.status).toBe(403);
expect(response.body.error).toBe('Forbidden');
});
});
it('should allow JSON payloads up to 500kb for the CONVERT route', async () => {
it('allows JSON payloads up to 500kb for the CONVERT route', async () => {
const app = initApp();
const largePayload = 'a'.repeat(400 * 1024); // 400kb payload
const response = await request(app)
.post(routes.CONVERT)
@@ -49,7 +46,9 @@ describe('Server Tests', () => {
expect(response.status).not.toBe(413);
});
it('should reject JSON payloads larger than 500kb for the CONVERT route', async () => {
it('rejects JSON payloads larger than 500kb for the CONVERT route', async () => {
const app = initApp();
const oversizedPayload = 'a'.repeat(501 * 1024); // 501kb payload
const response = await request(app)
.post(routes.CONVERT)
@@ -59,7 +58,9 @@ describe('Server Tests', () => {
expect(response.status).toBe(413);
});
it('should use the default JSON limit for other routes', async () => {
it('uses the default JSON limit for other routes', async () => {
const app = initApp();
const largePayload = 'a'.repeat(150 * 1024);
const response = await request(app)
.post('/some-other-route')

View File

@@ -1,12 +0,0 @@
const config = {
rootDir: './__tests__',
testEnvironment: 'node',
transform: {
'^.+\\.(ts)$': 'ts-jest',
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/../src/$1',
'^@blocknote/server-util$': '<rootDir>/../__mocks__/mock.js',
},
};
export default config;

View File

@@ -10,7 +10,7 @@
"dev": "cross-env COLLABORATION_LOGGING=true && nodemon --config nodemon.json",
"start": "node ./dist/start-server.js",
"lint": "eslint . --ext .ts",
"test": "jest"
"test": "vitest --run"
},
"engines": {
"node": ">=22"
@@ -33,19 +33,19 @@
"@types/cors": "2.8.19",
"@types/express": "5.0.3",
"@types/express-ws": "3.0.5",
"@types/jest": "30.0.0",
"@types/node": "*",
"@types/supertest": "6.0.3",
"@types/ws": "8.18.1",
"cross-env": "7.0.3",
"eslint-config-impress": "*",
"jest": "30.0.3",
"nodemon": "3.1.10",
"supertest": "7.1.1",
"ts-jest": "29.4.0",
"ts-node": "10.9.2",
"tsc-alias": "1.8.16",
"typescript": "*",
"vitest": "3.2.4",
"vitest-mock-extended": "3.1.0",
"ws": "8.18.2"
}
}

View File

@@ -4,15 +4,12 @@ import axios from 'axios';
import { COLLABORATION_BACKEND_BASE_URL } from '@/env';
enum LinkReach {
RESTRICTED = 'restricted',
PUBLIC = 'public',
AUTHENTICATED = 'authenticated',
}
enum LinkRole {
READER = 'reader',
EDITOR = 'editor',
export interface User {
id: string;
email: string;
full_name: string;
short_name: string;
language: string;
}
type Base64 = string;
@@ -23,8 +20,8 @@ interface Doc {
content: Base64;
creator: string;
is_favorite: boolean;
link_reach: LinkReach;
link_role: LinkRole;
link_reach: 'restricted' | 'public' | 'authenticated';
link_role: 'reader' | 'editor';
nb_accesses_ancestors: number;
nb_accesses_direct: number;
created_at: string;
@@ -54,23 +51,36 @@ interface Doc {
};
}
export const fetchDocument = async (
documentName: string,
async function fetch<T>(
path: string,
requestHeaders: IncomingHttpHeaders,
) => {
const response = await axios.get<Doc>(
`${COLLABORATION_BACKEND_BASE_URL}/api/v1.0/documents/${documentName}/`,
): Promise<T> {
const response = await axios.get<T>(
`${COLLABORATION_BACKEND_BASE_URL}${path}`,
{
headers: {
Cookie: requestHeaders['cookie'],
Origin: requestHeaders['origin'],
cookie: requestHeaders['cookie'],
origin: requestHeaders['origin'],
},
},
);
if (response.status !== 200) {
throw new Error(`Failed to fetch document: ${response.statusText}`);
throw new Error(`Failed to fetch ${path}: ${response.statusText}`);
}
return response.data;
};
}
export function fetchDocument(
name: string,
requestHeaders: IncomingHttpHeaders,
): Promise<Doc> {
return fetch<Doc>(`/api/v1.0/documents/${name}/`, requestHeaders);
}
export function fetchCurrentUser(
requestHeaders: IncomingHttpHeaders,
): Promise<User> {
return fetch<User>('/api/v1.0/users/me/', requestHeaders);
}

View File

@@ -1,31 +0,0 @@
import { IncomingHttpHeaders } from 'http';
import axios from 'axios';
import { COLLABORATION_BACKEND_BASE_URL } from '@/env';
export interface User {
id: string;
email: string;
full_name: string;
short_name: string;
language: string;
}
export const getMe = async (requestHeaders: IncomingHttpHeaders) => {
const response = await axios.get<User>(
`${COLLABORATION_BACKEND_BASE_URL}/api/v1.0/users/me/`,
{
headers: {
Cookie: requestHeaders['cookie'],
Origin: requestHeaders['origin'],
},
},
);
if (response.status !== 200) {
throw new Error(`Failed to fetch user: ${response.statusText}`);
}
return response.data;
};

View File

@@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import { hocusPocusServer } from '@/servers/hocusPocusServer';
import { hocuspocusServer } from '@/servers';
import { logger } from '@/utils';
type ResetConnectionsRequestQuery = {
@@ -25,12 +25,12 @@ export const collaborationResetConnectionsHandler = (
* If no user ID is provided, close all connections in the room
*/
if (!userId) {
hocusPocusServer.closeConnections(room);
hocuspocusServer.closeConnections(room);
} else {
/**
* Close connections for the user in the room
*/
hocusPocusServer.documents.forEach((doc) => {
hocuspocusServer.documents.forEach((doc) => {
if (doc.name !== room) {
return;
}

View File

@@ -1,11 +1,11 @@
import { Request } from 'express';
import * as ws from 'ws';
import { hocusPocusServer } from '@/servers/hocusPocusServer';
import { hocuspocusServer } from '@/servers/hocuspocusServer';
export const collaborationWSHandler = (ws: ws.WebSocket, req: Request) => {
try {
hocusPocusServer.handleConnection(ws, req);
hocuspocusServer.handleConnection(ws, req);
} catch (error) {
console.error('Failed to handle WebSocket connection:', error);
ws.close();

View File

@@ -16,6 +16,8 @@ interface ErrorResponse {
error: string;
}
const editor = ServerBlockNoteEditor.create();
export const convertHandler = async (
req: Request<
object,
@@ -33,8 +35,6 @@ export const convertHandler = async (
}
try {
const editor = ServerBlockNoteEditor.create();
// Perform the conversion from markdown to Blocknote.js blocks
const blocks = await editor.tryParseMarkdownToBlocks(content);

View File

@@ -1,26 +1,27 @@
// eslint-disable-next-line import/order
import '../services/sentry';
import * as Sentry from '@sentry/node';
import express from 'express';
import expressWebsockets from 'express-ws';
import { PORT } from '../env';
import {
collaborationResetConnectionsHandler,
collaborationWSHandler,
convertHandler,
} from '../handlers';
import { corsMiddleware, httpSecurity, wsSecurity } from '../middlewares';
import { routes } from '../routes';
import { logger } from '../utils';
} from '@/handlers';
import { corsMiddleware, httpSecurity, wsSecurity } from '@/middlewares';
import { routes } from '@/routes';
import { logger } from '@/utils';
/**
* init the collaboration server.
*
* @returns An object containing the Express app, Hocuspocus server, and HTTP server instance.
*/
export const initServer = () => {
export const initApp = () => {
const { app } = expressWebsockets(express());
app.use((req, res, next) => {
if (req.path === routes.CONVERT) {
// Large transcript files are bigger than the default '100kb' limit
@@ -62,9 +63,5 @@ export const initServer = () => {
res.status(403).json({ error: 'Forbidden' });
});
const server = app.listen(PORT, () =>
console.log('App listening on port :', PORT),
);
return { app, server };
return app;
};

View File

@@ -1,11 +1,10 @@
import { Server } from '@hocuspocus/server';
import { validate as uuidValidate, version as uuidVersion } from 'uuid';
import { fetchDocument } from '@/api/getDoc';
import { getMe } from '@/api/getMe';
import { fetchCurrentUser, fetchDocument } from '@/api/collaborationBackend';
import { logger } from '@/utils';
export const hocusPocusServer = Server.configure({
export const hocuspocusServer = Server.configure({
name: 'docs-collaboration',
timeout: 30000,
quiet: true,
@@ -37,7 +36,7 @@ export const hocusPocusServer = Server.configure({
return Promise.reject(new Error('Wrong room name: Unauthorized'));
}
let can_edit = false;
let canEdit = false;
try {
const document = await fetchDocument(documentName, requestHeaders);
@@ -50,7 +49,7 @@ export const hocusPocusServer = Server.configure({
return Promise.reject(new Error('Wrong abilities:Unauthorized'));
}
can_edit = document.abilities.update;
canEdit = document.abilities.update;
} catch (error: unknown) {
if (error instanceof Error) {
logger('onConnect: backend error', error.message);
@@ -59,14 +58,14 @@ export const hocusPocusServer = Server.configure({
return Promise.reject(new Error('Backend error: Unauthorized'));
}
connection.readOnly = !can_edit;
connection.readOnly = !canEdit;
/*
* Unauthenticated users can be allowed to connect
* so we flag only authenticated users
*/
try {
const user = await getMe(requestHeaders);
const user = await fetchCurrentUser(requestHeaders);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
context.userId = user.id;
} catch {}
@@ -75,7 +74,7 @@ export const hocusPocusServer = Server.configure({
'Connection established on room:',
documentName,
'canEdit:',
can_edit,
canEdit,
);
return Promise.resolve();
},

View File

@@ -1,2 +1,2 @@
export * from './appServer';
export * from './hocusPocusServer';
export * from './hocuspocusServer';

View File

@@ -1,3 +1,4 @@
import { initServer } from './servers/appServer';
import { PORT } from '@/env';
import { initApp } from '@/servers';
initServer();
initApp().listen(PORT, () => console.log('App listening on port :', PORT));

View File

@@ -0,0 +1,11 @@
import { URL, fileURLToPath } from 'url';
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
});