Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,18 @@ void setUp() throws Exception {

@AfterEach
void tearDown() {
contentSigningFormattingMock.stop();
timestampingFormattingMock.stop();
cryptographyProviderServerMock.stop();
signerConnectorServerMock.stop();
if (contentSigningFormattingMock != null) {
contentSigningFormattingMock.stop();
}
if (timestampingFormattingMock != null) {
timestampingFormattingMock.stop();
}
if (cryptographyProviderServerMock != null) {
cryptographyProviderServerMock.stop();
}
if (signerConnectorServerMock != null) {
signerConnectorServerMock.stop();
}
}

private void createSigningRecordFor(SigningProfileDto profile) {
Expand Down
11 changes: 5 additions & 6 deletions src/test/java/com/otilm/core/service/cmp/CmpTestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.otilm.api.model.connector.cryptography.operations.SignDataResponseDto;
import com.otilm.api.model.connector.cryptography.operations.data.SignatureResponseData;
import com.otilm.core.service.cmp.mock.CertTestUtil;
import com.otilm.core.util.LoopbackWireMock;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
Expand Down Expand Up @@ -97,9 +98,8 @@ public class CmpTestUtil {

public static WireMockServer createSigningPlatform() {
// prepare mock server - for cryptographic provider
WireMockServer mockServer = new WireMockServer(0);
mockServer.start();
WireMock.configureFor("localhost", mockServer.port());
WireMockServer mockServer = LoopbackWireMock.start();
WireMock.configureFor(LoopbackWireMock.HOST, mockServer.port());

// -- if there is a need something to sign (mock server is called)
// see
Expand All @@ -119,9 +119,8 @@ public static WireMockServer createSigningPlatform() {

public static WireMockServer createIssuingPlatform() {
// prepare mock server - for cryptographic provider
WireMockServer mockServer = new WireMockServer(0);
mockServer.start();
WireMock.configureFor("localhost", mockServer.port());
WireMockServer mockServer = LoopbackWireMock.start();
WireMock.configureFor(LoopbackWireMock.HOST, mockServer.port());

// -- if there is a need something to sign (mock server is called)
// see
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import com.otilm.core.service.v2.ComplianceProfileExternalService;
import com.otilm.core.service.v2.ComplianceProfileInternalService;
import com.otilm.core.util.BaseSpringBootTest;
import com.otilm.core.util.LoopbackWireMock;
import com.otilm.core.util.MetaDefinitions;
import java.util.List;
import java.util.UUID;
Expand Down Expand Up @@ -123,8 +124,7 @@ public abstract class BaseComplianceTest extends BaseSpringBootTest {

@BeforeEach
protected void setUp() throws AlreadyExistException {
mockServer = new WireMockServer(0);
mockServer.start();
mockServer = LoopbackWireMock.start();

mockComplianceProviderResponses(true);
mockComplianceProviderV1Responses();
Expand Down Expand Up @@ -194,7 +194,7 @@ private Connector createConnector(String name, FunctionGroupCode functionGroupCo
ConnectorVersion connectorVersion) {
Connector connector = new Connector();
connector.setName(name);
connector.setUrl("http://localhost:" + mockServer.port());
connector.setUrl(LoopbackWireMock.url(mockServer));
connector.setVersion(connectorVersion);
connector.setStatus(ConnectorStatus.CONNECTED);
connector = connectorRepository.save(connector);
Expand Down Expand Up @@ -367,7 +367,7 @@ protected void mockComplianceProviderResponses(boolean defaultResponses) {
.formatted(complianceV2Group2Uuid,
defaultResponses ? Resource.CERTIFICATE.getCode() : Resource.CRYPTOGRAPHIC_KEY.getCode());

WireMock.configureFor("localhost", mockServer.port());
WireMock.configureFor(LoopbackWireMock.HOST, mockServer.port());

WireMock
.stubFor(WireMock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import com.otilm.core.service.TimeQualityConfigurationExternalService;
import com.otilm.core.util.BaseSpringBootTest;
import com.otilm.core.util.CertificateTestUtil;
import com.otilm.core.util.LoopbackWireMock;
import com.otilm.core.util.MetaDefinitions;
import com.otilm.core.util.seeders.CryptographicKeySeeder;
import java.io.IOException;
Expand Down Expand Up @@ -203,9 +204,8 @@ public abstract class SigningProfileTestBase extends BaseSpringBootTest {
@BeforeEach
void setUp() throws CertificateException, IOException, NoSuchAlgorithmException, OperatorCreationException,
AlreadyExistException, AttributeException, ConnectorException, NotFoundException {
mockServer = new WireMockServer(0);
mockServer.start();
WireMock.configureFor("localhost", mockServer.port());
mockServer = LoopbackWireMock.start();
WireMock.configureFor(LoopbackWireMock.HOST, mockServer.port());
mockServer
.stubFor(WireMock
.get(WireMock.urlPathMatching(".*/v1/signatureProvider/formatting/attributes"))
Expand Down Expand Up @@ -465,7 +465,7 @@ protected RequestAttributeV2 buildDigestAttribute(DigestAlgorithm algorithm) {
protected Connector createFormattingConnector(String name) {
Connector connector = new Connector();
connector.setName(name);
connector.setUrl("http://localhost:" + mockServer.port() + "/" + name);
connector.setUrl(LoopbackWireMock.url(mockServer) + "/" + name);
connector.setVersion(ConnectorVersion.V2);
connector.setStatus(ConnectorStatus.CONNECTED);
connector = connectorRepository.save(connector);
Expand Down
32 changes: 32 additions & 0 deletions src/test/java/com/otilm/core/util/LoopbackWireMock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.otilm.core.util;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.extension.Extension;

/**
* Starts WireMock stubs on an OS-chosen port bound to the IPv4 loopback address.
* <p>
* The explicit bind address is the point: the OS grants a wildcard bind a port another process already holds on
* {@code 127.0.0.1}, and SO_REUSEADDR lets it succeed, leaving that process to answer the stub's requests.
*/
public final class LoopbackWireMock {

/** The IPv4 loopback address, used literally so a caller cannot resolve to {@code ::1} and miss the server. */
public static final String HOST = "127.0.0.1";

private LoopbackWireMock() {
}

/** Extensions must arrive here: WireMock registers response transformers only at server creation. */
public static WireMockServer start(Extension... extensions) {
WireMockServer server = new WireMockServer(
WireMockConfiguration.options().bindAddress(HOST).dynamicPort().extensions(extensions));
server.start();
return server;
}

public static String url(WireMockServer server) {
return "http://" + HOST + ":" + server.port();
}
}
14 changes: 4 additions & 10 deletions src/test/java/com/otilm/core/util/WireMockPorts.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,14 @@
* Fixed ports for the WireMock stubs that stand in for external services, plus the property assignments that bind those
* services to them.
* <p>
* <b>Why fixed ports are safe</b> — Surefire runs test classes sequentially in one fork, so no two stubs contend for a
* port, and every class stops its server when it finishes. A fixed port below the 49152 ephemeral floor is also immune
* to the collisions {@code dynamicPort()} suffers when another process asks for an ephemeral port.
* <b>When a fixed port is warranted</b> — only when the URL must be known before the Spring context is created, so it
* can be written into a {@code @TestPropertySource} or the test {@code application.yml}. The three services below
* qualify; stubs that hand their URL over at runtime use {@link LoopbackWireMock}.
* <p>
* <b>Why the ports are off the context signature</b> — a per-class {@code @TestPropertySource} forks a context, because
* {@link com.otilm.core.architecture.ContextSignature} compares the annotation's source text. The
* {@code *_URL_PROPERTY} assignments below are instead declared once on {@link BaseSpringBootTest} and
* {@link BaseSpringBootTestNoAuth}, so every subclass inherits identical text and none of them forks. Whether two
* classes stubbing the same service also share a cached context depends on their other context axes; the port is not
* one of them, which is the point.
* <p>
* <b>When a new constant is warranted</b> — only when two services must be stubbed with conflicting behaviour at the
* same time. Two classes stubbing the <em>same</em> service never need separate ports, because they never run
* concurrently.
* {@link BaseSpringBootTestNoAuth}, so every subclass inherits identical text and none of them forks.
* <p>
* <b>Duplication in YAML</b> — the test {@code application.yml} repeats these ports as literals, because it cannot
* reference constants, as the floor for the context-loading tests that extend neither base class.
Expand Down
16 changes: 5 additions & 11 deletions src/test/java/com/otilm/core/util/mocks/BaseConnectorMock.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.extension.Extension;
import com.otilm.api.model.client.connector.v2.ConnectorInterface;
import com.otilm.api.model.client.connector.v2.ConnectorInterfaceInfo;
import com.otilm.api.model.client.connector.v2.FeatureFlag;
import com.otilm.api.model.client.connector.v2.InfoResponse;
import com.otilm.core.util.LoopbackWireMock;
import java.util.List;

import static com.otilm.core.util.builders.ConnectorInfoBuilder.aConnectorInfo;
Expand All @@ -29,18 +29,12 @@ public abstract class BaseConnectorMock {

protected final WireMockServer server;

protected BaseConnectorMock() {
this.server = new WireMockServer(0);
this.server.start();
}

/**
* Variant for mocks whose responses are computed per request (e.g. real signing or token assembly): WireMock
* response transformers can only be registered at server creation time.
* Extensions are response transformers computing responses per request, such as real signing or token assembly.
* WireMock registers them only at server creation, so they arrive through the constructor.
*/
protected BaseConnectorMock(Extension... extensions) {
this.server = new WireMockServer(WireMockConfiguration.options().port(0).extensions(extensions));
this.server.start();
this.server = LoopbackWireMock.start(extensions);
}

protected static ConnectorInterfaceInfo interfaceInfo(ConnectorInterface code, List<FeatureFlag> features) {
Expand All @@ -52,7 +46,7 @@ protected static ConnectorInterfaceInfo interfaceInfo(ConnectorInterface code, L
}

public String getUrl() {
return "http://localhost:" + server.port();
return LoopbackWireMock.url(server);
}

public void stop() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

/**
* Spring-managed entry point for starting connector mocks. The mocks themselves are plain WireMock wrappers with a
* per-test lifecycle (fresh server and random port per start; callers stop them in {@code @AfterEach}), so they cannot
* be Spring beans — this factory bridges the two worlds by injecting the beans a mock needs at start. It is the only
* way to start a mock (constructors are package-private), which guarantees the cryptography-provider mock always seeds
* its function-group reference data consistently with what it advertises.
* per-test lifecycle (fresh server on a loopback-bound OS-chosen port per start; callers stop them in
* {@code @AfterEach}), so they cannot be Spring beans — this factory bridges the two worlds by injecting the beans a
* mock needs at start. It is the only way to start a mock (constructors are package-private), which guarantees the
* cryptography-provider mock always seeds its function-group reference data consistently with what it advertises.
* <p>
* Each start yields an independent server, so a test may hold several mocks of the same kind at once.
*/
@Component
public class ConnectorMockFactory {
Expand Down
Loading