All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.greenmoonsoftware.tea.Tea.groovy Maven / Gradle / Ivy

Go to download

A Groovy framework for functional testing rest services. Intended to help you relax while you REST.

The newest version!
package com.greenmoonsoftware.tea

import groovy.json.JsonBuilder
import groovy.json.JsonException
import groovy.json.JsonOutput
import groovy.transform.stc.ClosureParams
import groovy.transform.stc.SimpleType
import groovyx.net.http.ContentEncoding
import groovyx.net.http.HttpResponseDecorator
import groovyx.net.http.HttpResponseException
import groovyx.net.http.RESTClient

class Tea {
    def host
    def params
    private Map action
    private List asserts = []
    private Closure verifyResponseClosure
    private Closure verifyHeadersClosure
    private Closure configureClientClosure
    private Map headers = [:]
    private log = false
    private brewed = false
    private Map customParsers = [:].withDefault { return { } }
    private List recorders = []
    private boolean gzip
    private proxy = [:]

    Tea(String host, Map params = [:]) {
        this.host = host
        this.params = params
    }

    def configureClient(RESTClient rest) {
        if (gzip) { rest.contentEncoding = ContentEncoding.Type.GZIP }
        if (proxy) { rest.setProxy(proxy.host, proxy.port, proxy.scheme) }
        def restParams = rest.client.getParams()
        this.params.each { key, value -> restParams.setParameter(key, value) }
        rest.client.setParams(restParams)
        if (configureClientClosure) {
            configureClientClosure(rest)
        }
    }

    Result brew() {
        rejectIfReused()
        gatherHostAndUri()

        RESTClient rest = new GreenTeaRestClient(this.host)
        configureClient(rest)
        registerCustomParsers(rest)

        applyHeaders(rest)
        HttpResponseDecorator response = executeHttp(rest)
        record(rest, response)

        evaluateAsserts(response)
        evaluateHeaders(response)
        evaluateResponse(response)
        new Result(condition: (asserts.size() == 0) ? Result.Condition.WARN : Result.Condition.SUCCESS)
    }

    def record(GreenTeaRestClient rest, HttpResponseDecorator response) {
        if (recorders) {
            def request = rest.client.request
            def reqHeaders = [:]
            request.allHeaders.each { reqHeaders[it.name] = it.value }
            def respHeaders = [:]

            response.headers.each {
                if (!respHeaders.containsKey(it.name)) {
                    respHeaders.put(it.name, [])
                }
                respHeaders[it.name] << it.value
            }

            def data = new HttpMetaData(
                host: host
                , uri: action.params.path
                , method: action.method.toUpperCase()
                , responseStatus: response.status
                , requestHeaders: reqHeaders
                , queryParameters: action.params.query
                , requestBody: action.params.body
                , responseHeaders: respHeaders
                , responseBody: response.data
            )
            recorders.each {
                it(data)
            }
        }
    }

    private evaluateResponse(HttpResponseDecorator response) {
        if (verifyResponseClosure) {
            verifyResponseClosure(response.data)
        }
    }

    private evaluateHeaders(HttpResponseDecorator response) {
        if (verifyHeadersClosure) {
            verifyHeadersClosure(response.headers)
        }
    }

    private evaluateAsserts(HttpResponseDecorator response) {
        asserts.each { a ->
            a.eval(response)
        }
    }

    private printLog(HttpMetaData metaData) {
        def lines = []
        lines << "Request URL: ${metaData.host}${metaData.uri}"
        lines << "Request Method: ${metaData.method.toUpperCase()}"
        lines << "Status Code: ${metaData.responseStatus}"
        lines << "Request Headers"
        metaData.requestHeaders.each { k, v -> lines << "\t${k}: ${v}" }
        if (action.params.query) {
            lines << "Request Query Params"
            lines << "\t" + new JsonBuilder(action.params.query)
        }
        if (action.params.body) {
            lines << "Request Body"
            lines << "\t" + new JsonBuilder(action.params.body)
        }

        lines << "Response Headers"
        metaData.responseHeaders.each { k, v ->
            v.each {
                lines << "\t${k}: ${it}"
            }
        }

        def responseContentType = JsonRecorder.extractContentType(metaData.responseHeaders)
        def responseBody = JsonRecorder.encodeBody(responseContentType, metaData.responseBody)
        try {
            lines << JsonOutput.prettyPrint(responseBody)
        }
        catch (JsonException e) {
            lines << responseBody
        }

        println lines.join("\n")
    }

    private HttpResponseDecorator executeHttp(RESTClient rest) {
        HttpResponseDecorator response
        try {
            response = rest."${action.method}"(action.params.clone())
            //copy map since RESTClient messes with the provided map
        }
        catch (HttpResponseException ex) {
            response = ex.response
        }
        response
    }

    private applyHeaders(RESTClient rest) {
        headers.each { k, v ->
            rest.headers."${k}" = v
        }
    }

    private registerCustomParsers(RESTClient rest) {
        customParsers.each { k, v ->
            rest.parser."${k}" = v(rest)
        }
    }

    private gatherHostAndUri() {
        def (host, uri) = parseForHost(action.params.path)
        if (host != null) {
            this.host = host
        }
        action.params.path = uri
    }

    private rejectIfReused() {
        if (brewed) {
            throw new RuntimeException("This tea is old. You cannot brew the same instance more than once.")
        }
        brewed = true
    }

    private parseForHost(String url) {
        def host = null
        def uri = url
        if (url.indexOf('http') == 0) {
            def protocalPlus = url.split('://')
            def protocol = protocalPlus[0]
            def hostname = protocalPlus[1].substring(0, protocalPlus[1].indexOf('/'))
            host = protocol + "://" + hostname
            uri = protocalPlus[1].substring(protocalPlus[1].indexOf('/'))
        }
        return [host, uri]
    }

    Tea get(String url, Map query = null, String requestContentType = 'application/json') {
        if (url.contains("?")) {
            throw new IllegalArgumentException('URL cannot have query params. Please pass as a map as the second param to \'get\'.')
        }
        action = [method: "get", params: [path: url, query: query, requestContentType: requestContentType]]
        return this;
    }

    Tea post(String url, Object json = null, String requestContentType = 'application/json') {
        action = [method: "post", params: [path: url, body: json, requestContentType: requestContentType]]
        return this
    }

    Tea put(String url, Object json = null, String requestContentType = 'application/json') {
        action = [method: "put", params: [path: url, body: json, requestContentType: requestContentType]]
        return this
    }

    Tea patch(String url, Object json = null, String requestContentType = 'application/json') {
        action = [method: "patch", params: [path: url, body: json, requestContentType: requestContentType]]
        return this
    }

    Tea delete(String url, Map query = null) {
        action = [method: "delete", params: [path: url, query: query]]
        return this
    }

    Tea expectStatus(int code) {
        asserts.add([eval: { response ->
            assert response.status == code
        }])
        return this
    }

    Tea verifyResponse(Closure c) {
        verifyResponseClosure = c
        return this
    }

    Tea verifyHeaders(@ClosureParams(value = SimpleType, options = ["groovyx.net.http.HeadersDecorator"]) Closure c) {
        verifyHeadersClosure = c
        return this
    }

    Tea userAgent(String ua) {
        addHeader("User-Agent", ua)
        return this
    }

    Tea addHeader(String header, String value) {
        headers[header] = value
        return this
    }

    Tea basicAuth(String username, String password) {
        def auth = "Basic " + "${username}:${password}".getBytes().encodeBase64().toString()
        addHeader("Authorization", auth)
        return this
    }

    Tea log() {
        recorders << this.&printLog
        return this
    }

    Tea withParser(String contentType, @ClosureParams(value = SimpleType, options = ["groovyx.net.http.RESTClient"]) Closure createParser) {
        customParsers[contentType] = createParser
        return this
    }

    Tea withRecorder(@ClosureParams(value = SimpleType, options = ["com.greenmoonsoftware.tea.HttpMetaData"]) Closure r) {
        recorders << r
        return this
    }

    Tea gzip() {
        addHeader('Accept-Encoding', 'gzip')
        gzip = true
        return this
    }

    Tea proxy(String host, int port, String scheme = 'http') {
        this.proxy = [host: host, port: port, scheme: scheme]
        return this
    }

    Tea configureClient(@ClosureParams(value = SimpleType, options = ["groovyx.net.http.RESTClient"]) Closure c) {
        configureClientClosure = c
        return this
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy