mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-12 17:05:37 +02:00
Adds a passive log-health check that complements the active Connection Diagnostic. Scans Bambuddy's recent app log against a curated allowlist catalog of known failure signatures (rejected access code, FTPS :990 timeout, FTPS TLS failure, flapping MQTT, unreachable camera, SQLite "database is locked" contention), dedupes and classifies each finding as layer8/environment/bug, and deep-links to the troubleshooting wiki. Sample log lines are sanitized before they leave the process. Exposed via GET /system/health and surfaced on two surfaces sharing one SystemHealthPanel component: a System Health section on the System page, and inline in the bug reporter when the form opens. The Add-Printer and Edit-Printer dialogs gained a setup-time pre-flight: saving runs the connection diagnostic and, on a failed check, warns with a "save anyway" escape hatch instead of silently saving a printer that will immediately show offline. Log read/parse/sanitize primitives extracted from routes/support.py into a shared services/log_reader.py (behaviour-preserving); affected support tests repointed accordingly. Tests: test_log_health.py (11), test_system_api.py (2 new), SystemHealthPanel + BugReportBubble + AddPrinterPreflight + EditPrinterPreflight (8 frontend). All strings translated across the 9 locales. Backend ruff clean, full unit suite green, frontend build + eslint clean, i18n parity green.
94 lines
3.6 KiB
TypeScript
94 lines
3.6 KiB
TypeScript
/**
|
|
* Tests for the Add-Printer setup-time pre-flight.
|
|
*
|
|
* On save, the modal runs the connection diagnostic; if any check fails it
|
|
* warns (rather than blocks) before the printer is added.
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import { screen, waitFor } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { render } from '../utils';
|
|
import { PrintersPage } from '../../pages/PrintersPage';
|
|
import { http, HttpResponse } from 'msw';
|
|
import { server } from '../mocks/server';
|
|
|
|
describe('AddPrinterModal pre-flight', () => {
|
|
beforeEach(() => {
|
|
server.use(
|
|
http.get('/api/v1/printers/', () => HttpResponse.json([])),
|
|
http.get('/api/v1/queue/', () => HttpResponse.json([])),
|
|
http.get('/api/v1/discovery/info', () =>
|
|
HttpResponse.json({ is_docker: false, ssdp_running: false, scan_running: false, subnets: [] }),
|
|
),
|
|
);
|
|
});
|
|
|
|
it('warns instead of saving when a connection check fails', async () => {
|
|
const user = userEvent.setup();
|
|
server.use(
|
|
http.post('/api/v1/printers/diagnostic', () =>
|
|
HttpResponse.json({
|
|
printer_id: null,
|
|
ip_address: '192.168.1.55',
|
|
overall: 'problems',
|
|
checks: [{ id: 'developer_mode', status: 'fail', params: {} }],
|
|
}),
|
|
),
|
|
);
|
|
|
|
render(<PrintersPage />);
|
|
await user.click(await screen.findByText(/add printer/i));
|
|
|
|
await user.type(await screen.findByPlaceholderText('My Printer'), 'Test Printer');
|
|
await user.type(screen.getByPlaceholderText('192.168.1.100 or printer.local'), '192.168.1.55');
|
|
await user.type(screen.getByPlaceholderText('01P00A000000000'), '01P00A000000000');
|
|
await user.type(screen.getByPlaceholderText('From printer settings'), '12345678');
|
|
|
|
const submit = screen
|
|
.getAllByRole('button', { name: /add printer/i })
|
|
.find((b) => b.getAttribute('type') === 'submit')!;
|
|
await user.click(submit);
|
|
|
|
// The failed check surfaces a warning with a "save anyway" escape hatch.
|
|
expect(await screen.findByText(/Some connection checks failed/i)).toBeInTheDocument();
|
|
expect(screen.getByRole('button', { name: /save anyway/i })).toBeInTheDocument();
|
|
expect(screen.getByText(/LAN Developer Mode/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it('saves directly when all connection checks pass', async () => {
|
|
const user = userEvent.setup();
|
|
let created = false;
|
|
server.use(
|
|
http.post('/api/v1/printers/diagnostic', () =>
|
|
HttpResponse.json({
|
|
printer_id: null,
|
|
ip_address: '192.168.1.55',
|
|
overall: 'ok',
|
|
checks: [{ id: 'developer_mode', status: 'pass', params: {} }],
|
|
}),
|
|
),
|
|
http.post('/api/v1/printers/', async () => {
|
|
created = true;
|
|
return HttpResponse.json({ id: 9, name: 'Test Printer' });
|
|
}),
|
|
);
|
|
|
|
render(<PrintersPage />);
|
|
await user.click(await screen.findByText(/add printer/i));
|
|
|
|
await user.type(await screen.findByPlaceholderText('My Printer'), 'Test Printer');
|
|
await user.type(screen.getByPlaceholderText('192.168.1.100 or printer.local'), '192.168.1.55');
|
|
await user.type(screen.getByPlaceholderText('01P00A000000000'), '01P00A000000000');
|
|
await user.type(screen.getByPlaceholderText('From printer settings'), '12345678');
|
|
|
|
const submit = screen
|
|
.getAllByRole('button', { name: /add printer/i })
|
|
.find((b) => b.getAttribute('type') === 'submit')!;
|
|
await user.click(submit);
|
|
|
|
await waitFor(() => expect(created).toBe(true));
|
|
expect(screen.queryByText(/Some connection checks failed/i)).not.toBeInTheDocument();
|
|
});
|
|
});
|