diff --git a/apps/api/src/app.test.ts b/apps/api/src/app.test.ts new file mode 100644 index 0000000..cbd4c9d --- /dev/null +++ b/apps/api/src/app.test.ts @@ -0,0 +1,65 @@ +jest.mock('puppeteer-extra', () => ({ + use: jest.fn(), + launch: jest.fn(), +})); + +jest.mock('puppeteer-extra-plugin-stealth', () => () => ({})); + +jest.mock('puppeteer-extra-plugin-adblocker', () => () => ({})); + +jest.mock('puppeteer', () => ({ + DEFAULT_INTERCEPT_RESOLUTION_PRIORITY: 0, +})); + +jest.mock('./config/db', () => ({ + isDbConnected: jest.fn(() => false), +})); + +jest.mock('./config/logger', () => ({ + __esModule: true, + default: { + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, + setupErrorHandlers: jest.fn(), +})); + +jest.mock('./client/Instagram', () => ({ + getIgClient: jest.fn(), + closeIgClient: jest.fn(), + scrapeFollowersHandler: jest.fn(), + getIgClientStatus: jest.fn(() => ({ connected: false })), + getIgClientsSnapshot: jest.fn(() => ({})), +})); + +jest.mock('./services/actionLog', () => ({ + logAction: jest.fn().mockResolvedValue(undefined), + getActionSummary: jest.fn().mockResolvedValue({}), + listActionLogs: jest.fn().mockResolvedValue([]), +})); + +jest.mock('./services/metrics', () => ({ + metricsMiddleware: jest.fn((_req, _res, next) => next()), + getMetrics: jest.fn(() => ({ + uptime: 120, + uptimeFormatted: '2m 0s', + requests: 0, + })), +})); + +jest.mock('./config/accounts', () => ({ + getAccount: jest.fn(), + getAccountsMap: jest.fn(() => ({})), +})); + +import request from 'supertest'; +import app from './app'; + +describe('Root App Routes', () => { + test('GET /hello returns { ok: true }', async () => { + const res = await request(app).get('/hello'); + expect(res.status).toBe(200); + expect(res.body).toEqual({ ok: true }); + }); +}); diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts index 958beb1..3445fc7 100644 --- a/apps/api/src/app.ts +++ b/apps/api/src/app.ts @@ -58,6 +58,11 @@ app.use(express.static('frontend/dist')); // API Routes app.use('/api', apiRoutes); +// Hello world test endpoint for bot detection verification +app.get('/hello', (_req, res) => { + res.status(200).json({ ok: true }); +}); + // Admin dashboard app.get('/dashboard', (_req, res) => { res.type('html').send(dashboardHtml); diff --git a/apps/api/src/routes/api.test.ts b/apps/api/src/routes/api.test.ts index 68a2e30..ac172c9 100644 --- a/apps/api/src/routes/api.test.ts +++ b/apps/api/src/routes/api.test.ts @@ -55,6 +55,12 @@ app.use('/api', apiRoutes); describe('API routes', () => { describe('public endpoints', () => { + test('GET /api/hello returns { ok: true }', async () => { + const res = await request(app).get('/api/hello'); + expect(res.status).toBe(200); + expect(res.body).toEqual({ ok: true }); + }); + test('GET /api/ping returns pong', async () => { const res = await request(app).get('/api/ping'); expect(res.status).toBe(200); diff --git a/apps/api/src/routes/api.ts b/apps/api/src/routes/api.ts index ff9f3f2..17ec3c6 100644 --- a/apps/api/src/routes/api.ts +++ b/apps/api/src/routes/api.ts @@ -70,6 +70,12 @@ router.use(generalLimiter); // API Documentation endpoint - lists all available endpoints const apiEndpoints = [ // Public endpoints + { + method: 'GET', + path: '/api/hello', + auth: false, + description: 'Hello world test endpoint for bot detection verification (returns { ok: true })', + }, { method: 'GET', path: '/api/ping', @@ -268,6 +274,11 @@ const apiEndpoints = [ // Track server start time for uptime calculation const serverStartTime = Date.now(); +// Hello world test endpoint for bot detection verification +router.get('/hello', (_req: Request, res: Response) => { + return res.json({ ok: true }); +}); + // Simple ping endpoint for load balancers and uptime monitors router.get('/ping', (_req: Request, res: Response) => { return res.send('pong');