import React from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import AdminSystemPage from './views/AdminSystemPage'; import { I18nProvider } from './i18n/I18nProvider'; import { api } from './api'; jest.mock('./api', () => ({ api: { get: jest.fn(), post: jest.fn(), put: jest.fn(), patch: jest.fn(), delete: jest.fn(), interceptors: { request: { use: jest.fn() }, response: { use: jest.fn() } }, }, getApiErrorMessage: (error: any, fallback?: string) => fallback || 'Request failed.', })); const mockedApi = api as jest.Mocked; describe('AdminSystemPage', () => { beforeEach(() => { mockedApi.get.mockImplementation((url: string) => { if (url === '/admin/system') { return Promise.resolve({ data: { environment: 'Production', contentRoot: '/app', version: '1.2.3', commitSha: 'abc1234', buildStamp: '2026-03-23 11:00 UTC', storage: { dataRoot: '/data', dbPath: '/data/jobtracker.db', dbExists: true, dbSizeBytes: 2048, companyCount: 3, jobCount: 7, deletedCount: 1 }, email: { enabled: true, host: 'smtp.example.test', port: 587, enableSsl: true, from: 'noreply@example.test', fromName: 'Jobbjakt' }, database: { provider: 'mariadb', looksConfigured: true, canConnect: true, target: 'server=db', usesFileStorage: false, warning: null }, runtime: { framework: '.NET 9', osDescription: 'Linux', processArchitecture: 'X64', machineName: 'app-01' }, auth: { required: true, hasJwtKey: true, googleConfigured: true, gmailConfigured: true }, ai: { healthy: true, model: 'distilbart', device: 'cpu', gpuAvailable: false, gpuName: null, ocrAvailable: true, ocrLanguages: 'eng', ollamaConfigured: true, ollamaReachable: true, ollamaModel: 'qwen2.5:7b', ollamaModelAvailable: true, ollamaVersion: '0.7.0', ollamaInstalledModels: ['qwen2.5:7b', 'nomic-embed-text'], ollamaLoadedModels: ['qwen2.5:7b'], ollamaLoadedCount: 1, healthLatencyMs: 12.4, probeLatencyMs: 25.8, lastProbeAt: '2026-03-23T10:00:00Z', lastProbeSuccessAt: '2026-03-23T10:00:00Z', lastProbeFailureAt: null, probeFailures: 0, requests: 18, cacheHits: 9, cacheMisses: 9, failures: 0, averageLatencyMs: 42.2, ocrRequests: 5, ocrFailures: 0, averageOcrLatencyMs: 88.4, lastOcrSuccessAt: '2026-03-23T10:05:00Z', lastOcrFailureAt: null, lastSuccessAt: '2026-03-23T10:04:00Z', lastFailureAt: null, lastError: null, }, }, } as any); } if (url === '/admin/system/email-settings') { return Promise.resolve({ data: { enabled: true, host: 'smtp.example.test', port: 587, user: 'mailer@example.test', from: 'noreply@example.test', fromName: 'Jobbjakt', enableSsl: true, timeoutMs: 15000, usesOverrides: false, hasPassword: true, }, } as any); } if (url === '/admin/system/cv-benchmark') { return Promise.resolve({ data: { rootPath: '/data/CvBenchmarks/latest', lastUpdatedAtUtc: '2026-03-23T10:10:00Z', reportMarkdown: '# CV benchmark report\n\n- Files: 4', indexJson: JSON.stringify({ CorpusRoot: '/home/pi/cvs', OutputRoot: '/data/CvBenchmarks/latest', GeneratedAtUtc: '2026-03-23T10:10:00Z', TotalFiles: 4, AverageCoverage: 0.72, AverageConfidence: 0.66, AverageConsistency: 0.94, FilesWithSuspiciousLocations: 1, MissingApprovedFixtures: 4, Entries: [ { FileName: 'cv.txt', Slug: 'cv-txt', Extension: '.txt', Characters: 2000, OutputPath: '/data/CvBenchmarks/latest/outputs/cv-txt.json', ApprovedFixturePath: null, CandidateFixturePath: '/data/CvBenchmarks/latest/candidate-fixtures/cv-txt.json', ContactLocation: 'San Francisco, Hobbies', FirstJob: '* July', FirstJobLocation: null, FirstEducation: '* September', FirstEducationLocation: null, QualificationLevels: ['Other'], SuspiciousLocations: [], CoverageScore: 0.5, ConfidenceScore: 0.65, ConsistencyScore: 0.8, DiffSummary: 'No approved fixture yet — candidate fixture written.', }, ], }), }, } as any); } return Promise.resolve({ data: {} } as any); }); mockedApi.put.mockResolvedValue({ data: { enabled: true, host: 'smtp.changed.test', port: 2525, user: 'mailer@example.test', from: 'noreply@example.test', fromName: 'Jobbjakt', enableSsl: true, timeoutMs: 15000, usesOverrides: true, hasPassword: true, }, } as any); }); afterEach(() => { jest.clearAllMocks(); }); it('renders AI service health, latency, and OCR readiness', async () => { render( , ); await waitFor(() => { expect(screen.getByText('Production')).toBeTruthy(); }); expect(screen.getByText(/25.8 ms probe/i)).toBeTruthy(); expect(screen.getByText('OCR eng')).toBeTruthy(); expect(screen.getAllByText(/ollama configured/i).length).toBeGreaterThan(0); expect(screen.getByText(/ollama version/i)).toBeTruthy(); expect(screen.getByText(/model · qwen2.5:7b/i)).toBeTruthy(); expect(screen.getByText(/cv benchmark review/i)).toBeTruthy(); expect(screen.getByText(/top parser findings/i)).toBeTruthy(); expect(screen.getByText(/suspicious contact location: san francisco, hobbies/i)).toBeTruthy(); expect(screen.getByText('OCR avg latency')).toBeTruthy(); expect(screen.getByText('88.4 ms')).toBeTruthy(); }); it('loads and saves smtp settings from the settings tab', async () => { render( , ); expect(await screen.findByText('AI service')).toBeTruthy(); fireEvent.click(screen.getByRole('tab', { name: /settings/i })); const hostInput = await screen.findByLabelText(/host/i); fireEvent.change(hostInput, { target: { value: 'smtp.changed.test' } }); fireEvent.change(screen.getByLabelText(/port/i), { target: { value: '2525' } }); fireEvent.click(screen.getByRole('button', { name: /save settings/i })); await waitFor(() => { expect(mockedApi.put).toHaveBeenCalledWith('/admin/system/email-settings', expect.objectContaining({ host: 'smtp.changed.test', port: 2525, })); }); }); });