import { expect, tap } from '@git.zone/tstest/tapbundle'; import { AdguardClient, AdguardIntegration, type IAdguardSnapshot } from '../../ts/integrations/adguard/index.js'; const snapshot: IAdguardSnapshot = { online: true, host: '127.0.0.1', port: 3000, status: { running: true, version: 'v0.107.45', protection_enabled: false }, filtering: { enabled: true, filters: [{ name: 'Test', url: 'https://example.test/filter.txt', enabled: true, rules_count: 1 }] }, queryLog: { enabled: true, interval: 604800000, anonymize_client_ip: false, ignored: [], ignored_enabled: false }, safebrowsing: { enabled: false }, safesearch: { enabled: false }, parental: { enabled: false }, stats: { num_dns_queries: 1, num_blocked_filtering: 0, avg_processing_time: 0.01 }, }; tap.test('reads AdGuard Home snapshot from local HTTP API endpoints', async () => { const originalFetch = globalThis.fetch; const calls: Array<{ url: string; method?: string; authorization?: string }> = []; globalThis.fetch = (async (urlArg: URL | RequestInfo, initArg?: RequestInit) => { calls.push({ url: String(urlArg), method: initArg?.method, authorization: (initArg?.headers as Record | undefined)?.authorization }); const path = new URL(String(urlArg)).pathname; const responses: Record = { '/control/status': { running: true, version: 'v0.107.45', protection_enabled: true, http_port: 3000 }, '/control/filtering/status': { enabled: true, filters: [{ name: 'Filter', url: 'https://example.test/filter.txt', rules_count: 10, enabled: true }] }, '/control/querylog/config': { enabled: true, interval: 604800000, anonymize_client_ip: false, ignored: [], ignored_enabled: false }, '/control/safebrowsing/status': { enabled: true }, '/control/safesearch/status': { enabled: false }, '/control/parental/status': { enabled: false }, '/control/stats': { num_dns_queries: 40, num_blocked_filtering: 10, avg_processing_time: 0.02 }, '/control/version.json': { disabled: true }, }; return new Response(JSON.stringify(responses[path] || {}), { status: 200, headers: { 'content-type': 'application/json' } }); }) as typeof globalThis.fetch; try { const result = await new AdguardClient({ host: '127.0.0.1', port: 3000, ssl: false, username: 'admin', password: 'secret' }).getSnapshot(); expect(result.online).toBeTrue(); expect(result.status.version).toEqual('v0.107.45'); expect(result.stats.num_blocked_filtering).toEqual(10); expect(calls.some((callArg) => callArg.url === 'http://127.0.0.1:3000/control/status')).toBeTrue(); expect(calls[0].authorization).toEqual(`Basic ${Buffer.from('admin:secret').toString('base64')}`); } finally { globalThis.fetch = originalFetch; } }); tap.test('executes live AdGuard commands only through HTTP client or executor', async () => { const originalFetch = globalThis.fetch; const calls: Array<{ url: string; method?: string; body?: string }> = []; globalThis.fetch = (async (urlArg: URL | RequestInfo, initArg?: RequestInit) => { calls.push({ url: String(urlArg), method: initArg?.method, body: initArg?.body as string | undefined }); return new Response('{}', { status: 200, headers: { 'content-type': 'application/json' } }); }) as typeof globalThis.fetch; try { const runtime = await new AdguardIntegration().setup({ host: '127.0.0.1', port: 3000, snapshot }, {}); const result = await runtime.callService?.({ domain: 'switch', service: 'turn_on', target: { entityId: 'switch.adguard_home_protection' }, }); expect(result?.success).toBeTrue(); expect(calls[0].url).toEqual('http://127.0.0.1:3000/control/protection'); expect(calls[0].method).toEqual('POST'); expect(JSON.parse(calls[0].body || '{}')).toEqual({ enabled: true }); await runtime.destroy(); } finally { globalThis.fetch = originalFetch; } }); tap.test('does not report snapshot-only AdGuard commands as successful', async () => { const runtime = await new AdguardIntegration().setup({ snapshot: { ...snapshot, host: undefined } }, {}); const protectionEntity = (await runtime.entities()).find((entityArg) => entityArg.attributes?.adguardSwitchKey === 'protection'); const result = await runtime.callService?.({ domain: 'switch', service: 'turn_on', target: { entityId: protectionEntity?.id }, }); expect(result?.success).toBeFalse(); expect(result?.error).toEqual('AdGuard Home live commands require config.host or commandExecutor; snapshot-only commands are not reported as successful.'); await runtime.destroy(); }); export default tap.start();