From fda5b6ca5c2ca84d04b0a5ad8b718bb9f539d639 Mon Sep 17 00:00:00 2001 From: Dominik Adam Przybysz Date: Sat, 13 Dec 2014 18:56:07 +0100 Subject: [PATCH] Refactort server and client --- .../mockserver/server/ContextExecutor.groovy | 44 +++++----- .../mockserver/server/HttpMockServer.groovy | 82 +++++++++---------- .../mockserver/server/HttpServerWraper.groovy | 17 ++-- .../pl/touk/mockserver/server/Main.groovy | 4 +- .../pl/touk/mockserver/server/Mock.groovy | 62 +++++++++++++- .../{Request.groovy => MockRequest.groovy} | 24 +++--- .../mockserver/server/MockResponse.groovy | 16 ++++ .../InvalidMockDefinitionException.groovy | 7 +- .../client/MockAlreadyExists.groovy | 7 +- .../mockserver/client/MockDoesNotExist.groovy | 7 +- .../client/RemoveMockRequestData.groovy | 5 ++ .../pl/touk/mockserver/client/Util.groovy | 7 +- .../server/MockServerIntegrationTest.groovy | 2 +- 13 files changed, 188 insertions(+), 96 deletions(-) rename src/main/groovy/pl/touk/mockserver/server/{Request.groovy => MockRequest.groovy} (80%) create mode 100644 src/main/groovy/pl/touk/mockserver/server/MockResponse.groovy diff --git a/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy b/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy index 61ea501..1297ae4 100644 --- a/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy +++ b/src/main/groovy/pl/touk/mockserver/server/ContextExecutor.groovy @@ -1,38 +1,29 @@ package pl.touk.mockserver.server import com.sun.net.httpserver.HttpExchange -import groovy.util.slurpersupport.GPathResult +import groovy.transform.PackageScope import java.util.concurrent.CopyOnWriteArrayList +@PackageScope class ContextExecutor { private final HttpServerWraper httpServerWraper final String path private final List mocks - ContextExecutor(HttpServerWraper httpServerWraper, String path, Mock initialMock) { + ContextExecutor(HttpServerWraper httpServerWraper, Mock initialMock) { this.httpServerWraper = httpServerWraper - this.path = path + this.path = initialMock.path this.mocks = new CopyOnWriteArrayList<>([initialMock]) httpServerWraper.createContext(path, { HttpExchange ex -> - Request request = new Request(ex.requestBody.text, ex.requestHeaders, ex.requestURI.query) + MockRequest request = new MockRequest(ex.requestBody.text, ex.requestHeaders, ex.requestURI.query) println "Mock received input" for (Mock mock : mocks) { try { - if (ex.requestMethod == mock.method && - mock.predicate(request)) { - println "Mock ${mock.name} invoked" - ++mock.counter - String response = mock.responseOk(request) - mock.responseHeaders(request).each { - ex.responseHeaders.add(it.key as String, it.value as String) - } - ex.sendResponseHeaders(mock.statusCode, response ? 0 : -1) - if (response) { - ex.responseBody << (mock.soap ? wrapSoap(response) : response) - ex.responseBody.close() - } + if (mock.match(ex.requestMethod, request)) { + MockResponse httpResponse = mock.apply(request) + fillExchange(ex, httpResponse) return } } catch (Exception e) { @@ -45,6 +36,18 @@ class ContextExecutor { }) } + private static void fillExchange(HttpExchange httpExchange, MockResponse response) { + response.headers.each { + httpExchange.responseHeaders.add(it.key, it.value) + } + String responseText = response.response + httpExchange.sendResponseHeaders(response.statusCode, responseText ? responseText.length() : -1) + if (responseText) { + httpExchange.responseBody << responseText + httpExchange.responseBody.close() + } + } + int removeMock(String name) { Mock mock = mocks.find { it.name == name } if (mock) { @@ -57,11 +60,4 @@ class ContextExecutor { void addMock(Mock mock) { mocks << mock } - - private static String wrapSoap(String request) { - """ - - ${request} - """ - } } diff --git a/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy b/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy index ca53bb0..bb95396 100644 --- a/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy +++ b/src/main/groovy/pl/touk/mockserver/server/HttpMockServer.groovy @@ -1,32 +1,34 @@ package pl.touk.mockserver.server import com.sun.net.httpserver.HttpExchange +import groovy.transform.PackageScope import groovy.util.slurpersupport.GPathResult import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArraySet +@PackageScope class HttpMockServer { private final HttpServerWraper httpServerWraper private final List childServers = new CopyOnWriteArrayList<>() private final Set mockNames = new CopyOnWriteArraySet<>() - HttpMockServer(int port = 9999){ - httpServerWraper= new HttpServerWraper(port) + HttpMockServer(int port = 9999) { + httpServerWraper = new HttpServerWraper(port) httpServerWraper.createContext('/serverControl', { HttpExchange ex -> - try{ + try { GPathResult request = new XmlSlurper().parse(ex.requestBody) - if(ex.requestMethod== 'POST' && request.name() == 'addMock'){ + if (ex.requestMethod == 'POST' && request.name() == 'addMock') { addMock(request, ex) - }else if(ex.requestMethod == 'POST' && request.name() == 'removeMock'){ + } else if (ex.requestMethod == 'POST' && request.name() == 'removeMock') { removeMock(request, ex) } //TODO add get mock report //TODO add list mock - }catch(Exception e){ + } catch (Exception e) { createErrorResponse(ex, e) } }) @@ -37,53 +39,45 @@ class HttpMockServer { if (name in mockNames) { throw new RuntimeException('mock already registered') } - println "Adding $name" - String mockPath = request.path - int mockPort = Integer.valueOf(request.port as String) - Mock mock = new Mock(name, mockPath, mockPort) - String predicate = request.predicate - if(predicate){ - mock.predicate = Eval.me(predicate) as Closure - } - String okResponse = request.response - if(okResponse){ - mock.responseOk = Eval.me(okResponse) as Closure - } - String soap = request.soap - if(soap){ - mock.soap = Boolean.valueOf(soap) - } - String statusCode = request.statusCode - if(statusCode){ - mock.statusCode = Integer.valueOf(statusCode) - } - String method = request.method - if(method){ - mock.method = method - } - String responseHeaders = request.responseHeaders - if(responseHeaders){ - mock.responseHeaders = Eval.me(responseHeaders) as Closure - } - HttpServerWraper child = childServers.find { it.port == mockPort } - if (!child) { - child = new HttpServerWraper(mockPort) - childServers << child - } - child.addMock(mockPath, mock) + Mock mock = mockFromRequest(request) + HttpServerWraper child = getOrCreateChildServer(mock.port) + child.addMock(mock) mockNames << name ex.sendResponseHeaders(200, 0) ex.responseBody << '' ex.responseBody.close() } + private static Mock mockFromRequest(GPathResult request) { + String name = request.name + String mockPath = request.path + int mockPort = Integer.valueOf(request.port as String) + Mock mock = new Mock(name, mockPath, mockPort) + mock.predicate = request.predicate + mock.response = request.response + mock.soap = request.soap + mock.statusCode = request.statusCode + mock.method = request.method + mock.responseHeaders = request.responseHeaders + return mock + } + + private HttpServerWraper getOrCreateChildServer(int mockPort) { + HttpServerWraper child = childServers.find { it.port == mockPort } + if (!child) { + child = new HttpServerWraper(mockPort) + childServers << child + } + return child + } + private void removeMock(GPathResult request, HttpExchange ex) { String name = request.name - if (! (name in mockNames)) { + if (!(name in mockNames)) { throw new RuntimeException('mock not registered') } println "Removing $name" - int used = childServers.inject(0) { int res, HttpServerWraper server-> server.removeMock(name) + res} + int used = childServers.inject(0) { int res, HttpServerWraper server -> server.removeMock(name) + res } mockNames.remove(name) ex.sendResponseHeaders(200, 0) ex.responseBody << "$used" @@ -96,8 +90,8 @@ class HttpMockServer { ex.responseBody.close() } - void stop(){ - childServers.each {it.stop()} + void stop() { + childServers.each { it.stop() } httpServerWraper.stop() } } diff --git a/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy b/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy index 058fef0..684825e 100644 --- a/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy +++ b/src/main/groovy/pl/touk/mockserver/server/HttpServerWraper.groovy @@ -2,12 +2,14 @@ package pl.touk.mockserver.server import com.sun.net.httpserver.HttpHandler import com.sun.net.httpserver.HttpServer +import groovy.transform.PackageScope import java.util.concurrent.Executors +@PackageScope class HttpServerWraper { private final HttpServer httpServer - private final int port + final int port private List executors = [] @@ -16,22 +18,23 @@ class HttpServerWraper { InetSocketAddress addr = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port) httpServer = HttpServer.create(addr, 0) httpServer.executor = Executors.newCachedThreadPool() - println "Http server statrting on port $port..." + println("Http server statrting on port $port...") httpServer.start() - println 'Http server is started' + println('Http server is started') } void createContext(String context, HttpHandler handler) { httpServer.createContext(context, handler) } - void addMock(String path, Mock mock) { - ContextExecutor executor = executors.find { it.path == path } + void addMock(Mock mock) { + ContextExecutor executor = executors.find { it.path == mock.path } if (executor) { executor.addMock(mock) } else { - executors << new ContextExecutor(this, path, mock) + executors << new ContextExecutor(this, mock) } + println "Added mock ${mock.name}" } void stop() { @@ -40,6 +43,6 @@ class HttpServerWraper { } int removeMock(String name) { - executors.inject(0) { int res, ContextExecutor e -> e.removeMock(name) + res} + executors.inject(0) { int res, ContextExecutor e -> e.removeMock(name) + res } } } diff --git a/src/main/groovy/pl/touk/mockserver/server/Main.groovy b/src/main/groovy/pl/touk/mockserver/server/Main.groovy index a1b4a1a..0923ba9 100644 --- a/src/main/groovy/pl/touk/mockserver/server/Main.groovy +++ b/src/main/groovy/pl/touk/mockserver/server/Main.groovy @@ -1,7 +1,7 @@ package pl.touk.mockserver.server class Main { - static void main(String [] args) { + static void main(String[] args) { HttpMockServer httpMockServer = new HttpMockServer() Runtime.runtime.addShutdownHook(new Thread({ @@ -10,7 +10,7 @@ class Main { println 'Http server is stopped' } as Runnable)) - while(true){ + while (true) { Thread.sleep(10000) } } diff --git a/src/main/groovy/pl/touk/mockserver/server/Mock.groovy b/src/main/groovy/pl/touk/mockserver/server/Mock.groovy index 4b66cb6..e86097b 100644 --- a/src/main/groovy/pl/touk/mockserver/server/Mock.groovy +++ b/src/main/groovy/pl/touk/mockserver/server/Mock.groovy @@ -1,18 +1,20 @@ package pl.touk.mockserver.server import groovy.transform.EqualsAndHashCode +import groovy.transform.PackageScope +@PackageScope @EqualsAndHashCode(excludes = ["counter"]) class Mock { final String name final String path final int port Closure predicate = { _ -> true } - Closure responseOk = { _ -> '' } + Closure response = { _ -> '' } + Closure responseHeaders = { _ -> [:] } boolean soap = false int statusCode = 200 String method = 'POST' - Closure responseHeaders = {_ -> [:]} int counter = 0 Mock(String name, String path, int port) { @@ -20,4 +22,60 @@ class Mock { this.path = path this.port = port } + + boolean match(String method, MockRequest request) { + return this.method == method && predicate(request) + } + + MockResponse apply(MockRequest request) { + println "Mock $name invoked" + ++counter + String responseText = response(request) + String response = soap ? wrapSoap(responseText) : responseText + Map headers = responseHeaders(request) + return new MockResponse(statusCode, response, headers) + } + + private static String wrapSoap(String request) { + """ + + ${request} + """ + } + + void setPredicate(String predicate){ + if (predicate) { + this.predicate = Eval.me(predicate) as Closure + } + } + + void setResponse(String response){ + if (response) { + this.response = Eval.me(response) as Closure + } + } + + void setSoap(String soap){ + if (soap) { + this.soap = Boolean.valueOf(soap) + } + } + + void setStatusCode(String statusCode){ + if (statusCode) { + this.statusCode = Integer.valueOf(statusCode) + } + } + + void setMethod(String method){ + if(method){ + this.method = method + } + } + + void setResponseHeaders(String responseHeaders){ + if (responseHeaders) { + this.responseHeaders = Eval.me(responseHeaders) as Closure + } + } } diff --git a/src/main/groovy/pl/touk/mockserver/server/Request.groovy b/src/main/groovy/pl/touk/mockserver/server/MockRequest.groovy similarity index 80% rename from src/main/groovy/pl/touk/mockserver/server/Request.groovy rename to src/main/groovy/pl/touk/mockserver/server/MockRequest.groovy index a5e954c..56cdfbd 100644 --- a/src/main/groovy/pl/touk/mockserver/server/Request.groovy +++ b/src/main/groovy/pl/touk/mockserver/server/MockRequest.groovy @@ -2,41 +2,43 @@ package pl.touk.mockserver.server import com.sun.net.httpserver.Headers import groovy.json.JsonSlurper +import groovy.transform.PackageScope import groovy.util.slurpersupport.GPathResult -class Request { +@PackageScope +class MockRequest { final String text - final Map headers - final Map query + final Map headers + final Map query final GPathResult xml final GPathResult soap final Object json - Request(String text, Headers headers, String query) { + MockRequest(String text, Headers headers, String query) { this.text = text this.headers = headersToMap(headers) this.query = queryParamsToMap(query) this.xml = inputToXml(text) this.soap = inputToSoap(xml) - this.json= inputToJson(text) + this.json = inputToJson(text) } private static GPathResult inputToXml(String text) { - try{ + try { return new XmlSlurper().parseText(text) - }catch (Exception _){ + } catch (Exception _) { return null } } private static GPathResult inputToSoap(GPathResult xml) { - try{ + try { if (xml.name() == 'Envelope' && xml.Body.size() > 0) { return getSoapBodyContent(xml) } else { return null } - }catch (Exception _){ + } catch (Exception _) { return null } } @@ -46,9 +48,9 @@ class Request { } private static Object inputToJson(String text) { - try{ + try { return new JsonSlurper().parseText(text) - }catch (Exception _){ + } catch (Exception _) { return null } } diff --git a/src/main/groovy/pl/touk/mockserver/server/MockResponse.groovy b/src/main/groovy/pl/touk/mockserver/server/MockResponse.groovy new file mode 100644 index 0000000..e8e2825 --- /dev/null +++ b/src/main/groovy/pl/touk/mockserver/server/MockResponse.groovy @@ -0,0 +1,16 @@ +package pl.touk.mockserver.server + +import groovy.transform.PackageScope + +@PackageScope +class MockResponse { + final int statusCode + final String response + final Map headers + + MockResponse(int statusCode, String response, Map headers) { + this.statusCode = statusCode + this.response = response + this.headers = headers + } +} diff --git a/src/test/groovy/pl/touk/mockserver/client/InvalidMockDefinitionException.groovy b/src/test/groovy/pl/touk/mockserver/client/InvalidMockDefinitionException.groovy index 96156a1..769a435 100644 --- a/src/test/groovy/pl/touk/mockserver/client/InvalidMockDefinitionException.groovy +++ b/src/test/groovy/pl/touk/mockserver/client/InvalidMockDefinitionException.groovy @@ -1,6 +1,11 @@ package pl.touk.mockserver.client -class InvalidMockDefinitionException extends RuntimeException{ +import groovy.transform.CompileStatic +import groovy.transform.TypeChecked + +@CompileStatic +@TypeChecked +class InvalidMockDefinitionException extends RuntimeException { InvalidMockDefinitionException(String s) { super(s) } diff --git a/src/test/groovy/pl/touk/mockserver/client/MockAlreadyExists.groovy b/src/test/groovy/pl/touk/mockserver/client/MockAlreadyExists.groovy index 6fce531..f0c00a9 100644 --- a/src/test/groovy/pl/touk/mockserver/client/MockAlreadyExists.groovy +++ b/src/test/groovy/pl/touk/mockserver/client/MockAlreadyExists.groovy @@ -1,3 +1,8 @@ package pl.touk.mockserver.client -class MockAlreadyExists extends RuntimeException{} \ No newline at end of file +import groovy.transform.CompileStatic +import groovy.transform.TypeChecked + +@CompileStatic +@TypeChecked +class MockAlreadyExists extends RuntimeException {} \ No newline at end of file diff --git a/src/test/groovy/pl/touk/mockserver/client/MockDoesNotExist.groovy b/src/test/groovy/pl/touk/mockserver/client/MockDoesNotExist.groovy index 881eb2c..02d0bee 100644 --- a/src/test/groovy/pl/touk/mockserver/client/MockDoesNotExist.groovy +++ b/src/test/groovy/pl/touk/mockserver/client/MockDoesNotExist.groovy @@ -1,4 +1,9 @@ package pl.touk.mockserver.client -class MockDoesNotExist extends RuntimeException{ +import groovy.transform.CompileStatic +import groovy.transform.TypeChecked + +@CompileStatic +@TypeChecked +class MockDoesNotExist extends RuntimeException { } diff --git a/src/test/groovy/pl/touk/mockserver/client/RemoveMockRequestData.groovy b/src/test/groovy/pl/touk/mockserver/client/RemoveMockRequestData.groovy index 58c936a..e2c15c9 100644 --- a/src/test/groovy/pl/touk/mockserver/client/RemoveMockRequestData.groovy +++ b/src/test/groovy/pl/touk/mockserver/client/RemoveMockRequestData.groovy @@ -1,5 +1,10 @@ package pl.touk.mockserver.client +import groovy.transform.CompileStatic +import groovy.transform.TypeChecked + +@CompileStatic +@TypeChecked class RemoveMockRequestData { String name } diff --git a/src/test/groovy/pl/touk/mockserver/client/Util.groovy b/src/test/groovy/pl/touk/mockserver/client/Util.groovy index ce9c939..1194202 100644 --- a/src/test/groovy/pl/touk/mockserver/client/Util.groovy +++ b/src/test/groovy/pl/touk/mockserver/client/Util.groovy @@ -1,14 +1,17 @@ package pl.touk.mockserver.client import groovy.json.JsonSlurper -import groovy.transform.PackageScope +import groovy.transform.CompileStatic +import groovy.transform.TypeChecked import groovy.util.slurpersupport.GPathResult import org.apache.http.HttpEntity import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.util.EntityUtils +@CompileStatic +@TypeChecked class Util { - static GPathResult extractXmlResponse(CloseableHttpResponse response){ + static GPathResult extractXmlResponse(CloseableHttpResponse response) { HttpEntity entity = response.entity GPathResult xml = new XmlSlurper().parseText(EntityUtils.toString(entity)) EntityUtils.consumeQuietly(entity) diff --git a/src/test/groovy/pl/touk/mockserver/server/MockServerIntegrationTest.groovy b/src/test/groovy/pl/touk/mockserver/server/MockServerIntegrationTest.groovy index f7e2753..c6ebfac 100644 --- a/src/test/groovy/pl/touk/mockserver/server/MockServerIntegrationTest.groovy +++ b/src/test/groovy/pl/touk/mockserver/server/MockServerIntegrationTest.groovy @@ -334,8 +334,8 @@ class MockServerIntegrationTest extends Specification { when: CloseableHttpResponse response = client.execute(restHead) then: - response.statusLine.statusCode == 200 EntityUtils.consumeQuietly(response.entity) + response.statusLine.statusCode == 200 } def "should dispatch rest mock with options method"() {