const { resolveServiceUrl } = require('../url-resolver'); describe('URL Resolver — DashCaddy service URL resolution', () => { const buildServiceUrl = jest.fn(id => `https://${id}.sami`); beforeEach(() => { buildServiceUrl.mockClear(); }); describe('Internet connectivity check', () => { it('always resolves "internet" to google.com regardless of config', () => { expect(resolveServiceUrl('internet', null, null, buildServiceUrl)) .toBe('https://www.google.com'); expect(buildServiceUrl).not.toHaveBeenCalled(); }); it('ignores service object for internet ID', () => { const service = { url: 'http://custom.test', isExternal: true, externalUrl: 'http://ext.test' }; expect(resolveServiceUrl('internet', service, {}, buildServiceUrl)) .toBe('https://www.google.com'); }); }); describe('External services (seedhost, cloud-hosted)', () => { it('uses externalUrl for services marked isExternal', () => { const service = { isExternal: true, externalUrl: 'https://usw123.seedhost.eu/sami/radarr' }; expect(resolveServiceUrl('radarr', service, {}, buildServiceUrl)) .toBe('https://usw123.seedhost.eu/sami/radarr'); }); it('ignores isExternal if externalUrl is missing', () => { const service = { isExternal: true }; expect(resolveServiceUrl('plex', service, {}, buildServiceUrl)) .toBe('https://plex.sami'); }); }); describe('Custom URL override on service', () => { it('uses service.url with http prefix as-is', () => { const service = { url: 'http://192.168.1.100:32400' }; expect(resolveServiceUrl('plex', service, {}, buildServiceUrl)) .toBe('http://192.168.1.100:32400'); }); it('uses service.url with https prefix as-is', () => { const service = { url: 'https://plex.mydomain.com' }; expect(resolveServiceUrl('plex', service, {}, buildServiceUrl)) .toBe('https://plex.mydomain.com'); }); it('prepends https:// to bare hostnames', () => { const service = { url: 'plex.sami' }; expect(resolveServiceUrl('plex', service, {}, buildServiceUrl)) .toBe('https://plex.sami'); }); }); describe('DNS server resolution (Technitium, Pi-hole)', () => { it('resolves DNS server by ID from siteConfig', () => { const siteConfig = { dnsServers: { dns1: { ip: '192.168.254.204', port: 5380 }, dns2: { ip: '100.74.102.61', port: 5380 }, } }; expect(resolveServiceUrl('dns1', null, siteConfig, buildServiceUrl)) .toBe('http://192.168.254.204:5380'); expect(resolveServiceUrl('dns2', null, siteConfig, buildServiceUrl)) .toBe('http://100.74.102.61:5380'); }); it('defaults to port 5380 when port is omitted', () => { const siteConfig = { dnsServers: { dns1: { ip: '10.0.0.1' } } }; expect(resolveServiceUrl('dns1', null, siteConfig, buildServiceUrl)) .toBe('http://10.0.0.1:5380'); }); }); describe('Fallback to buildServiceUrl (Caddy subdomain/subdirectory)', () => { it('falls back for local services with no special config', () => { resolveServiceUrl('radarr', { name: 'Radarr' }, {}, buildServiceUrl); expect(buildServiceUrl).toHaveBeenCalledWith('radarr'); }); it('works when service is null (top-card items)', () => { expect(resolveServiceUrl('sonarr', null, {}, buildServiceUrl)) .toBe('https://sonarr.sami'); }); it('works when siteConfig is null', () => { expect(resolveServiceUrl('jellyfin', null, null, buildServiceUrl)) .toBe('https://jellyfin.sami'); }); }); describe('Priority chain — higher priority wins', () => { const fullService = { isExternal: true, externalUrl: 'https://external.test', url: 'http://custom.test', }; const siteConfig = { dnsServers: { myservice: { ip: '10.0.0.1', port: 5380 } } }; it('externalUrl wins over service.url and DNS', () => { expect(resolveServiceUrl('myservice', fullService, siteConfig, buildServiceUrl)) .toBe('https://external.test'); }); it('service.url wins over DNS and fallback', () => { const service = { url: 'http://custom.test' }; expect(resolveServiceUrl('myservice', service, siteConfig, buildServiceUrl)) .toBe('http://custom.test'); }); it('DNS wins over fallback', () => { expect(resolveServiceUrl('myservice', null, siteConfig, buildServiceUrl)) .toBe('http://10.0.0.1:5380'); }); }); });