diff --git a/build-logic/tools/build.gradle.kts b/build-logic/tools/build.gradle.kts index 8d886b449..508049dc9 100644 --- a/build-logic/tools/build.gradle.kts +++ b/build-logic/tools/build.gradle.kts @@ -18,4 +18,6 @@ ktlint { dependencies { implementation(libs.dnsjava) + testImplementation(testLibs.junit.junit) + testImplementation(testLibs.mockk) } diff --git a/build-logic/tools/src/main/java/org/signal/buildtools/StaticIpResolver.kt b/build-logic/tools/src/main/java/org/signal/buildtools/StaticIpResolver.kt index edb9f04bf..8ec5f0ea8 100644 --- a/build-logic/tools/src/main/java/org/signal/buildtools/StaticIpResolver.kt +++ b/build-logic/tools/src/main/java/org/signal/buildtools/StaticIpResolver.kt @@ -14,7 +14,7 @@ import kotlin.streams.toList * Feeds into our custom DNS resolver to provide a static IP fallback for our services. */ class StaticIpResolver @JvmOverloads constructor( - private val resolverProvider: () -> Resolver = { SimpleResolver("1.1.1.1") } + private val recordFetcher: RecordFetcher = RealRecordFetcher ) { /** @@ -56,13 +56,7 @@ class StaticIpResolver @JvmOverloads constructor( private fun resolveOnce(hostName: String): List { try { - val resolver = resolverProvider() - val lookup: Lookup = doLookup(hostName) - - lookup.setResolver(resolver) - - val records: Array? = lookup.run() - + val records = recordFetcher.fetchRecords(hostName) if (records != null) { return records .filter { it.type == Type.A } @@ -71,19 +65,34 @@ class StaticIpResolver @JvmOverloads constructor( .map { it.hostAddress } .filterNotNull() } else { - throw IllegalStateException("Failed to resolve host! $hostName") + throw IllegalStateException("Failed to resolve host! Lookup did not return any records.. $hostName") } } catch (e: UnknownHostException) { - throw IllegalStateException("Failed to resolve host! $hostName") + throw IllegalStateException("Failed to resolve host! $hostName", e) } } - @Throws(UnknownHostException::class) - private fun doLookup(hostname: String): Lookup { - try { - return Lookup(hostname) - } catch (e: Throwable) { - throw UnknownHostException() + interface RecordFetcher { + fun fetchRecords(hostName: String): Array? + } + + private object RealRecordFetcher : RecordFetcher { + override fun fetchRecords(hostName: String): Array? { + val resolver = SimpleResolver("1.1.1.1") + val lookup: Lookup = doLookup(hostName) + + lookup.setResolver(resolver) + + return lookup.run() + } + + @Throws(UnknownHostException::class) + private fun doLookup(hostname: String): Lookup { + try { + return Lookup(hostname) + } catch (e: Throwable) { + throw UnknownHostException() + } } } } diff --git a/build-logic/tools/src/test/java/org/signal/buildtools/StaticIpResolverTest.kt b/build-logic/tools/src/test/java/org/signal/buildtools/StaticIpResolverTest.kt new file mode 100644 index 000000000..cafadc624 --- /dev/null +++ b/build-logic/tools/src/test/java/org/signal/buildtools/StaticIpResolverTest.kt @@ -0,0 +1,50 @@ +package org.signal.buildtools + +import io.mockk.every +import io.mockk.mockk +import org.junit.Assert.assertEquals +import org.junit.Assert.fail +import org.junit.Test +import org.xbill.DNS.ARecord +import org.xbill.DNS.DClass +import org.xbill.DNS.Name +import org.xbill.DNS.Record +import java.lang.IllegalStateException +import java.net.Inet4Address + +class StaticIpResolverTest { + + companion object { + const val SIGNAL_DOT_ORG = "www.signal.org" + val SIGNAL_IP = byteArrayOf(123, 45, 67, 89) + val STRINGIFIED_IP = SIGNAL_IP.joinToString(".") + } + + @Test + fun `Given a hostname with records, when I resolveToBuildConfig, then I expect a matching IP`() { + val staticIpResolver = StaticIpResolver(FakeRecordFetcher()) + val actual = staticIpResolver.resolveToBuildConfig(SIGNAL_DOT_ORG) + val expected = """ + new String[]{"$STRINGIFIED_IP"} + """.trimIndent() + + assertEquals(expected, actual) + } + + @Test(expected = IllegalStateException::class) + fun `Given a hostname without records, when I resolveToBuildConfig, then I expect`() { + val staticIpResolver = StaticIpResolver(FakeRecordFetcher(emptyMap())) + staticIpResolver.resolveToBuildConfig(SIGNAL_DOT_ORG) + } + + private class FakeRecordFetcher(private val recordMap: Map?> = mapOf( + SIGNAL_DOT_ORG to arrayOf(ARecord(Name.fromString("www."), DClass.ANY, 0L, mockk { + every { address } returns SIGNAL_IP + every { hostAddress } returns STRINGIFIED_IP + })) + )) : StaticIpResolver.RecordFetcher { + override fun fetchRecords(hostName: String): Array? { + return recordMap[hostName] + } + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3e557faef..d42e3c2af 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,7 @@ task qa { group 'Verification' description 'Quality Assurance. Run before pushing.' dependsOn 'clean', + ':build-logic:tools:test', ':Signal-Android:testPlayProdReleaseUnitTest', ':Signal-Android:lintPlayProdRelease', 'Signal-Android:ktlintCheck',