diff --git a/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/OperationTest.java b/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/OperationTest.java index 1f3ec91..bdfaecc 100644 --- a/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/OperationTest.java +++ b/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/OperationTest.java @@ -11,6 +11,7 @@ static class SampleParam implements Request.Param { String slug = "abc"; } + // Checks that path variables are replaced with matching parameter fields @Test void replacePathVariablesSubstitutesFields() throws Exception { Operation op = new Operation() { diff --git a/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/RequestParamTest.java b/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/RequestParamTest.java index cb27d58..6bc90f8 100644 --- a/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/RequestParamTest.java +++ b/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/RequestParamTest.java @@ -6,12 +6,14 @@ class RequestParamTest { + // Ensures a request parameter defaults to using the PATH kind @Test void defaultKindIsPath() { Request.Param param = new Request.Param() {}; assertThat(param.getKind()).isEqualTo(Request.Param.Kind.PATH); } + // Verifies enum access and placeholder classes work as expected @Test void enumValuesAndVoidClasses() { assertThat(Request.Param.Kind.valueOf("QUERY")).isEqualTo(Request.Param.Kind.QUERY); diff --git a/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/RequestTest.java b/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/RequestTest.java index e4674f5..ea2f6e7 100644 --- a/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/RequestTest.java +++ b/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/RequestTest.java @@ -6,6 +6,7 @@ class RequestTest { + // Ensures the default getResponse implementation throws an exception @Test void defaultGetResponseThrowsUnsupportedOperationException() { Request request = new Request() {}; diff --git a/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/util/ConfigurationTest.java b/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/util/ConfigurationTest.java index 592a850..bb8ed44 100644 --- a/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/util/ConfigurationTest.java +++ b/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/util/ConfigurationTest.java @@ -18,6 +18,7 @@ void setUp() { configuration = new Configuration("test", url); } + // Verifies all getter methods return expected defaults or file values @Test void keysAndGettersHandleDefaultsProperly() { assertThat(configuration.keys()).containsExactlyInAnyOrder("string", "int", "long", "double", "boolean"); @@ -47,12 +48,14 @@ void keysAndGettersHandleDefaultsProperly() { assertThat(configuration.getBoolean("boolean", false)).isTrue(); } + // Ensures the default constructor loads properties from the classpath @Test void defaultConstructorLoadsAppProperties() { Configuration cfg = new Configuration("test"); assertThat(cfg.get("string")).isEqualTo("fromFile"); } + // Confirms environment variables take precedence over property files @Test void environmentVariablesOverrideProperties() throws Exception { String classpath = System.getProperty("java.class.path"); diff --git a/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/util/ConstantsTest.java b/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/util/ConstantsTest.java index 36c7283..4ad80c0 100644 --- a/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/util/ConstantsTest.java +++ b/phoenixd-base/src/test/java/xyz/tcheeric/phoenixd/common/rest/util/ConstantsTest.java @@ -6,6 +6,7 @@ class ConstantsTest { + // Validates that HTTP method constants expose the correct strings @Test void httpMethodConstantsMatchValues() { assertThat(Constants.HTTP_GET_METHOD).isEqualTo("GET"); diff --git a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/CreateInvoiceParamTest.java b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/CreateInvoiceParamTest.java index 0435bd9..a15e10c 100644 --- a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/CreateInvoiceParamTest.java +++ b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/CreateInvoiceParamTest.java @@ -9,6 +9,7 @@ class CreateInvoiceParamTest { + // Ensures toString properly URL-encodes provided values @Test void toStringEncodesValues() throws MalformedURLException { CreateInvoiceParam param = new CreateInvoiceParam(); @@ -26,6 +27,7 @@ void toStringEncodesValues() throws MalformedURLException { .contains("webhookUrl=https%3A%2F%2Fexample.com%2Fhook%3Ffoo%3Dbar%26baz%3Dqux"); } + // Verifies getters, setters and toString when a webhook URL is supplied @Test void settersGettersAndToStringWithWebhook() throws Exception { CreateInvoiceParam param = new CreateInvoiceParam(); @@ -46,6 +48,7 @@ void settersGettersAndToStringWithWebhook() throws Exception { "description=desc&amountSat=1&expirySeconds=2&externalId=id&webhookUrl=https://example.com"); } + // Confirms toString omits the webhook URL when it is not set @Test void toStringWithoutWebhook() { CreateInvoiceParam param = new CreateInvoiceParam(); diff --git a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/DecodeInvoiceParamTest.java b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/DecodeInvoiceParamTest.java index d597640..4263168 100644 --- a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/DecodeInvoiceParamTest.java +++ b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/DecodeInvoiceParamTest.java @@ -6,6 +6,7 @@ class DecodeInvoiceParamTest { + // Ensures the invoice field is handled correctly in getters and toString @Test void settersGettersAndToString() { DecodeInvoiceParam param = new DecodeInvoiceParam(); diff --git a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/PayBolt11InvoiceParamTest.java b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/PayBolt11InvoiceParamTest.java index b584c24..4a26787 100644 --- a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/PayBolt11InvoiceParamTest.java +++ b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/PayBolt11InvoiceParamTest.java @@ -6,6 +6,7 @@ class PayBolt11InvoiceParamTest { + // Validates getters, setters and string representation of the param @Test void settersGettersAndToString() { PayBolt11InvoiceParam param = new PayBolt11InvoiceParam(); diff --git a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/PayLightningAddressParamTest.java b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/PayLightningAddressParamTest.java index 9652c90..c13f874 100644 --- a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/PayLightningAddressParamTest.java +++ b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/param/PayLightningAddressParamTest.java @@ -6,6 +6,7 @@ class PayLightningAddressParamTest { + // Ensures setters, getters, and toString handle a message field @Test void settersGettersAndToStringWithMessage() { PayLightningAddressParam param = new PayLightningAddressParam(); @@ -19,6 +20,7 @@ void settersGettersAndToStringWithMessage() { assertThat(param.toString()).isEqualTo("amountSat=70&address=addr&message=hello"); } + // Confirms toString omits the message when it is empty or null @Test void toStringWithoutMessage() { PayLightningAddressParam param = new PayLightningAddressParam(); diff --git a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/CreateInvoiceResponseTest.java b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/CreateInvoiceResponseTest.java index 7cce56b..a1951c3 100644 --- a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/CreateInvoiceResponseTest.java +++ b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/CreateInvoiceResponseTest.java @@ -6,6 +6,7 @@ class CreateInvoiceResponseTest { + // Validates fields and string representation for invoice creation responses @Test void settersGettersAndToString() { CreateInvoiceResponse response = new CreateInvoiceResponse(); diff --git a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/DecodeInvoiceResponseTest.java b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/DecodeInvoiceResponseTest.java index a2c0bc6..7a496c8 100644 --- a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/DecodeInvoiceResponseTest.java +++ b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/DecodeInvoiceResponseTest.java @@ -8,6 +8,7 @@ class DecodeInvoiceResponseTest { + // Checks getters, setters, and string output for complex invoice responses @Test void settersGettersAndToString() { DecodeInvoiceResponse response = new DecodeInvoiceResponse(); diff --git a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/GetLightningAddressResponseTest.java b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/GetLightningAddressResponseTest.java index 38d7158..6d1a134 100644 --- a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/GetLightningAddressResponseTest.java +++ b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/GetLightningAddressResponseTest.java @@ -6,6 +6,7 @@ class GetLightningAddressResponseTest { + // Ensures lightning addresses are handled and formatted correctly @Test void settersGettersAndToString() { GetLightningAddressResponse response = new GetLightningAddressResponse(); diff --git a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayBolt11InvoiceInvoiceResponseTest.java b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayBolt11InvoiceInvoiceResponseTest.java index 46377cb..fb00876 100644 --- a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayBolt11InvoiceInvoiceResponseTest.java +++ b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayBolt11InvoiceInvoiceResponseTest.java @@ -6,6 +6,7 @@ class PayBolt11InvoiceInvoiceResponseTest { + // Validates Bolt11 payment response fields and string output @Test void settersGettersAndToString() { PayBolt11InvoiceInvoiceResponse response = new PayBolt11InvoiceInvoiceResponse(); diff --git a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayInvoiceResponseTest.java b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayInvoiceResponseTest.java index b0f4995..738bfb0 100644 --- a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayInvoiceResponseTest.java +++ b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayInvoiceResponseTest.java @@ -6,6 +6,7 @@ class PayInvoiceResponseTest { + // Confirms fields and string output for generic payment invoices @Test void settersGettersAndToString() { PayInvoiceResponse response = new PayInvoiceResponse(); diff --git a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayLightningAddressInvoiceResponseTest.java b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayLightningAddressInvoiceResponseTest.java index 790eb58..bea7100 100644 --- a/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayLightningAddressInvoiceResponseTest.java +++ b/phoenixd-model/src/test/java/xyz/tcheeric/phoenixd/model/response/PayLightningAddressInvoiceResponseTest.java @@ -6,6 +6,7 @@ class PayLightningAddressInvoiceResponseTest { + // Checks values and string output for lightning address payment invoices @Test void settersGettersAndToString() { PayLightningAddressInvoiceResponse response = new PayLightningAddressInvoiceResponse(); diff --git a/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/operation/AbstractOperationTest.java b/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/operation/AbstractOperationTest.java index dcf39b3..033a072 100644 --- a/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/operation/AbstractOperationTest.java +++ b/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/operation/AbstractOperationTest.java @@ -4,13 +4,16 @@ import okhttp3.mockwebserver.MockWebServer; import org.junit.jupiter.api.Test; import xyz.tcheeric.phoenixd.operation.impl.GetOperation; +import xyz.tcheeric.phoenixd.operation.impl.PostOperation; import java.net.http.HttpRequest; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class AbstractOperationTest { + // Confirms operations execute once and capture the response body @Test void executeMakesSingleNetworkCall() throws Exception { MockWebServer server = new MockWebServer(); @@ -31,4 +34,36 @@ void executeMakesSingleNetworkCall() throws Exception { server.shutdown(); } } + + // Ensures non-success HTTP status codes trigger exceptions + @Test + void executeThrowsOnNon2xxResponse() throws Exception { + MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setResponseCode(400)); + server.start(); + try { + HttpRequest request = HttpRequest.newBuilder(server.url("/fail").uri()).GET().build(); + GetOperation operation = new GetOperation(request); + assertThatThrownBy(operation::execute).isInstanceOf(Exception.class); + } finally { + server.shutdown(); + } + } + + // Verifies adding a header replaces any existing value + @Test + void addHeaderReplacesExisting() { + PostOperation op = new PostOperation("/items", "data"); + String original = op.getHeader("Authorization"); + op.addHeader("Authorization", "Basic new"); + assertThat(op.getHeader("Authorization")).isEqualTo("Basic new"); + assertThat(original).isNotEqualTo(op.getHeader("Authorization")); + } + + // Checks that removing headers from a POST operation is unsupported + @Test + void removeHeaderUnsupported() { + PostOperation op = new PostOperation("/items", "data"); + assertThatThrownBy(() -> op.removeHeader("X")).isInstanceOf(UnsupportedOperationException.class); + } } diff --git a/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/operation/OperationsTest.java b/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/operation/OperationsTest.java new file mode 100644 index 0000000..d983e7f --- /dev/null +++ b/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/operation/OperationsTest.java @@ -0,0 +1,119 @@ +package xyz.tcheeric.phoenixd.operation; + +import org.junit.jupiter.api.Test; +import xyz.tcheeric.phoenixd.common.rest.Request; +import xyz.tcheeric.phoenixd.operation.impl.DeleteOperation; +import xyz.tcheeric.phoenixd.operation.impl.PatchOperation; +import xyz.tcheeric.phoenixd.operation.impl.PostOperation; +import xyz.tcheeric.phoenixd.operation.impl.GetOperation; + +import java.net.URI; +import java.net.http.HttpRequest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class OperationsTest { + + static class PathParam implements Request.Param { + String id = "123"; + @Override + public String toString() { + return "id=" + id; + } + } + + // Verifies POST operations set content type and expand path variables + @Test + void postOperationSetsContentTypeAndResolvesPath() { + PostOperation op = new PostOperation("/items/{id}", new PathParam(), "data"); + assertThat(op.getHeader("Content-Type")).isEqualTo("application/x-www-form-urlencoded"); + assertThat(op.getHttpRequest().uri().getPath()).isEqualTo("/items/123"); + } + + // Ensures POST operations with body still assign content type header + @Test + void postOperationWithBodyOnlySetsHeader() { + PostOperation op = new PostOperation("/items", "data"); + assertThat(op.getHeader("Content-Type")).isEqualTo("application/x-www-form-urlencoded"); + } + + // Confirms constructing a POST operation from an HttpRequest retains the path + @Test + void postOperationFromHttpRequest() { + HttpRequest request = HttpRequest.newBuilder(URI.create("http://localhost/post")) + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + PostOperation op = new PostOperation(request); + assertThat(op.getHttpRequest().uri().getPath()).isEqualTo("/post"); + } + + // Checks that PATCH operations support adding headers + @Test + void patchOperationAddsHeader() { + PatchOperation op = new PatchOperation("/patch"); + op.addHeader("X-Test", "value"); + assertThat(op.getHeader("X-Test")).isEqualTo("value"); + } + + // Validates removing headers from PATCH operations is unsupported + @Test + void patchOperationRemoveHeaderThrows() { + PatchOperation op = new PatchOperation("/patch"); + assertThatThrownBy(() -> op.removeHeader("X")) + .isInstanceOf(UnsupportedOperationException.class); + } + + // Ensures DELETE operations allow removal of default headers + @Test + void deleteOperationRemovesHeader() { + DeleteOperation op = new DeleteOperation("/delete"); + assertThat(op.getHeader("Authorization")).isNotNull(); + op.removeHeader("Authorization"); + assertThat(op.getHeader("Authorization")).isNull(); + } + + // Verifies DELETE operations resolve path variables from parameters + @Test + void deleteOperationWithParamResolvesPath() { + DeleteOperation op = new DeleteOperation("/items/{id}", new PathParam()); + assertThat(op.getHttpRequest().uri().getPath()).isEqualTo("/items/123"); + } + + // Confirms DELETE operations built from HttpRequest keep the same path + @Test + void deleteOperationFromHttpRequest() { + HttpRequest request = HttpRequest.newBuilder(URI.create("http://localhost/delete")) + .DELETE() + .build(); + DeleteOperation op = new DeleteOperation(request); + assertThat(op.getHttpRequest().uri().getPath()).isEqualTo("/delete"); + } + + // Checks GET operations append query parameters for QUERY-kind params + @Test + void getOperationWithQueryParamAppendsQuery() { + class QueryParam extends PathParam { + @Override + public Request.Param.Kind getKind() { + return Request.Param.Kind.QUERY; + } + } + GetOperation op = new GetOperation("/query", new QueryParam()); + assertThat(op.getHttpRequest().uri().getQuery()).isEqualTo("id=123"); + } + + // Ensures constructors validate against null arguments + @Test + void nullArgumentsThrow() { + assertThatThrownBy(() -> new PostOperation(null, "data")).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new PostOperation("/x", (Request.Param) null, "data")).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new PostOperation("/x", new PathParam(), null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new PatchOperation((String) null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new PatchOperation("/x", null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new DeleteOperation((String) null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new DeleteOperation("/x", null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new GetOperation((String) null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new GetOperation("/x", null)).isInstanceOf(NullPointerException.class); + } +} diff --git a/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/request/AbstractRequestTest.java b/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/request/AbstractRequestTest.java new file mode 100644 index 0000000..789a848 --- /dev/null +++ b/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/request/AbstractRequestTest.java @@ -0,0 +1,96 @@ +package xyz.tcheeric.phoenixd.request; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.jupiter.api.Test; +import xyz.tcheeric.phoenixd.common.rest.Operation; +import xyz.tcheeric.phoenixd.common.rest.VoidRequestParam; +import xyz.tcheeric.phoenixd.common.rest.VoidResponse; +import xyz.tcheeric.phoenixd.model.response.CreateInvoiceResponse; +import xyz.tcheeric.phoenixd.model.response.GetLightningAddressResponse; +import xyz.tcheeric.phoenixd.operation.impl.GetOperation; +import xyz.tcheeric.phoenixd.operation.AbstractOperation; + +import java.net.URI; +import java.net.http.HttpRequest; + +import static org.assertj.core.api.Assertions.assertThat; + +class AbstractRequestTest { + + // Parses JSON bodies into response objects + @Test + void parsesJsonResponse() throws Exception { + MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"amountSat\":1,\"paymentHash\":\"h\",\"serialized\":\"s\"}")); + server.start(); + try { + HttpRequest httpRequest = HttpRequest.newBuilder(server.url("/invoice").uri()).GET().build(); + GetOperation op = new GetOperation(httpRequest); + AbstractRequest req = new AbstractRequest<>("/invoice", null, op) {}; + CreateInvoiceResponse resp = req.getResponse(); + assertThat(resp.getAmountSat()).isEqualTo(1); + assertThat(resp.getPaymentHash()).isEqualTo("h"); + assertThat(server.getRequestCount()).isEqualTo(1); + } finally { + server.shutdown(); + } + } + + // Removes bitcoin prefix from lightning address responses + @Test + void handlesLightningAddressWithPrefix() throws Exception { + MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200).setBody("â‚¿user@domain")); + server.start(); + try { + HttpRequest httpRequest = HttpRequest.newBuilder(server.url("/ln").uri()).GET().build(); + GetOperation op = new GetOperation(httpRequest); + AbstractRequest req = new AbstractRequest<>("/ln", null, op) {}; + GetLightningAddressResponse resp = req.getResponse(); + assertThat(resp.getLightningAddress()).isEqualTo("user@domain"); + assertThat(server.getRequestCount()).isEqualTo(1); + } finally { + server.shutdown(); + } + } + + // Accepts lightning addresses returned without any prefix + @Test + void handlesLightningAddressWithoutPrefix() throws Exception { + MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200).setBody("user@domain")); + server.start(); + try { + HttpRequest httpRequest = HttpRequest.newBuilder(server.url("/ln").uri()).GET().build(); + GetOperation op = new GetOperation(httpRequest); + AbstractRequest req = new AbstractRequest<>("/ln", null, op) {}; + GetLightningAddressResponse resp = req.getResponse(); + assertThat(resp.getLightningAddress()).isEqualTo("user@domain"); + assertThat(server.getRequestCount()).isEqualTo(1); + } finally { + server.shutdown(); + } + } + + // Ensures operations aren't executed when expecting a void response + @Test + void voidResponseDoesNotExecuteOperation() { + class DummyOperation extends AbstractOperation { + boolean executed = false; + DummyOperation() { + super(HttpRequest.newBuilder(URI.create("http://localhost/test")).GET().build()); + } + @Override + public Operation execute() { + executed = true; + return this; + } + } + DummyOperation op = new DummyOperation(); + AbstractRequest req = new AbstractRequest<>("/test", null, op) {}; + VoidResponse resp = req.getResponse(); + assertThat(resp).isNotNull(); + assertThat(op.executed).isFalse(); + } +} diff --git a/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/request/impl/RequestTest.java b/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/request/impl/RequestTest.java new file mode 100644 index 0000000..a17660e --- /dev/null +++ b/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/request/impl/RequestTest.java @@ -0,0 +1,109 @@ +package xyz.tcheeric.phoenixd.request.impl; + +import org.junit.jupiter.api.Test; +import xyz.tcheeric.phoenixd.common.rest.Request; +import xyz.tcheeric.phoenixd.common.rest.VoidRequestParam; +import xyz.tcheeric.phoenixd.common.rest.VoidResponse; +import xyz.tcheeric.phoenixd.model.param.CreateInvoiceParam; +import xyz.tcheeric.phoenixd.model.param.DecodeInvoiceParam; +import xyz.tcheeric.phoenixd.request.impl.rest.CreateBolt11InvoiceRequest; +import xyz.tcheeric.phoenixd.request.impl.rest.DecodeInvoiceRequest; +import xyz.tcheeric.phoenixd.request.impl.rest.GetLightningAddressRequest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class RequestTest { + + static class DummyParam implements Request.Param { + String id = "123"; + @Override + public String toString() { + return ""; + } + } + + // Verifies header management and path substitution for POST and DELETE requests + @Test + void postAndDeleteRequestHeaders() { + PostRequest post = new PostRequest<>("/post", "body"); + post.addHeader("X-Test", "value"); + assertThat(post.getOperation().getHeader("X-Test")).isEqualTo("value"); + + DummyParam param = new DummyParam(); + PostRequest postWithParam = new PostRequest<>("/post", param); + postWithParam.addHeader("A", "B"); + PostRequest postWithParamAndBody = new PostRequest<>("/post/{id}", param, "data"); + postWithParamAndBody.addHeader("A", "B"); + assertThat(((xyz.tcheeric.phoenixd.operation.AbstractOperation) postWithParam.getOperation()).getHttpRequest().uri().getPath()).isEqualTo("/post"); + assertThat(((xyz.tcheeric.phoenixd.operation.AbstractOperation) postWithParamAndBody.getOperation()).getHttpRequest().uri().getPath()).isEqualTo("/post/123"); + assertThat(postWithParam.getParam()).isEqualTo(param); + assertThat(postWithParamAndBody.getParam()).isEqualTo(param); + + DeleteRequest delete = new DeleteRequest<>("/delete"); + assertThat(delete.getOperation().getHeader("Authorization")).isNotNull(); + delete.removeHeader("Authorization"); + assertThat(delete.getOperation().getHeader("Authorization")).isNull(); + + DeleteRequest deleteWithParam = new DeleteRequest<>("/delete/{id}", new DummyParam()); + assertThat(((xyz.tcheeric.phoenixd.operation.AbstractOperation) deleteWithParam.getOperation()).getHttpRequest().uri().getPath()).isEqualTo("/delete/123"); + assertThat(deleteWithParam.getParam()).isNotNull(); + } + + // Checks GET and PATCH requests handle parameters and paths + @Test + void getAndPatchRequestConstructors() { + GetRequest getNoParam = new GetRequest<>("/get"); + GetRequest get = new GetRequest<>("/get", new DummyParam()); + PatchRequest patchNoParam = new PatchRequest<>("/patch"); + PatchRequest patch = new PatchRequest<>("/patch", new DummyParam()); + assertThat(getNoParam.getPath()).isEqualTo("/get"); + assertThat(get.getPath()).isEqualTo("/get"); + assertThat(get.getParam()).isNotNull(); + assertThat(patchNoParam.getPath()).isEqualTo("/patch"); + assertThat(patch.getPath()).isEqualTo("/patch"); + assertThat(patch.getParam()).isNotNull(); + } + + // Ensures specialized request subclasses can be constructed + @Test + void specializedRequestsConstructors() { + CreateInvoiceParam createParam = new CreateInvoiceParam(); + createParam.setDescription("desc"); + new CreateBolt11InvoiceRequest(createParam); + + DecodeInvoiceParam decodeParam = new DecodeInvoiceParam(); + decodeParam.setInvoice("lnbc1abcde"); + new DecodeInvoiceRequest(decodeParam); + + new GetLightningAddressRequest(); + } + + // Confirms POST request constructors throw on null arguments + @Test + void postRequestNullArgumentsThrow() { + DummyParam param = new DummyParam(); + assertThatThrownBy(() -> new PostRequest(null, param)) + .isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new PostRequest("/post", (String) null)) + .isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new PostRequest(null, param, "data")) + .isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new PostRequest("/post", (DummyParam) null, "data")) + .isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new PostRequest("/post", param, null)) + .isInstanceOf(NullPointerException.class); + } + + // Validates other request types enforce non-null parameters + @Test + void otherRequestsNullArgumentsThrow() { + DummyParam param = new DummyParam(); + assertThatThrownBy(() -> new GetRequest(null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new GetRequest("/get", null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new PatchRequest(null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new PatchRequest("/patch", null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new DeleteRequest(null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> new DeleteRequest("/del", null)).isInstanceOf(NullPointerException.class); + } +} diff --git a/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/PayRequestFactoryTest.java b/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/PayRequestFactoryTest.java new file mode 100644 index 0000000..f31ca6e --- /dev/null +++ b/phoenixd-rest/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/PayRequestFactoryTest.java @@ -0,0 +1,37 @@ +package xyz.tcheeric.phoenixd.request.impl.rest; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class PayRequestFactoryTest { + + // Ensures lightning addresses produce the proper request type + @Test + void createsLightningAddressRequest() { + BasePayRequest request = PayRequestFactory.createPayRequest("user@example.com"); + assertThat(request).isInstanceOf(PayLightningAddressRequest.class); + } + + // Checks that Bolt11 invoices are routed to the correct request + @Test + void createsBolt11InvoiceRequest() { + BasePayRequest request = PayRequestFactory.createPayRequest("lnbc1abcde"); + assertThat(request).isInstanceOf(PayBolt11InvoiceRequest.class); + } + + // Verifies invalid strings result in an IllegalArgumentException + @Test + void throwsOnInvalidRequest() { + assertThatThrownBy(() -> PayRequestFactory.createPayRequest("invalid")) + .isInstanceOf(IllegalArgumentException.class); + } + + // Confirms null inputs are rejected with a NullPointerException + @Test + void nullRequestThrows() { + assertThatThrownBy(() -> PayRequestFactory.createPayRequest(null)) + .isInstanceOf(NullPointerException.class); + } +} diff --git a/phoenixd-rest/src/test/resources/app.properties b/phoenixd-rest/src/test/resources/app.properties new file mode 100644 index 0000000..70a85b7 --- /dev/null +++ b/phoenixd-rest/src/test/resources/app.properties @@ -0,0 +1,5 @@ +phoenixd.username=user +phoenixd.password=pass +phoenixd.base_url=http://localhost +phoenixd.timeout=5000 +phoenixd.webhook_secret= diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/PathVariableReplacementTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/PathVariableReplacementTest.java index 5352a6b..11cde47 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/PathVariableReplacementTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/PathVariableReplacementTest.java @@ -23,6 +23,7 @@ public String toString() { } } + // Verifies multiple path variables are replaced in the URI @Test public void replacesMultipleVariables() { MultiPathParam param = new MultiPathParam("123", "456"); diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/AddHeaderOperationTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/AddHeaderOperationTest.java index 201de97..091802d 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/AddHeaderOperationTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/AddHeaderOperationTest.java @@ -7,6 +7,7 @@ public class AddHeaderOperationTest { + // Ensures adding a header preserves the HTTP method for GET operations @Test void addHeaderDoesNotChangeMethodForGetOperation() { GetOperation operation = new GetOperation("/test"); @@ -17,6 +18,7 @@ void addHeaderDoesNotChangeMethodForGetOperation() { assertThat(operation.getHeader("X-Test")).isEqualTo("value"); } + // Verifies PATCH operations keep their method after adding headers @Test void addHeaderDoesNotChangeMethodForPatchOperation() { PatchOperation operation = new PatchOperation("/test"); diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/test/DeleteOperationTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/test/DeleteOperationTest.java index 5f8495e..1c048d0 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/test/DeleteOperationTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/test/DeleteOperationTest.java @@ -31,6 +31,7 @@ public TestDeleteRequest() { } } + // Verifies DELETE operations handle headers and responses appropriately @Test public void testDeleteOperation() { TestDeleteRequest request = new TestDeleteRequest(); diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/test/PatchOperationTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/test/PatchOperationTest.java index 096d953..ba47531 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/test/PatchOperationTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/impl/test/PatchOperationTest.java @@ -31,6 +31,7 @@ public TestPatchRequest() { } } + // Ensures PATCH requests use proper method, headers, and response mapping @Test public void testPatchOperation() { TestPatchRequest request = new TestPatchRequest(); diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/test/AbstractOperationTimeoutTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/test/AbstractOperationTimeoutTest.java index 5925b1e..a7c8af2 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/test/AbstractOperationTimeoutTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/operation/test/AbstractOperationTimeoutTest.java @@ -13,6 +13,7 @@ static class TestOperation extends AbstractOperation { } } + // Ensures operations use the default timeout when none is set @Test void usesDefaultTimeoutWhenMissing() { TestOperation operation = new TestOperation(); diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/CreateBolt11InvoiceRequestTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/CreateBolt11InvoiceRequestTest.java index fbdc205..8032e3b 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/CreateBolt11InvoiceRequestTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/CreateBolt11InvoiceRequestTest.java @@ -24,6 +24,7 @@ public class CreateBolt11InvoiceRequestTest { private static final Logger log = Logger.getLogger(CreateBolt11InvoiceRequestTest.class.getName()); + // Checks constructor initializes request with correct path @Test public void testConstructor() { // Arrange and Act @@ -33,6 +34,7 @@ public void testConstructor() { assertEquals("/createinvoice", createBolt11InvoiceRequest.getPath()); } + // Ensures headers can be added to the underlying operation @Test public void testAddHeader() { // Arrange @@ -45,6 +47,7 @@ public void testAddHeader() { assertEquals("value", createBolt11InvoiceRequest.getOperation().getHeader("key")); } + // Validates response parsing for a successful invoice creation @Test public void testGetResponse() throws Exception { // Arrange @@ -65,6 +68,7 @@ public void testGetResponse() throws Exception { log.log(Level.ALL, "Invoice: {0}", response.getSerialized()); } + // Verifies request URI and default headers are set properly @Test public void testUriAndHeaders() { // Arrange @@ -76,6 +80,7 @@ public void testUriAndHeaders() { assertEquals("application/x-www-form-urlencoded", request.getOperation().getHeader("Content-Type")); } + // Confirms IOExceptions are thrown when server returns an error @Test public void testErrorHandling() throws Exception { HttpServer errorServer = HttpServer.create(new InetSocketAddress(ERROR_SERVER_PORT), 0); diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/DecodeInvoiceRequestTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/DecodeInvoiceRequestTest.java index 356b44b..57523c5 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/DecodeInvoiceRequestTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/DecodeInvoiceRequestTest.java @@ -12,6 +12,7 @@ @ExtendWith(LocalTestServerExtension.class) public class DecodeInvoiceRequestTest { + // Ensures a decode invoice request posts data and returns parsed response @Test public void testConstructor() { // Arrange diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/GetLightningAddressResponseTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/GetLightningAddressResponseTest.java index 7c4c25c..ff5bbb5 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/GetLightningAddressResponseTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/GetLightningAddressResponseTest.java @@ -13,6 +13,7 @@ @ExtendWith(LocalTestServerExtension.class) public class GetLightningAddressResponseTest { + // Ensures the request retrieves a lightning address and has correct path @Test public void testConstructor() { // Arrange diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayBolt11InvoiceRequestTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayBolt11InvoiceRequestTest.java index 406bb9d..9767d2a 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayBolt11InvoiceRequestTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayBolt11InvoiceRequestTest.java @@ -12,6 +12,7 @@ @ExtendWith(LocalTestServerExtension.class) public class PayBolt11InvoiceRequestTest { + // Verifies the request initializes with the proper path, param, and operation @Test public void testConstructor() { // Arrange diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayLightningAddressTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayLightningAddressTest.java index a1cca29..756a9f6 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayLightningAddressTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayLightningAddressTest.java @@ -22,6 +22,7 @@ @ExtendWith(LocalTestServerExtension.class) public class PayLightningAddressTest { + // Validates the request sends correct data and parses the payment response @Test public void testConstructor() { // Arrange @@ -42,6 +43,7 @@ public void testConstructor() { assertEquals(10, response.getRecipientAmountSat()); } + // Checks default URI and headers on the built request @Test public void testUriAndHeaders() { PayLightningAddressParam param = new PayLightningAddressParam(); @@ -55,6 +57,7 @@ public void testUriAndHeaders() { assertEquals("application/x-www-form-urlencoded", request.getOperation().getHeader("Content-Type")); } + // Ensures IOExceptions are raised when the server responds with an error @Test public void testErrorHandling() throws Exception { HttpServer errorServer = HttpServer.create(new InetSocketAddress(9751), 0); diff --git a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayRequestFactoryTest.java b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayRequestFactoryTest.java index 318e8e6..d062906 100644 --- a/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayRequestFactoryTest.java +++ b/phoenixd-test/src/test/java/xyz/tcheeric/phoenixd/request/impl/rest/test/PayRequestFactoryTest.java @@ -7,11 +7,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import xyz.tcheeric.phoenixd.request.impl.rest.PayLightningAddressRequest; public class PayRequestFactoryTest { + // Ensures factory handles uppercase Bolt11 invoices @Test public void testCreatePayRequestWithUppercaseInvoice() { String invoice = "LNBC1TESTINVOICE"; @@ -22,16 +25,19 @@ public void testCreatePayRequestWithUppercaseInvoice() { assertEquals(PayBolt11InvoiceRequest.class, request.getClass()); } + // Verifies lightning address inputs produce corresponding requests @Test public void testCreateLightningAddressRequest() { assertTrue(PayRequestFactory.createPayRequest("alice@example.com") instanceof PayLightningAddressRequest); } + // Checks that lowercase invoices produce Bolt11 requests @Test public void testCreateBolt11InvoiceRequest() { assertTrue(PayRequestFactory.createPayRequest("lnbc1u1pw0kx7pp5") instanceof PayBolt11InvoiceRequest); } + // Confirms invalid strings cause an exception @Test public void testInvalidRequestThrows() { assertThrows(IllegalArgumentException.class, () -> PayRequestFactory.createPayRequest("invalid"));