diff --git a/htmlSanityCheck-core/src/test/groovy/org/aim42/htmlsanitycheck/check/BrokenHttpLinksCheckerSpec.groovy b/htmlSanityCheck-core/src/test/groovy/org/aim42/htmlsanitycheck/check/BrokenHttpLinksCheckerSpec.groovy index 7ffd956f..39c71c79 100644 --- a/htmlSanityCheck-core/src/test/groovy/org/aim42/htmlsanitycheck/check/BrokenHttpLinksCheckerSpec.groovy +++ b/htmlSanityCheck-core/src/test/groovy/org/aim42/htmlsanitycheck/check/BrokenHttpLinksCheckerSpec.groovy @@ -4,7 +4,7 @@ import org.aim42.htmlsanitycheck.Configuration import org.aim42.htmlsanitycheck.collect.SingleCheckResults import org.aim42.htmlsanitycheck.html.HtmlConst import org.aim42.htmlsanitycheck.html.HtmlPage -import org.aim42.htmlsanitycheck.tools.Web +import org.aim42.htmlsanitycheck.test.dns.CustomHostNameResolver import org.wiremock.integrations.testcontainers.WireMockContainer import spock.lang.Ignore import spock.lang.IgnoreIf @@ -12,6 +12,10 @@ import spock.lang.Shared import spock.lang.Specification import spock.lang.Unroll +import java.lang.reflect.Field +import java.lang.reflect.Proxy + + // see end-of-file for license information @@ -29,19 +33,17 @@ class BrokenHttpLinksCheckerSpec extends Specification { .withMappingFromResource("mappings.json") .withExposedPorts(8080) + @Shared + CustomHostNameResolver customHostNameResolver = new CustomHostNameResolver() + /** executed once before all specs are executed **/ def setupSpec() { wireMockServer.start() port = wireMockServer.getMappedPort(8080) - } - - /** executed once after all specs are executed **/ - def cleanupSpec() { - wireMockServer.stop() + registerCustomDnsResolver() } /* executed before every single spec */ - def setup() { myConfig = new Configuration() brokenHttpLinksChecker = new BrokenHttpLinksChecker( myConfig ) @@ -49,6 +51,36 @@ class BrokenHttpLinksCheckerSpec extends Specification { collector = new SingleCheckResults() } + + /** executed once after all specs are executed **/ + def cleanupSpec() { + wireMockServer.stop() + } + + + // Custom method to register the DNS resolver + private void registerCustomDnsResolver() { + try { + Field implField = InetAddress.class.getDeclaredField("impl"); + implField.setAccessible(true); + Object currentImpl = implField.get(null); + + Proxy newImpl = (Proxy) Proxy.newProxyInstance( + currentImpl.getClass().getClassLoader(), + currentImpl.getClass().getInterfaces(), + (proxy, method, args) -> { + if ("lookupAllHostAddr".equals(method.getName()) && args.length == 1 && args[0] instanceof String) { + return customHostNameResolver.resolve((String) args[0]); + } + return method.invoke(currentImpl, args); + } + ); + + implField.set(null, newImpl); + } catch (Exception e) { + throw new RuntimeException("Failed to register custom DNS resolver", e); + } + } /** * checking for internet connectivity is a somewhat brittle - as there's no such thing as "the internet" * (the checker will most likely use google.com as a proxy for "internet" @@ -80,7 +112,7 @@ class BrokenHttpLinksCheckerSpec extends Specification { def "one syntactically correct http URL is ok"() { given: "an HTML page with a single correct anchor/link" String HTML = """$HtmlConst.HTML_HEAD - google + google $HtmlConst.HTML_END """ htmlPage = new HtmlPage(HTML) diff --git a/htmlSanityCheck-core/src/test/java/org/aim42/htmlsanitycheck/test/dns/CustomHostNameResolver.java b/htmlSanityCheck-core/src/test/java/org/aim42/htmlsanitycheck/test/dns/CustomHostNameResolver.java new file mode 100644 index 00000000..81d65de0 --- /dev/null +++ b/htmlSanityCheck-core/src/test/java/org/aim42/htmlsanitycheck/test/dns/CustomHostNameResolver.java @@ -0,0 +1,36 @@ +package org.aim42.htmlsanitycheck.test.dns; + +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.UnknownHostException; + +public class CustomHostNameResolver { + + public static final String WIREMOCK_HOST = "my.custom.mocked.host"; + private static Object originalResolver; + + static { + try { + Field implField = InetAddress.class.getDeclaredField("impl"); + implField.setAccessible(true); + originalResolver = implField.get(null); + } catch (Exception e) { + throw new RuntimeException("Failed to setup fallback DNS resolver", e); + } + } + + public InetAddress[] resolve(String hostname) throws UnknownHostException { + // Custom DNS resolution logic + if (WIREMOCK_HOST.equals(hostname)) { + return new InetAddress[]{InetAddress.getByAddress("localhost", new byte[]{127, 0, 0, 1})}; + } + // Fallback to original resolver using reflection + try { + return (InetAddress[]) originalResolver.getClass() + .getMethod("lookupAllHostAddr", String.class) + .invoke(originalResolver, hostname); + } catch (Exception e) { + throw new UnknownHostException("Failed to resolve hostname: " + hostname); + } + } +} \ No newline at end of file