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.DynDns;
|
||||||
import de.rpr.ddnsclient.dyndns.DynDnsRouter;
|
import de.rpr.ddnsclient.dyndns.DynDnsRouter;
|
||||||
import de.rpr.ddnsclient.lookup.DnsResolver;
|
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.Config;
|
||||||
import de.rpr.ddnsclient.model.DyndnsAuth;
|
import de.rpr.ddnsclient.model.DyndnsAuth;
|
||||||
import de.rpr.ddnsclient.model.IPs;
|
import de.rpr.ddnsclient.model.IPs;
|
||||||
|
@ -23,7 +23,7 @@ public class Updater {
|
||||||
private static final Logger log = Logger.getLogger(Updater.class);
|
private static final Logger log = Logger.getLogger(Updater.class);
|
||||||
|
|
||||||
private final DynDnsRouter dynDnsRouter;
|
private final DynDnsRouter dynDnsRouter;
|
||||||
private final IpProvider ipProvider;
|
private final PublicIpLookup publicIpLookup;
|
||||||
private final DnsResolver dnsResolver;
|
private final DnsResolver dnsResolver;
|
||||||
private final Duration backoff;
|
private final Duration backoff;
|
||||||
|
|
||||||
|
@ -33,12 +33,12 @@ public class Updater {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Updater(DynDnsRouter dynDnsRouter,
|
public Updater(DynDnsRouter dynDnsRouter,
|
||||||
IpProvider ipProvider,
|
PublicIpLookup publicIpLookup,
|
||||||
DnsResolver dnsResolver,
|
DnsResolver dnsResolver,
|
||||||
Config config,
|
Config config,
|
||||||
@ConfigProperty(name = "ddnsclient.backoff-duration:300s") Duration backoff) {
|
@ConfigProperty(name = "ddnsclient.backoff-duration:300s") Duration backoff) {
|
||||||
this.dynDnsRouter = dynDnsRouter;
|
this.dynDnsRouter = dynDnsRouter;
|
||||||
this.ipProvider = ipProvider;
|
this.publicIpLookup = publicIpLookup;
|
||||||
this.dnsResolver = dnsResolver;
|
this.dnsResolver = dnsResolver;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.backoff = backoff;
|
this.backoff = backoff;
|
||||||
|
@ -51,7 +51,7 @@ public class Updater {
|
||||||
throw new IllegalStateException("Missing configuration");
|
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());
|
log.debugf("Public ips - v4: %s, v6: %s", publicIps.v4(), publicIps.v6());
|
||||||
|
|
||||||
config.forEach(cfg -> {
|
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;
|
import java.util.Optional;
|
||||||
|
|
||||||
@ApplicationScoped
|
@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 {
|
enum Providers {
|
||||||
IFCONFIG_ME("ifconfig.me", "ifconfig.me"),
|
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();
|
private Optional<Providers> provider = Optional.empty();
|
||||||
|
|
||||||
void onStart(@Observes StartupEvent event) {
|
void onStart(@Observes StartupEvent event) {
|
||||||
|
@ -63,11 +71,11 @@ public class IpProvider {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Quarkus.asyncExit(1);
|
throw new IllegalStateException("Unknow provider configured!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPs getPublicIps() {
|
public IPs get() {
|
||||||
log.trace("Retrieving public ips.");
|
log.trace("Retrieving public ips.");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -76,9 +84,9 @@ public class IpProvider {
|
||||||
IPs.Builder ipsBuilder = new IPs.Builder();
|
IPs.Builder ipsBuilder = new IPs.Builder();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Process curl = new ProcessBuilder("curl", "--ipv4", provider).start();
|
Process curlProcess = curlProcessFactory.create(provider, CurlProcessFactory.IpClass.V4).start();
|
||||||
curl.waitFor();
|
curlProcess.waitFor();
|
||||||
InputStream inputStream = curl.getInputStream();
|
InputStream inputStream = curlProcess.getInputStream();
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
String v4 = reader.readLine();
|
String v4 = reader.readLine();
|
||||||
log.tracef("Retrieved ipv4: %s", v4);
|
log.tracef("Retrieved ipv4: %s", v4);
|
||||||
|
@ -88,9 +96,9 @@ public class IpProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Process curl = new ProcessBuilder("curl", "--ipv6", provider).start();
|
Process curlProcess = curlProcessFactory.create(provider, CurlProcessFactory.IpClass.V6).start();
|
||||||
curl.waitFor();
|
curlProcess.waitFor();
|
||||||
InputStream inputStream = curl.getInputStream();
|
InputStream inputStream = curlProcess.getInputStream();
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
String v6 = reader.readLine();
|
String v6 = reader.readLine();
|
||||||
log.tracef("Retrieved ipv6: %s", v6);
|
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.DynDns;
|
||||||
import de.rpr.ddnsclient.dyndns.DynDnsRouter;
|
import de.rpr.ddnsclient.dyndns.DynDnsRouter;
|
||||||
import de.rpr.ddnsclient.lookup.DnsResolver;
|
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.Config;
|
||||||
import de.rpr.ddnsclient.model.DyndnsAuth;
|
import de.rpr.ddnsclient.model.DyndnsAuth;
|
||||||
import de.rpr.ddnsclient.model.IPs;
|
import de.rpr.ddnsclient.model.IPs;
|
||||||
|
@ -27,7 +27,7 @@ class UpdaterTest {
|
||||||
@Mock
|
@Mock
|
||||||
DynDnsRouter dynDnsRouter;
|
DynDnsRouter dynDnsRouter;
|
||||||
@Mock
|
@Mock
|
||||||
IpProvider ipProvider;
|
PublicIpLookup publicIpLookup;
|
||||||
@Mock
|
@Mock
|
||||||
DnsResolver dnsResolver;
|
DnsResolver dnsResolver;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ class UpdaterTest {
|
||||||
@Test
|
@Test
|
||||||
void should_throw_exception_if_config_is_empty() {
|
void should_throw_exception_if_config_is_empty() {
|
||||||
Config config = new Config();
|
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);
|
Assertions.assertThrows(IllegalStateException.class, updater::run);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class UpdaterTest {
|
||||||
void should_process_all_config_entries() {
|
void should_process_all_config_entries() {
|
||||||
IPs ips = new IPs("ipv4", "ipv6");
|
IPs ips = new IPs("ipv4", "ipv6");
|
||||||
|
|
||||||
when(ipProvider.getPublicIps()).thenReturn(ips);
|
when(publicIpLookup.get()).thenReturn(ips);
|
||||||
when(dnsResolver.resolve(any())).thenReturn(ips);
|
when(dnsResolver.resolve(any())).thenReturn(ips);
|
||||||
|
|
||||||
Config multipleConfigEntries = new Config() {{
|
Config multipleConfigEntries = new Config() {{
|
||||||
|
@ -56,7 +56,7 @@ class UpdaterTest {
|
||||||
add(new Value("ddnss", "host2.example.org", null, null, "token"));
|
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();
|
updater.run();
|
||||||
|
|
||||||
ArgumentCaptor<String> hostnameCaptor = ArgumentCaptor.forClass(String.class);
|
ArgumentCaptor<String> hostnameCaptor = ArgumentCaptor.forClass(String.class);
|
||||||
|
@ -71,10 +71,10 @@ class UpdaterTest {
|
||||||
|
|
||||||
IPs ips = new IPs("ipv4", "ipv6");
|
IPs ips = new IPs("ipv4", "ipv6");
|
||||||
|
|
||||||
when(ipProvider.getPublicIps()).thenReturn(ips);
|
when(publicIpLookup.get()).thenReturn(ips);
|
||||||
when(dnsResolver.resolve("example.org")).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();
|
updater.run();
|
||||||
|
|
||||||
verify(dynDnsRouter, never()).get(any());
|
verify(dynDnsRouter, never()).get(any());
|
||||||
|
@ -89,10 +89,10 @@ class UpdaterTest {
|
||||||
when(dynDnsRouter.get("ddnss")).thenReturn(ddnss);
|
when(dynDnsRouter.get("ddnss")).thenReturn(ddnss);
|
||||||
|
|
||||||
IPs publicIps = new IPs("ipv4", "ipv6");
|
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"));
|
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();
|
||||||
|
|
||||||
verify(ddnss).update(
|
verify(ddnss).update(
|
||||||
|
@ -110,10 +110,10 @@ class UpdaterTest {
|
||||||
when(dynDnsRouter.get("ddnss")).thenReturn(ddnss);
|
when(dynDnsRouter.get("ddnss")).thenReturn(ddnss);
|
||||||
|
|
||||||
IPs publicIps = new IPs("ipv4", "ipv6");
|
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"));
|
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();
|
||||||
|
|
||||||
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