[#5] WIP on GroovyDSL to Wiremock conversion
This commit is contained in:
committed by
Marcin Grzejszczak
parent
c314286911
commit
f03ef21f66
@@ -0,0 +1,12 @@
|
||||
package io.coderate.accurest
|
||||
|
||||
/**
|
||||
* @author Jakub Kubrynski
|
||||
*/
|
||||
class AccurestException extends RuntimeException {
|
||||
|
||||
def AccurestException(String message) {
|
||||
super(message)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
''')
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user