spring update 4
This commit is contained in:
parent
22077d40a8
commit
a914ef1ec4
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.3.4</version>
|
||||
<version>4.1.0</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
package dev.locqr.server.crypto;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.locqr.server.config.LocqrProperties;
|
||||
import dev.locqr.server.util.Base64Url;
|
||||
import org.erdtman.jcs.JsonCanonicalizer;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
@ -34,7 +35,7 @@ import java.util.Map;
|
||||
public class SigningService {
|
||||
|
||||
private final PrivateKey ed25519SecretKey;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final ObjectMapper objectMapper = new JsonMapper();
|
||||
|
||||
public SigningService(LocqrProperties properties) {
|
||||
this.ed25519SecretKey = loadEd25519SecretKey(properties.ed25519SecretKeyPath());
|
||||
@ -62,8 +63,10 @@ public class SigningService {
|
||||
|
||||
/** RFC 8785 (JCS) canonicalization — mandatory, no bespoke implementation. */
|
||||
public byte[] canonicalize(Map<String, Object> payload) {
|
||||
try {
|
||||
// writeValueAsString throws the unchecked JacksonException (Jackson 3); only
|
||||
// JsonCanonicalizer's own IOException (io.github.erdtman, unrelated library) needs catching.
|
||||
String json = objectMapper.writeValueAsString(payload);
|
||||
try {
|
||||
return new JsonCanonicalizer(json).getEncodedUTF8();
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to JCS-canonicalize signed payload", e);
|
||||
@ -114,10 +117,6 @@ public class SigningService {
|
||||
}
|
||||
|
||||
public String toBase64UrlJson(Map<String, Object> envelope) {
|
||||
try {
|
||||
return Base64Url.encode(objectMapper.writeValueAsBytes(envelope));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,5 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
public record DomainStatusResponse(@JsonProperty("status") String status) {
|
||||
public static final String VALID = "valid";
|
||||
public static final String REJECTED = "rejected";
|
||||
public static final String SUSPENDED = "suspended";
|
||||
// "suspended" (interfaces.md) is a valid wire value but nothing in the
|
||||
// current (PoC) scope ever computes it — no account-suspension logic
|
||||
// exists yet. Add the constant back when that logic is added.
|
||||
}
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
package dev.locqr.server.relay;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.locqr.server.dto.RelayMessage;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@ -26,7 +28,7 @@ public class RelayHttpRequestHandler implements HttpRequestHandler {
|
||||
|
||||
private final WebSocketHttpRequestHandler webSocketHandler;
|
||||
private final RelaySessionRegistry registry;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final ObjectMapper objectMapper = new JsonMapper();
|
||||
|
||||
public RelayHttpRequestHandler(RelayWebSocketHandler relayWebSocketHandler, RelaySessionRegistry registry) {
|
||||
this.webSocketHandler = new WebSocketHttpRequestHandler(relayWebSocketHandler);
|
||||
@ -34,7 +36,7 @@ public class RelayHttpRequestHandler implements HttpRequestHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
|
||||
public void handleRequest(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
switch (request.getMethod()) {
|
||||
case "GET" -> webSocketHandler.handleRequest(request, response);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package dev.locqr.server.relay;
|
||||
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
@ -24,14 +25,14 @@ public class RelayWebSocketHandler extends TextWebSocketHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) {
|
||||
public void afterConnectionEstablished(@NonNull WebSocketSession session) {
|
||||
String runId = runIdFrom(session);
|
||||
session.getAttributes().put(RUN_ID_ATTR, runId);
|
||||
registry.register(runId, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
|
||||
public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus status) {
|
||||
String runId = (String) session.getAttributes().get(RUN_ID_ATTR);
|
||||
if (runId != null) {
|
||||
registry.unregister(runId, session);
|
||||
|
||||
@ -24,20 +24,20 @@ public class RegistrationCertificateStore {
|
||||
private static final Logger log = LoggerFactory.getLogger(RegistrationCertificateStore.class);
|
||||
|
||||
private final String testDomain;
|
||||
private final Optional<String> cert;
|
||||
private final String cert; // null if no cert file was found at startup
|
||||
|
||||
public RegistrationCertificateStore(LocqrProperties properties) {
|
||||
this.testDomain = properties.testDomain();
|
||||
this.cert = load(properties.registrationCertPath());
|
||||
}
|
||||
|
||||
private static Optional<String> load(String path) {
|
||||
private static String load(String path) {
|
||||
try {
|
||||
return Optional.of(Files.readString(Path.of(path)).trim());
|
||||
return Files.readString(Path.of(path)).trim();
|
||||
} catch (IOException e) {
|
||||
log.warn("No registration certificate at {} — GET /domain/registration will 404 "
|
||||
+ "until the dev setup script (dev.md) generates it.", path);
|
||||
return Optional.empty();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +45,6 @@ public class RegistrationCertificateStore {
|
||||
if (!testDomain.equals(domain)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return cert;
|
||||
return Optional.ofNullable(cert);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package dev.locqr.server.util;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
@ -18,8 +17,4 @@ public final class Base64Url {
|
||||
public static byte[] decode(String s) {
|
||||
return Base64.getUrlDecoder().decode(s);
|
||||
}
|
||||
|
||||
public static String encodeUtf8(String s) {
|
||||
return encode(s.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
package dev.locqr.server;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.locqr.server.dto.*;
|
||||
import dev.locqr.server.support.TestEd25519Keys;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||
import org.springframework.test.context.DynamicPropertySource;
|
||||
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -32,6 +34,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* WebSocket stack rather than by inspecting code. A hermetic, generated
|
||||
* Ed25519 key is used — these tests never depend on the committed
|
||||
* {@code dev/keys/} material, so they're unaffected by dev key rotation.
|
||||
*
|
||||
* <p>Uses {@link RestTestClient} (Spring Framework 7 / Boot 4), not the
|
||||
* older {@code TestRestTemplate} — that class no longer ships in
|
||||
* {@code spring-boot-starter-test} as of Boot 4.
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
class RunFlowIntegrationTest {
|
||||
@ -39,10 +45,13 @@ class RunFlowIntegrationTest {
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@org.springframework.beans.factory.annotation.Autowired
|
||||
TestRestTemplate restTemplate;
|
||||
private RestTestClient client;
|
||||
private final ObjectMapper objectMapper = new JsonMapper();
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@BeforeEach
|
||||
void setUpClient() {
|
||||
client = RestTestClient.bindToServer().baseUrl("http://localhost:" + port).build();
|
||||
}
|
||||
|
||||
@DynamicPropertySource
|
||||
static void dynamicKeyAndCert(DynamicPropertyRegistry registry) throws Exception {
|
||||
@ -65,62 +74,82 @@ class RunFlowIntegrationTest {
|
||||
var upload = new RunBundleUploadRequest(
|
||||
runId, "https://test.locqr.dev", randomBase64(32), randomBase64(1184));
|
||||
|
||||
ResponseEntity<RunBundleUploadResponse> uploadResponse =
|
||||
restTemplate.postForEntity("/run/bundle", upload, RunBundleUploadResponse.class);
|
||||
RunBundleUploadResponse body = client.post().uri("/run/bundle")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(upload)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(RunBundleUploadResponse.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertThat(uploadResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
RunBundleUploadResponse body = uploadResponse.getBody();
|
||||
assertThat(body).isNotNull();
|
||||
assertThat(body.qrTtl()).isEqualTo(90);
|
||||
assertThat(body.maxAutoRefresh()).isEqualTo(3);
|
||||
assertThat(body.pinTtl()).isEqualTo(120);
|
||||
assertThat(body.signedToken()).isNotBlank();
|
||||
|
||||
ResponseEntity<RunBundleFetchResponse> firstFetch =
|
||||
restTemplate.getForEntity("/run/bundle/{runId}", RunBundleFetchResponse.class, runId);
|
||||
assertThat(firstFetch.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(firstFetch.getBody()).isNotNull();
|
||||
assertThat(firstFetch.getBody().x25519Pubkey()).isEqualTo(upload.x25519Pubkey());
|
||||
assertThat(firstFetch.getBody().kemPubkey()).isEqualTo(upload.kemPubkey());
|
||||
RunBundleFetchResponse firstFetch = client.get().uri("/run/bundle/{runId}", runId)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(RunBundleFetchResponse.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
ResponseEntity<RunBundleFetchResponse> secondFetch =
|
||||
restTemplate.getForEntity("/run/bundle/{runId}", RunBundleFetchResponse.class, runId);
|
||||
assertThat(secondFetch.getStatusCode())
|
||||
.as("at-most-once: the bundle must be gone after the first fetch")
|
||||
.isEqualTo(HttpStatus.NOT_FOUND);
|
||||
assertThat(firstFetch).isNotNull();
|
||||
assertThat(firstFetch.x25519Pubkey()).isEqualTo(upload.x25519Pubkey());
|
||||
assertThat(firstFetch.kemPubkey()).isEqualTo(upload.kemPubkey());
|
||||
|
||||
// at-most-once: the bundle must be gone after the first fetch
|
||||
client.get().uri("/run/bundle/{runId}", runId)
|
||||
.exchange()
|
||||
.expectStatus().isNotFound();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fetchingAnUnknownRunId_returnsNotFound() {
|
||||
ResponseEntity<RunBundleFetchResponse> response = restTemplate.getForEntity(
|
||||
"/run/bundle/{runId}", RunBundleFetchResponse.class, "never-uploaded-" + UUID.randomUUID());
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
client.get().uri("/run/bundle/{runId}", "never-uploaded-" + UUID.randomUUID())
|
||||
.exchange()
|
||||
.expectStatus().isNotFound();
|
||||
}
|
||||
|
||||
// --- Domain status / registration (interfaces.md) ---
|
||||
|
||||
@Test
|
||||
void domainStatus_isValidForTheTestDomain_andRejectedForAnyOther() {
|
||||
ResponseEntity<DomainStatusResponse> valid = restTemplate.getForEntity(
|
||||
"/domain/status?domain={d}", DomainStatusResponse.class, "test.locqr.dev");
|
||||
assertThat(valid.getBody().status()).isEqualTo(DomainStatusResponse.VALID);
|
||||
DomainStatusResponse valid = client.get().uri("/domain/status?domain={d}", "test.locqr.dev")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(DomainStatusResponse.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
assertThat(valid).isNotNull();
|
||||
assertThat(valid.status()).isEqualTo(DomainStatusResponse.VALID);
|
||||
|
||||
ResponseEntity<DomainStatusResponse> rejected = restTemplate.getForEntity(
|
||||
"/domain/status?domain={d}", DomainStatusResponse.class, "evil.example.com");
|
||||
assertThat(rejected.getBody().status()).isEqualTo(DomainStatusResponse.REJECTED);
|
||||
DomainStatusResponse rejected = client.get().uri("/domain/status?domain={d}", "evil.example.com")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(DomainStatusResponse.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
assertThat(rejected).isNotNull();
|
||||
assertThat(rejected.status()).isEqualTo(DomainStatusResponse.REJECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registration_returnsTheCert_forTheTestDomain_andNotFoundForAnyOther() {
|
||||
ResponseEntity<RegistrationResponse> found = restTemplate.getForEntity(
|
||||
"/domain/registration?domain={d}", RegistrationResponse.class, "test.locqr.dev");
|
||||
assertThat(found.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(found.getBody().cert()).isEqualTo("dummy-registration-envelope");
|
||||
RegistrationResponse found = client.get().uri("/domain/registration?domain={d}", "test.locqr.dev")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(RegistrationResponse.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
assertThat(found).isNotNull();
|
||||
assertThat(found.cert()).isEqualTo("dummy-registration-envelope");
|
||||
|
||||
ResponseEntity<RegistrationResponse> notFound = restTemplate.getForEntity(
|
||||
"/domain/registration?domain={d}", RegistrationResponse.class, "evil.example.com");
|
||||
assertThat(notFound.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
client.get().uri("/domain/registration?domain={d}", "evil.example.com")
|
||||
.exchange()
|
||||
.expectStatus().isNotFound();
|
||||
}
|
||||
|
||||
// --- Security report (interfaces.md) ---
|
||||
@ -129,9 +158,11 @@ class RunFlowIntegrationTest {
|
||||
void securityReport_isAccepted_unauthenticated() {
|
||||
var report = new SecurityReportRequest(UUID.randomUUID().toString(), "pin_mismatch", 1748390461L);
|
||||
|
||||
ResponseEntity<Void> response = restTemplate.postForEntity("/security/report", report, Void.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
client.post().uri("/security/report")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(report)
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
}
|
||||
|
||||
// --- Relay: WebSocket + POST both live at /run/relay/:runId (server/claude.md) ---
|
||||
@ -149,37 +180,39 @@ class RunFlowIntegrationTest {
|
||||
var buffered = new RelayMessage(RelayMessage.KEM_CIPHERTEXT, "QlVGRkVSRUQ");
|
||||
|
||||
// POSTed before any socket is open -> must be buffered, not dropped or rejected.
|
||||
ResponseEntity<Void> bufferedPost =
|
||||
restTemplate.postForEntity("/run/relay/{runId}", buffered, Void.class, runId);
|
||||
assertThat(bufferedPost.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
client.post().uri("/run/relay/{runId}", runId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(buffered)
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
|
||||
BlockingQueue<String> received = new LinkedBlockingQueue<>();
|
||||
StandardWebSocketClient client = new StandardWebSocketClient();
|
||||
WebSocketSession session = client.execute(
|
||||
StandardWebSocketClient wsClient = new StandardWebSocketClient();
|
||||
|
||||
try (WebSocketSession session = wsClient.execute(
|
||||
new TextWebSocketHandler() {
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession s, TextMessage message) {
|
||||
protected void handleTextMessage(@NonNull WebSocketSession s, @NonNull TextMessage message) {
|
||||
received.add(message.getPayload());
|
||||
}
|
||||
},
|
||||
"ws://localhost:{port}/run/relay/{runId}", port, runId)
|
||||
.get(5, TimeUnit.SECONDS);
|
||||
.get(5, TimeUnit.SECONDS)) {
|
||||
|
||||
try {
|
||||
String flushedJson = received.poll(5, TimeUnit.SECONDS);
|
||||
assertThat(flushedJson).isNotNull();
|
||||
assertThat(objectMapper.readValue(flushedJson, RelayMessage.class)).isEqualTo(buffered);
|
||||
|
||||
var live = new RelayMessage(RelayMessage.CREDENTIAL, "TElWRQ");
|
||||
ResponseEntity<Void> livePost =
|
||||
restTemplate.postForEntity("/run/relay/{runId}", live, Void.class, runId);
|
||||
assertThat(livePost.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
client.post().uri("/run/relay/{runId}", runId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(live)
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
|
||||
String liveJson = received.poll(5, TimeUnit.SECONDS);
|
||||
assertThat(liveJson).isNotNull();
|
||||
assertThat(objectMapper.readValue(liveJson, RelayMessage.class)).isEqualTo(live);
|
||||
} finally {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,10 +221,11 @@ class RunFlowIntegrationTest {
|
||||
String runId = "ws-it-invalid-" + UUID.randomUUID();
|
||||
var invalid = new RelayMessage("not_a_real_type", "AAAA");
|
||||
|
||||
ResponseEntity<Void> response =
|
||||
restTemplate.postForEntity("/run/relay/{runId}", invalid, Void.class, runId);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
client.post().uri("/run/relay/{runId}", runId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(invalid)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
private static String randomBase64(int numBytes) {
|
||||
|
||||
@ -9,7 +9,6 @@ import org.springframework.web.socket.WebSocketSession;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
@ -41,10 +41,10 @@ class InMemoryKeyBundleStoreTest {
|
||||
store.put(bundle);
|
||||
|
||||
int attempts = 50;
|
||||
var pool = java.util.concurrent.Executors.newFixedThreadPool(attempts);
|
||||
var successes = new java.util.concurrent.atomic.AtomicInteger();
|
||||
var latch = new java.util.concurrent.CountDownLatch(attempts);
|
||||
|
||||
try (var pool = java.util.concurrent.Executors.newFixedThreadPool(attempts)) {
|
||||
for (int i = 0; i < attempts; i++) {
|
||||
pool.submit(() -> {
|
||||
try {
|
||||
@ -57,13 +57,16 @@ class InMemoryKeyBundleStoreTest {
|
||||
});
|
||||
}
|
||||
latch.await();
|
||||
pool.shutdown();
|
||||
}
|
||||
|
||||
assertThat(successes.get())
|
||||
.as("ConcurrentHashMap.remove() must be atomic: exactly one concurrent fetch wins")
|
||||
.isEqualTo(1);
|
||||
}
|
||||
|
||||
// Only ever called with 60s in this file, so IntelliJ flags the parameter —
|
||||
// kept anyway, since "an unexpired bundle" is the meaningful concept a
|
||||
// reader needs, not the literal 60.
|
||||
private static KeyBundle bundleExpiringIn(long seconds) {
|
||||
return new KeyBundle(
|
||||
"run-1", "https://test.locqr.dev", "x25519pub", "kempub", Instant.now().plusSeconds(seconds));
|
||||
|
||||
@ -11,15 +11,16 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class RegistrationCertificateStoreTest {
|
||||
|
||||
private static final String TEST_DOMAIN = "test.locqr.dev";
|
||||
|
||||
@Test
|
||||
void certFor_returnsTheFileContents_forTheConfiguredTestDomain(@TempDir Path dir) throws Exception {
|
||||
Path certFile = dir.resolve("test-registration.b64");
|
||||
Files.writeString(certFile, "dummy-envelope\n");
|
||||
|
||||
RegistrationCertificateStore store =
|
||||
new RegistrationCertificateStore(properties("test.locqr.dev", certFile.toString()));
|
||||
RegistrationCertificateStore store = new RegistrationCertificateStore(properties(certFile.toString()));
|
||||
|
||||
assertThat(store.certFor("test.locqr.dev")).contains("dummy-envelope");
|
||||
assertThat(store.certFor(TEST_DOMAIN)).contains("dummy-envelope");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -27,8 +28,7 @@ class RegistrationCertificateStoreTest {
|
||||
Path certFile = dir.resolve("test-registration.b64");
|
||||
Files.writeString(certFile, "dummy-envelope");
|
||||
|
||||
RegistrationCertificateStore store =
|
||||
new RegistrationCertificateStore(properties("test.locqr.dev", certFile.toString()));
|
||||
RegistrationCertificateStore store = new RegistrationCertificateStore(properties(certFile.toString()));
|
||||
|
||||
assertThat(store.certFor("evil.example.com")).isEmpty();
|
||||
}
|
||||
@ -36,14 +36,14 @@ class RegistrationCertificateStoreTest {
|
||||
@Test
|
||||
void certFor_isEmpty_whenTheCertFileDoesNotExist_ratherThanThrowingAtStartup() {
|
||||
RegistrationCertificateStore store =
|
||||
new RegistrationCertificateStore(properties("test.locqr.dev", "/definitely/does/not/exist.b64"));
|
||||
new RegistrationCertificateStore(properties("/definitely/does/not/exist.b64"));
|
||||
|
||||
assertThat(store.certFor("test.locqr.dev")).isEmpty();
|
||||
assertThat(store.certFor(TEST_DOMAIN)).isEmpty();
|
||||
}
|
||||
|
||||
private static LocqrProperties properties(String testDomain, String certPath) {
|
||||
private static LocqrProperties properties(String certPath) {
|
||||
return new LocqrProperties(
|
||||
testDomain,
|
||||
TEST_DOMAIN,
|
||||
"unused",
|
||||
certPath,
|
||||
new LocqrProperties.Run(90, 3, 120),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user