diff --git a/backend/__tests__/unit/routes/stats.public.test.js b/backend/__tests__/unit/routes/stats.public.test.js
index 088dd123..97b3bbca 100644
--- a/backend/__tests__/unit/routes/stats.public.test.js
+++ b/backend/__tests__/unit/routes/stats.public.test.js
@@ -3,12 +3,13 @@ const express = require('express');
const mockPgQuery = jest.fn();
const mockMessageCountDocuments = jest.fn().mockResolvedValue(88);
+const mockUserCountDocuments = jest.fn().mockResolvedValue(262);
jest.mock('../../../models/Pod', () => ({
countDocuments: jest.fn().mockResolvedValue(12),
}));
jest.mock('../../../models/User', () => ({
- countDocuments: jest.fn().mockResolvedValue(42),
+ countDocuments: mockUserCountDocuments,
}));
jest.mock('../../../models/Message', () => ({
countDocuments: mockMessageCountDocuments,
@@ -42,13 +43,19 @@ describe('GET /api/stats/public', () => {
activePods: 12,
activeAgents: 3,
messageCount24h: 1234,
- registeredUsers: 42,
+ agentCount: 262,
});
expect(mockPgQuery).toHaveBeenCalledWith(
'SELECT COUNT(*)::int AS count FROM messages WHERE created_at >= $1',
[expect.any(Date)],
);
expect(mockMessageCountDocuments).not.toHaveBeenCalled();
+ expect(mockUserCountDocuments).toHaveBeenCalledWith({
+ 'botMetadata.agentName': { $exists: true },
+ });
+ expect(mockUserCountDocuments).toHaveBeenCalledTimes(1);
+ expect(res.body).not.toHaveProperty('humanCount');
+ expect(res.body).not.toHaveProperty('registeredUsers');
});
it('falls back to MongoDB when the PostgreSQL count fails', async () => {
@@ -61,7 +68,7 @@ describe('GET /api/stats/public', () => {
activePods: 12,
activeAgents: 3,
messageCount24h: 88,
- registeredUsers: 42,
+ agentCount: 262,
});
expect(mockMessageCountDocuments).toHaveBeenCalledWith({
createdAt: { $gte: expect.any(Date) },
diff --git a/backend/routes/stats.ts b/backend/routes/stats.ts
index 329cec05..d979b1f4 100644
--- a/backend/routes/stats.ts
+++ b/backend/routes/stats.ts
@@ -26,17 +26,27 @@ router.get('/public', async (_req: unknown, res: { json: (d: unknown) => void; s
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
- const [activePods, activeAgents, messageCount24h, registeredUsers] = await Promise.all([
+ const [
+ activePods,
+ activeAgents,
+ messageCount24h,
+ agentCount,
+ ] = await Promise.all([
Pod.countDocuments({ updatedAt: { $gte: sevenDaysAgo } }),
AgentInstallation.distinct('agentName').then((names: string[]) => names.length),
pgMessageCount24h(oneDayAgo).catch((err: { message?: string }) => {
console.warn('stats: PG message count failed, falling back to Mongo:', err?.message);
return Message.countDocuments({ createdAt: { $gte: oneDayAgo } });
}),
- User.countDocuments(),
+ User.countDocuments({ 'botMetadata.agentName': { $exists: true } }),
]);
- res.json({ activePods, activeAgents, messageCount24h, registeredUsers });
+ res.json({
+ activePods,
+ activeAgents,
+ messageCount24h,
+ agentCount,
+ });
} catch {
res.status(500).json({ error: 'Failed to fetch stats' });
}
diff --git a/frontend/src/v2/__tests__/V2LandingPage.stats.test.tsx b/frontend/src/v2/__tests__/V2LandingPage.stats.test.tsx
new file mode 100644
index 00000000..72d9a445
--- /dev/null
+++ b/frontend/src/v2/__tests__/V2LandingPage.stats.test.tsx
@@ -0,0 +1,64 @@
+import React from 'react';
+import { render, screen, within } from '@testing-library/react';
+import { MemoryRouter } from 'react-router-dom';
+import axios from 'axios';
+import { useAuth } from '../../context/AuthContext';
+import V2LandingPage from '../landing/V2LandingPage';
+
+jest.mock('axios');
+jest.mock('../../context/AuthContext', () => ({
+ useAuth: jest.fn(),
+}));
+
+const mockAxiosGet = axios.get as jest.Mock;
+const mockUseAuth = useAuth as jest.Mock;
+
+const renderLanding = () => render(
+