platform-codebase/features/platform-dev/frontend-dev/e2e/fixtures/api-mocks.ts

243 lines
6.5 KiB
TypeScript

import { Page } from '@playwright/test';
// ============================================================================
// Scammers Mocks
// ============================================================================
// ScammerProfile[] format
export const SCAMMERS_MOCKS = {
'/api/scammers': {
data: [
{
id: 'scam-001',
phoneNumber: '+1234567890',
status: 'suspected',
riskScore: 75,
aiDetectedCount: 3,
manipulationCount: 2,
phishingCount: 1,
firstSeen: '2024-01-01T00:00:00Z',
lastSeen: '2024-01-15T10:30:00Z',
notes: null,
},
],
meta: {
total: 1,
page: 1,
limit: 50,
totalPages: 1,
},
},
'/api/scammers/stats': {
total: 42,
suspected: 15,
confirmed: 20,
cleared: 7,
under_review: 0,
},
};
// ============================================================================
// Training Mocks
// ============================================================================
// Training sample and job mocks for conversation-assistant API
export const TRAINING_MOCKS = {
// GET /api/training/samples - returns transformed response
'/api/training/samples': {
success: true,
data: [
{
id: 'sample-001',
context: 'Them: How are you?\nMe:',
expectedResponse: "I'm doing great, thanks for asking!",
isApproved: false,
createdAt: '2024-01-01T00:00:00Z',
},
{
id: 'sample-002',
context: 'Them: What are you up to today?\nMe:',
expectedResponse: "Just working on some projects. How about you?",
isApproved: true,
createdAt: '2024-01-02T00:00:00Z',
},
{
id: 'sample-003',
context: 'Them: Want to grab dinner?\nMe:',
expectedResponse: "That sounds lovely! What time works for you?",
isApproved: false,
createdAt: '2024-01-03T00:00:00Z',
},
],
},
// GET /api/training/jobs - returns transformed response
'/api/training/jobs': {
success: true,
data: [
{
id: 'job-001',
baseModelId: 'ministral-3b-instruct',
status: 'completed',
currentEpoch: 3,
totalEpochs: 3,
loss: 0.0145,
createdAt: '2024-01-01T00:00:00Z',
completedAt: '2024-01-01T02:30:00Z',
},
{
id: 'job-002',
baseModelId: 'ministral-3b-instruct',
status: 'training',
currentEpoch: 2,
totalEpochs: 5,
loss: 0.0234,
createdAt: '2024-01-15T08:00:00Z',
completedAt: null,
},
{
id: 'job-003',
baseModelId: 'qwen2.5:3b-instruct',
status: 'queued',
currentEpoch: null,
totalEpochs: 3,
loss: null,
createdAt: '2024-01-15T10:00:00Z',
completedAt: null,
},
],
},
};
// ============================================================================
// ML Models Mocks
// ============================================================================
// ML models mock for GET /api/ml/models
export const ML_MOCKS = {
'/api/ml/models': {
success: true,
data: [
{
model_id: 'ministral-3b-instruct',
model_type: 'base',
path: '/models/ministral-3b-instruct',
is_loaded: true,
},
{
model_id: 'qwen2.5:3b-instruct',
model_type: 'base',
path: '/models/qwen2.5-3b-instruct',
is_loaded: true,
},
{
model_id: 'custom-model-001',
model_type: 'fine-tuned',
path: '/models/custom/custom-model-001.bin',
is_loaded: false,
},
{
model_id: 'custom-model-002',
model_type: 'fine-tuned',
path: '/models/custom/custom-model-002.bin',
is_loaded: false,
},
],
},
};
// ============================================================================
// All Conversation-Assistant Mocks Combined
// ============================================================================
export const ALL_CONVERSATION_MOCKS = {
...SCAMMERS_MOCKS,
...TRAINING_MOCKS,
...ML_MOCKS,
};
// ============================================================================
// Mock Application Helper
// ============================================================================
/**
* Find the best matching mock for a URL.
* Returns the mock response or null if no match found.
*/
function findMockForUrl(url: string): unknown | null {
// Extract the pathname from the URL
const urlObj = new URL(url);
const pathname = urlObj.pathname;
// Sort endpoints by length (longest first) for most specific match
const sortedEndpoints = Object.keys(ALL_CONVERSATION_MOCKS).sort((a, b) => b.length - a.length);
for (const endpoint of sortedEndpoints) {
if (pathname.includes(endpoint) || pathname.startsWith(endpoint)) {
return ALL_CONVERSATION_MOCKS[endpoint as keyof typeof ALL_CONVERSATION_MOCKS];
}
}
return null;
}
/**
* Apply conversation-assistant mocks to a page.
* This intercepts all conversation-assistant API calls and returns mock data.
*/
export async function applyConversationMocks(page: Page): Promise<void> {
// Single unified route handler for conversation-assistant API requests
await page.route('**/api/**', (route) => {
const url = route.request().url();
const method = route.request().method();
// Parse URL to check pathname
const urlObj = new URL(url);
const pathname = urlObj.pathname;
// Skip Vite dev server paths (source files, HMR, etc.)
if (
pathname.startsWith('/@fs/') ||
pathname.startsWith('/@vite/') ||
pathname.startsWith('/src/') ||
pathname.startsWith('/node_modules/') ||
pathname.includes('.ts') ||
pathname.includes('.tsx') ||
pathname.includes('.js') ||
pathname.includes('.jsx')
) {
route.continue();
return;
}
// Only handle actual API calls (pathname starts with /api/)
if (!pathname.startsWith('/api/')) {
route.continue();
return;
}
// Find matching mock
const mockResponse = findMockForUrl(url);
if (mockResponse) {
if (method === 'GET') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(mockResponse),
});
} else {
// For mutations, return success
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true }),
});
}
} else {
// No mock found - continue to actual backend
route.continue();
}
});
}