import { expect, tap } from '@git.zone/tstest/tapbundle'; import { PiHoleMapper, type IPiHoleSnapshot } from '../../ts/integrations/pi_hole/index.js'; const v5Snapshot: IPiHoleSnapshot = PiHoleMapper.toSnapshot({ config: { host: '192.168.1.2', port: 80, name: 'Home DNS', apiVersion: 5, rawData: { v5Summary: { status: 'enabled', ads_blocked_today: 42, ads_percentage_today: 21.55, clients_ever_seen: 8, dns_queries_today: 200, domains_being_blocked: 150000, queries_cached: 50, queries_forwarded: 120, unique_clients: 5, unique_domains: 90, }, v5Versions: { core_current: 'v5.18.3', core_latest: 'v5.18.4', core_update: true, web_current: 'v5.21', web_latest: 'v5.21', web_update: false, FTL_current: 'v5.25.2', FTL_latest: 'v5.25.2', FTL_update: false, }, }, }, online: true, source: 'manual', }); tap.test('maps Pi-hole v5 status, statistics, updates, and switch control', async () => { const devices = PiHoleMapper.toDevices(v5Snapshot); const entities = PiHoleMapper.toEntities(v5Snapshot); expect(devices[0].id).toEqual('pi_hole.service.192_168_1_2_80'); expect(devices[0].online).toBeTrue(); expect(entities.find((entityArg) => entityArg.id === 'switch.home_dns')?.state).toEqual('on'); expect(entities.find((entityArg) => entityArg.id === 'binary_sensor.home_dns_status')?.state).toEqual('on'); expect(entities.find((entityArg) => entityArg.id === 'sensor.home_dns_ads_blocked_today')?.state).toEqual(42); expect(entities.find((entityArg) => entityArg.id === 'sensor.home_dns_ads_percentage_blocked_today')?.state).toEqual(21.6); expect(entities.find((entityArg) => entityArg.id === 'sensor.home_dns_dns_queries_today')?.state).toEqual(200); expect(entities.find((entityArg) => entityArg.id === 'update.home_dns_core_update_available')?.attributes?.latestVersion).toEqual('v5.18.4'); }); tap.test('models Pi-hole enable, disable, and refresh commands without secrets', async () => { const disableCommand = PiHoleMapper.commandForService(v5Snapshot, { domain: 'switch', service: 'turn_off', target: { entityId: 'switch.home_dns' }, data: { duration: '00:10:00' }, }); const enableCommand = PiHoleMapper.commandForService(v5Snapshot, { domain: 'pi_hole', service: 'enable', target: { deviceId: 'pi_hole.service.192_168_1_2_80' }, }); const refreshCommand = PiHoleMapper.commandForService(v5Snapshot, { domain: 'pi_hole', service: 'refresh', target: {}, }); expect(Boolean(disableCommand && !('error' in disableCommand))).toBeTrue(); if (disableCommand && !('error' in disableCommand)) { expect(disableCommand.type).toEqual('disable'); expect(disableCommand.path).toEqual('/admin/api.php'); expect(disableCommand.query).toEqual({ disable: 600 }); expect(JSON.stringify(disableCommand).includes('auth')).toBeFalse(); } expect(Boolean(enableCommand && !('error' in enableCommand))).toBeTrue(); if (enableCommand && !('error' in enableCommand)) { expect(enableCommand.query).toEqual({ enable: 'True' }); } expect(refreshCommand && !('error' in refreshCommand) ? refreshCommand.type : undefined).toEqual('refresh'); }); tap.test('maps Pi-hole v6 nested summary and version payloads', async () => { const snapshot = PiHoleMapper.toSnapshot({ config: { host: 'pihole.local', name: 'Family DNS', apiVersion: 6, rawData: { v6Blocking: { blocking: 'disabled' }, v6Summary: { queries: { blocked: 12, percent_blocked: 24.123, total: 50, cached: 8, forwarded: 30, unique_domains: 40 }, clients: { total: 6, active: 3 }, gravity: { domains_being_blocked: 180000 }, }, v6Versions: { version: { core: { local: { version: 'v6.0', hash: 'a' }, remote: { version: 'v6.1', hash: 'b' } }, web: { local: { version: 'v6.0', hash: 'c' }, remote: { version: 'v6.0', hash: 'c' } }, ftl: { local: { version: 'v6.0', hash: 'd' }, remote: { version: 'v6.0', hash: 'd' } }, }, }, }, }, online: true, source: 'manual', }); const entities = PiHoleMapper.toEntities(snapshot); expect(entities.find((entityArg) => entityArg.id === 'switch.family_dns')?.state).toEqual('off'); expect(entities.find((entityArg) => entityArg.id === 'sensor.family_dns_ads_blocked')?.state).toEqual(12); expect(entities.find((entityArg) => entityArg.id === 'sensor.family_dns_ads_percentage_blocked')?.state).toEqual(24.12); expect(entities.find((entityArg) => entityArg.id === 'sensor.family_dns_dns_queries')?.state).toEqual(50); expect(entities.find((entityArg) => entityArg.id === 'update.family_dns_core_update_available')?.state).toEqual('on'); }); export default tap.start();