- monitored_services table (mig 015) replaces config/services.json (now a boot seed) - owner CRUD over /api/health/services; GET is DB-backed; cron+worker read the DB - discover.lan worker: pure-Node TCP sweep + HTTP-title probe -> disabled 'discovered' candidates (never clobbers curated entries); POST /api/health/discover + GET .../discovered - dashboard: Scan button + Discovered(N) section with one-click promote Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
41 lines
2.1 KiB
JavaScript
41 lines
2.1 KiB
JavaScript
import { describe, it, expect, beforeAll, beforeEach, afterEach, vi } from 'vitest';
|
|
import { resetDb } from '../helpers/db.js';
|
|
import { migrateUp } from '../../lib/db/migrate.js';
|
|
import * as worker from '../../lib/jobs/workers/discover.js';
|
|
import * as services from '../../lib/db/repos/monitored_services.js';
|
|
|
|
beforeAll(async () => { await resetDb(); await migrateUp(); });
|
|
beforeEach(async () => { await resetDb(); await migrateUp(); });
|
|
afterEach(() => worker._setProbes()); // restore real probes
|
|
|
|
describe('discover.lan worker', () => {
|
|
it('upserts disabled discovered candidates for live HTTP host:ports', async () => {
|
|
// Fake: only .50:3000 and .60:8096 are "open".
|
|
const tcp = vi.fn(async (host, port) =>
|
|
(host === '192.168.1.50' && port === 3000) || (host === '192.168.1.60' && port === 8096));
|
|
const http = vi.fn(async (url) => ({ code: 200, title: url.includes('8096') ? 'Jellyfin' : 'Gitea' }));
|
|
worker._setProbes({ tcp, http });
|
|
|
|
const out = await worker.handler({ data: { subnet: '192.168.1' } });
|
|
expect(out.open).toBe(2);
|
|
expect(out.added).toBe(2);
|
|
|
|
const disc = await services.listDiscovered();
|
|
expect(disc.map(s => s.name).sort()).toEqual(['Gitea', 'Jellyfin']);
|
|
expect(disc.every(s => s.source === 'discovered' && s.enabled === false)).toBe(true);
|
|
expect(await services.listEnabled()).toHaveLength(0); // candidates don't auto-join the band
|
|
});
|
|
|
|
it('does not re-add a service whose url already exists (manual wins, idempotent)', async () => {
|
|
await services.create({ id: 'plex', name: 'Plex', category: 'media', url: 'http://192.168.1.50:32400', check: { type: 'http' } });
|
|
const tcp = vi.fn(async (host, port) => host === '192.168.1.50' && port === 32400);
|
|
const http = vi.fn(async () => ({ code: 200, title: 'Plex' }));
|
|
worker._setProbes({ tcp, http });
|
|
|
|
const out = await worker.handler({ data: { subnet: '192.168.1' } });
|
|
expect(out.open).toBe(1);
|
|
expect(out.added).toBe(0); // url already known → skipped
|
|
expect(await services.listDiscovered()).toHaveLength(0);
|
|
});
|
|
});
|