diff --git a/mockserver-api/pom.xml b/mockserver-api/pom.xml new file mode 100644 index 0000000..a7c59b9 --- /dev/null +++ b/mockserver-api/pom.xml @@ -0,0 +1,20 @@ + + + + http-mock-server + pl.touk.mockserver + 1.1.1-SNAPSHOT + + 4.0.0 + + mockserver-api + + + + org.projectlombok + lombok + + + \ No newline at end of file diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/request/AddMock.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/AddMock.java new file mode 100644 index 0000000..80ad580 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/AddMock.java @@ -0,0 +1,33 @@ +package pl.touk.mockserver.api.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@Data +@EqualsAndHashCode(callSuper = false) +public class AddMock extends MockServerRequest { + @XmlElement(required = true) + private String name; + + @XmlElement(required = true) + private String path; + + @XmlElement(required = true) + private int port; + + private String predicate; + + private String response; + + private Boolean soap; + + private Integer statusCode; + + private Method method; + + private String responseHeaders; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/request/Method.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/Method.java new file mode 100644 index 0000000..9e471a1 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/Method.java @@ -0,0 +1,15 @@ +package pl.touk.mockserver.api.request; + +import javax.xml.bind.annotation.XmlEnum; + +@XmlEnum +public enum Method { + POST, + GET, + DELETE, + PUT, + TRACE, + HEAD, + OPTIONS, + PATCH; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/request/MockServerRequest.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/MockServerRequest.java new file mode 100644 index 0000000..91c1ba9 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/MockServerRequest.java @@ -0,0 +1,4 @@ +package pl.touk.mockserver.api.request; + +public abstract class MockServerRequest { +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/request/PeekMock.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/PeekMock.java new file mode 100644 index 0000000..d4f14ca --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/PeekMock.java @@ -0,0 +1,15 @@ +package pl.touk.mockserver.api.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@Data +@EqualsAndHashCode(callSuper = false) +public class PeekMock extends MockServerRequest{ + @XmlElement(required = true) + private String name; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/request/RemoveMock.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/RemoveMock.java new file mode 100644 index 0000000..49c8b0b --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/RemoveMock.java @@ -0,0 +1,17 @@ +package pl.touk.mockserver.api.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@Data +@EqualsAndHashCode(callSuper = false) +public class RemoveMock extends MockServerRequest { + @XmlElement(required = true) + private String name; + + private Boolean skipReport; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/request/package-info.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/package-info.java new file mode 100644 index 0000000..6ccf1df --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/request/package-info.java @@ -0,0 +1,4 @@ +@XmlAccessorType(XmlAccessType.FIELD) package pl.touk.mockserver.api.request; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; \ No newline at end of file diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/ExceptionOccured.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/ExceptionOccured.java new file mode 100644 index 0000000..a10382b --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/ExceptionOccured.java @@ -0,0 +1,13 @@ +package pl.touk.mockserver.api.response; + +import lombok.Data; + +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlValue; + +@XmlRootElement +@Data +public class ExceptionOccured { + @XmlValue + private String message; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockAdded.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockAdded.java new file mode 100644 index 0000000..8dc13a9 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockAdded.java @@ -0,0 +1,12 @@ +package pl.touk.mockserver.api.response; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@Data +@EqualsAndHashCode(callSuper = false) +public class MockAdded extends MockServerResponse { +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockEventReport.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockEventReport.java new file mode 100644 index 0000000..faa2497 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockEventReport.java @@ -0,0 +1,14 @@ +package pl.touk.mockserver.api.response; + +import lombok.Data; + +import javax.xml.bind.annotation.XmlElement; + +@Data +public class MockEventReport { + @XmlElement(required = true) + private MockRequestReport request; + + @XmlElement(required = true) + private MockResponseReport response; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockListing.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockListing.java new file mode 100644 index 0000000..77f5e24 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockListing.java @@ -0,0 +1,16 @@ +package pl.touk.mockserver.api.response; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement(name = "mocks") +@Data +@EqualsAndHashCode(callSuper = false) +public class MockListing extends MockServerResponse { + @XmlElement(name = "mock") + private List mocks; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockPeeked.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockPeeked.java new file mode 100644 index 0000000..a23daae --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockPeeked.java @@ -0,0 +1,16 @@ +package pl.touk.mockserver.api.response; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement +@Data +@EqualsAndHashCode(callSuper = false) +public class MockPeeked extends MockServerResponse { + @XmlElement(name = "mockEvent") + private List mockEvents; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockRemoved.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockRemoved.java new file mode 100644 index 0000000..008d318 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockRemoved.java @@ -0,0 +1,16 @@ +package pl.touk.mockserver.api.response; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement +@Data +@EqualsAndHashCode(callSuper = false) +public class MockRemoved extends MockServerResponse { + @XmlElement(name = "mockEvent") + private List mockEvents; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockReport.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockReport.java new file mode 100644 index 0000000..4ea7449 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockReport.java @@ -0,0 +1,27 @@ +package pl.touk.mockserver.api.response; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.xml.bind.annotation.XmlElement; + +@Data +public class MockReport { + @XmlElement(required = true) + private String name; + + @XmlElement(required = true) + private String path; + + @XmlElement(required = true) + private int port; + + @XmlElement(required = true) + private String predicate; + + @XmlElement(required = true) + private String response; + + @XmlElement(required = true) + private String responseHeaders; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockRequestReport.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockRequestReport.java new file mode 100644 index 0000000..194b27d --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockRequestReport.java @@ -0,0 +1,24 @@ +package pl.touk.mockserver.api.response; + +import lombok.Data; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import java.util.List; + +@Data +public class MockRequestReport { + private String text; + + @XmlElementWrapper(name = "headers") + @XmlElement(name = "param") + private List headers; + + @XmlElementWrapper(name = "query") + @XmlElement(name = "param") + private List queryParams; + + @XmlElementWrapper(name = "path") + @XmlElement(name = "elem") + private List paths; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockResponseReport.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockResponseReport.java new file mode 100644 index 0000000..56ac5d5 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockResponseReport.java @@ -0,0 +1,19 @@ +package pl.touk.mockserver.api.response; + +import lombok.Data; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import java.util.List; + +@Data +public class MockResponseReport { + @XmlElement(required = true) + private int statusCode; + + private String text; + + @XmlElementWrapper(name = "headers") + @XmlElement(name = "param") + private List headers; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockServerResponse.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockServerResponse.java new file mode 100644 index 0000000..bf7e0ae --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/MockServerResponse.java @@ -0,0 +1,4 @@ +package pl.touk.mockserver.api.response; + +public abstract class MockServerResponse { +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/Parameter.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/Parameter.java new file mode 100644 index 0000000..6a9c0c3 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/Parameter.java @@ -0,0 +1,15 @@ +package pl.touk.mockserver.api.response; + +import lombok.Data; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlValue; + +@Data +public class Parameter { + @XmlAttribute(required = true) + private String name; + + @XmlValue + private String value; +} diff --git a/mockserver-api/src/main/java/pl/touk/mockserver/api/response/package-info.java b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/package-info.java new file mode 100644 index 0000000..d317cb9 --- /dev/null +++ b/mockserver-api/src/main/java/pl/touk/mockserver/api/response/package-info.java @@ -0,0 +1,4 @@ +@XmlAccessorType(XmlAccessType.FIELD) package pl.touk.mockserver.api.response; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; \ No newline at end of file diff --git a/mockserver-api/src/main/resource/pl/touk/mockserver/api/request/jaxb.index b/mockserver-api/src/main/resource/pl/touk/mockserver/api/request/jaxb.index new file mode 100644 index 0000000..233becc --- /dev/null +++ b/mockserver-api/src/main/resource/pl/touk/mockserver/api/request/jaxb.index @@ -0,0 +1,4 @@ +AddMock +Method +PeekMock +RemoveMock \ No newline at end of file diff --git a/mockserver-api/src/main/resource/pl/touk/mockserver/api/response/jaxb.index b/mockserver-api/src/main/resource/pl/touk/mockserver/api/response/jaxb.index new file mode 100644 index 0000000..86bdb57 --- /dev/null +++ b/mockserver-api/src/main/resource/pl/touk/mockserver/api/response/jaxb.index @@ -0,0 +1,11 @@ +ExceptionOccured +MockAdded +MockEventReport +MockListing +MockPeeked +MockRemoved +MockReport +MockRequestReport +MockResponseReport +MockServerResponse +Parameter \ No newline at end of file diff --git a/mockserver-client/pom.xml b/mockserver-client/pom.xml index dc604e9..5041602 100644 --- a/mockserver-client/pom.xml +++ b/mockserver-client/pom.xml @@ -25,5 +25,9 @@ org.apache.commons commons-lang3 + + pl.touk.mockserver + mockserver-api + diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/AddMockRequestData.groovy b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/AddMockRequestData.groovy deleted file mode 100644 index 6da9a6e..0000000 --- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/AddMockRequestData.groovy +++ /dev/null @@ -1,32 +0,0 @@ -package pl.touk.mockserver.client - -import groovy.transform.CompileStatic -import groovy.transform.TypeChecked -import org.apache.commons.lang3.StringEscapeUtils - -@CompileStatic -@TypeChecked -class AddMockRequestData { - String name - String path - Integer port - String predicate - String response - Boolean soap - Integer statusCode - Method method - String responseHeaders - - void setPredicate(String predicate) { - this.predicate = StringEscapeUtils.escapeXml11(predicate) - } - - void setResponse(String response) { - this.response = StringEscapeUtils.escapeXml11(response) - } - - void setResponseHeaders(String responseHeaders) { - this.responseHeaders = StringEscapeUtils.escapeXml11(responseHeaders) - } -} - diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/Method.groovy b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/Method.groovy deleted file mode 100644 index 82aefea..0000000 --- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/Method.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package pl.touk.mockserver.client - -enum Method { - POST, - GET, - DELETE, - PUT, - TRACE, - HEAD, - OPTIONS, - PATCH -} diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockEvent.groovy b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockEvent.groovy deleted file mode 100644 index 8c61ab3..0000000 --- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockEvent.groovy +++ /dev/null @@ -1,18 +0,0 @@ -package pl.touk.mockserver.client - -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -import groovy.transform.TypeChecked - -@EqualsAndHashCode -@CompileStatic -@TypeChecked -class MockEvent { - final MockRequest request - final MockResponse response - - MockEvent(MockRequest request, MockResponse response) { - this.request = request - this.response = response - } -} diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockRequest.groovy b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockRequest.groovy deleted file mode 100644 index c73c11d..0000000 --- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockRequest.groovy +++ /dev/null @@ -1,22 +0,0 @@ -package pl.touk.mockserver.client - -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -import groovy.transform.TypeChecked - -@CompileStatic -@TypeChecked -@EqualsAndHashCode -class MockRequest { - final String text - final Map headers - final Map query - final List path - - MockRequest(String text, Map headers, Map query, List path) { - this.text = text - this.headers = headers - this.query = query - this.path = path - } -} diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockResponse.groovy b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockResponse.groovy deleted file mode 100644 index 07212fb..0000000 --- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/MockResponse.groovy +++ /dev/null @@ -1,20 +0,0 @@ -package pl.touk.mockserver.client - -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -import groovy.transform.TypeChecked - -@CompileStatic -@TypeChecked -@EqualsAndHashCode -class MockResponse { - final int statusCode - final String text - final Map headers - - MockResponse(int statusCode, String text, Map headers) { - this.statusCode = statusCode - this.text = text - this.headers = headers - } -} diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/PeekMockRequestData.groovy b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/PeekMockRequestData.groovy deleted file mode 100644 index 3d0e546..0000000 --- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/PeekMockRequestData.groovy +++ /dev/null @@ -1,10 +0,0 @@ -package pl.touk.mockserver.client - -import groovy.transform.CompileStatic -import groovy.transform.TypeChecked - -@CompileStatic -@TypeChecked -class PeekMockRequestData { - String name -} diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RegisteredMock.groovy b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RegisteredMock.groovy deleted file mode 100644 index f755592..0000000 --- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RegisteredMock.groovy +++ /dev/null @@ -1,28 +0,0 @@ -package pl.touk.mockserver.client - -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -import groovy.transform.ToString -import groovy.transform.TypeChecked - -@CompileStatic -@TypeChecked -@EqualsAndHashCode -@ToString -class RegisteredMock { - final String name - final String path - final int port - final String predicate - final String response - final String responseHeaders - - RegisteredMock(String name, String path, int port, String predicate, String response, String responseHeaders) { - this.name = name - this.path = path - this.port = port - this.predicate = predicate - this.response = response - this.responseHeaders = responseHeaders - } -} diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RemoteMockServer.groovy b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RemoteMockServer.groovy index 6afce3d..14664e0 100644 --- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RemoteMockServer.groovy +++ b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RemoteMockServer.groovy @@ -1,6 +1,5 @@ package pl.touk.mockserver.client -import groovy.util.slurpersupport.GPathResult import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.client.methods.HttpGet import org.apache.http.client.methods.HttpPost @@ -8,112 +7,70 @@ import org.apache.http.entity.ContentType import org.apache.http.entity.StringEntity import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.HttpClients +import pl.touk.mockserver.api.request.AddMock +import pl.touk.mockserver.api.request.MockServerRequest +import pl.touk.mockserver.api.request.PeekMock +import pl.touk.mockserver.api.request.RemoveMock +import pl.touk.mockserver.api.response.* + +import javax.xml.bind.JAXBContext class RemoteMockServer { private final String address private final CloseableHttpClient client = HttpClients.createDefault() + private static final JAXBContext requestContext = JAXBContext.newInstance(AddMock.package.name, AddMock.classLoader) + private static + final JAXBContext responseContext = JAXBContext.newInstance(MockAdded.package.name, MockAdded.classLoader) RemoteMockServer(String host, int port) { address = "http://$host:$port/serverControl" } - void addMock(AddMockRequestData addMockRequestData) { + void addMock(AddMock addMockData) { HttpPost addMockPost = new HttpPost(address) - addMockPost.entity = buildAddMockRequest(addMockRequestData) + addMockPost.entity = buildAddMockRequest(addMockData) CloseableHttpResponse response = client.execute(addMockPost) - GPathResult responseXml = Util.extractXmlResponse(response) - if (responseXml.name() != 'mockAdded') { - if (responseXml.text() == 'mock already registered') { - throw new MockAlreadyExists() - - } - throw new InvalidMockDefinition(responseXml.text()) - } + Util.extractResponse(response) } - List removeMock(String name, boolean skipReport = false) { + List removeMock(String name, boolean skipReport = false) { HttpPost removeMockPost = new HttpPost(address) - removeMockPost.entity = buildRemoveMockRequest(new RemoveMockRequestData(name: name, skipReport: skipReport)) + removeMockPost.entity = buildRemoveMockRequest(new RemoveMock(name: name, skipReport: skipReport)) CloseableHttpResponse response = client.execute(removeMockPost) - GPathResult responseXml = Util.extractXmlResponse(response) - if (responseXml.name() == 'mockRemoved') { - return responseXml.'mockEvent'.collect { - new MockEvent(mockRequestFromXml(it.request), mockResponseFromXml(it.response)) - } - } - throw new MockDoesNotExist() + MockRemoved mockRemoved = Util.extractResponse(response) as MockRemoved + return mockRemoved.mockEvents ?: [] } - List peekMock(String name) { + List peekMock(String name) { HttpPost removeMockPost = new HttpPost(address) - removeMockPost.entity = buildPeekMockRequest(new PeekMockRequestData(name: name)) + removeMockPost.entity = buildPeekMockRequest(new PeekMock(name: name)) CloseableHttpResponse response = client.execute(removeMockPost) - GPathResult responseXml = Util.extractXmlResponse(response) - if (responseXml.name() == 'mockPeeked') { - return responseXml.'mockEvent'.collect { - new MockEvent(mockRequestFromXml(it.request), mockResponseFromXml(it.response)) - } - } - throw new MockDoesNotExist() + MockPeeked mockPeeked = Util.extractResponse(response) as MockPeeked + return mockPeeked.mockEvents ?: [] } - private static MockResponse mockResponseFromXml(GPathResult xml) { - return new MockResponse(xml.statusCode.text() as int, xml.text.text(), xml.headers.param.collectEntries { - [(it.@name.text()): it.text()] - }) + private static StringEntity buildRemoveMockRequest(RemoveMock data) { + return new StringEntity(marshallRequest(data), ContentType.create("text/xml", "UTF-8")) } - private static MockRequest mockRequestFromXml(GPathResult xml) { - return new MockRequest( - xml.text.text(), - xml.headers.param.collectEntries { [(it.@name.text()): it.text()] }, - xml.query.param.collectEntries { [(it.@name.text()): it.text()] }, - xml.path.elem*.text() - ) + private static String marshallRequest(MockServerRequest data) { + StringWriter sw = new StringWriter() + requestContext.createMarshaller().marshal(data, sw) + return sw.toString() } - private static StringEntity buildRemoveMockRequest(RemoveMockRequestData data) { - return new StringEntity("""\ - - ${data.name} - ${data.skipReport} - - """, ContentType.create("text/xml", "UTF-8")) + private static StringEntity buildPeekMockRequest(PeekMock peekMock) { + return new StringEntity(marshallRequest(peekMock), ContentType.create("text/xml", "UTF-8")) } - private static StringEntity buildPeekMockRequest(PeekMockRequestData data) { - return new StringEntity("""\ - - ${data.name} - - """, ContentType.create("text/xml", "UTF-8")) + private static StringEntity buildAddMockRequest(AddMock data) { + return new StringEntity(marshallRequest(data), ContentType.create("text/xml", "UTF-8")) } - private static StringEntity buildAddMockRequest(AddMockRequestData data) { - return new StringEntity("""\ - - ${data.name} - ${data.path} - ${data.port} - ${data.predicate ? "${data.predicate}" : ''} - ${data.response ? "${data.response}" : ''} - ${data.soap != null ? "${data.soap}" : ''} - ${data.statusCode ? "${data.statusCode}" : ''} - ${data.method ? "${data.method}" : ''} - ${data.responseHeaders ? "${data.responseHeaders}" : ''} - - """, ContentType.create("text/xml", "UTF-8")) - } - - List listMocks() { + List listMocks() { HttpGet get = new HttpGet(address) CloseableHttpResponse response = client.execute(get) - GPathResult xml = Util.extractXmlResponse(response) - if (xml.name() == 'mocks') { - return xml.mock.collect { - new RegisteredMock(it.name.text(), it.path.text(), it.port.text() as int, it.predicate.text(), it.response.text(), it.responseHeaders.text()) - } - } - return [] + MockListing mockListing = Util.extractResponse(response) as MockListing + return mockListing.mocks } } diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RemoveMockRequestData.groovy b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RemoveMockRequestData.groovy deleted file mode 100644 index 1d37545..0000000 --- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/RemoveMockRequestData.groovy +++ /dev/null @@ -1,11 +0,0 @@ -package pl.touk.mockserver.client - -import groovy.transform.CompileStatic -import groovy.transform.TypeChecked - -@CompileStatic -@TypeChecked -class RemoveMockRequestData { - String name - boolean skipReport = false -} diff --git a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/Util.groovy b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/Util.groovy index 8cffc3b..446ebf0 100644 --- a/mockserver-client/src/main/groovy/pl/touk/mockserver/client/Util.groovy +++ b/mockserver-client/src/main/groovy/pl/touk/mockserver/client/Util.groovy @@ -7,15 +7,42 @@ import groovy.util.slurpersupport.GPathResult import org.apache.http.HttpEntity import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.util.EntityUtils +import pl.touk.mockserver.api.response.ExceptionOccured +import pl.touk.mockserver.api.response.MockAdded +import pl.touk.mockserver.api.response.MockServerResponse + +import javax.xml.bind.JAXBContext @CompileStatic @TypeChecked class Util { + private static + final JAXBContext responseContext = JAXBContext.newInstance(MockAdded.package.name, MockAdded.classLoader) + static GPathResult extractXmlResponse(CloseableHttpResponse response) { + return new XmlSlurper().parseText(extractStringResponse(response)) + } + static String extractStringResponse(CloseableHttpResponse response) { HttpEntity entity = response.entity - GPathResult xml = new XmlSlurper().parseText(EntityUtils.toString(entity, 'UTF-8')) + String responseString = EntityUtils.toString(entity, 'UTF-8') EntityUtils.consumeQuietly(entity) - return xml + return responseString + } + + static MockServerResponse extractResponse(CloseableHttpResponse response) { + String responseString = extractStringResponse(response) + if (response.statusLine.statusCode == 200) { + return responseContext.createUnmarshaller().unmarshal(new StringReader(responseString)) as MockServerResponse + } + ExceptionOccured exceptionOccured = responseContext.createUnmarshaller().unmarshal(new StringReader(responseString)) as ExceptionOccured + String message = exceptionOccured.message + if (message == 'mock already registered') { + throw new MockAlreadyExists() + } + if (message == 'mock not registered') { + throw new MockDoesNotExist() + } + throw new InvalidMockDefinition(message) } static String soap(String request) { @@ -26,13 +53,11 @@ class Util { } static Object extractJsonResponse(CloseableHttpResponse response) { - HttpEntity entity = response.entity - Object json = new JsonSlurper().parseText(EntityUtils.toString(entity, 'UTF-8')) - EntityUtils.consumeQuietly(entity) - return json + return new JsonSlurper().parseText(extractStringResponse(response)) } static void consumeResponse(CloseableHttpResponse response) { EntityUtils.consumeQuietly(response.entity) } + } diff --git a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy index 7790522..1fba403 100644 --- a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy +++ b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy @@ -7,6 +7,11 @@ import org.apache.http.entity.StringEntity import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.HttpClients import org.apache.http.util.EntityUtils +import pl.touk.mockserver.api.request.AddMock +import pl.touk.mockserver.api.request.Method +import pl.touk.mockserver.api.response.MockEventReport +import pl.touk.mockserver.api.response.MockReport +import pl.touk.mockserver.api.response.Parameter import pl.touk.mockserver.client.* import pl.touk.mockserver.server.HttpMockServer import spock.lang.Shared @@ -33,7 +38,7 @@ class MockServerIntegrationTest extends Specification { def "should add working rest mock on endpoint"() { expect: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, @@ -54,7 +59,7 @@ class MockServerIntegrationTest extends Specification { def "should add working rest mock on endpoint with utf"() { expect: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRestUtf', path: 'testEndpoint', port: 9999, @@ -76,7 +81,7 @@ class MockServerIntegrationTest extends Specification { def "should add soap mock on endpoint"() { expect: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testSoap', path: 'testEndpoint', port: 9999, @@ -102,7 +107,7 @@ class MockServerIntegrationTest extends Specification { then: thrown(MockDoesNotExist) expect: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testSoap', path: 'testEndpoint', port: 9999, @@ -120,7 +125,7 @@ class MockServerIntegrationTest extends Specification { def "should not add mock with existing name"() { expect: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testSoap', path: 'testEndpoint', port: 9999, @@ -129,7 +134,7 @@ class MockServerIntegrationTest extends Specification { soap: true )) when: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testSoap', path: 'testEndpoint2', port: 9998, @@ -143,7 +148,7 @@ class MockServerIntegrationTest extends Specification { def "should not add mock with empty name"() { when: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: '', path: 'testEndpoint2', port: 9998, @@ -157,7 +162,7 @@ class MockServerIntegrationTest extends Specification { def "should add mock after deleting old mock with the same name"() { expect: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testSoap', path: 'testEndpoint', port: 9999, @@ -168,7 +173,7 @@ class MockServerIntegrationTest extends Specification { and: remoteMockServer.removeMock('testSoap') == [] and: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testSoap', path: 'testEndpoint', port: 9999, @@ -180,14 +185,14 @@ class MockServerIntegrationTest extends Specification { def "should add simultaneously working post and rest mocks with the same predicate and endpoint nad port"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, predicate: '''{req -> req.xml.name() == 'request'}''', response: '''{req -> ""}''' )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testSoap', path: 'testEndpoint', port: 9999, @@ -215,14 +220,14 @@ class MockServerIntegrationTest extends Specification { @Unroll def "should dispatch rest mocks when second on #name"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest1', path: 'test1', port: 9999, predicate: '''{req -> req.xml.name() == 'request1'}''', response: '''{req -> ""}''' )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: secondPath, port: secondPort, @@ -254,7 +259,7 @@ class MockServerIntegrationTest extends Specification { @Unroll def "should dispatch rest mock with response code"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest1', path: 'test1', port: 9999, @@ -276,7 +281,7 @@ class MockServerIntegrationTest extends Specification { def "should return response code 404 and error body the same as request body when mocks does not apply"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest1', path: 'test1', port: 9999, @@ -295,7 +300,7 @@ class MockServerIntegrationTest extends Specification { def "should inform that there was problem during adding mock - invalid port"() { when: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testSoap', path: 'testEndpoint2', port: -1, @@ -309,13 +314,13 @@ class MockServerIntegrationTest extends Specification { def "should dispatch rest mock with get method"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, response: '''{_ -> ""}''' )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: 'testEndpoint', port: 9999, @@ -332,13 +337,13 @@ class MockServerIntegrationTest extends Specification { def "should dispatch rest mock with trace method"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, response: '''{_ -> ""}''' )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: 'testEndpoint', port: 9999, @@ -355,13 +360,13 @@ class MockServerIntegrationTest extends Specification { def "should dispatch rest mock with head method"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, response: '''{_ -> ""}''' )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: 'testEndpoint', port: 9999, @@ -377,13 +382,13 @@ class MockServerIntegrationTest extends Specification { def "should dispatch rest mock with options method"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, response: '''{_ -> ""}''' )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: 'testEndpoint', port: 9999, @@ -399,13 +404,13 @@ class MockServerIntegrationTest extends Specification { def "should dispatch rest mock with put method"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'test1', port: 9999, response: '''{_ -> ""}''' )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: 'test1', port: 9999, @@ -424,13 +429,13 @@ class MockServerIntegrationTest extends Specification { def "should dispatch rest mock with delete method"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'test1', port: 9999, response: '''{_ -> ""}''' )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: 'test1', port: 9999, @@ -447,13 +452,13 @@ class MockServerIntegrationTest extends Specification { def "should dispatch rest mock with patch method"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'test1', port: 9999, response: '''{_ -> ""}''' )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: 'test1', port: 9999, @@ -472,7 +477,7 @@ class MockServerIntegrationTest extends Specification { def "should add mock that return headers"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, @@ -492,7 +497,7 @@ class MockServerIntegrationTest extends Specification { def "should add mock that accepts only when certain request headers exists"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, @@ -521,7 +526,7 @@ class MockServerIntegrationTest extends Specification { def "should add mock that accepts only when certain query params exists"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, @@ -545,7 +550,7 @@ class MockServerIntegrationTest extends Specification { def "should add mock that accepts only when request has specific body"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, @@ -570,7 +575,7 @@ class MockServerIntegrationTest extends Specification { def "should add mock which response json to json"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, @@ -595,7 +600,7 @@ class MockServerIntegrationTest extends Specification { def "should get list mocks"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: 'testEndpoint', port: 9998, @@ -603,27 +608,27 @@ class MockServerIntegrationTest extends Specification { response: '''{ req -> '' }''', responseHeaders: '{ _ -> [a: "b"] }' )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest4', path: 'testEndpoint', port: 9999 )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest3', path: 'testEndpoint2', port: 9999 )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest5', path: 'testEndpoint', port: 9999 )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest6', path: 'testEndpoint2', port: 9999 )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999 @@ -631,17 +636,17 @@ class MockServerIntegrationTest extends Specification { remoteMockServer.removeMock('testRest5') expect: remoteMockServer.listMocks() == [ - new RegisteredMock('testRest', 'testEndpoint', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }'), - new RegisteredMock('testRest2', 'testEndpoint', 9998, '''{ req -> req.xml.name() == 'request1'}''', '''{ req -> '' }''', '{ _ -> [a: "b"] }'), - new RegisteredMock('testRest3', 'testEndpoint2', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }'), - new RegisteredMock('testRest4', 'testEndpoint', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }'), - new RegisteredMock('testRest6', 'testEndpoint2', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }') + new MockReport(name: 'testRest', path: 'testEndpoint', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }'), + new MockReport(name: 'testRest2', path: 'testEndpoint', port: 9998, predicate: '''{ req -> req.xml.name() == 'request1'}''', response: '''{ req -> '' }''', responseHeaders: '{ _ -> [a: "b"] }'), + new MockReport(name: 'testRest3', path: 'testEndpoint2', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }'), + new MockReport(name: 'testRest4', path: 'testEndpoint', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }'), + new MockReport(name: 'testRest6', path: 'testEndpoint2', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }') ] } def "should add mock accepts path certain path params"() { given: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, @@ -664,7 +669,7 @@ class MockServerIntegrationTest extends Specification { def "should get mock report when deleting mock"() { expect: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, @@ -674,7 +679,7 @@ class MockServerIntegrationTest extends Specification { responseHeaders: '''{req -> ['aaa':'14']}''', soap: false )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: 'testEndpoint', port: 9999, @@ -706,40 +711,40 @@ class MockServerIntegrationTest extends Specification { GPathResult restPostResponse3 = Util.extractXmlResponse(response3) restPostResponse3.name() == 'goodResponseRest' when: - List mockEvents1 = remoteMockServer.removeMock('testRest') + List mockEvents1 = remoteMockServer.removeMock('testRest') then: mockEvents1.size() == 2 mockEvents1[0].request.text == '' - !mockEvents1[0].request.headers?.keySet()?.empty - mockEvents1[0].request.query == [:] - mockEvents1[0].request.path == ['testEndpoint'] - !mockEvents1[0].response.headers?.keySet()?.empty + !mockEvents1[0].request.headers?.empty + mockEvents1[0].request.queryParams == [] + mockEvents1[0].request.paths == ['testEndpoint'] + !mockEvents1[0].response.headers?.empty mockEvents1[0].response.text == '' mockEvents1[0].response.statusCode == 201 mockEvents1[1].request.text == '' - !mockEvents1[1].request.headers?.keySet()?.empty - mockEvents1[1].request.query == [:] - mockEvents1[1].request.path == ['testEndpoint', 'hello'] - !mockEvents1[1].response.headers?.keySet()?.empty + !mockEvents1[1].request.headers?.empty + mockEvents1[1].request.queryParams == [] + mockEvents1[1].request.paths == ['testEndpoint', 'hello'] + !mockEvents1[1].response.headers?.empty mockEvents1[1].response.text == '' mockEvents1[1].response.statusCode == 201 when: - List mockEvents2 = remoteMockServer.removeMock('testRest2') + List mockEvents2 = remoteMockServer.removeMock('testRest2') then: mockEvents2.size() == 1 mockEvents2[0].request.text == '' - !mockEvents2[0].request.headers?.keySet()?.empty - mockEvents2[0].request.query == [id: '123'] - mockEvents2[0].request.path == ['testEndpoint'] - mockEvents2[0].response.headers.aaa == '15' + !mockEvents2[0].request.headers?.empty + mockEvents2[0].request.queryParams == [new Parameter(name: 'id', value: '123')] + mockEvents2[0].request.paths == ['testEndpoint'] + mockEvents2[0].response.headers.find { it.name == 'aaa' }?.value == '15' mockEvents2[0].response.text == '' mockEvents2[0].response.statusCode == 202 } def "should get mock report when peeking mock"() { expect: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, @@ -749,7 +754,7 @@ class MockServerIntegrationTest extends Specification { responseHeaders: '''{req -> ['aaa':'14']}''', soap: false )) - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest2', path: 'testEndpoint', port: 9999, @@ -781,33 +786,33 @@ class MockServerIntegrationTest extends Specification { GPathResult restPostResponse3 = Util.extractXmlResponse(response3) restPostResponse3.name() == 'goodResponseRest' when: - List mockEvents1 = remoteMockServer.peekMock('testRest') + List mockEvents1 = remoteMockServer.peekMock('testRest') then: mockEvents1.size() == 2 mockEvents1[0].request.text == '' - !mockEvents1[0].request.headers?.keySet()?.empty - mockEvents1[0].request.query == [:] - mockEvents1[0].request.path == ['testEndpoint'] - !mockEvents1[0].response.headers?.keySet()?.empty + !mockEvents1[0].request.headers?.empty + mockEvents1[0].request.queryParams == [] + mockEvents1[0].request.paths == ['testEndpoint'] + !mockEvents1[0].response.headers?.empty mockEvents1[0].response.text == '' mockEvents1[0].response.statusCode == 201 mockEvents1[1].request.text == '' - !mockEvents1[1].request.headers?.keySet()?.empty - mockEvents1[1].request.query == [:] - mockEvents1[1].request.path == ['testEndpoint', 'hello'] - !mockEvents1[1].response.headers?.keySet()?.empty + !mockEvents1[1].request.headers?.empty + mockEvents1[1].request.queryParams == [] + mockEvents1[1].request.paths == ['testEndpoint', 'hello'] + !mockEvents1[1].response.headers?.empty mockEvents1[1].response.text == '' mockEvents1[1].response.statusCode == 201 when: - List mockEvents2 = remoteMockServer.peekMock('testRest2') + List mockEvents2 = remoteMockServer.peekMock('testRest2') then: mockEvents2.size() == 1 mockEvents2[0].request.text == '' - !mockEvents2[0].request.headers?.keySet()?.empty - mockEvents2[0].request.query == [id: '123'] - mockEvents2[0].request.path == ['testEndpoint'] - mockEvents2[0].response.headers.aaa == '15' + !mockEvents2[0].request.headers?.empty + mockEvents2[0].request.queryParams == [new Parameter(name: 'id', value: '123')] + mockEvents2[0].request.paths == ['testEndpoint'] + mockEvents2[0].response.headers.find {it.name == 'aaa'}?.value == '15' mockEvents2[0].response.text == '' mockEvents2[0].response.statusCode == 202 } @@ -815,7 +820,7 @@ class MockServerIntegrationTest extends Specification { @Unroll def "should return mock report with #mockEvents events when deleting mock with flag skip mock = #skipReport"() { expect: - remoteMockServer.addMock(new AddMockRequestData( + remoteMockServer.addMock(new AddMock( name: 'testRest', path: 'testEndpoint', port: 9999, diff --git a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/ServerMockPT.groovy b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/ServerMockPT.groovy index 2ee891c..4ab9e3e 100644 --- a/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/ServerMockPT.groovy +++ b/mockserver-tests/src/test/groovy/pl/touk/mockserver/tests/ServerMockPT.groovy @@ -1,34 +1,40 @@ package pl.touk.mockserver.tests -import groovy.util.slurpersupport.GPathResult import org.apache.http.client.HttpClient import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.client.methods.HttpPost import org.apache.http.entity.ContentType import org.apache.http.entity.StringEntity import org.apache.http.impl.client.HttpClients -import pl.touk.mockserver.client.AddMockRequestData +import pl.touk.mockserver.api.request.AddMock import pl.touk.mockserver.client.RemoteMockServer import pl.touk.mockserver.client.Util import pl.touk.mockserver.server.HttpMockServer import spock.lang.Specification +import spock.lang.Timeout + +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit class ServerMockPT extends Specification { + + @Timeout(value = 60) def "should handle many request simultaneously"() { given: + HttpClient client = HttpClients.createDefault() HttpMockServer httpMockServer = new HttpMockServer() RemoteMockServer controlServerClient = new RemoteMockServer("localhost", 9999) - HttpClient client = HttpClients.createDefault() int requestAmount = 1000 - GPathResult[] responses = new GPathResult[requestAmount] - Thread[] threads = new Thread[requestAmount] + String[] responses = new String[requestAmount] + ExecutorService executorService = Executors.newFixedThreadPool(20) for (int i = 0; i < requestAmount; ++i) { int current = i - threads[i] = new Thread({ + executorService.submit { int endpointNumber = current % 10 int port = 9000 + (current % 7) - controlServerClient.addMock(new AddMockRequestData( + controlServerClient.addMock(new AddMock( name: "testRest$current", path: "testEndpoint$endpointNumber", port: port, @@ -38,15 +44,14 @@ class ServerMockPT extends Specification { HttpPost restPost = new HttpPost("http://localhost:$port/testEndpoint$endpointNumber") restPost.entity = new StringEntity("", ContentType.create("text/xml", "UTF-8")) CloseableHttpResponse response = client.execute(restPost) - responses[current] = Util.extractXmlResponse(response) - assert controlServerClient.removeMock("testRest$current").size() == 1 - }) + responses[current] = Util.extractStringResponse(response) + assert controlServerClient.removeMock("testRest$current", false).size() == 1 + } } when: - threads*.start() - Thread.sleep(60000) + executorService.awaitTermination(60, TimeUnit.SECONDS) then: - responses.eachWithIndex { res, i -> assert res.name() == "goodResponse$i" } + responses.eachWithIndex { res, i -> assert new XmlSlurper().parseText(res).name() == "goodResponse$i" as String } cleanup: httpMockServer.stop() } diff --git a/mockserver/pom.xml b/mockserver/pom.xml index b6ddbb7..6223937 100644 --- a/mockserver/pom.xml +++ b/mockserver/pom.xml @@ -26,6 +26,10 @@ org.apache.commons commons-lang3 + + pl.touk.mockserver + mockserver-api + diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy index eae893d..322ec64 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy @@ -2,9 +2,13 @@ package pl.touk.mockserver.server import com.sun.net.httpserver.HttpExchange import groovy.util.logging.Slf4j -import groovy.util.slurpersupport.GPathResult -import groovy.xml.MarkupBuilder +import pl.touk.mockserver.api.request.AddMock +import pl.touk.mockserver.api.request.MockServerRequest +import pl.touk.mockserver.api.request.PeekMock +import pl.touk.mockserver.api.request.RemoveMock +import pl.touk.mockserver.api.response.* +import javax.xml.bind.JAXBContext import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArraySet @@ -17,6 +21,9 @@ class HttpMockServer { private final List childServers = new CopyOnWriteArrayList<>() private final Set mockNames = new CopyOnWriteArraySet<>() + private static + final JAXBContext requestJaxbContext = JAXBContext.newInstance(AddMock.package.name, AddMock.classLoader) + HttpMockServer(int port = 9999) { httpServerWraper = new HttpServerWraper(port) @@ -26,12 +33,12 @@ class HttpMockServer { if (ex.requestMethod == 'GET') { listMocks(ex) } else if (ex.requestMethod == 'POST') { - GPathResult request = new XmlSlurper().parse(ex.requestBody) - if (request.name() == 'addMock') { + MockServerRequest request = requestJaxbContext.createUnmarshaller().unmarshal(ex.requestBody) as MockServerRequest + if (request instanceof AddMock) { addMock(request, ex) - } else if (request.name() == 'removeMock') { + } else if (request instanceof RemoveMock) { removeMock(request, ex) - } else if (request.name() == 'peekMock') { + } else if (request instanceof PeekMock) { peekMock(request, ex) } else { throw new RuntimeException('Unknown request') @@ -46,29 +53,26 @@ class HttpMockServer { } void listMocks(HttpExchange ex) { - StringWriter sw = new StringWriter() - MarkupBuilder builder = new MarkupBuilder(sw) - builder.mocks { - listMocks().each { - Mock mock -> - builder.mock { - name mock.name - path mock.path - port mock.port - predicate mock.predicateClosureText - response mock.responseClosureText - responseHeaders mock.responseHeadersClosureText - } - } - } - createResponse(ex, sw.toString(), 200) + MockListing mockListing = new MockListing( + mocks: listMocks().collect { + new MockReport( + name: it.name, + path: it.path, + port: it.port, + predicate: it.predicateClosureText, + response: it.responseClosureText, + responseHeaders: it.responseHeadersClosureText + ) + } + ) + createResponse(ex, mockListing, 200) } Set listMocks() { - return childServers.collect { it.mocks }.flatten() as TreeSet + return childServers.collect { it.mocks }.flatten() as TreeSet } - private void addMock(GPathResult request, HttpExchange ex) { + private void addMock(AddMock request, HttpExchange ex) { String name = request.name if (name in mockNames) { throw new RuntimeException('mock already registered') @@ -77,13 +81,13 @@ class HttpMockServer { HttpServerWraper child = getOrCreateChildServer(mock.port) child.addMock(mock) mockNames << name - createResponse(ex, '', 200) + createResponse(ex, new MockAdded(), 200) } - private static Mock mockFromRequest(GPathResult request) { + private static Mock mockFromRequest(AddMock request) { String name = request.name String mockPath = request.path - int mockPort = Integer.valueOf(request.port as String) + int mockPort = request.port Mock mock = new Mock(name, mockPath, mockPort) mock.predicate = request.predicate mock.response = request.response @@ -103,85 +107,62 @@ class HttpMockServer { return child } - private void removeMock(GPathResult request, HttpExchange ex) { + private void removeMock(RemoveMock request, HttpExchange ex) { String name = request.name - boolean skipReport = Boolean.parseBoolean(request.skipReport?.toString() ?: 'false') + boolean skipReport = request.skipReport ?: false if (!(name in mockNames)) { throw new RuntimeException('mock not registered') } log.info("Removing mock $name") - List mockEvents = skipReport ? [] : childServers.collect { it.removeMock(name) }.flatten() + List mockEvents = skipReport ? [] : childServers.collect { + it.removeMock(name) + }.flatten() as List mockNames.remove(name) - createResponse(ex, createMockRemovedResponse(mockEvents), 200) + MockRemoved mockRemoved = new MockRemoved( + mockEvents: createMockEventReports(mockEvents) + ) + createResponse(ex, mockRemoved, 200) } - private void peekMock(GPathResult request, HttpExchange ex) { + private static List createMockEventReports(List mockEvents) { + return mockEvents.collect { + new MockEventReport( + request: new MockRequestReport( + text: it.request.text, + headers: it.request.headers.collect { + new Parameter(name: it.key, value: it.value) + }, + queryParams: it.request.query.collect { + new Parameter(name: it.key, value: it.value) + }, + paths: it.request.path + ), + response: new MockResponseReport( + statusCode: it.response.statusCode, + text: it.response.text, + headers: it.response.headers.collect { + new Parameter(name: it.key, value: it.value) + } + ) + ) + } + } + + private void peekMock(PeekMock request, HttpExchange ex) { String name = request.name if (!(name in mockNames)) { throw new RuntimeException('mock not registered') } log.trace("Peeking mock $name") - List mockEvents = childServers.collect { it.peekMock(name) }.flatten() - createResponse(ex, createMockPeekedResponse(mockEvents), 200) - } - - private static String createMockRemovedResponse(List mockEvents) { - StringWriter sw = new StringWriter() - MarkupBuilder builder = new MarkupBuilder(sw) - builder.mockRemoved { - mockEventsToXml(mockEvents, builder) - } - return sw.toString() - } - - private static String createMockPeekedResponse(List mockEvents) { - StringWriter sw = new StringWriter() - MarkupBuilder builder = new MarkupBuilder(sw) - builder.mockPeeked { - mockEventsToXml(mockEvents, builder) - } - return sw.toString() - } - - private static void mockEventsToXml(List events, MarkupBuilder builder) { - events.each { MockEvent event -> - builder.mockEvent { - builder.request { - text event.request.text - headers { - event.request.headers.each { - builder.param(name: it.key, it.value) - } - } - query { - event.request.query.each { - builder.param(name: it.key, it.value) - } - } - path { - event.request.path.each { - builder.elem it - } - } - } - builder.response { - text event.response.text - headers { - event.response.headers.each { - builder.param(name: it.key, it.value) - } - } - statusCode event.response.statusCode - } - } - } + List mockEvents = childServers.collect { it.peekMock(name) }.flatten() as List + MockPeeked mockPeeked = new MockPeeked( + mockEvents: createMockEventReports(mockEvents) + ) + createResponse(ex, mockPeeked, 200) } private static void createErrorResponse(HttpExchange ex, Exception e) { - StringWriter sw = new StringWriter() - MarkupBuilder builder = new MarkupBuilder(sw) - builder.exceptionOccured e.message - createResponse(ex, sw.toString(), 400) + createResponse(ex, new ExceptionOccured(message: e.message), 400) } void stop() { diff --git a/mockserver/src/main/groovy/pl/touk/mockserver/server/Util.groovy b/mockserver/src/main/groovy/pl/touk/mockserver/server/Util.groovy index 93ab7a5..3864afa 100644 --- a/mockserver/src/main/groovy/pl/touk/mockserver/server/Util.groovy +++ b/mockserver/src/main/groovy/pl/touk/mockserver/server/Util.groovy @@ -1,14 +1,32 @@ package pl.touk.mockserver.server import com.sun.net.httpserver.HttpExchange +import pl.touk.mockserver.api.response.MockAdded + +import javax.xml.bind.JAXBContext class Util { - static void createResponse(HttpExchange ex, String response, int statusCode) { - byte[] responseBytes = response ? response.getBytes('UTF-8') : new byte[0] + + private static + final JAXBContext responseJaxbContext = JAXBContext.newInstance(MockAdded.package.name, MockAdded.classLoader) + + static void createResponse(HttpExchange ex, Object response, int statusCode) { + String responseString = marshall(response) + createResponse(ex, responseString, statusCode) + } + + static void createResponse(HttpExchange ex, String responseString, int statusCode) { + byte[] responseBytes = responseString ? responseString.getBytes('UTF-8') : new byte[0] ex.sendResponseHeaders(statusCode, responseBytes.length ?: -1) - if (response) { + if (responseString) { ex.responseBody << responseBytes ex.responseBody.close() } } + + private static String marshall(Object response) { + StringWriter sw = new StringWriter() + responseJaxbContext.createMarshaller().marshal(response, sw) + return sw.toString() + } } diff --git a/pom.xml b/pom.xml index 7bb19e5..223871d 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,7 @@ mockserver-client mockserver mockserver-tests + mockserver-api @@ -22,6 +23,7 @@ 3.3.2 1.7.7 1.0.13 + 1.16.6 @@ -63,6 +65,16 @@ logback-classic ${logback-classic.version} + + org.projectlombok + lombok + ${lombok.version} + + + pl.touk.mockserver + mockserver-api + ${project.version} +