diff --git a/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy b/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy index d7cf14a..c2bd629 100644 --- a/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy +++ b/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy @@ -49,13 +49,13 @@ class ContextExecutor { Util.createResponse(httpExchange, response.text, response.statusCode) } - int removeMock(String name) { + List removeMock(String name) { Mock mock = mocks.find { it.name == name } if (mock) { mocks.remove(mock) - return mock.counter + return mock.history } - return 0 + return [] } void addMock(Mock mock) { diff --git a/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy b/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy index c8da178..9238e57 100644 --- a/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy +++ b/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy @@ -1,7 +1,6 @@ package pl.touk.mockserver.server import com.sun.net.httpserver.HttpExchange -import groovy.transform.PackageScope import groovy.util.slurpersupport.GPathResult import groovy.xml.MarkupBuilder @@ -10,7 +9,6 @@ import java.util.concurrent.CopyOnWriteArraySet import static pl.touk.mockserver.server.Util.createResponse -@PackageScope class HttpMockServer { private final HttpServerWraper httpServerWraper @@ -104,11 +102,43 @@ class HttpMockServer { throw new RuntimeException('mock not registered') } println "Removing $name" - int used = childServers.inject(0) { int res, HttpServerWraper server -> server.removeMock(name) + res } + List mockEvents = childServers.collect { it.removeMock(name) }.flatten() mockNames.remove(name) StringWriter sw = new StringWriter() MarkupBuilder builder = new MarkupBuilder(sw) - builder.mockRemoved used + builder.mockRemoved { + mockEvents.each { MockEvent m -> + builder.mockEvent { + builder.request { + text m.request.text + headers { + m.request.headers.each { + builder.param(name: it.key, it.value) + } + } + query { + m.request.query.each { + builder.param(name: it.key, it.value) + } + } + path { + m.request.path.each { + builder.elem it + } + } + } + builder.response { + text m.response.text + headers { + m.response.headers.each { + builder.param(name: it.key, it.value) + } + } + statusCode m.response.statusCode + } + } + } + } createResponse(ex, sw.toString(), 200) } diff --git a/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy b/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy index 36ef984..d8847ca 100644 --- a/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy +++ b/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy @@ -42,11 +42,11 @@ class HttpServerWraper { httpServer.stop(0) } - int removeMock(String name) { - return executors.inject(0) { int res, ContextExecutor e -> e.removeMock(name) + res } + List removeMock(String name) { + return executors.collect { it.removeMock(name) }.flatten() } - List getMocks(){ - return executors.collect {it.mocks}.flatten() + List getMocks() { + return executors.collect { it.mocks }.flatten() } } diff --git a/src/main/groovy/pl/touk/mockserver/server/Mock.groovy b/src/main/groovy/pl/touk/mockserver/server/Mock.groovy index 9158560..33e9c3c 100644 --- a/src/main/groovy/pl/touk/mockserver/server/Mock.groovy +++ b/src/main/groovy/pl/touk/mockserver/server/Mock.groovy @@ -3,6 +3,8 @@ package pl.touk.mockserver.server import groovy.transform.EqualsAndHashCode import groovy.transform.PackageScope +import java.util.concurrent.CopyOnWriteArrayList + @PackageScope @EqualsAndHashCode(excludes = ["counter"]) class Mock implements Comparable { @@ -16,6 +18,7 @@ class Mock implements Comparable { int statusCode = 200 String method = 'POST' int counter = 0 + final List history = new CopyOnWriteArrayList<>() Mock(String name, String path, int port) { if (!(name)) { @@ -36,7 +39,9 @@ class Mock implements Comparable { String responseText = response(request) String response = soap ? wrapSoap(responseText) : responseText Map headers = responseHeaders(request) - return new MockResponse(statusCode, response, headers) + MockResponse mockResponse = new MockResponse(statusCode, response, headers) + history << new MockEvent(request, mockResponse) + return mockResponse } private static String wrapSoap(String request) { diff --git a/src/main/groovy/pl/touk/mockserver/server/MockEvent.groovy b/src/main/groovy/pl/touk/mockserver/server/MockEvent.groovy new file mode 100644 index 0000000..97a46a2 --- /dev/null +++ b/src/main/groovy/pl/touk/mockserver/server/MockEvent.groovy @@ -0,0 +1,14 @@ +package pl.touk.mockserver.server + +import groovy.transform.PackageScope + +@PackageScope +class MockEvent { + final MockRequest request + final MockResponse response + + MockEvent(MockRequest request, MockResponse response) { + this.request = request + this.response = response + } +} diff --git a/src/test/groovy/pl/touk/mockserver/client/ControlServerClient.groovy b/src/test/groovy/pl/touk/mockserver/client/ControlServerClient.groovy index 6fa5613..38f6c18 100644 --- a/src/test/groovy/pl/touk/mockserver/client/ControlServerClient.groovy +++ b/src/test/groovy/pl/touk/mockserver/client/ControlServerClient.groovy @@ -31,17 +31,32 @@ class ControlServerClient { } } - int removeMock(String name) { + List removeMock(String name) { HttpPost removeMockPost = new HttpPost(address) removeMockPost.entity = buildRemoveMockRequest(new RemoveMockRequestData(name: name)) CloseableHttpResponse response = client.execute(removeMockPost) GPathResult responseXml = Util.extractXmlResponse(response) if (responseXml.name() == 'mockRemoved') { - return responseXml.text() as int + return responseXml.'mockEvent'.collect { + new MockEvent(mockRequestFromXml(it.request), mockResponseFromXml(it.response)) + } } throw new MockDoesNotExist() } + 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 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 StringEntity buildRemoveMockRequest(RemoveMockRequestData data) { return new StringEntity("""\ @@ -70,8 +85,8 @@ class ControlServerClient { 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)} + if (xml.name() == 'mocks') { + return xml.mock.collect { new RegisteredMock(it.name.text(), it.path.text(), it.port.text() as int) } } return [] } diff --git a/src/test/groovy/pl/touk/mockserver/client/MockEvent.groovy b/src/test/groovy/pl/touk/mockserver/client/MockEvent.groovy new file mode 100644 index 0000000..8c61ab3 --- /dev/null +++ b/src/test/groovy/pl/touk/mockserver/client/MockEvent.groovy @@ -0,0 +1,18 @@ +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/src/test/groovy/pl/touk/mockserver/client/MockRequest.groovy b/src/test/groovy/pl/touk/mockserver/client/MockRequest.groovy new file mode 100644 index 0000000..c73c11d --- /dev/null +++ b/src/test/groovy/pl/touk/mockserver/client/MockRequest.groovy @@ -0,0 +1,22 @@ +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/src/test/groovy/pl/touk/mockserver/client/MockResponse.groovy b/src/test/groovy/pl/touk/mockserver/client/MockResponse.groovy new file mode 100644 index 0000000..07212fb --- /dev/null +++ b/src/test/groovy/pl/touk/mockserver/client/MockResponse.groovy @@ -0,0 +1,20 @@ +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/src/test/groovy/pl/touk/mockserver/server/MockServerIntegrationTest.groovy b/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy similarity index 86% rename from src/test/groovy/pl/touk/mockserver/server/MockServerIntegrationTest.groovy rename to src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy index f6fb5b2..bf4b3ae 100644 --- a/src/test/groovy/pl/touk/mockserver/server/MockServerIntegrationTest.groovy +++ b/src/test/groovy/pl/touk/mockserver/tests/MockServerIntegrationTest.groovy @@ -1,4 +1,4 @@ -package pl.touk.mockserver.server +package pl.touk.mockserver.tests import groovy.util.slurpersupport.GPathResult import org.apache.http.client.methods.* @@ -8,10 +8,10 @@ import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.HttpClients import org.apache.http.util.EntityUtils import pl.touk.mockserver.client.* +import pl.touk.mockserver.server.HttpMockServer import spock.lang.Shared import spock.lang.Specification import spock.lang.Unroll -import pl.touk.mockserver.client.Util class MockServerIntegrationTest extends Specification { @@ -49,7 +49,7 @@ class MockServerIntegrationTest extends Specification { GPathResult restPostResponse = Util.extractXmlResponse(response) restPostResponse.name() == 'goodResponseRest-request' expect: - controlServerClient.removeMock('testRest') == 1 + controlServerClient.removeMock('testRest')?.size() == 1 } def "should add soap mock on endpoint"() { @@ -71,10 +71,10 @@ class MockServerIntegrationTest extends Specification { soapPostResponse.name() == 'Envelope' soapPostResponse.Body.'goodResponseSoap-request'.size() == 1 expect: - controlServerClient.removeMock('testSoap') == 1 + controlServerClient.removeMock('testSoap')?.size() == 1 } - def "should not remove mock when it does not exist"() { + def "should throw exception when try to remove mock when it does not exist"() { when: controlServerClient.removeMock('testSoap') then: @@ -89,7 +89,7 @@ class MockServerIntegrationTest extends Specification { soap: true )) and: - controlServerClient.removeMock('testSoap') == 0 + controlServerClient.removeMock('testSoap') == [] when: controlServerClient.removeMock('testSoap') then: @@ -144,7 +144,7 @@ class MockServerIntegrationTest extends Specification { soap: true )) and: - controlServerClient.removeMock('testSoap') == 0 + controlServerClient.removeMock('testSoap') == [] and: controlServerClient.addMock(new AddMockRequestData( name: 'testSoap', @@ -637,5 +637,78 @@ class MockServerIntegrationTest extends Specification { restPostResponse.name == 'goodResponse-15' } - //TODO def "should get mock report"(){} + def "should get mock report when deleting mock"() { + expect: + controlServerClient.addMock(new AddMockRequestData( + name: 'testRest', + path: 'testEndpoint', + port: 9999, + predicate: '''{req -> req.xml.name()[0..6] == 'request' }''', + response: '''{req -> ""}''', + statusCode: 201, + responseHeaders: '''{req -> ['aaa':'14']}''', + soap: false + )) + controlServerClient.addMock(new AddMockRequestData( + name: 'testRest2', + path: 'testEndpoint', + port: 9999, + predicate: '''{req -> req.xml.name() == 'reqXYZ' }''', + response: '''{req -> ""}''', + statusCode: 202, + responseHeaders: '''{req -> ['aaa':'15']}''', + soap: false + )) + when: + HttpPost post1 = new HttpPost('http://localhost:9999/testEndpoint') + post1.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8")) + CloseableHttpResponse response1 = client.execute(post1) + then: + GPathResult restPostResponse1 = Util.extractXmlResponse(response1) + restPostResponse1.name() == 'goodResponseRest-request' + when: + HttpPost post2 = new HttpPost('http://localhost:9999/testEndpoint/hello') + post2.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8")) + CloseableHttpResponse response2 = client.execute(post2) + then: + GPathResult restPostResponse2 = Util.extractXmlResponse(response2) + restPostResponse2.name() == 'goodResponseRest-request15' + when: + HttpPost post3 = new HttpPost('http://localhost:9999/testEndpoint?id=123') + post3.entity = new StringEntity('', ContentType.create("text/xml", "UTF-8")) + CloseableHttpResponse response3 = client.execute(post3) + then: + GPathResult restPostResponse3 = Util.extractXmlResponse(response3) + restPostResponse3.name() == 'goodResponseRest' + when: + List mockHistories1 = controlServerClient.removeMock('testRest') + then: + mockHistories1.size() == 2 + mockHistories1[0].request.text == '' + !mockHistories1[0].request.headers?.keySet()?.empty + mockHistories1[0].request.query == [:] + mockHistories1[0].request.path == ['testEndpoint'] + !mockHistories1[0].response.headers?.keySet()?.empty + mockHistories1[0].response.text == '' + mockHistories1[0].response.statusCode == 201 + + mockHistories1[1].request.text == '' + !mockHistories1[1].request.headers?.keySet()?.empty + mockHistories1[1].request.query == [:] + mockHistories1[1].request.path == ['testEndpoint', 'hello'] + !mockHistories1[1].response.headers?.keySet()?.empty + mockHistories1[1].response.text == '' + mockHistories1[1].response.statusCode == 201 + when: + List mockHistories2 = controlServerClient.removeMock('testRest2') + then: + mockHistories2.size() == 1 + mockHistories2[0].request.text == '' + !mockHistories2[0].request.headers?.keySet()?.empty + mockHistories2[0].request.query == [id: '123'] + mockHistories2[0].request.path == ['testEndpoint'] + mockHistories2[0].response.headers.aaa == '15' + mockHistories2[0].response.text == '' + mockHistories2[0].response.statusCode == 202 + } } diff --git a/src/test/groovy/pl/touk/mockserver/server/ServerMockPT.groovy b/src/test/groovy/pl/touk/mockserver/tests/ServerMockPT.groovy similarity index 95% rename from src/test/groovy/pl/touk/mockserver/server/ServerMockPT.groovy rename to src/test/groovy/pl/touk/mockserver/tests/ServerMockPT.groovy index 36f6579..1fd932a 100644 --- a/src/test/groovy/pl/touk/mockserver/server/ServerMockPT.groovy +++ b/src/test/groovy/pl/touk/mockserver/tests/ServerMockPT.groovy @@ -1,4 +1,4 @@ -package pl.touk.mockserver.server +package pl.touk.mockserver.tests import groovy.util.slurpersupport.GPathResult import org.apache.http.client.HttpClient @@ -10,6 +10,7 @@ import org.apache.http.impl.client.HttpClients import pl.touk.mockserver.client.AddMockRequestData import pl.touk.mockserver.client.ControlServerClient import pl.touk.mockserver.client.Util +import pl.touk.mockserver.server.HttpMockServer import spock.lang.Specification class ServerMockPT extends Specification { @@ -38,7 +39,7 @@ class ServerMockPT extends Specification { 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") == 1 + assert controlServerClient.removeMock("testRest$current").size() == 1 }) } when: