Restructuring and more tests
This commit is contained in:
parent
4b0f3cefb8
commit
70a1141750
5 changed files with 139 additions and 29 deletions
|
@ -3,7 +3,7 @@ package de.rpr.ddnsclient;
|
|||
import de.rpr.ddnsclient.dyndns.DynDns;
|
||||
import de.rpr.ddnsclient.dyndns.DynDnsRouter;
|
||||
import de.rpr.ddnsclient.lookup.DnsResolver;
|
||||
import de.rpr.ddnsclient.lookup.IpProvider;
|
||||
import de.rpr.ddnsclient.lookup.PublicIpLookup;
|
||||
import de.rpr.ddnsclient.model.Config;
|
||||
import de.rpr.ddnsclient.model.DyndnsAuth;
|
||||
import de.rpr.ddnsclient.model.IPs;
|
||||
|
@ -23,7 +23,7 @@ public class Updater {
|
|||
private static final Logger log = Logger.getLogger(Updater.class);
|
||||
|
||||
private final DynDnsRouter dynDnsRouter;
|
||||
private final IpProvider ipProvider;
|
||||
private final PublicIpLookup publicIpLookup;
|
||||
private final DnsResolver dnsResolver;
|
||||
private final Duration backoff;
|
||||
|
||||
|
@ -33,12 +33,12 @@ public class Updater {
|
|||
|
||||
@Inject
|
||||
public Updater(DynDnsRouter dynDnsRouter,
|
||||
IpProvider ipProvider,
|
||||
PublicIpLookup publicIpLookup,
|
||||
DnsResolver dnsResolver,
|
||||
Config config,
|
||||
@ConfigProperty(name = "ddnsclient.backoff-duration:300s") Duration backoff) {
|
||||
this.dynDnsRouter = dynDnsRouter;
|
||||
this.ipProvider = ipProvider;
|
||||
this.publicIpLookup = publicIpLookup;
|
||||
this.dnsResolver = dnsResolver;
|
||||
this.config = config;
|
||||
this.backoff = backoff;
|
||||
|
@ -51,7 +51,7 @@ public class Updater {
|
|||
throw new IllegalStateException("Missing configuration");
|
||||
}
|
||||
|
||||
IPs publicIps = ipProvider.getPublicIps();
|
||||
IPs publicIps = publicIpLookup.get();
|
||||
log.debugf("Public ips - v4: %s, v6: %s", publicIps.v4(), publicIps.v6());
|
||||
|
||||
config.forEach(cfg -> {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package de.rpr.ddnsclient.lookup;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@ApplicationScoped
|
||||
class CurlProcessFactory {
|
||||
enum IpClass {
|
||||
V4,
|
||||
V6
|
||||
}
|
||||
|
||||
// Workaround: Can't get Mockito 5 to mock final classes
|
||||
static class ProcessBuilderWrapper {
|
||||
private final ProcessBuilder processBuilder;
|
||||
|
||||
ProcessBuilderWrapper(String... args) {
|
||||
this.processBuilder = new ProcessBuilder(args);
|
||||
}
|
||||
|
||||
Process start() throws IOException {
|
||||
return processBuilder.start();
|
||||
}
|
||||
}
|
||||
|
||||
ProcessBuilderWrapper create(String provider, IpClass ipClass) {
|
||||
if (ipClass == IpClass.V4) {
|
||||
return new ProcessBuilderWrapper("curl", "--ipv4", provider);
|
||||
} else {
|
||||
return new ProcessBuilderWrapper("curl", "--ipv6", provider);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -17,9 +17,20 @@ import java.util.NoSuchElementException;
|
|||
import java.util.Optional;
|
||||
|
||||
@ApplicationScoped
|
||||
public class IpProvider {
|
||||
public class PublicIpLookup {
|
||||
|
||||
private static final Logger log = Logger.getLogger(IpProvider.class);
|
||||
private static final Logger log = Logger.getLogger(PublicIpLookup.class);
|
||||
|
||||
private final CurlProcessFactory curlProcessFactory;
|
||||
private final Optional<String> configuredProvider;
|
||||
|
||||
PublicIpLookup(
|
||||
CurlProcessFactory curlProcessFactory,
|
||||
@ConfigProperty(name = "ddnsclient.ip-provider") Optional<String> configuredProvider
|
||||
) {
|
||||
this.curlProcessFactory = curlProcessFactory;
|
||||
this.configuredProvider = configuredProvider;
|
||||
}
|
||||
|
||||
enum Providers {
|
||||
IFCONFIG_ME("ifconfig.me", "ifconfig.me"),
|
||||
|
@ -50,9 +61,6 @@ public class IpProvider {
|
|||
}
|
||||
}
|
||||
|
||||
@ConfigProperty(name = "ddnsclient.ip-provider")
|
||||
Optional<String> configuredProvider;
|
||||
|
||||
private Optional<Providers> provider = Optional.empty();
|
||||
|
||||
void onStart(@Observes StartupEvent event) {
|
||||
|
@ -63,11 +71,11 @@ public class IpProvider {
|
|||
}
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Quarkus.asyncExit(1);
|
||||
throw new IllegalStateException("Unknow provider configured!", e);
|
||||
}
|
||||
}
|
||||
|
||||
public IPs getPublicIps() {
|
||||
public IPs get() {
|
||||
log.trace("Retrieving public ips.");
|
||||
|
||||
try {
|
||||
|
@ -76,9 +84,9 @@ public class IpProvider {
|
|||
IPs.Builder ipsBuilder = new IPs.Builder();
|
||||
|
||||
try {
|
||||
Process curl = new ProcessBuilder("curl", "--ipv4", provider).start();
|
||||
curl.waitFor();
|
||||
InputStream inputStream = curl.getInputStream();
|
||||
Process curlProcess = curlProcessFactory.create(provider, CurlProcessFactory.IpClass.V4).start();
|
||||
curlProcess.waitFor();
|
||||
InputStream inputStream = curlProcess.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String v4 = reader.readLine();
|
||||
log.tracef("Retrieved ipv4: %s", v4);
|
||||
|
@ -88,9 +96,9 @@ public class IpProvider {
|
|||
}
|
||||
|
||||
try {
|
||||
Process curl = new ProcessBuilder("curl", "--ipv6", provider).start();
|
||||
curl.waitFor();
|
||||
InputStream inputStream = curl.getInputStream();
|
||||
Process curlProcess = curlProcessFactory.create(provider, CurlProcessFactory.IpClass.V6).start();
|
||||
curlProcess.waitFor();
|
||||
InputStream inputStream = curlProcess.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String v6 = reader.readLine();
|
||||
log.tracef("Retrieved ipv6: %s", v6);
|
|
@ -4,7 +4,7 @@ import de.rpr.ddnsclient.dyndns.Ddnss;
|
|||
import de.rpr.ddnsclient.dyndns.DynDns;
|
||||
import de.rpr.ddnsclient.dyndns.DynDnsRouter;
|
||||
import de.rpr.ddnsclient.lookup.DnsResolver;
|
||||
import de.rpr.ddnsclient.lookup.IpProvider;
|
||||
import de.rpr.ddnsclient.lookup.PublicIpLookup;
|
||||
import de.rpr.ddnsclient.model.Config;
|
||||
import de.rpr.ddnsclient.model.DyndnsAuth;
|
||||
import de.rpr.ddnsclient.model.IPs;
|
||||
|
@ -27,7 +27,7 @@ class UpdaterTest {
|
|||
@Mock
|
||||
DynDnsRouter dynDnsRouter;
|
||||
@Mock
|
||||
IpProvider ipProvider;
|
||||
PublicIpLookup publicIpLookup;
|
||||
@Mock
|
||||
DnsResolver dnsResolver;
|
||||
|
||||
|
@ -40,7 +40,7 @@ class UpdaterTest {
|
|||
@Test
|
||||
void should_throw_exception_if_config_is_empty() {
|
||||
Config config = new Config();
|
||||
Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, config, backoff);
|
||||
Updater updater = new Updater(dynDnsRouter, publicIpLookup, dnsResolver, config, backoff);
|
||||
Assertions.assertThrows(IllegalStateException.class, updater::run);
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ class UpdaterTest {
|
|||
void should_process_all_config_entries() {
|
||||
IPs ips = new IPs("ipv4", "ipv6");
|
||||
|
||||
when(ipProvider.getPublicIps()).thenReturn(ips);
|
||||
when(publicIpLookup.get()).thenReturn(ips);
|
||||
when(dnsResolver.resolve(any())).thenReturn(ips);
|
||||
|
||||
Config multipleConfigEntries = new Config() {{
|
||||
|
@ -56,7 +56,7 @@ class UpdaterTest {
|
|||
add(new Value("ddnss", "host2.example.org", null, null, "token"));
|
||||
}};
|
||||
|
||||
Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, multipleConfigEntries, backoff);
|
||||
Updater updater = new Updater(dynDnsRouter, publicIpLookup, dnsResolver, multipleConfigEntries, backoff);
|
||||
updater.run();
|
||||
|
||||
ArgumentCaptor<String> hostnameCaptor = ArgumentCaptor.forClass(String.class);
|
||||
|
@ -71,10 +71,10 @@ class UpdaterTest {
|
|||
|
||||
IPs ips = new IPs("ipv4", "ipv6");
|
||||
|
||||
when(ipProvider.getPublicIps()).thenReturn(ips);
|
||||
when(publicIpLookup.get()).thenReturn(ips);
|
||||
when(dnsResolver.resolve("example.org")).thenReturn(ips);
|
||||
|
||||
Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, ddnssConfig, backoff);
|
||||
Updater updater = new Updater(dynDnsRouter, publicIpLookup, dnsResolver, ddnssConfig, backoff);
|
||||
updater.run();
|
||||
|
||||
verify(dynDnsRouter, never()).get(any());
|
||||
|
@ -89,10 +89,10 @@ class UpdaterTest {
|
|||
when(dynDnsRouter.get("ddnss")).thenReturn(ddnss);
|
||||
|
||||
IPs publicIps = new IPs("ipv4", "ipv6");
|
||||
when(ipProvider.getPublicIps()).thenReturn(publicIps);
|
||||
when(publicIpLookup.get()).thenReturn(publicIps);
|
||||
when(dnsResolver.resolve("example.org")).thenReturn(new IPs("registered_ipv4", "registered_ipv6"));
|
||||
|
||||
Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, ddnssConfig, backoff);
|
||||
Updater updater = new Updater(dynDnsRouter, publicIpLookup, dnsResolver, ddnssConfig, backoff);
|
||||
updater.run();
|
||||
|
||||
verify(ddnss).update(
|
||||
|
@ -110,10 +110,10 @@ class UpdaterTest {
|
|||
when(dynDnsRouter.get("ddnss")).thenReturn(ddnss);
|
||||
|
||||
IPs publicIps = new IPs("ipv4", "ipv6");
|
||||
when(ipProvider.getPublicIps()).thenReturn(publicIps);
|
||||
when(publicIpLookup.get()).thenReturn(publicIps);
|
||||
when(dnsResolver.resolve("example.org")).thenReturn(new IPs("registered_ipv4", "registered_ipv6"));
|
||||
|
||||
Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, ddnssConfig, backoff);
|
||||
Updater updater = new Updater(dynDnsRouter, publicIpLookup, dnsResolver, ddnssConfig, backoff);
|
||||
updater.run();
|
||||
|
||||
updater.run();
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package de.rpr.ddnsclient.lookup;
|
||||
|
||||
import de.rpr.ddnsclient.model.IPs;
|
||||
import io.quarkus.runtime.StartupEvent;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PublicIpLookupTest {
|
||||
|
||||
@Mock
|
||||
CurlProcessFactory curlProcessFactory;
|
||||
|
||||
@Test
|
||||
void unknown_provider_should_throw_exception() {
|
||||
PublicIpLookup lookup = new PublicIpLookup(curlProcessFactory, Optional.of("unknown"));
|
||||
Assertions.assertThatExceptionOfType(IllegalStateException.class)
|
||||
.isThrownBy(() -> lookup.onStart(new StartupEvent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void valid_provider_should_be_configurable() {
|
||||
PublicIpLookup lookup = new PublicIpLookup(curlProcessFactory, Optional.of("ifconfig.me"));
|
||||
lookup.onStart(new StartupEvent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_use_configured_provider_for_lookups() {
|
||||
Process v4CurlProcess = mock(Process.class);
|
||||
when(v4CurlProcess.getInputStream()).thenReturn(IOUtils.toInputStream("ipv4", "UTF-8"));
|
||||
|
||||
CurlProcessFactory.ProcessBuilderWrapper v4CurlProcessBuilderWrapper = mock(CurlProcessFactory.ProcessBuilderWrapper.class, invocationOnMock -> v4CurlProcess);
|
||||
when(curlProcessFactory.create(any(), eq(CurlProcessFactory.IpClass.V4))).thenReturn(v4CurlProcessBuilderWrapper);
|
||||
|
||||
Process v6CurlProcess = mock(Process.class);
|
||||
when(v6CurlProcess.getInputStream()).thenReturn(IOUtils.toInputStream("ipv6", "UTF-8"));
|
||||
|
||||
CurlProcessFactory.ProcessBuilderWrapper v6CurlProcessBuilderWrapper = mock(CurlProcessFactory.ProcessBuilderWrapper.class, invocationOnMock -> v6CurlProcess);
|
||||
when(curlProcessFactory.create(any(), eq(CurlProcessFactory.IpClass.V6))).thenReturn(v6CurlProcessBuilderWrapper);
|
||||
|
||||
PublicIpLookup lookup = new PublicIpLookup(curlProcessFactory, Optional.of("ifconfig.me"));
|
||||
lookup.onStart(new StartupEvent());
|
||||
IPs result = lookup.get();
|
||||
|
||||
assertThat(result).isEqualTo(new IPs("ipv4", "ipv6"));
|
||||
|
||||
ArgumentCaptor<String> providerCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(curlProcessFactory, times(2)).create(providerCaptor.capture(), any());
|
||||
|
||||
assertThat(providerCaptor.getAllValues()).containsExactly("ifconfig.me", "ifconfig.me");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue