118 lines
5.1 KiB
TypeScript
118 lines
5.1 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const createServiceAppMock = vi.fn();
|
|
const registerOptionalJwtContextMock = vi.fn(async () => undefined);
|
|
const startServiceMock = vi.fn(async () => undefined);
|
|
const initCosmosIfNeededMock = vi.fn(async () => undefined);
|
|
const initDatastoreMock = vi.fn(() => undefined);
|
|
const diagnosticsRoutesMock = vi.fn(async () => undefined);
|
|
const startSchedulerLoopMock = vi.fn();
|
|
const stopSchedulerLoopMock = vi.fn();
|
|
const initWebhookSubscriberMock = vi.fn();
|
|
const stopWebhookSubscriberMock = vi.fn();
|
|
|
|
const appMock = {
|
|
register: vi.fn(async () => undefined),
|
|
get: vi.fn(),
|
|
post: vi.fn(),
|
|
addHook: vi.fn(),
|
|
log: {
|
|
error: vi.fn(),
|
|
info: vi.fn(),
|
|
},
|
|
};
|
|
|
|
vi.mock('@bytelyst/fastify-core', () => ({
|
|
createServiceApp: createServiceAppMock,
|
|
registerOptionalJwtContext: registerOptionalJwtContextMock,
|
|
startService: startServiceMock,
|
|
}));
|
|
|
|
vi.mock('jose', () => ({
|
|
jwtVerify: vi.fn(async () => ({ payload: { sub: 'user_1', productId: 'notelett' } })),
|
|
}));
|
|
|
|
vi.mock('./modules/note-agent-actions/routes.js', () => ({ noteAgentActionRoutes: vi.fn() }));
|
|
vi.mock('./modules/note-artifacts/routes.js', () => ({ noteArtifactRoutes: vi.fn() }));
|
|
vi.mock('./modules/notes/routes.js', () => ({ noteRoutes: vi.fn() }));
|
|
vi.mock('./modules/note-relationships/routes.js', () => ({ noteRelationshipRoutes: vi.fn() }));
|
|
vi.mock('./modules/note-tasks/routes.js', () => ({ noteTaskRoutes: vi.fn() }));
|
|
vi.mock('./modules/saved-views/routes.js', () => ({ savedViewRoutes: vi.fn() }));
|
|
vi.mock('./modules/workspaces/routes.js', () => ({ workspaceRoutes: vi.fn() }));
|
|
vi.mock('./modules/ecosystem-phase1/routes.js', () => ({ ecosystemPhase1Routes: vi.fn() }));
|
|
vi.mock('./modules/ecosystem-phase3/routes.js', () => ({ ecosystemPhase3Routes: vi.fn() }));
|
|
vi.mock('./modules/note-prompts/routes.js', () => ({ notePromptRoutes: vi.fn() }));
|
|
vi.mock('./modules/note-prompts/scheduler.js', () => ({
|
|
promptSchedulerRoutes: vi.fn(),
|
|
startSchedulerLoop: startSchedulerLoopMock,
|
|
stopSchedulerLoop: stopSchedulerLoopMock,
|
|
}));
|
|
vi.mock('./modules/intake/routes.js', () => ({ intakeRoutes: vi.fn() }));
|
|
vi.mock('./modules/note-collaborators/routes.js', () => ({ noteCollaboratorRoutes: vi.fn() }));
|
|
vi.mock('./modules/palace/routes.js', () => ({ palaceRoutes: vi.fn() }));
|
|
vi.mock('./lib/cosmos-init.js', () => ({ initCosmosIfNeeded: initCosmosIfNeededMock }));
|
|
vi.mock('./lib/datastore.js', () => ({ initDatastore: initDatastoreMock }));
|
|
vi.mock('./lib/config.js', () => ({
|
|
config: {
|
|
SERVICE_NAME: 'notelett-backend',
|
|
CORS_ORIGIN: '*',
|
|
PORT: 4016,
|
|
HOST: '0.0.0.0',
|
|
JWT_SECRET: 'test-secret',
|
|
NODE_ENV: 'test',
|
|
},
|
|
}));
|
|
vi.mock('./lib/product-config.js', () => ({
|
|
DISPLAY_NAME: 'NoteLett',
|
|
PRODUCT_ID: 'notelett',
|
|
productConfig: { productId: 'notelett', displayName: 'NoteLett' },
|
|
}));
|
|
vi.mock('./lib/field-encrypt.js', () => ({ initEncryption: vi.fn(async () => undefined), getEncryptor: vi.fn() }));
|
|
vi.mock('./lib/request-context.js', () => ({
|
|
createOutboundRequestId: vi.fn(() => 'startup-request-id'),
|
|
getUserId: vi.fn(),
|
|
getRequestProductId: vi.fn(),
|
|
}));
|
|
vi.mock('./lib/feature-flags.js', () => ({ getAllFlags: vi.fn(() => ({})) }));
|
|
vi.mock('./lib/telemetry.js', () => ({ getBufferedEvents: vi.fn(() => []), flushEvents: vi.fn(() => []) }));
|
|
vi.mock('./lib/diagnostics-routes.js', () => ({ diagnosticsRoutes: diagnosticsRoutesMock }));
|
|
vi.mock('./lib/webhook-subscriber.js', () => ({
|
|
initWebhookSubscriber: initWebhookSubscriberMock,
|
|
stopWebhookSubscriber: stopWebhookSubscriberMock,
|
|
}));
|
|
vi.mock('./modules/note-shares/repository.js', () => ({ findShareByToken: vi.fn(async () => null) }));
|
|
vi.mock('./modules/notes/repository.js', () => ({ getNote: vi.fn(async () => null) }));
|
|
|
|
describe('server bootstrap', () => {
|
|
beforeEach(() => {
|
|
vi.resetModules();
|
|
vi.clearAllMocks();
|
|
createServiceAppMock.mockResolvedValue(appMock);
|
|
appMock.register.mockReset();
|
|
appMock.register.mockResolvedValue(undefined);
|
|
});
|
|
|
|
it('initializes app, routes, and starts service', async () => {
|
|
await import('./server.js');
|
|
|
|
expect(initCosmosIfNeededMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
error: expect.any(Function),
|
|
info: expect.any(Function),
|
|
}));
|
|
expect(initDatastoreMock).toHaveBeenCalledOnce();
|
|
expect(createServiceAppMock).toHaveBeenCalledOnce();
|
|
expect(registerOptionalJwtContextMock).toHaveBeenCalledOnce();
|
|
expect(appMock.register).toHaveBeenCalledTimes(14);
|
|
expect(diagnosticsRoutesMock).toHaveBeenCalledWith(appMock);
|
|
expect(startSchedulerLoopMock).toHaveBeenCalledWith(60_000, appMock.log);
|
|
expect(initWebhookSubscriberMock).toHaveBeenCalledOnce();
|
|
expect(appMock.addHook).toHaveBeenCalledWith('onClose', expect.any(Function));
|
|
const onCloseHook = appMock.addHook.mock.calls.find(([event]) => event === 'onClose')?.[1];
|
|
expect(onCloseHook).toEqual(expect.any(Function));
|
|
await onCloseHook();
|
|
expect(stopSchedulerLoopMock).toHaveBeenCalledOnce();
|
|
expect(stopWebhookSubscriberMock).toHaveBeenCalledOnce();
|
|
expect(startServiceMock).toHaveBeenCalledWith(appMock, { port: 4016, host: '0.0.0.0' });
|
|
});
|
|
});
|