Make api with jaxb

Change-Id: Ic0ac5ce212fac17583699868709b67a701231755
This commit is contained in:
Dominik Adam Przybysz 2015-08-29 14:53:54 +02:00
parent 5545b67ebd
commit 82434f46a3
38 changed files with 583 additions and 422 deletions

20
mockserver-api/pom.xml Normal file
View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>http-mock-server</artifactId>
<groupId>pl.touk.mockserver</groupId>
<version>1.1.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mockserver-api</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -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;
}

View file

@ -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;
}

View file

@ -0,0 +1,4 @@
package pl.touk.mockserver.api.request;
public abstract class MockServerRequest {
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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 {
}

View file

@ -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;
}

View file

@ -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<MockReport> mocks;
}

View file

@ -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<MockEventReport> mockEvents;
}

View file

@ -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<MockEventReport> mockEvents;
}

View file

@ -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;
}

View file

@ -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<Parameter> headers;
@XmlElementWrapper(name = "query")
@XmlElement(name = "param")
private List<Parameter> queryParams;
@XmlElementWrapper(name = "path")
@XmlElement(name = "elem")
private List<String> paths;
}

View file

@ -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<Parameter> headers;
}

View file

@ -0,0 +1,4 @@
package pl.touk.mockserver.api.response;
public abstract class MockServerResponse {
}

View file

@ -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;
}

View file

@ -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;

View file

@ -0,0 +1,4 @@
AddMock
Method
PeekMock
RemoveMock

View file

@ -0,0 +1,11 @@
ExceptionOccured
MockAdded
MockEventReport
MockListing
MockPeeked
MockRemoved
MockReport
MockRequestReport
MockResponseReport
MockServerResponse
Parameter

View file

@ -25,5 +25,9 @@
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
</dependency> </dependency>
<dependency>
<groupId>pl.touk.mockserver</groupId>
<artifactId>mockserver-api</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View file

@ -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)
}
}

View file

@ -1,12 +0,0 @@
package pl.touk.mockserver.client
enum Method {
POST,
GET,
DELETE,
PUT,
TRACE,
HEAD,
OPTIONS,
PATCH
}

View file

@ -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
}
}

View file

@ -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<String, String> headers
final Map<String, String> query
final List<String> path
MockRequest(String text, Map<String, String> headers, Map<String, String> query, List<String> path) {
this.text = text
this.headers = headers
this.query = query
this.path = path
}
}

View file

@ -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<String, String> headers
MockResponse(int statusCode, String text, Map<String, String> headers) {
this.statusCode = statusCode
this.text = text
this.headers = headers
}
}

View file

@ -1,10 +0,0 @@
package pl.touk.mockserver.client
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
@CompileStatic
@TypeChecked
class PeekMockRequestData {
String name
}

View file

@ -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
}
}

View file

@ -1,6 +1,5 @@
package pl.touk.mockserver.client package pl.touk.mockserver.client
import groovy.util.slurpersupport.GPathResult
import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpGet import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost 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.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClients 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 { class RemoteMockServer {
private final String address private final String address
private final CloseableHttpClient client = HttpClients.createDefault() 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) { RemoteMockServer(String host, int port) {
address = "http://$host:$port/serverControl" address = "http://$host:$port/serverControl"
} }
void addMock(AddMockRequestData addMockRequestData) { void addMock(AddMock addMockData) {
HttpPost addMockPost = new HttpPost(address) HttpPost addMockPost = new HttpPost(address)
addMockPost.entity = buildAddMockRequest(addMockRequestData) addMockPost.entity = buildAddMockRequest(addMockData)
CloseableHttpResponse response = client.execute(addMockPost) CloseableHttpResponse response = client.execute(addMockPost)
GPathResult responseXml = Util.extractXmlResponse(response) Util.extractResponse(response)
if (responseXml.name() != 'mockAdded') {
if (responseXml.text() == 'mock already registered') {
throw new MockAlreadyExists()
}
throw new InvalidMockDefinition(responseXml.text())
}
} }
List<MockEvent> removeMock(String name, boolean skipReport = false) { List<MockEventReport> removeMock(String name, boolean skipReport = false) {
HttpPost removeMockPost = new HttpPost(address) 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) CloseableHttpResponse response = client.execute(removeMockPost)
GPathResult responseXml = Util.extractXmlResponse(response) MockRemoved mockRemoved = Util.extractResponse(response) as MockRemoved
if (responseXml.name() == 'mockRemoved') { return mockRemoved.mockEvents ?: []
return responseXml.'mockEvent'.collect {
new MockEvent(mockRequestFromXml(it.request), mockResponseFromXml(it.response))
}
}
throw new MockDoesNotExist()
} }
List<MockEvent> peekMock(String name) { List<MockEventReport> peekMock(String name) {
HttpPost removeMockPost = new HttpPost(address) HttpPost removeMockPost = new HttpPost(address)
removeMockPost.entity = buildPeekMockRequest(new PeekMockRequestData(name: name)) removeMockPost.entity = buildPeekMockRequest(new PeekMock(name: name))
CloseableHttpResponse response = client.execute(removeMockPost) CloseableHttpResponse response = client.execute(removeMockPost)
GPathResult responseXml = Util.extractXmlResponse(response) MockPeeked mockPeeked = Util.extractResponse(response) as MockPeeked
if (responseXml.name() == 'mockPeeked') { return mockPeeked.mockEvents ?: []
return responseXml.'mockEvent'.collect {
new MockEvent(mockRequestFromXml(it.request), mockResponseFromXml(it.response))
}
}
throw new MockDoesNotExist()
} }
private static MockResponse mockResponseFromXml(GPathResult xml) { private static StringEntity buildRemoveMockRequest(RemoveMock data) {
return new MockResponse(xml.statusCode.text() as int, xml.text.text(), xml.headers.param.collectEntries { return new StringEntity(marshallRequest(data), ContentType.create("text/xml", "UTF-8"))
[(it.@name.text()): it.text()]
})
} }
private static MockRequest mockRequestFromXml(GPathResult xml) { private static String marshallRequest(MockServerRequest data) {
return new MockRequest( StringWriter sw = new StringWriter()
xml.text.text(), requestContext.createMarshaller().marshal(data, sw)
xml.headers.param.collectEntries { [(it.@name.text()): it.text()] }, return sw.toString()
xml.query.param.collectEntries { [(it.@name.text()): it.text()] },
xml.path.elem*.text()
)
} }
private static StringEntity buildRemoveMockRequest(RemoveMockRequestData data) { private static StringEntity buildPeekMockRequest(PeekMock peekMock) {
return new StringEntity("""\ return new StringEntity(marshallRequest(peekMock), ContentType.create("text/xml", "UTF-8"))
<removeMock>
<name>${data.name}</name>
<skipReport>${data.skipReport}</skipReport>
</removeMock>
""", ContentType.create("text/xml", "UTF-8"))
} }
private static StringEntity buildPeekMockRequest(PeekMockRequestData data) { private static StringEntity buildAddMockRequest(AddMock data) {
return new StringEntity("""\ return new StringEntity(marshallRequest(data), ContentType.create("text/xml", "UTF-8"))
<peekMock>
<name>${data.name}</name>
</peekMock>
""", ContentType.create("text/xml", "UTF-8"))
} }
private static StringEntity buildAddMockRequest(AddMockRequestData data) { List<MockReport> listMocks() {
return new StringEntity("""\
<addMock>
<name>${data.name}</name>
<path>${data.path}</path>
<port>${data.port}</port>
${data.predicate ? "<predicate>${data.predicate}</predicate>" : ''}
${data.response ? "<response>${data.response}</response>" : ''}
${data.soap != null ? "<soap>${data.soap}</soap>" : ''}
${data.statusCode ? "<statusCode>${data.statusCode}</statusCode>" : ''}
${data.method ? "<method>${data.method}</method>" : ''}
${data.responseHeaders ? "<responseHeaders>${data.responseHeaders}</responseHeaders>" : ''}
</addMock>
""", ContentType.create("text/xml", "UTF-8"))
}
List<RegisteredMock> listMocks() {
HttpGet get = new HttpGet(address) HttpGet get = new HttpGet(address)
CloseableHttpResponse response = client.execute(get) CloseableHttpResponse response = client.execute(get)
GPathResult xml = Util.extractXmlResponse(response) MockListing mockListing = Util.extractResponse(response) as MockListing
if (xml.name() == 'mocks') { return mockListing.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 []
} }
} }

View file

@ -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
}

View file

@ -7,15 +7,42 @@ import groovy.util.slurpersupport.GPathResult
import org.apache.http.HttpEntity import org.apache.http.HttpEntity
import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.util.EntityUtils 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 @CompileStatic
@TypeChecked @TypeChecked
class Util { class Util {
private static
final JAXBContext responseContext = JAXBContext.newInstance(MockAdded.package.name, MockAdded.classLoader)
static GPathResult extractXmlResponse(CloseableHttpResponse response) { static GPathResult extractXmlResponse(CloseableHttpResponse response) {
return new XmlSlurper().parseText(extractStringResponse(response))
}
static String extractStringResponse(CloseableHttpResponse response) {
HttpEntity entity = response.entity HttpEntity entity = response.entity
GPathResult xml = new XmlSlurper().parseText(EntityUtils.toString(entity, 'UTF-8')) String responseString = EntityUtils.toString(entity, 'UTF-8')
EntityUtils.consumeQuietly(entity) 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) { static String soap(String request) {
@ -26,13 +53,11 @@ class Util {
} }
static Object extractJsonResponse(CloseableHttpResponse response) { static Object extractJsonResponse(CloseableHttpResponse response) {
HttpEntity entity = response.entity return new JsonSlurper().parseText(extractStringResponse(response))
Object json = new JsonSlurper().parseText(EntityUtils.toString(entity, 'UTF-8'))
EntityUtils.consumeQuietly(entity)
return json
} }
static void consumeResponse(CloseableHttpResponse response) { static void consumeResponse(CloseableHttpResponse response) {
EntityUtils.consumeQuietly(response.entity) EntityUtils.consumeQuietly(response.entity)
} }
} }

View file

@ -7,6 +7,11 @@ import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClients import org.apache.http.impl.client.HttpClients
import org.apache.http.util.EntityUtils 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.client.*
import pl.touk.mockserver.server.HttpMockServer import pl.touk.mockserver.server.HttpMockServer
import spock.lang.Shared import spock.lang.Shared
@ -33,7 +38,7 @@ class MockServerIntegrationTest extends Specification {
def "should add working rest mock on endpoint"() { def "should add working rest mock on endpoint"() {
expect: expect:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -54,7 +59,7 @@ class MockServerIntegrationTest extends Specification {
def "should add working rest mock on endpoint with utf"() { def "should add working rest mock on endpoint with utf"() {
expect: expect:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRestUtf', name: 'testRestUtf',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -76,7 +81,7 @@ class MockServerIntegrationTest extends Specification {
def "should add soap mock on endpoint"() { def "should add soap mock on endpoint"() {
expect: expect:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testSoap', name: 'testSoap',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -102,7 +107,7 @@ class MockServerIntegrationTest extends Specification {
then: then:
thrown(MockDoesNotExist) thrown(MockDoesNotExist)
expect: expect:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testSoap', name: 'testSoap',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -120,7 +125,7 @@ class MockServerIntegrationTest extends Specification {
def "should not add mock with existing name"() { def "should not add mock with existing name"() {
expect: expect:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testSoap', name: 'testSoap',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -129,7 +134,7 @@ class MockServerIntegrationTest extends Specification {
soap: true soap: true
)) ))
when: when:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testSoap', name: 'testSoap',
path: 'testEndpoint2', path: 'testEndpoint2',
port: 9998, port: 9998,
@ -143,7 +148,7 @@ class MockServerIntegrationTest extends Specification {
def "should not add mock with empty name"() { def "should not add mock with empty name"() {
when: when:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: '', name: '',
path: 'testEndpoint2', path: 'testEndpoint2',
port: 9998, port: 9998,
@ -157,7 +162,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock after deleting old mock with the same name"() { def "should add mock after deleting old mock with the same name"() {
expect: expect:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testSoap', name: 'testSoap',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -168,7 +173,7 @@ class MockServerIntegrationTest extends Specification {
and: and:
remoteMockServer.removeMock('testSoap') == [] remoteMockServer.removeMock('testSoap') == []
and: and:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testSoap', name: 'testSoap',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, 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"() { def "should add simultaneously working post and rest mocks with the same predicate and endpoint nad port"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
predicate: '''{req -> req.xml.name() == 'request'}''', predicate: '''{req -> req.xml.name() == 'request'}''',
response: '''{req -> "<goodResponseRest-${req.xml.name()}/>"}''' response: '''{req -> "<goodResponseRest-${req.xml.name()}/>"}'''
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testSoap', name: 'testSoap',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -215,14 +220,14 @@ class MockServerIntegrationTest extends Specification {
@Unroll @Unroll
def "should dispatch rest mocks when second on #name"() { def "should dispatch rest mocks when second on #name"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest1', name: 'testRest1',
path: 'test1', path: 'test1',
port: 9999, port: 9999,
predicate: '''{req -> req.xml.name() == 'request1'}''', predicate: '''{req -> req.xml.name() == 'request1'}''',
response: '''{req -> "<goodResponseRest1/>"}''' response: '''{req -> "<goodResponseRest1/>"}'''
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: secondPath, path: secondPath,
port: secondPort, port: secondPort,
@ -254,7 +259,7 @@ class MockServerIntegrationTest extends Specification {
@Unroll @Unroll
def "should dispatch rest mock with response code"() { def "should dispatch rest mock with response code"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest1', name: 'testRest1',
path: 'test1', path: 'test1',
port: 9999, 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"() { def "should return response code 404 and error body the same as request body when mocks does not apply"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest1', name: 'testRest1',
path: 'test1', path: 'test1',
port: 9999, port: 9999,
@ -295,7 +300,7 @@ class MockServerIntegrationTest extends Specification {
def "should inform that there was problem during adding mock - invalid port"() { def "should inform that there was problem during adding mock - invalid port"() {
when: when:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testSoap', name: 'testSoap',
path: 'testEndpoint2', path: 'testEndpoint2',
port: -1, port: -1,
@ -309,13 +314,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with get method"() { def "should dispatch rest mock with get method"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
response: '''{_ -> "<defaultResponse/>"}''' response: '''{_ -> "<defaultResponse/>"}'''
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -332,13 +337,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with trace method"() { def "should dispatch rest mock with trace method"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
response: '''{_ -> "<defaultResponse/>"}''' response: '''{_ -> "<defaultResponse/>"}'''
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -355,13 +360,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with head method"() { def "should dispatch rest mock with head method"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
response: '''{_ -> "<defaultResponse/>"}''' response: '''{_ -> "<defaultResponse/>"}'''
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -377,13 +382,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with options method"() { def "should dispatch rest mock with options method"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
response: '''{_ -> "<defaultResponse/>"}''' response: '''{_ -> "<defaultResponse/>"}'''
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -399,13 +404,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with put method"() { def "should dispatch rest mock with put method"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'test1', path: 'test1',
port: 9999, port: 9999,
response: '''{_ -> "<defaultResponse/>"}''' response: '''{_ -> "<defaultResponse/>"}'''
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: 'test1', path: 'test1',
port: 9999, port: 9999,
@ -424,13 +429,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with delete method"() { def "should dispatch rest mock with delete method"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'test1', path: 'test1',
port: 9999, port: 9999,
response: '''{_ -> "<defaultResponse/>"}''' response: '''{_ -> "<defaultResponse/>"}'''
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: 'test1', path: 'test1',
port: 9999, port: 9999,
@ -447,13 +452,13 @@ class MockServerIntegrationTest extends Specification {
def "should dispatch rest mock with patch method"() { def "should dispatch rest mock with patch method"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'test1', path: 'test1',
port: 9999, port: 9999,
response: '''{_ -> "<defaultResponse/>"}''' response: '''{_ -> "<defaultResponse/>"}'''
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: 'test1', path: 'test1',
port: 9999, port: 9999,
@ -472,7 +477,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock that return headers"() { def "should add mock that return headers"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -492,7 +497,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock that accepts only when certain request headers exists"() { def "should add mock that accepts only when certain request headers exists"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -521,7 +526,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock that accepts only when certain query params exists"() { def "should add mock that accepts only when certain query params exists"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -545,7 +550,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock that accepts only when request has specific body"() { def "should add mock that accepts only when request has specific body"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -570,7 +575,7 @@ class MockServerIntegrationTest extends Specification {
def "should add mock which response json to json"() { def "should add mock which response json to json"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -595,7 +600,7 @@ class MockServerIntegrationTest extends Specification {
def "should get list mocks"() { def "should get list mocks"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: 'testEndpoint', path: 'testEndpoint',
port: 9998, port: 9998,
@ -603,27 +608,27 @@ class MockServerIntegrationTest extends Specification {
response: '''{ req -> '<response/>' }''', response: '''{ req -> '<response/>' }''',
responseHeaders: '{ _ -> [a: "b"] }' responseHeaders: '{ _ -> [a: "b"] }'
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest4', name: 'testRest4',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999 port: 9999
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest3', name: 'testRest3',
path: 'testEndpoint2', path: 'testEndpoint2',
port: 9999 port: 9999
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest5', name: 'testRest5',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999 port: 9999
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest6', name: 'testRest6',
path: 'testEndpoint2', path: 'testEndpoint2',
port: 9999 port: 9999
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999 port: 9999
@ -631,17 +636,17 @@ class MockServerIntegrationTest extends Specification {
remoteMockServer.removeMock('testRest5') remoteMockServer.removeMock('testRest5')
expect: expect:
remoteMockServer.listMocks() == [ remoteMockServer.listMocks() == [
new RegisteredMock('testRest', 'testEndpoint', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }'), new MockReport(name: 'testRest', path: 'testEndpoint', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }'),
new RegisteredMock('testRest2', 'testEndpoint', 9998, '''{ req -> req.xml.name() == 'request1'}''', '''{ req -> '<response/>' }''', '{ _ -> [a: "b"] }'), new MockReport(name: 'testRest2', path: 'testEndpoint', port: 9998, predicate: '''{ req -> req.xml.name() == 'request1'}''', response: '''{ req -> '<response/>' }''', responseHeaders: '{ _ -> [a: "b"] }'),
new RegisteredMock('testRest3', 'testEndpoint2', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }'), new MockReport(name: 'testRest3', path: 'testEndpoint2', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }'),
new RegisteredMock('testRest4', 'testEndpoint', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }'), new MockReport(name: 'testRest4', path: 'testEndpoint', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }'),
new RegisteredMock('testRest6', 'testEndpoint2', 9999, '{ _ -> true }', '''{ _ -> '' }''', '{ _ -> [:] }') new MockReport(name: 'testRest6', path: 'testEndpoint2', port: 9999, predicate: '{ _ -> true }', response: '''{ _ -> '' }''', responseHeaders: '{ _ -> [:] }')
] ]
} }
def "should add mock accepts path certain path params"() { def "should add mock accepts path certain path params"() {
given: given:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -664,7 +669,7 @@ class MockServerIntegrationTest extends Specification {
def "should get mock report when deleting mock"() { def "should get mock report when deleting mock"() {
expect: expect:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -674,7 +679,7 @@ class MockServerIntegrationTest extends Specification {
responseHeaders: '''{req -> ['aaa':'14']}''', responseHeaders: '''{req -> ['aaa':'14']}''',
soap: false soap: false
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -706,40 +711,40 @@ class MockServerIntegrationTest extends Specification {
GPathResult restPostResponse3 = Util.extractXmlResponse(response3) GPathResult restPostResponse3 = Util.extractXmlResponse(response3)
restPostResponse3.name() == 'goodResponseRest' restPostResponse3.name() == 'goodResponseRest'
when: when:
List<MockEvent> mockEvents1 = remoteMockServer.removeMock('testRest') List<MockEventReport> mockEvents1 = remoteMockServer.removeMock('testRest')
then: then:
mockEvents1.size() == 2 mockEvents1.size() == 2
mockEvents1[0].request.text == '<request/>' mockEvents1[0].request.text == '<request/>'
!mockEvents1[0].request.headers?.keySet()?.empty !mockEvents1[0].request.headers?.empty
mockEvents1[0].request.query == [:] mockEvents1[0].request.queryParams == []
mockEvents1[0].request.path == ['testEndpoint'] mockEvents1[0].request.paths == ['testEndpoint']
!mockEvents1[0].response.headers?.keySet()?.empty !mockEvents1[0].response.headers?.empty
mockEvents1[0].response.text == '<goodResponseRest-request/>' mockEvents1[0].response.text == '<goodResponseRest-request/>'
mockEvents1[0].response.statusCode == 201 mockEvents1[0].response.statusCode == 201
mockEvents1[1].request.text == '<request15/>' mockEvents1[1].request.text == '<request15/>'
!mockEvents1[1].request.headers?.keySet()?.empty !mockEvents1[1].request.headers?.empty
mockEvents1[1].request.query == [:] mockEvents1[1].request.queryParams == []
mockEvents1[1].request.path == ['testEndpoint', 'hello'] mockEvents1[1].request.paths == ['testEndpoint', 'hello']
!mockEvents1[1].response.headers?.keySet()?.empty !mockEvents1[1].response.headers?.empty
mockEvents1[1].response.text == '<goodResponseRest-request15/>' mockEvents1[1].response.text == '<goodResponseRest-request15/>'
mockEvents1[1].response.statusCode == 201 mockEvents1[1].response.statusCode == 201
when: when:
List<MockEvent> mockEvents2 = remoteMockServer.removeMock('testRest2') List<MockEventReport> mockEvents2 = remoteMockServer.removeMock('testRest2')
then: then:
mockEvents2.size() == 1 mockEvents2.size() == 1
mockEvents2[0].request.text == '<reqXYZ/>' mockEvents2[0].request.text == '<reqXYZ/>'
!mockEvents2[0].request.headers?.keySet()?.empty !mockEvents2[0].request.headers?.empty
mockEvents2[0].request.query == [id: '123'] mockEvents2[0].request.queryParams == [new Parameter(name: 'id', value: '123')]
mockEvents2[0].request.path == ['testEndpoint'] mockEvents2[0].request.paths == ['testEndpoint']
mockEvents2[0].response.headers.aaa == '15' mockEvents2[0].response.headers.find { it.name == 'aaa' }?.value == '15'
mockEvents2[0].response.text == '<goodResponseRest/>' mockEvents2[0].response.text == '<goodResponseRest/>'
mockEvents2[0].response.statusCode == 202 mockEvents2[0].response.statusCode == 202
} }
def "should get mock report when peeking mock"() { def "should get mock report when peeking mock"() {
expect: expect:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -749,7 +754,7 @@ class MockServerIntegrationTest extends Specification {
responseHeaders: '''{req -> ['aaa':'14']}''', responseHeaders: '''{req -> ['aaa':'14']}''',
soap: false soap: false
)) ))
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest2', name: 'testRest2',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,
@ -781,33 +786,33 @@ class MockServerIntegrationTest extends Specification {
GPathResult restPostResponse3 = Util.extractXmlResponse(response3) GPathResult restPostResponse3 = Util.extractXmlResponse(response3)
restPostResponse3.name() == 'goodResponseRest' restPostResponse3.name() == 'goodResponseRest'
when: when:
List<MockEvent> mockEvents1 = remoteMockServer.peekMock('testRest') List<MockEventReport> mockEvents1 = remoteMockServer.peekMock('testRest')
then: then:
mockEvents1.size() == 2 mockEvents1.size() == 2
mockEvents1[0].request.text == '<request/>' mockEvents1[0].request.text == '<request/>'
!mockEvents1[0].request.headers?.keySet()?.empty !mockEvents1[0].request.headers?.empty
mockEvents1[0].request.query == [:] mockEvents1[0].request.queryParams == []
mockEvents1[0].request.path == ['testEndpoint'] mockEvents1[0].request.paths == ['testEndpoint']
!mockEvents1[0].response.headers?.keySet()?.empty !mockEvents1[0].response.headers?.empty
mockEvents1[0].response.text == '<goodResponseRest-request/>' mockEvents1[0].response.text == '<goodResponseRest-request/>'
mockEvents1[0].response.statusCode == 201 mockEvents1[0].response.statusCode == 201
mockEvents1[1].request.text == '<request15/>' mockEvents1[1].request.text == '<request15/>'
!mockEvents1[1].request.headers?.keySet()?.empty !mockEvents1[1].request.headers?.empty
mockEvents1[1].request.query == [:] mockEvents1[1].request.queryParams == []
mockEvents1[1].request.path == ['testEndpoint', 'hello'] mockEvents1[1].request.paths == ['testEndpoint', 'hello']
!mockEvents1[1].response.headers?.keySet()?.empty !mockEvents1[1].response.headers?.empty
mockEvents1[1].response.text == '<goodResponseRest-request15/>' mockEvents1[1].response.text == '<goodResponseRest-request15/>'
mockEvents1[1].response.statusCode == 201 mockEvents1[1].response.statusCode == 201
when: when:
List<MockEvent> mockEvents2 = remoteMockServer.peekMock('testRest2') List<MockEventReport> mockEvents2 = remoteMockServer.peekMock('testRest2')
then: then:
mockEvents2.size() == 1 mockEvents2.size() == 1
mockEvents2[0].request.text == '<reqXYZ/>' mockEvents2[0].request.text == '<reqXYZ/>'
!mockEvents2[0].request.headers?.keySet()?.empty !mockEvents2[0].request.headers?.empty
mockEvents2[0].request.query == [id: '123'] mockEvents2[0].request.queryParams == [new Parameter(name: 'id', value: '123')]
mockEvents2[0].request.path == ['testEndpoint'] mockEvents2[0].request.paths == ['testEndpoint']
mockEvents2[0].response.headers.aaa == '15' mockEvents2[0].response.headers.find {it.name == 'aaa'}?.value == '15'
mockEvents2[0].response.text == '<goodResponseRest/>' mockEvents2[0].response.text == '<goodResponseRest/>'
mockEvents2[0].response.statusCode == 202 mockEvents2[0].response.statusCode == 202
} }
@ -815,7 +820,7 @@ class MockServerIntegrationTest extends Specification {
@Unroll @Unroll
def "should return mock report with #mockEvents events when deleting mock with flag skip mock = #skipReport"() { def "should return mock report with #mockEvents events when deleting mock with flag skip mock = #skipReport"() {
expect: expect:
remoteMockServer.addMock(new AddMockRequestData( remoteMockServer.addMock(new AddMock(
name: 'testRest', name: 'testRest',
path: 'testEndpoint', path: 'testEndpoint',
port: 9999, port: 9999,

View file

@ -1,34 +1,40 @@
package pl.touk.mockserver.tests package pl.touk.mockserver.tests
import groovy.util.slurpersupport.GPathResult
import org.apache.http.client.HttpClient import org.apache.http.client.HttpClient
import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpPost import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.ContentType import org.apache.http.entity.ContentType
import org.apache.http.entity.StringEntity import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.HttpClients 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.RemoteMockServer
import pl.touk.mockserver.client.Util import pl.touk.mockserver.client.Util
import pl.touk.mockserver.server.HttpMockServer import pl.touk.mockserver.server.HttpMockServer
import spock.lang.Specification 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 { class ServerMockPT extends Specification {
@Timeout(value = 60)
def "should handle many request simultaneously"() { def "should handle many request simultaneously"() {
given: given:
HttpClient client = HttpClients.createDefault()
HttpMockServer httpMockServer = new HttpMockServer() HttpMockServer httpMockServer = new HttpMockServer()
RemoteMockServer controlServerClient = new RemoteMockServer("localhost", 9999) RemoteMockServer controlServerClient = new RemoteMockServer("localhost", 9999)
HttpClient client = HttpClients.createDefault()
int requestAmount = 1000 int requestAmount = 1000
GPathResult[] responses = new GPathResult[requestAmount] String[] responses = new String[requestAmount]
Thread[] threads = new Thread[requestAmount] ExecutorService executorService = Executors.newFixedThreadPool(20)
for (int i = 0; i < requestAmount; ++i) { for (int i = 0; i < requestAmount; ++i) {
int current = i int current = i
threads[i] = new Thread({ executorService.submit {
int endpointNumber = current % 10 int endpointNumber = current % 10
int port = 9000 + (current % 7) int port = 9000 + (current % 7)
controlServerClient.addMock(new AddMockRequestData( controlServerClient.addMock(new AddMock(
name: "testRest$current", name: "testRest$current",
path: "testEndpoint$endpointNumber", path: "testEndpoint$endpointNumber",
port: port, port: port,
@ -38,15 +44,14 @@ class ServerMockPT extends Specification {
HttpPost restPost = new HttpPost("http://localhost:$port/testEndpoint$endpointNumber") HttpPost restPost = new HttpPost("http://localhost:$port/testEndpoint$endpointNumber")
restPost.entity = new StringEntity("<request$current/>", ContentType.create("text/xml", "UTF-8")) restPost.entity = new StringEntity("<request$current/>", ContentType.create("text/xml", "UTF-8"))
CloseableHttpResponse response = client.execute(restPost) CloseableHttpResponse response = client.execute(restPost)
responses[current] = Util.extractXmlResponse(response) responses[current] = Util.extractStringResponse(response)
assert controlServerClient.removeMock("testRest$current").size() == 1 assert controlServerClient.removeMock("testRest$current", false).size() == 1
}) }
} }
when: when:
threads*.start() executorService.awaitTermination(60, TimeUnit.SECONDS)
Thread.sleep(60000)
then: 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: cleanup:
httpMockServer.stop() httpMockServer.stop()
} }

View file

@ -26,6 +26,10 @@
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
</dependency> </dependency>
<dependency>
<groupId>pl.touk.mockserver</groupId>
<artifactId>mockserver-api</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View file

@ -2,9 +2,13 @@ package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpExchange import com.sun.net.httpserver.HttpExchange
import groovy.util.logging.Slf4j import groovy.util.logging.Slf4j
import groovy.util.slurpersupport.GPathResult import pl.touk.mockserver.api.request.AddMock
import groovy.xml.MarkupBuilder 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.CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
@ -17,6 +21,9 @@ class HttpMockServer {
private final List<HttpServerWraper> childServers = new CopyOnWriteArrayList<>() private final List<HttpServerWraper> childServers = new CopyOnWriteArrayList<>()
private final Set<String> mockNames = new CopyOnWriteArraySet<>() private final Set<String> mockNames = new CopyOnWriteArraySet<>()
private static
final JAXBContext requestJaxbContext = JAXBContext.newInstance(AddMock.package.name, AddMock.classLoader)
HttpMockServer(int port = 9999) { HttpMockServer(int port = 9999) {
httpServerWraper = new HttpServerWraper(port) httpServerWraper = new HttpServerWraper(port)
@ -26,12 +33,12 @@ class HttpMockServer {
if (ex.requestMethod == 'GET') { if (ex.requestMethod == 'GET') {
listMocks(ex) listMocks(ex)
} else if (ex.requestMethod == 'POST') { } else if (ex.requestMethod == 'POST') {
GPathResult request = new XmlSlurper().parse(ex.requestBody) MockServerRequest request = requestJaxbContext.createUnmarshaller().unmarshal(ex.requestBody) as MockServerRequest
if (request.name() == 'addMock') { if (request instanceof AddMock) {
addMock(request, ex) addMock(request, ex)
} else if (request.name() == 'removeMock') { } else if (request instanceof RemoveMock) {
removeMock(request, ex) removeMock(request, ex)
} else if (request.name() == 'peekMock') { } else if (request instanceof PeekMock) {
peekMock(request, ex) peekMock(request, ex)
} else { } else {
throw new RuntimeException('Unknown request') throw new RuntimeException('Unknown request')
@ -46,29 +53,26 @@ class HttpMockServer {
} }
void listMocks(HttpExchange ex) { void listMocks(HttpExchange ex) {
StringWriter sw = new StringWriter() MockListing mockListing = new MockListing(
MarkupBuilder builder = new MarkupBuilder(sw) mocks: listMocks().collect {
builder.mocks { new MockReport(
listMocks().each { name: it.name,
Mock mock -> path: it.path,
builder.mock { port: it.port,
name mock.name predicate: it.predicateClosureText,
path mock.path response: it.responseClosureText,
port mock.port responseHeaders: it.responseHeadersClosureText
predicate mock.predicateClosureText )
response mock.responseClosureText }
responseHeaders mock.responseHeadersClosureText )
} createResponse(ex, mockListing, 200)
}
}
createResponse(ex, sw.toString(), 200)
} }
Set<Mock> listMocks() { Set<Mock> listMocks() {
return childServers.collect { it.mocks }.flatten() as TreeSet return childServers.collect { it.mocks }.flatten() as TreeSet<Mock>
} }
private void addMock(GPathResult request, HttpExchange ex) { private void addMock(AddMock request, HttpExchange ex) {
String name = request.name String name = request.name
if (name in mockNames) { if (name in mockNames) {
throw new RuntimeException('mock already registered') throw new RuntimeException('mock already registered')
@ -77,13 +81,13 @@ class HttpMockServer {
HttpServerWraper child = getOrCreateChildServer(mock.port) HttpServerWraper child = getOrCreateChildServer(mock.port)
child.addMock(mock) child.addMock(mock)
mockNames << name mockNames << name
createResponse(ex, '<mockAdded/>', 200) createResponse(ex, new MockAdded(), 200)
} }
private static Mock mockFromRequest(GPathResult request) { private static Mock mockFromRequest(AddMock request) {
String name = request.name String name = request.name
String mockPath = request.path String mockPath = request.path
int mockPort = Integer.valueOf(request.port as String) int mockPort = request.port
Mock mock = new Mock(name, mockPath, mockPort) Mock mock = new Mock(name, mockPath, mockPort)
mock.predicate = request.predicate mock.predicate = request.predicate
mock.response = request.response mock.response = request.response
@ -103,85 +107,62 @@ class HttpMockServer {
return child return child
} }
private void removeMock(GPathResult request, HttpExchange ex) { private void removeMock(RemoveMock request, HttpExchange ex) {
String name = request.name String name = request.name
boolean skipReport = Boolean.parseBoolean(request.skipReport?.toString() ?: 'false') boolean skipReport = request.skipReport ?: false
if (!(name in mockNames)) { if (!(name in mockNames)) {
throw new RuntimeException('mock not registered') throw new RuntimeException('mock not registered')
} }
log.info("Removing mock $name") log.info("Removing mock $name")
List<MockEvent> mockEvents = skipReport ? [] : childServers.collect { it.removeMock(name) }.flatten() List<MockEvent> mockEvents = skipReport ? [] : childServers.collect {
it.removeMock(name)
}.flatten() as List<MockEvent>
mockNames.remove(name) 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<MockEventReport> createMockEventReports(List<MockEvent> 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 String name = request.name
if (!(name in mockNames)) { if (!(name in mockNames)) {
throw new RuntimeException('mock not registered') throw new RuntimeException('mock not registered')
} }
log.trace("Peeking mock $name") log.trace("Peeking mock $name")
List<MockEvent> mockEvents = childServers.collect { it.peekMock(name) }.flatten() List<MockEvent> mockEvents = childServers.collect { it.peekMock(name) }.flatten() as List<MockEvent>
createResponse(ex, createMockPeekedResponse(mockEvents), 200) MockPeeked mockPeeked = new MockPeeked(
} mockEvents: createMockEventReports(mockEvents)
)
private static String createMockRemovedResponse(List<MockEvent> mockEvents) { createResponse(ex, mockPeeked, 200)
StringWriter sw = new StringWriter()
MarkupBuilder builder = new MarkupBuilder(sw)
builder.mockRemoved {
mockEventsToXml(mockEvents, builder)
}
return sw.toString()
}
private static String createMockPeekedResponse(List<MockEvent> mockEvents) {
StringWriter sw = new StringWriter()
MarkupBuilder builder = new MarkupBuilder(sw)
builder.mockPeeked {
mockEventsToXml(mockEvents, builder)
}
return sw.toString()
}
private static void mockEventsToXml(List<MockEvent> 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
}
}
}
} }
private static void createErrorResponse(HttpExchange ex, Exception e) { private static void createErrorResponse(HttpExchange ex, Exception e) {
StringWriter sw = new StringWriter() createResponse(ex, new ExceptionOccured(message: e.message), 400)
MarkupBuilder builder = new MarkupBuilder(sw)
builder.exceptionOccured e.message
createResponse(ex, sw.toString(), 400)
} }
void stop() { void stop() {

View file

@ -1,14 +1,32 @@
package pl.touk.mockserver.server package pl.touk.mockserver.server
import com.sun.net.httpserver.HttpExchange import com.sun.net.httpserver.HttpExchange
import pl.touk.mockserver.api.response.MockAdded
import javax.xml.bind.JAXBContext
class Util { 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) ex.sendResponseHeaders(statusCode, responseBytes.length ?: -1)
if (response) { if (responseString) {
ex.responseBody << responseBytes ex.responseBody << responseBytes
ex.responseBody.close() ex.responseBody.close()
} }
} }
private static String marshall(Object response) {
StringWriter sw = new StringWriter()
responseJaxbContext.createMarshaller().marshal(response, sw)
return sw.toString()
}
} }

12
pom.xml
View file

@ -10,6 +10,7 @@
<module>mockserver-client</module> <module>mockserver-client</module>
<module>mockserver</module> <module>mockserver</module>
<module>mockserver-tests</module> <module>mockserver-tests</module>
<module>mockserver-api</module>
</modules> </modules>
<properties> <properties>
@ -22,6 +23,7 @@
<commons-lang3.version>3.3.2</commons-lang3.version> <commons-lang3.version>3.3.2</commons-lang3.version>
<slf4j-api.version>1.7.7</slf4j-api.version> <slf4j-api.version>1.7.7</slf4j-api.version>
<logback-classic.version>1.0.13</logback-classic.version> <logback-classic.version>1.0.13</logback-classic.version>
<lombok.version>1.16.6</lombok.version>
</properties> </properties>
<scm> <scm>
@ -63,6 +65,16 @@
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
<version>${logback-classic.version}</version> <version>${logback-classic.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>pl.touk.mockserver</groupId>
<artifactId>mockserver-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>