diff --git a/pom.xml b/pom.xml index f426226..e257e73 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ 17 ${java.version} ${java.version} + ${java.version} ${git.commit.author.time} diff --git a/src/main/java/org/flowable/mockwebserver/MockHttpStatus.java b/src/main/java/org/flowable/mockwebserver/MockHttpStatus.java index 41aea81..3c5d348 100644 --- a/src/main/java/org/flowable/mockwebserver/MockHttpStatus.java +++ b/src/main/java/org/flowable/mockwebserver/MockHttpStatus.java @@ -14,6 +14,7 @@ /** * An enum of some known HTTP status codes. + * This is inspired by the Spring Framework HttpStatus enum. * * @author Filip Hrisafov */ @@ -21,44 +22,168 @@ public enum MockHttpStatus implements MockHttpStatusCode { // 2xx Success + /** + * {@code 200 OK}. + * @see HTTP/1.1: Semantics and Content, section 6.3.1 + */ OK(200, "OK"), + /** + * {@code 201 Created}. + * @see HTTP/1.1: Semantics and Content, section 6.3.2 + */ CREATED(201, "Created"), + /** + * {@code 202 Accepted}. + * @see HTTP/1.1: Semantics and Content, section 6.3.3 + */ ACCEPTED(202, "Accepted"), + /** + * {@code 204 No Content}. + * @see HTTP/1.1: Semantics and Content, section 6.3.5 + */ NO_CONTENT(204, "No Content"), // 3xx Redirection + /** + * {@code 300 Multiple Choices}. + * @see HTTP/1.1: Semantics and Content, section 6.4.1 + */ MULTIPLE_CHOICES(300, "Multiple Choices"), + /** + * {@code 301 Moved Permanently}. + * @see HTTP/1.1: Semantics and Content, section 6.4.2 + */ MOVED_PERMANENTLY(301, "Moved Permanently"), + /** + * {@code 302 Found}. + * @see HTTP/1.1: Semantics and Content, section 6.4.3 + */ FOUND(302, "Found"), + /** + * {@code 303 See Other}. + * @see HTTP/1.1: Semantics and Content, section 6.4.4 + */ SEE_OTHER(303, "See Other"), + /** + * {@code 304 Not Modified}. + * @see HTTP/1.1: Conditional Requests, section 4.1 + */ NOT_MODIFIED(304, "Not Modified"), + /** + * {@code 307 Temporary Redirect}. + * @see HTTP/1.1: Semantics and Content, section 6.4.7 + */ TEMPORARY_REDIRECT(307, "Temporary Redirect"), + /** + * {@code 308 Permanent Redirect}. + * @see RFC 7238 + */ PERMANENT_REDIRECT(308, "Permanent Redirect"), // 4xx Client Error + /** + * {@code 400 Bad Request}. + * @see HTTP/1.1: Semantics and Content, section 6.5.1 + */ BAD_REQUEST(400, "Bad Request"), + /** + * {@code 401 Unauthorized}. + * @see HTTP/1.1: Authentication, section 3.1 + */ UNAUTHORIZED(401, "Unauthorized"), + /** + * {@code 402 Payment Required}. + * @see HTTP/1.1: Semantics and Content, section 6.5.2 + */ PAYMENT_REQUIRED(402, "Payment Required"), + /** + * {@code 403 Forbidden}. + * @see HTTP/1.1: Semantics and Content, section 6.5.3 + */ FORBIDDEN(403, "Forbidden"), + /** + * {@code 404 Not Found}. + * @see HTTP/1.1: Semantics and Content, section 6.5.4 + */ NOT_FOUND(404, "Not Found"), + /** + * {@code 405 Method Not Allowed}. + * @see HTTP/1.1: Semantics and Content, section 6.5.5 + */ METHOD_NOT_ALLOWED(405, "Method Not Allowed"), + /** + * {@code 406 Not Acceptable}. + * @see HTTP/1.1: Semantics and Content, section 6.5.6 + */ NOT_ACCEPTABLE(406, "Not Acceptable"), + /** + * {@code 409 Conflict}. + * @see HTTP/1.1: Semantics and Content, section 6.5.8 + */ CONFLICT(409, "Conflict"), + /** + * {@code 411 Length Required}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.10 + */ LENGTH_REQUIRED(411, "Length Required"), + /** + * {@code 413 Payload Too Large}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.11 + */ PAYLOAD_TOO_LARGE(413, "Payload Too Large"), + /** + * {@code 414 URI Too Long}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.12 + */ URI_TOO_LONG(414, "URI Too Long"), + /** + * {@code 415 Unsupported Media Type}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.13 + */ UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), + /** + * {@code 418 I'm a teapot}. + * @see HTCPCP/1.0 + */ I_AM_A_TEAPOT(418, "I'm a teapot"), + /** + * {@code 429 Too Many Requests}. + * @see Additional HTTP Status Codes + */ TOO_MANY_REQUESTS(429, "Too Many Requests"), // 5xx Server Error + /** + * {@code 500 Internal Server Error}. + * @see HTTP/1.1: Semantics and Content, section 6.6.1 + */ INTERNAL_SERVER_ERROR(500, "Internal Server Error"), + /** + * {@code 501 Not Implemented}. + * @see HTTP/1.1: Semantics and Content, section 6.6.2 + */ NOT_IMPLEMENTED(501, "Not Implemented"), + /** + * {@code 502 Bad Gateway}. + * @see HTTP/1.1: Semantics and Content, section 6.6.3 + */ BAD_GATEWAY(502, "Bad Gateway"), + /** + * {@code 503 Service Unavailable}. + * @see HTTP/1.1: Semantics and Content, section 6.6.4 + */ SERVICE_UNAVAILABLE(503, "Service Unavailable"), + /** + * {@code 504 Gateway Timeout}. + * @see HTTP/1.1: Semantics and Content, section 6.6.5 + */ GATEWAY_TIMEOUT(504, "Gateway Timeout"), ; diff --git a/src/main/java/org/flowable/mockwebserver/MockHttpStatusCode.java b/src/main/java/org/flowable/mockwebserver/MockHttpStatusCode.java index 1d8fbc6..ee8468b 100644 --- a/src/main/java/org/flowable/mockwebserver/MockHttpStatusCode.java +++ b/src/main/java/org/flowable/mockwebserver/MockHttpStatusCode.java @@ -14,6 +14,7 @@ /** * Represents an HTTP status code. + * This is inspired by the Spring Framework HttpStatusCode interface. * * @author Filip Hrisafov * @see MockHttpStatus for known status codes @@ -22,11 +23,15 @@ public sealed interface MockHttpStatusCode permits MockHttpStatus, CustomMockHttpStatus { /** + * The HTTP status code. + * * @return The HTTP status code */ int code(); /** + * The reason for the HTTP status code. + * * @return The reason for the HTTP status code */ String reason(); @@ -73,6 +78,13 @@ static MockHttpStatusCode from(int code) { }; } + /** + * Create a custom {@link MockHttpStatusCode} with the given code and reason + * + * @param code the code to create the status for + * @param reason the custom reason for the code + * @return the custom status + */ static MockHttpStatusCode from(int code, String reason) { return new CustomMockHttpStatus(code, reason); } diff --git a/src/main/java/org/flowable/mockwebserver/MockResponse.java b/src/main/java/org/flowable/mockwebserver/MockResponse.java index 12ae001..14586dd 100644 --- a/src/main/java/org/flowable/mockwebserver/MockResponse.java +++ b/src/main/java/org/flowable/mockwebserver/MockResponse.java @@ -17,20 +17,24 @@ import org.microhttp.Response; /** + * The response that will be returned by the {@link MockWebServer} when a request is made. + * * @author Filip Hrisafov */ -public class MockResponse { +public final class MockResponse { - protected final Response response; - protected final Duration delay; + final Response response; + final Duration delay; - protected MockResponse(Response response, Duration delay) { + MockResponse(Response response, Duration delay) { this.response = response; this.delay = delay; } /** - * Create a new builder for a {@link MockResponse} with a default status of {@link MockHttpStatus#OK} + * Create a new builder for a {@link MockResponse} with a default status of {@link MockHttpStatus#OK}. + * + * @return The builder for fluent API */ public static MockResponseBuilder newBuilder() { return MockResponseBuilder.newBuilder(); diff --git a/src/main/java/org/flowable/mockwebserver/MockResponseBuilder.java b/src/main/java/org/flowable/mockwebserver/MockResponseBuilder.java index f6b63c4..1f55a92 100644 --- a/src/main/java/org/flowable/mockwebserver/MockResponseBuilder.java +++ b/src/main/java/org/flowable/mockwebserver/MockResponseBuilder.java @@ -33,63 +33,73 @@ * * @author Filip Hrisafov */ -public class MockResponseBuilder { +public final class MockResponseBuilder { - protected static final byte[] EMPTY_BYTE = new byte[0]; + private static final byte[] EMPTY_BYTE = new byte[0]; - protected MockHttpStatusCode status = MockHttpStatus.OK; - protected final Map> headers = new LinkedHashMap<>(); - protected byte[] body = EMPTY_BYTE; - protected Duration delay; + private MockHttpStatusCode status = MockHttpStatus.OK; + private final Map> headers = new LinkedHashMap<>(); + private byte[] body = EMPTY_BYTE; + private Duration delay; - protected MockResponseBuilder() { + private MockResponseBuilder() { header("Content-Length", "0"); } /** - * Create a new builder for a {@link MockResponse} with a default status of {@link MockHttpStatus#OK} + * Create a new builder for a {@link MockResponse} with a default status of {@link MockHttpStatus#OK}. + * + * @return The builder for fluent API */ public static MockResponseBuilder newBuilder() { return new MockResponseBuilder(); } /** - * Set the status of the response to {@link MockHttpStatus#OK} + * Set the status of the response to {@link MockHttpStatus#OK}. + * + * @return The builder for fluent API */ public MockResponseBuilder ok() { return status(MockHttpStatus.OK); } /** - * Set the status of the response to {@link MockHttpStatus#NOT_FOUND} + * Set the status of the response to {@link MockHttpStatus#NOT_FOUND}. + * + * @return The builder for fluent API */ public MockResponseBuilder notFound() { return status(MockHttpStatus.NOT_FOUND); } /** - * Set the status of the response to the given code - */ - public MockResponseBuilder responseCode(int code) { - return status(MockHttpStatusCode.from(code)); - } - - /** - * Set the status of the response to the given code + * Set the status of the response to the given code. + * + * @param code the status code of the response + * @return The builder for fluent API */ public MockResponseBuilder statusCode(int code) { return status(MockHttpStatusCode.from(code)); } /** - * Set the status of the response to the given code and reason + * Set the status of the response to the given code and reason. + * + * @param code the status code of the response + * @param reason the reason of the response + * @return The builder for fluent API */ public MockResponseBuilder status(int code, String reason) { return status(MockHttpStatusCode.from(code, reason)); } /** - * Set the status of the response + * Set the status of the response. + * + * @param status the status of the response + * + * @return The builder for fluent API */ public MockResponseBuilder status(MockHttpStatusCode status) { if (status == null) { @@ -105,6 +115,7 @@ public MockResponseBuilder status(MockHttpStatusCode status) { * * @param name the name of the header * @param value the value of the header + * @return The builder for fluent API */ public MockResponseBuilder header(String name, String value) { headers.remove(name); @@ -112,10 +123,11 @@ public MockResponseBuilder header(String name, String value) { } /** - * Add a header to the response + * Add a header to the response. * * @param name the name of the header * @param value the value of the header + * @return The builder for fluent API */ public MockResponseBuilder addHeader(String name, String value) { this.headers.computeIfAbsent(name, k -> new ArrayList<>()) @@ -128,6 +140,7 @@ public MockResponseBuilder addHeader(String name, String value) { * This method will read all the bytes from the body. * * @param body the input stream to read the bytes from + * @return The builder for fluent API */ public MockResponseBuilder body(InputStream body) { try { @@ -142,6 +155,7 @@ public MockResponseBuilder body(InputStream body) { * This method will read all the bytes from the body. * * @param body the input stream to read the bytes from + * @return The builder for fluent API */ public MockResponseBuilder body(byte[] body) { this.body = Arrays.copyOf(body, body.length); @@ -155,6 +169,7 @@ public MockResponseBuilder body(byte[] body) { * The charset used is {@link StandardCharsets#UTF_8}. * * @param body the body of the response + * @return The builder for fluent API */ public MockResponseBuilder jsonBody(String body) { return jsonBody(body, StandardCharsets.UTF_8); @@ -166,6 +181,7 @@ public MockResponseBuilder jsonBody(String body) { * * @param body the body of the response * @param charset the charset of the body + * @return The builder for fluent API */ public MockResponseBuilder jsonBody(String body, Charset charset) { body(body, charset); @@ -177,6 +193,7 @@ public MockResponseBuilder jsonBody(String body, Charset charset) { * The charset used is {@link StandardCharsets#UTF_8}. * * @param body the body of the response + * @return The builder for fluent API */ public MockResponseBuilder body(String body) { return body(body, StandardCharsets.UTF_8); @@ -187,6 +204,7 @@ public MockResponseBuilder body(String body) { * * @param body the body of the response * @param charset the charset of the body + * @return The builder for fluent API */ public MockResponseBuilder body(String body, Charset charset) { this.body = body.getBytes(charset); @@ -200,6 +218,7 @@ public MockResponseBuilder body(String body, Charset charset) { * * @param timeout the timeout of the delay * @param timeUnit the time unit of the timeout + * @return The builder for fluent API */ public MockResponseBuilder bodyDelay(long timeout, TimeUnit timeUnit) { if (timeout <= 0) { @@ -213,6 +232,7 @@ public MockResponseBuilder bodyDelay(long timeout, TimeUnit timeUnit) { * The delay must be positive. * * @param delay the delay of the response body + * @return The builder for fluent API */ public MockResponseBuilder bodyDelay(Duration delay) { if (delay.isNegative() || delay.isZero()) { @@ -223,7 +243,9 @@ public MockResponseBuilder bodyDelay(Duration delay) { } /** - * Build the {@link MockResponse} instance + * Build the {@link MockResponse} instance. + * + * @return the {@link MockResponse} instance */ public MockResponse build() { List
headers = new ArrayList<>(this.headers.size()); diff --git a/src/main/java/org/flowable/mockwebserver/MockWebServer.java b/src/main/java/org/flowable/mockwebserver/MockWebServer.java index 92eafce..0f1fbb2 100644 --- a/src/main/java/org/flowable/mockwebserver/MockWebServer.java +++ b/src/main/java/org/flowable/mockwebserver/MockWebServer.java @@ -34,11 +34,11 @@ * * @author Filip Hrisafov */ -public class MockWebServer { +public final class MockWebServer { - protected final TestHandler handler; - protected EventLoop eventLoop; - protected Options options; + private final TestHandler handler; + private EventLoop eventLoop; + private Options options; /** * Create a new mock webserver with a queue based response provider. @@ -103,16 +103,22 @@ public void shutdown() { } /** - * Returns a URL for connecting to this server under the {@code /} path. + * The URL for connecting to this server. + * This will return something like {@code http://:/}. + * + * @return a URL for connecting to this server under the {@code /} path. */ public String url() { return url("/"); } /** - * Returns a URL for connecting to this server. + * Return a URL for connecting to this server using the given path. + * This will return something like {@code http://:}. + * Note if the path does not start with {@code /} it will be added. * - * @param path the request path, such as "/". + * @param path the request path, such as "/v1". + * @return a URL for connecting to this server using the given path. */ public String url(String path) { if (eventLoop == null) { @@ -120,7 +126,7 @@ public String url(String path) { } try { - return "http://" + options.host() + ":" + eventLoop.getPort() + path; + return "http://" + options.host() + ":" + eventLoop.getPort() + (path.startsWith("/") ? path : "/" + path); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -228,10 +234,10 @@ public int getPort() { } } - protected static class QueueResponseProvider implements Function { + private static class QueueResponseProvider implements Function { - protected final BlockingQueue responseQueue = new LinkedBlockingQueue<>(); - protected MockResponse defaultResponse; + private final BlockingQueue responseQueue = new LinkedBlockingQueue<>(); + private MockResponse defaultResponse; @Override public MockResponse apply(RecordedRequest recordedRequest) { @@ -243,13 +249,13 @@ public MockResponse apply(RecordedRequest recordedRequest) { } } - protected static class TestHandler implements Handler { + private static class TestHandler implements Handler { - protected final Function responseProvider; - protected final BlockingQueue requestQueue = new LinkedBlockingQueue<>(); - protected final AtomicLong requestCount = new AtomicLong(0); + private final Function responseProvider; + private final BlockingQueue requestQueue = new LinkedBlockingQueue<>(); + private final AtomicLong requestCount = new AtomicLong(0); - protected TestHandler(Function responseProvider) { + private TestHandler(Function responseProvider) { this.responseProvider = responseProvider; } @@ -264,7 +270,7 @@ public void handle(Request request, Consumer callback) { } } - protected void handle(MockResponse response, Consumer callback) { + private void handle(MockResponse response, Consumer callback) { Duration delay = response.delay; if (delay != null) { long sleep = delay.toMillis(); @@ -282,7 +288,7 @@ protected void handle(MockResponse response, Consumer callback) { } - protected long requestCount() { + private long requestCount() { return requestCount.get(); } } diff --git a/src/main/java/org/flowable/mockwebserver/RecordedRequest.java b/src/main/java/org/flowable/mockwebserver/RecordedRequest.java index f9f88ff..4a1edb5 100644 --- a/src/main/java/org/flowable/mockwebserver/RecordedRequest.java +++ b/src/main/java/org/flowable/mockwebserver/RecordedRequest.java @@ -29,11 +29,11 @@ * * @author Filip Hrisafov */ -public class RecordedRequest { +public final class RecordedRequest { - protected final Request request; + private final Request request; - public RecordedRequest(Request request) { + RecordedRequest(Request request) { this.request = request; } @@ -59,6 +59,8 @@ public RequestUrl requestUrl() { } /** + * The method of the request. + * * @return the method of the request */ public String method() { @@ -116,15 +118,20 @@ public Body body() { return new Body(request.body()); } - public static class Body { + /** + * This represents the body of the request. + */ + public static final class Body { - protected final byte[] bytes; + private final byte[] bytes; - protected Body(byte[] bytes) { + private Body(byte[] bytes) { this.bytes = bytes; } /** + * The body of the request as a string using the UTF-8 charset. + * * @return the body of the request as a string using the UTF-8 charset. */ public String asString() { @@ -132,6 +139,9 @@ public String asString() { } /** + * The body of the request as a string using the given charset. + * + * @param charset the charset to use to parse the body * @return the body of the request as a string using give charset */ public String asString(Charset charset) { @@ -139,6 +149,8 @@ public String asString(Charset charset) { } /** + * The body of the request as a byte array. + * * @return the body of the request as a byte array */ public byte[] asByteArray() { @@ -146,8 +158,20 @@ public byte[] asByteArray() { } } + /** + * This represents the parsed request URL. + * + * @param path the path of the request (without the query parameters) + * @param queryParameters the query parameters of the request + */ public record RequestUrl(String path, Map queryParameters) { + /** + * Create a new instance of the {@link RequestUrl}. + * + * @param path the path of the request (without the query parameters) + * @param queryParameters the query parameters of the request + */ public RequestUrl { queryParameters = Collections.unmodifiableMap(queryParameters); } diff --git a/src/test/java/org/flowable/mockwebserver/ExampleTest.java b/src/test/java/org/flowable/mockwebserver/ExampleTest.java index afbb281..920076d 100644 --- a/src/test/java/org/flowable/mockwebserver/ExampleTest.java +++ b/src/test/java/org/flowable/mockwebserver/ExampleTest.java @@ -125,5 +125,7 @@ void queryPets() throws IOException, InterruptedException { "type": "dog" } """); + + assertThat(server.requestCount()).isEqualTo(2); } }