[#5] WIP on GroovyDSL to Wiremock conversion

This commit is contained in:
Jakub Kubrynski
2015-01-29 14:14:16 +01:00
committed by Marcin Grzejszczak
parent c314286911
commit f03ef21f66
8 changed files with 572 additions and 5 deletions

View File

@@ -0,0 +1,12 @@
package io.coderate.accurest
/**
* @author Jakub Kubrynski
*/
class AccurestException extends RuntimeException {
def AccurestException(String message) {
super(message)
}
}

View File

@@ -32,7 +32,7 @@ class TestGenerator {
this.targetDirectory = accurestConfigProperties.generatedTestSourcesDir
File stubsResource = new File(accurestConfigProperties.stubsBaseDirectory)
if (stubsResource == null) {
throw new IllegalStateException("Stubs directory not found under " + accurestConfigProperties.stubsBaseDirectory)
throw new AccurestException("Stubs directory not found under " + accurestConfigProperties.stubsBaseDirectory)
}
this.stubsBaseDirectory = stubsResource.path
}

View File

@@ -0,0 +1,23 @@
package io.coderate.accurest.dsl
import groovy.transform.TypeChecked
@TypeChecked
class GroovyDsl {
Request request
static GroovyDsl make(Closure closure) {
GroovyDsl dsl = new GroovyDsl()
closure.delegate = dsl
closure()
return dsl
}
void request(@DelegatesTo(Request) Closure closure) {
Request request = new Request()
this.request = request
closure.delegate = request
closure()
}
}

View File

@@ -0,0 +1,172 @@
package io.coderate.accurest.dsl
import groovy.transform.TypeChecked
@TypeChecked
class Request {
String method
String url
StringCustomizableProperty urlPattern
String urlPath
Headers headers
void method(String method) {
this.method = method
}
void url(String url) {
this.url = url
}
void urlPattern(@DelegatesTo(StringCustomizableProperty) Closure closure) {
StringCustomizableProperty urlPattern = new StringCustomizableProperty()
this.urlPattern = urlPattern
delegateToClosure(closure, urlPattern)
}
void headers(@DelegatesTo(Headers) Closure closure) {
Headers headers = new Headers()
this.headers = headers
delegateToClosure(closure, headers)
}
void urlPath(String urlPath) {
this.urlPath = urlPath
}
//TODO: Can we have different types for Client/Server (client: pattern(String), server: value(Boolean/Int)) ?
class CustomizableProperty<T, V> {
private T client
private V server
void client(T client) {
this.client = client
}
void server(V server) {
this.server = server
}
T toClientSide() {
return client
}
V toServerSide() {
return server
}
}
private class StringCustomizableProperty extends CustomizableProperty<String, String> {
@Override
String toClientSide() {
return super.toClientSide() as String
}
@Override
String toServerSide() {
return super.toServerSide() as String
}
}
private class NoOpCustomizableProperty<T> extends CustomizableProperty<T, T> {
NoOpCustomizableProperty(T value) {
client(value)
server(value)
}
@Override
public String toString() {
return toClientSide()
}
}
class Headers {
private Map<String, WithValuePattern> headers = [:]
WithValuePattern header(String headerName) {
WithValuePattern withValuePattern = new WithValuePattern()
headers[headerName] = withValuePattern
return withValuePattern
}
Set<Map.Entry<String, WithValuePattern>> entries() {
return headers.entrySet()
}
}
/**
*
* From Wiremock
private Map<String, ValuePattern> queryParamPatterns;
private List<ValuePattern> bodyPatterns;
private String equalToJson;
private String equalToXml;
private String matchesXPath;
private JSONCompareMode jsonCompareMode;
private String equalTo;
private String contains;
private String matches;
private String doesNotMatch;
private Boolean absent;
private String matchesJsonPath;
*/
class WithValuePattern {
NoOpCustomizableProperty<String> equalToJson
NoOpCustomizableProperty<String> equalToXml
StringCustomizableProperty matchesXPath
NoOpCustomizableProperty<JSONCompareMode> jsonCompareMode
NoOpCustomizableProperty<String> equalTo
StringCustomizableProperty contains
StringCustomizableProperty matches
StringCustomizableProperty doesNotMatch
CustomizableProperty<Boolean, Boolean> absent
StringCustomizableProperty matchesJsonPath
// void equalToJson(String equalToJson) {
// this.equalToJson = equalToJson
// }
//
void equalTo(String equalTo) {
this.equalTo = new NoOpCustomizableProperty(equalTo)
}
// void equalToXml(String equalToXml) {
// this.equalToXml = equalToXml
// }
void matches(@DelegatesTo(StringCustomizableProperty) Closure closure) {
StringCustomizableProperty placeholderHaving = new StringCustomizableProperty()
this.matches = placeholderHaving
delegateToClosure(closure, placeholderHaving)
}
void doesNotMatch(@DelegatesTo(StringCustomizableProperty) Closure closure) {
StringCustomizableProperty placeholderHaving = new StringCustomizableProperty()
this.doesNotMatch = placeholderHaving
delegateToClosure(closure, placeholderHaving)
}
void contains(@DelegatesTo(StringCustomizableProperty) Closure closure) {
StringCustomizableProperty placeholderHaving = new StringCustomizableProperty()
this.contains = placeholderHaving
delegateToClosure(closure, placeholderHaving)
}
// void jsonCompareMode(JSONCompareMode jsonCompareMode) {
// this.jsonCompareMode = jsonCompareMode
// }
}
enum JSONCompareMode {
STRICT, LENIENT, NON_EXTENSIBLE, STRICT_ORDER;
}
private static <T> void delegateToClosure(@DelegatesTo(T) Closure closure, T delegate) {
closure.delegate = delegate
closure()
}
}

View File

@@ -0,0 +1,86 @@
package io.coderate.accurest.dsl
import groovy.json.JsonOutput
import groovy.transform.CompileStatic
@CompileStatic
class WiremockStubStrategy {
private final Request request
WiremockStubStrategy(GroovyDsl groovyDsl) {
this.request = groovyDsl.request
}
String toWiremockClientStub() {
return JsonOutput.toJson(buildClientRequest(request))
}
String toWiremockServerStub() {
return JsonOutput.toJson(buildServerRequest(request))
}
private Map buildClientRequest(Request request) {
return getRequestSection(request,
{ request.urlPattern?.toClientSide() },
{ buildClientHeadersSection(request.headers) })
}
private Map buildServerRequest(Request request) {
return getRequestSection(request,
{ request.urlPattern?.toServerSide() },
{ buildServerHeadersSection(request.headers) })
}
private Map<String, Map<String, Object>> getRequestSection(Request request, Closure<String> buildUrlPattern, Closure<Map> buildHeaders) {
return [request: [method : request.method,
url : request.url,
urlPattern: buildUrlPattern(),
urlPath : request.urlPath,
headers : buildHeaders()].findAll { it.value }]
}
private Map buildClientHeadersSection(Request.Headers headers) {
return createHeadersSection(headers) {
Map.Entry<String, Request.WithValuePattern> entry -> [(entry.key): buildClientHeaderFromValuePattern(entry.value)]
}
}
private Map buildServerHeadersSection(Request.Headers headers) {
return createHeadersSection(headers) {
Map.Entry<String, Request.WithValuePattern> entry -> [(entry.key): buildServerHeaderFromValuePattern(entry.value)]
}
}
private Map createHeadersSection(Request.Headers headers, Closure closure) {
return headers?.entries()?.collectEntries(closure)
}
private Map buildClientHeaderFromValuePattern(Request.WithValuePattern valuePattern) {
return getValuePatternSection(valuePattern)
.findAll { it.value }
.collectEntries { [(it.key): it.value.toClientSide()] }
}
private Map buildServerHeaderFromValuePattern(Request.WithValuePattern valuePattern) {
return getValuePatternSection(valuePattern)
.findAll { it.value }
.collectEntries { [(it.key): it.value.toServerSide()] }
}
private Map<String, Request.CustomizableProperty> getValuePatternSection(Request.WithValuePattern valuePattern) {
return [equalToJson : valuePattern.equalToJson,
equalToXml : valuePattern.equalToXml,
matchesXPath : valuePattern.matchesXPath,
jsonCompareMode: valuePattern.jsonCompareMode,
equalTo : valuePattern.equalTo,
contains : valuePattern.contains,
matches : valuePattern.matches,
doesNotMatch : valuePattern.doesNotMatch,
absent : valuePattern.absent,
matchesJsonPath: valuePattern.matchesJsonPath]
}
}

View File

@@ -0,0 +1,270 @@
package io.codearte.accurest.dsl
import groovy.json.JsonSlurper
import io.coderate.accurest.dsl.GroovyDsl
import io.coderate.accurest.dsl.WiremockStubStrategy
import spock.lang.Ignore
import spock.lang.Specification
class WiremockGroovyDslSpec extends Specification {
// TODO: add alias instead of placeholder
@Ignore
def 'should convert groovy dsl stub to wiremock stub'() {
given:
GroovyDsl dsl = GroovyDsl.make {
request {
method('GET')
urlPattern {
client('/[0-9]{2}')
server('/12')
}
}
response {
status(200)
body {
withPlaceholder(client: '2015-01-14', server: '$anyInt($it)')
withTemplate('''
{
"date" : "$placeholder0"
}
''')
}
headers {
Content-Type('text/plain')
}
}
}
expect:
dsl.toWiremockClientStub() == '''
{
"request": {
"method": "GET",
"urlPattern": "/[0-9]{2}"
},
"response": {
"status": 200,
"body": "2015-01-14",
"headers": {
"Content-Type": "text/plain"
}
}
}
'''
}
def "should generate stub with GET"() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method("GET")
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request":{
"method":"GET"
}
}
''')
}
def "should generate request when two elements are provided "() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method("GET")
url("/sth")
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request":{
"method":"GET",
"url":"/sth"
}
}
''')
}
def "should generate request with urlPattern for client side"() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
urlPattern {
client('/^[0-9]{2}$')
}
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request":{
"urlPattern":"/^[0-9]{2}$"
}
}
''')
}
def "should generate stub with urlPattern for server side"() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
urlPattern {
client('/^[0-9]{2}$')
server('/12')
}
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockServerStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request":{
"urlPattern":"/12"
}
}
''')
}
def "should generate stub with urlPath for client side"() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
urlPath ('/12')
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request":{
"urlPath":"/12"
}
}
''')
}
def "should generate stub with urlPath for server side"() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
urlPath ('/12')
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockServerStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request":{
"urlPath":"/12"
}
}
''')
}
def "should generate stub with some headers section for client side"() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
headers {
header('Content-Type').equalTo('text/xml')
header('Accept').matches {
client('text/.*')
server('text/plain')
}
header('etag').doesNotMatch {
client('abcd.*')
server('abcdef')
}
header('X-Custom-Header').contains {
client('2134')
server('121345')
}
}
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request":{
"headers": {
"Content-Type": {
"equalTo": "text/xml"
},
"Accept": {
"matches": "text/.*"
},
"etag": {
"doesNotMatch": "abcd.*"
},
"X-Custom-Header": {
"contains": "2134"
}
}
}
}
''')
}
def "should generate stub with some headers section for server side"() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
headers {
header('Content-Type').equalTo('text/xml')
header('Accept').matches {
client('text/.*')
server('text/plain')
}
header('etag').doesNotMatch {
client('abcd.*')
server('abcdef')
}
header('X-Custom-Header').contains {
client('2134')
server('121345')
}
}
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockServerStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request":{
"headers": {
"Content-Type": {
"equalTo": "text/xml"
},
"Accept": {
"matches": "text/plain"
},
"etag": {
"doesNotMatch": "abcdef"
},
"X-Custom-Header": {
"contains": "121345"
}
}
}
}
''')
}
}

View File

@@ -1,5 +1,6 @@
package io.codearte.accurest.plugin
import io.coderate.accurest.AccurestException
import io.coderate.accurest.TestGenerator
import io.coderate.accurest.config.AccurestConfigProperties
import org.gradle.api.Plugin
@@ -24,19 +25,20 @@ class AccurestGradlePlugin implements Plugin<Project> {
project.logger.info("Accurest Plugin: Invoking test sources generation")
extension.stubsBaseDirectory = project.projectDir.path + File.separator + extension.stubsBaseDirectory
extension.generatedTestSourcesDir = buildGeneratedSourcesDir(project, extension)
project.sourceSets.test.groovy {
project.logger.info("Registering $extension.generatedTestSourcesDir as test source directory")
srcDir extension.generatedTestSourcesDir
}
extension.generatedTestSourcesDir = buildGeneratedSourcesDir(project, extension)
try {
TestGenerator generator = new TestGenerator(extension)
int generatedClasses = generator.generate()
project.logger.info("Generated {} test classes", generatedClasses)
} catch (IllegalStateException e) {
project.logger.error("Accurest Plugin: {}", e.getMessage())
} catch (AccurestException e) {
project.logger.error("Accurest Plugin exception: {}", e.getMessage())
}
}

View File

@@ -24,7 +24,9 @@ subprojects {
dependencies {
compile localGroovy()
testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
testCompile('org.spockframework:spock-core:0.7-groovy-2.0') {
exclude(group: 'org.codehaus.groovy')
}
}
publishing {