import { beforeEach, describe, expect, it, vi } from 'vitest'; const resolveSecretsMock = vi.fn(async () => undefined); const createServiceAppMock = vi.fn(); const registerOptionalJwtContextMock = vi.fn(async () => undefined); const startServiceMock = vi.fn(async () => undefined); const loadProductCacheMock = vi.fn(async () => undefined); const initCosmosIfNeededMock = vi.fn(async () => undefined); const verifyTokenMock = vi.fn(async () => ({ sub: 'user_1', productId: 'lysnrai' })); const seedDefaultFlagsMock = vi.fn(async () => undefined); const runPendingMigrationsMock = vi.fn(async () => undefined); const registerDiagnosticsSubscribersMock = vi.fn(); const startTriggerEvaluationJobMock = vi.fn(); const appMock = { register: vi.fn(async () => undefined), addHook: vi.fn(), }; vi.mock('@bytelyst/config', () => ({ LYSNR_SECRETS: { COSMOS_KEY: 'cosmos-key', COSMOS_ENDPOINT: 'cosmos-endpoint', JWT_SECRET: 'jwt-secret', STRIPE_SECRET_KEY: 'stripe-secret', STRIPE_WEBHOOK_SECRET: 'stripe-webhook', AZURE_BLOB_CONNECTION_STRING: 'blob-conn', AZURE_BLOB_ACCOUNT_KEY: 'blob-key', }, resolveSecrets: resolveSecretsMock, loadProductIdentity: () => ({ productId: 'lysnrai', displayName: 'LysnrAI', licensePrefix: 'LYSNR', }), })); vi.mock('@bytelyst/fastify-core', () => ({ createServiceApp: createServiceAppMock, registerOptionalJwtContext: registerOptionalJwtContextMock, startService: startServiceMock, })); vi.mock('./modules/products/routes.js', () => ({ productRoutes: vi.fn() })); vi.mock('./modules/products/cache.js', () => ({ loadProductCache: loadProductCacheMock })); vi.mock('./modules/auth/routes.js', () => ({ authRoutes: vi.fn() })); vi.mock('./modules/audit/routes.js', () => ({ auditRoutes: vi.fn() })); vi.mock('./modules/notifications/routes.js', () => ({ notificationRoutes: vi.fn() })); vi.mock('./modules/flags/routes.js', () => ({ flagRoutes: vi.fn() })); vi.mock('./modules/ratelimit/routes.js', () => ({ rateLimitRoutes: vi.fn() })); vi.mock('./modules/blob/routes.js', () => ({ blobRoutes: vi.fn() })); vi.mock('./modules/invitations/routes.js', () => ({ invitationRoutes: vi.fn() })); vi.mock('./modules/referrals/routes.js', () => ({ referralRoutes: vi.fn() })); vi.mock('./modules/promos/routes.js', () => ({ promoRoutes: vi.fn() })); vi.mock('./modules/subscriptions/routes.js', () => ({ subscriptionRoutes: vi.fn() })); vi.mock('./modules/usage/routes.js', () => ({ usageRoutes: vi.fn() })); vi.mock('./modules/plans/routes.js', () => ({ planRoutes: vi.fn() })); vi.mock('./modules/licenses/routes.js', () => ({ licenseRoutes: vi.fn() })); vi.mock('./modules/stripe/routes.js', () => ({ stripeRoutes: vi.fn() })); vi.mock('./modules/settings/routes.js', () => ({ settingsRoutes: vi.fn() })); vi.mock('./modules/items/routes.js', () => ({ itemRoutes: vi.fn() })); vi.mock('./modules/comments/routes.js', () => ({ commentRoutes: vi.fn() })); vi.mock('./modules/votes/routes.js', () => ({ voteRoutes: vi.fn() })); vi.mock('./modules/public/routes.js', () => ({ publicRoutes: vi.fn() })); vi.mock('./modules/tokens/routes.js', () => ({ tokenRoutes: vi.fn() })); vi.mock('./modules/themes/routes.js', () => ({ themeRoutes: vi.fn() })); vi.mock('./lib/cosmos-init.js', () => ({ initCosmosIfNeeded: initCosmosIfNeededMock })); vi.mock('./lib/config.js', () => ({ config: { CORS_ORIGIN: '*', PORT: 4003, HOST: '0.0.0.0' } })); vi.mock('./modules/auth/jwt.js', () => ({ verifyToken: verifyTokenMock })); vi.mock('./modules/flags/seed.js', () => ({ seedDefaultFlags: seedDefaultFlagsMock })); vi.mock('./migrations/runner.js', () => ({ runPendingMigrations: runPendingMigrationsMock })); vi.mock('./modules/diagnostics/subscribers.js', () => ({ registerDiagnosticsSubscribers: registerDiagnosticsSubscribersMock, })); vi.mock('./modules/diagnostics/trigger-job.js', () => ({ startTriggerEvaluationJob: startTriggerEvaluationJobMock, })); describe('server bootstrap', () => { beforeEach(() => { vi.resetModules(); vi.clearAllMocks(); createServiceAppMock.mockResolvedValue(appMock); appMock.register.mockReset(); appMock.register.mockResolvedValue(undefined); appMock.addHook.mockReset(); }); it('initializes secrets, app, routes, and starts service', async () => { await import('./server.js'); expect(resolveSecretsMock).toHaveBeenCalledOnce(); expect(initCosmosIfNeededMock).toHaveBeenCalledOnce(); expect(loadProductCacheMock).toHaveBeenCalledOnce(); expect(createServiceAppMock).toHaveBeenCalledOnce(); expect(registerOptionalJwtContextMock).toHaveBeenCalledOnce(); expect(runPendingMigrationsMock).toHaveBeenCalledOnce(); expect(seedDefaultFlagsMock).toHaveBeenCalledOnce(); expect(registerDiagnosticsSubscribersMock).toHaveBeenCalledOnce(); expect(startTriggerEvaluationJobMock).toHaveBeenCalledOnce(); expect(appMock.register).toHaveBeenCalled(); expect(appMock.register.mock.calls.length).toBeGreaterThan(15); expect(startServiceMock).toHaveBeenCalledWith(appMock, { port: 4003, host: '0.0.0.0' }); }, 15000); it('registers optional jwt parsing with the shared fastify-core helper', async () => { await import('./server.js'); const helperCall = registerOptionalJwtContextMock.mock.calls[0] as unknown as [ unknown, { verifyToken: (token: string) => Promise; }, ]; const options = helperCall[1]; expect(options).toBeDefined(); expect(options.verifyToken).toBeTypeOf('function'); await expect(options.verifyToken('token_abc')).resolves.toEqual({ sub: 'user_1', productId: 'lysnrai', }); expect(verifyTokenMock).toHaveBeenCalledWith('token_abc'); }); it('passes through verifyToken rejections to the shared helper callback', async () => { verifyTokenMock.mockRejectedValueOnce(new Error('invalid token')); await import('./server.js'); const helperCall = registerOptionalJwtContextMock.mock.calls[0] as unknown as [ unknown, { verifyToken: (token: string) => Promise; }, ]; const options = helperCall[1]; await expect(options.verifyToken('broken')).rejects.toThrow('invalid token'); }); });