Skip to content

Instantly share code, notes, and snippets.

@tomaslin
Last active December 21, 2015 03:48
Show Gist options
  • Save tomaslin/6244676 to your computer and use it in GitHub Desktop.
Save tomaslin/6244676 to your computer and use it in GitHub Desktop.
Ratpack tests from ratpack/ratpack Aug 15
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/block/BlockingSpec.groovy
********************************************************************************
class BlockingSpec extends RatpackGroovyDslSpec {
def "can perform blocking operations"() {
when:
def steps = []
app {
handlers {
get {
steps << "start"
exec blocking, { sleep 300; steps << "operation"; 2 }, { Integer result ->
steps << "then"
response.send result.toString()
}
steps << "end"
}
}
}
then:
text == "2"
steps == ["start", "end", "operation", "then"]
}
def "by default errors during blocking operations are forwarded to server error handler"() {
when:
app {
modules {
bind ServerErrorHandler, PrintingServerErrorHandler
}
handlers {
get {
exec blocking,
{ sleep 300; throw new Exception("!") }, // blocking
{ /* never called */ } // on success
}
}
}
then:
text.startsWith(new Exception("!").toString())
response.statusCode == 500
}
def "can use custom error handler"() {
when:
app {
handlers {
get {
exec blocking,
{ sleep 300; throw new Exception("!") }, // blocking
{ response.status(210).send("error: $it.message") }, // on error
{ /* never called */ } // on success
}
}
}
then:
text == "error: !"
response.statusCode == 210
}
def "errors in custom error handlers are forwarded to the server error handler"() {
when:
app {
modules {
bind ServerErrorHandler, PrintingServerErrorHandler
}
handlers {
get {
exec blocking,
{ throw new Exception("!") },
{ throw new Exception("!!", it) },
{ /* never called */ }
}
}
}
then:
text.startsWith(new Exception("!!", new Exception("!")).toString())
response.statusCode == 500
}
def "errors in success handlers are forwarded to the server error handler"() {
when:
app {
modules {
bind ServerErrorHandler, PrintingServerErrorHandler
}
handlers {
get {
exec blocking,
{ 1 },
{ throw new Exception("!") }, // not expecting this to be called
{ throw new Exception("success") }
}
}
}
then:
text.startsWith(new Exception("success").toString())
response.statusCode == 500
}
def "closure arg type mismatch errors on success handler are handled well"() {
when:
app {
modules {
bind ServerErrorHandler, PrintingServerErrorHandler
}
handlers {
get {
exec blocking,
{ 1 },
{ List<String> result -> response.send("unexpected") }
}
}
}
then:
text.startsWith "groovy.lang.MissingMethodException"
response.statusCode == 500
}
def "closure arg type mismatch errors on error handler are handled well"() {
when:
app {
modules {
bind ServerErrorHandler, PrintingServerErrorHandler
}
handlers {
get {
exec blocking,
{ throw new Exception("!") },
{ String string -> response.send("unexpected") },
{ response.send("unexpected - value") }
}
}
}
then:
text.startsWith "groovy.lang.MissingMethodException"
response.statusCode == 500
}
def "delegate in closure actions is no the arg"() {
when:
app {
handlers {
handler {
exec blocking, {
[foo: "bar"]
}, {
response.send it.toString()
}
}
}
}
then:
getText() == [foo: "bar"].toString()
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/file/StaticFileSpec.groovy
********************************************************************************
class StaticFileSpec extends RatpackGroovyDslSpec {
def "root listing disabled"() {
given:
file("public/static.text") << "hello!"
file("public/foo/static.text") << "hello!"
when:
app {
handlers {
assets("public")
}
}
then:
get("").statusCode == FORBIDDEN.code()
get("foo").statusCode == FORBIDDEN.code()
get("foos").statusCode == NOT_FOUND.code()
}
def "can serve static file"() {
given:
def file = file("public/static.text") << "hello!"
when:
app {
handlers {
assets("public")
}
}
then:
getText("static.text") == "hello!"
getText("static.text") == "hello!"
with(head("static.text")) {
statusCode == OK.code()
asByteArray().length == 0
getHeader("content-length") == file.length().toString()
}
}
def "can serve index files"() {
file("public/index.html") << "foo"
file("public/dir/index.xhtml") << "bar"
when:
app {
handlers {
assets("public", "index.html", "index.xhtml")
}
}
then:
getText() == "foo"
getText("dir") == "bar"
getText("dir/") == "bar"
}
def "can serve files with query strings"() {
given:
file("public/index.html") << "foo"
when:
app {
handlers {
assets("public", "index.html")
}
}
then:
getText("?abc") == "foo"
getText("index.html?abc") == "foo"
}
def "can map to multiple dirs"() {
given:
file("d1/f1.txt") << "1"
file("d2/f2.txt") << "2"
when:
app {
handlers {
prefix("a") {
assets("d1")
}
prefix("b") {
assets("d2")
}
}
}
then:
getText("a/f1.txt") == "1"
getText("b/f2.txt") == "2"
}
def "decodes URL paths correctly"() {
given:
file("d1/some other.txt") << "1"
file("d1/some+more.txt") << "2"
file("d1/path to/some+where/test.txt") << "3"
when:
app {
handlers {
assets("d1")
}
}
then:
getText("some%20other.txt") == "1"
getText("some+more.txt") == "2"
getText("path%20to/some+where/test.txt") == "3"
get("some+other.txt").statusCode == NOT_FOUND.code()
get("some%20more.txt").statusCode == NOT_FOUND.code()
}
def "can nest file system binding handlers"() {
given:
file("d1/d2/d3/dir/index.html") << "3"
when:
app {
handlers {
fileSystem("d1") {
fileSystem("d2") {
assets("d3", "index.html")
}
}
}
}
then:
getText("dir") == "3"
}
def "can bind to subpath"() {
given:
file("foo/file.txt") << "file"
when:
app {
handlers {
prefix("bar") {
assets("foo")
}
}
}
then:
get("foo/file.txt").statusCode == NOT_FOUND.code()
getText("bar/file.txt") == "file"
}
def "files report a last modified time"() {
given:
def file = file("public/file.txt") << "hello!"
and:
app {
handlers {
assets("public")
}
}
expect:
def response = get("file.txt")
response.statusCode == OK.code()
// compare the last modified dates formatted as milliseconds are stripped when added as a response header
formatDateHeader(parseDateHeader(response, LAST_MODIFIED)) == formatDateHeader(file.lastModified())
}
def "404 when posting to a non existent asset"() {
given:
file("public/file.txt") << "a"
when:
app {
handlers {
assets("public")
}
}
then:
this.get("file.txt").statusCode == OK.code()
this.post("file.txt").statusCode == METHOD_NOT_ALLOWED.code()
this.get("nothing").statusCode == NOT_FOUND.code()
this.post("nothing").statusCode == NOT_FOUND.code()
}
@Unroll
def "asset handler returns a #statusCode if file is #state the request's if-modified-since header"() {
given:
def file = file("public/file.txt") << "hello!"
and:
app {
handlers {
assets("public")
}
}
and:
request.header IF_MODIFIED_SINCE, formatDateHeader(file.lastModified() + ifModifiedSince)
expect:
def response = get("file.txt")
response.statusCode == statusCode.code()
response.getHeader(CONTENT_LENGTH).toInteger() == contentLength
where:
ifModifiedSince | statusCode | contentLength
-1000 | OK | "hello!".length()
+1000 | OK | "hello!".length()
0 | NOT_MODIFIED | 0
state = ifModifiedSince < 0 ? "newer than" : ifModifiedSince == 0 ? "the same age as" : "older than"
}
def "asset handler respect if-modified-since header when serving index files"() {
given:
def file = file("public/index.txt") << "hello!"
and:
app {
handlers {
assets("public", "index.txt")
}
}
and:
request.header IF_MODIFIED_SINCE, formatDateHeader(file.lastModified())
expect:
def response = get("")
response.statusCode == NOT_MODIFIED.code()
response.getHeader(CONTENT_LENGTH).toInteger() == 0
}
def "can serve large static file"() {
given:
def file = file("public/static.text") << RandomStringUtils.randomAscii(fileSize)
when:
app {
handlers {
assets("public")
}
}
then:
getText("static.text").bytes.length == file.length()
with(head("static.text")) {
statusCode == OK.code()
asByteArray().length == 0
getHeader("content-length") == file.length().toString()
}
where:
fileSize = 2131795 // taken from original bug report
}
private static Date parseDateHeader(Response response, String name) {
HttpHeaderDateFormat.get().parse(response.getHeader(name))
}
private static String formatDateHeader(long timestamp) {
formatDateHeader(new Date(timestamp))
}
private static String formatDateHeader(Date date) {
HttpHeaderDateFormat.get().format(date)
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/groovy/handling/ErrorHandlingSpec.groovy
********************************************************************************
class ErrorHandlingSpec extends RatpackGroovyDslSpec {
def "handles 404"() {
when:
app {}
then:
get().statusCode == 404
}
def "handles internal error"() {
when:
app {
handlers {
get { throw new RuntimeException('error here') }
}
}
then:
get().statusCode == 500
}
def "can handle errors on forked threads"() {
given:
def errorHandler = new ServerErrorHandler() {
void error(Context exchange, Exception exception) {
exchange.response.send("Caught: $exception.message")
}
}
when:
app {
modules {
bind ServerErrorHandler, errorHandler
}
handlers {
get {
withErrorHandling new Thread({
throw new Exception("thrown in forked thread")
})
}
}
}
then:
text == "Caught: thrown in forked thread"
}
def "can use service on forked threads"() {
given:
def errorHandler1 = new ServerErrorHandler() {
void error(Context exchange, Exception exception) {
exchange.response.send("1: $exception.message")
}
}
def errorHandler2 = new ServerErrorHandler() {
void error(Context exchange, Exception exception) {
exchange.response.send("2: $exception.message")
}
}
when:
app {
modules {
bind ServerErrorHandler, errorHandler1
}
handlers {
get { exchange ->
withErrorHandling new Thread({
insert(ServerErrorHandler, errorHandler2, Arrays.asList(new Handler() {
@Override
void handle(Context context) {
throw new Exception("down here")
}
}))
})
}
}
}
then:
text == "2: down here"
}
def "can segment error handlers"() {
given:
def errorHandler1 = new ServerErrorHandler() {
void error(Context exchange, Exception exception) {
exchange.response.send("1: $exception.message")
}
}
def errorHandler2 = new ServerErrorHandler() {
void error(Context exchange, Exception exception) {
exchange.response.send("2: $exception.message")
}
}
when:
app {
modules {
bind ServerErrorHandler, errorHandler1
}
handlers {
register(ServerErrorHandler, errorHandler2) {
get("a") {
throw new Exception("1")
}
}
get("b") {
throw new Exception("2")
}
}
}
then:
getText("a") == "2: 1"
getText("b") == "1: 2"
}
def "can use service handler"() {
given:
def errorHandler = new ServerErrorHandler() {
void error(Context exchange, Exception exception) {
exchange.response.send("Caught: $exception.message")
}
}
when:
app {
modules {
bind ServerErrorHandler, errorHandler
}
handlers {
get {
throw new Exception("thrown")
}
}
}
then:
text == "Caught: thrown"
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/handling/HandlersSpec.groovy
********************************************************************************
class HandlersSpec extends RatpackGroovyDslSpec {
def "empty chain handler"() {
when:
app {
handlers {
chain([])
}
}
then:
get().statusCode == 404
}
def "single chain handler"() {
when:
app {
handlers {
handler chain(
Handlers.get(asHandler { response.send("foo") })
)
}
}
then:
text == "foo"
}
def "multi chain handler"() {
when:
app {
handlers {
handler chain(
Handlers.get("a", asHandler { response.send("foo") }),
Handlers.get("b", asHandler { response.send("bar") })
)
}
}
then:
getText("a") == "foo"
getText("b") == "bar"
}
def "default services available"() {
when:
app {
handlers {
handler {
get(ServerErrorHandler)
get(ClientErrorHandler)
get(MimeTypes)
get(LaunchConfig)
get(FileSystemBinding)
response.send "ok"
}
}
}
then:
text == "ok"
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/handling/ServiceUsingHandlerSpec.groovy
********************************************************************************
class ServiceUsingHandlerSpec extends DefaultRatpackSpec {
static class NoHandleMethod extends ServiceUsingHandler {}
def "must have handle method"() {
when:
new NoHandleMethod()
then:
thrown ServiceUsingHandler.NoSuitableHandleMethodException
}
static class NoHandleMethodWithContextAsFirstParam extends ServiceUsingHandler {
void handle(String foo) {}
}
def "must have handle method with context as first param"() {
when:
new NoHandleMethodWithContextAsFirstParam()
then:
thrown ServiceUsingHandler.NoSuitableHandleMethodException
}
static class InjectedHandler extends ServiceUsingHandler {
@SuppressWarnings(["GrMethodMayBeStatic", "GroovyUnusedDeclaration"])
protected handle(Context exchange, FileSystemBinding fileSystemBinding) {
assert fileSystemBinding.is(exchange.get(FileSystemBinding))
exchange.response.send(fileSystemBinding.class.name)
}
}
def "can inject"() {
when:
app {
handlers {
handler new InjectedHandler()
}
}
then:
text == DefaultFileSystemBinding.class.name
}
static class Injected2Handler extends ServiceUsingHandler {
@SuppressWarnings(["GrMethodMayBeStatic", "GroovyUnusedDeclaration"])
protected handle(Context exchange, FileSystemBinding fileSystemBinding, ServerErrorHandler serverErrorHandler) {
assert fileSystemBinding.is(exchange.get(FileSystemBinding))
assert serverErrorHandler.is(exchange.get(ServerErrorHandler))
exchange.response.send(serverErrorHandler.class.name)
}
}
def "can inject more than one"() {
when:
app {
handlers {
handler new Injected2Handler()
}
}
then:
text == DefaultServerErrorHandler.class.name
}
static class InjectedBadHandler extends ServiceUsingHandler {
@SuppressWarnings(["GrMethodMayBeStatic", "GroovyUnusedDeclaration"])
protected handle(Context exchange, FileSystemBinding fileSystemBinding, Exception notInRegistry) {}
}
static class MessageServerErrorHandler implements ServerErrorHandler {
void error(Context exchange, Exception exception) {
exchange.response.status(500).send(exception.message)
}
}
def "error when cant inject"() {
when:
app {
handlers {
handler register(ServerErrorHandler, new MessageServerErrorHandler(), new Action<Chain>() {
void execute(Chain handlers) {
handlers.handler(new InjectedBadHandler())
}
})
}
}
then:
text =~ "No object for type 'java.lang.Exception' in registry"
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/http/ConcurrentRequestSpec.groovy
********************************************************************************
class ConcurrentRequestSpec extends RatpackGroovyDslSpec {
def "can serve requests concurrently without mixing up params"() {
when:
app {
handlers {
get(":id") {
response.send pathTokens.id + ":" + request.queryParams.id
}
}
}
and:
def threads = 500
def latch = new CountDownLatch(threads)
def results = []
threads.times {
results << null
}
startServerIfNeeded()
threads.times { i ->
Thread.start {
try {
def text = createRequest().get(toUrl("$i?id=$i")).asString()
assert text ==~ "\\d+:\\d+"
def (id, value) = text.split(':').collect { it.toInteger() }
results[id] = value
} finally {
latch.countDown()
}
}
}
latch.await(30, TimeUnit.SECONDS)
then:
(0..<threads).each {
assert results[it] == it
}
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/http/ContentNegotiationSpec.groovy
********************************************************************************
class ContentNegotiationSpec extends RatpackGroovyDslSpec {
def "can content negotiate"() {
when:
app {
handlers {
get {
respond byContent.
type("application/json") {
response.send "json"
}.
type("text/html") {
response.send "html"
}
}
get("noneRegistered") {
respond byContent
}
}
}
then:
request.header("Accept", "application/json;q=0.5,text/html;q=1")
text == "html"
response.header("Content-Type") == "text/html;charset=UTF-8"
response.statusCode == 200
then:
request.header("Accept", "application/json,text/html")
text == "json"
response.header("Content-Type") == "application/json"
response.statusCode == 200
then:
request.header("Accept", "*")
text == "json"
then:
request.header("Accept", "*/*")
text == "json"
then:
request.header("Accept", "text/*")
text == "html"
then:
request.header("Accept", "")
text == "json"
then:
request.header("Accept", "some/nonsense")
text == ""
response.statusCode == 406
then:
getText("noneRegistered") == ""
response.statusCode == 406
}
def "by accepts responder mime types"() {
setup:
"setting up the handler chain"
app {
handlers {
get {
respond byContent.
json { response.send "json" }.
xml { response.send "xml" }.
plainText { response.send "text" }.
html { response.send "html" }
}
}
}
when:
"testing json"
request.header("Accept", "application/json")
then:
"the json accept type should be invoked"
text == "json"
when:
"testing xml"
request.header("Accept", "application/xml")
then:
"the xml accept type should be invoked"
text == "xml"
when:
"testing plainText"
request.header("Accept", "text/plain")
then:
"the plain text accept type should be invoked"
text == "text"
when:
"testing html"
request.header("Accept", "text/html")
then:
"the plain text accept type should be invoked"
text == "html"
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/http/CookiesSpec.groovy
********************************************************************************
class CookiesSpec extends RatpackGroovyDslSpec {
def "can get and set cookies"() {
given:
app {
handlers {
get("get/:name") {
response.send request.oneCookie(pathTokens.name) ?: "null"
}
get("set/:name/:value") {
response.cookie(pathTokens.name, pathTokens.value)
response.send()
}
get("clear/:name") {
response.expireCookie(pathTokens.name)
response.send()
}
}
}
when:
getText("set/a/1")
then:
getText("get/a") == "1"
when:
getText("set/a/2")
getText("set/b/1")
then:
getText("get/a") == "2"
getText("get/b") == "1"
when:
getText("clear/a")
then:
getText("get/a") == "null"
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/http/DefaultMediaTypeSpec.groovy
********************************************************************************
class DefaultMediaTypeSpec extends Specification {
def "parsing"() {
expect:
ct(" application/json ").type == "application/json"
ct(" application/json ").params.isEmpty()
ct(null).type == null
ct(null).params.isEmpty()
ct(" ").type == null
ct(" ").params.isEmpty()
ct(" application/json;charset=foo ").params.charset == "foo"
ct(" application/json;charset ;foo=bar ").params == [charset: "", foo: "bar"]
}
def "tostring"() {
expect:
cts("") == ""
cts(" application/json ") == "application/json"
cts("application/json;foo=bar") == "application/json;foo=bar"
cts("application/json;foo") == "application/json;foo"
cts("application/json;a=1 ; b=2") == "application/json;a=1;b=2"
}
private MediaType ct(s) {
DefaultMediaType.get(s)
}
private String cts(s) {
ct(s).toString()
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/http/RequestBodyReadingSpec.groovy
********************************************************************************
class RequestBodyReadingSpec extends RatpackGroovyDslSpec {
def "can get request body as bytes"() {
when:
app {
handlers {
post {
response.send new String(request.bytes, "utf8")
}
}
}
then:
request.body("foo".getBytes("utf8"))
postText() == "foo"
}
def "can get request body as input stream"() {
when:
app {
handlers {
post {
response.send new String(request.inputStream.bytes, "utf8")
}
}
}
then:
request.body("foo".getBytes("utf8"))
postText() == "foo"
}
def "can get large request body as bytes"() {
given:
def string = "a" * 1024 * 9
when:
app {
handlers {
post {
response.send new String(request.bytes, "utf8")
}
}
}
then:
request.body(string.getBytes("utf8"))
postText() == string
}
def "get bytes on get request"() {
when:
app {
handlers {
handler {
response.send request.bytes.length.toString()
}
}
}
then:
getText() == "0"
postText() == "0"
putText() == "0"
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/http/RequestParametersSpec.groovy
********************************************************************************
class RequestParametersSpec extends RatpackGroovyDslSpec {
def "can get query params"() {
when:
app {
handlers {
get {
response.send request.queryParams.toString()
}
}
}
then:
getText() == "[:]" && resetRequest()
getText("?a=b") == "[a:[b]]" && resetRequest()
request {
queryParam "a", "b", "c"
queryParam "d", "e"
}
getText() == "[a:[b, c], d:[e]]" && resetRequest()
getText("?abc") == "[abc:[]]" && resetRequest()
}
def "can get form params"() {
when:
app {
handlers {
post {
response.send request.form.toString()
}
}
}
then:
postText() == "[:]" && resetRequest()
request {
param "a", "b"
}
postText() == "[a:[b]]" && resetRequest()
request {
param "a", "b", "c"
param "d", "e"
param "abc"
}
postText() == "[a:[b, c], d:[e], abc:[]]" && resetRequest()
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/http/SpecifiedContentTypeSpec.groovy
********************************************************************************
@Issue("https://github.com/ratpack/ratpack/issues/127")
class SpecifiedContentTypeSpec extends RatpackGroovyDslSpec {
def "content type can be specified by handler"() {
given:
app {
handlers {
get("path") {
response.contentType mimeType
response.send content
}
}
}
when:
get("path")
then:
with(response) {
statusCode == OK.code()
response.body.asString() == content
response.contentType == "$mimeType;charset=UTF-8"
}
where:
content | mimeType
"x,y,z" | "text/csv"
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/path/PathAndMethodRoutingSpec.groovy
********************************************************************************
class PathAndMethodRoutingSpec extends RatpackGroovyDslSpec {
def "can use path and method routes"() {
when:
app {
handlers {
get("a/b/c") {
response.headers.set("X-value", request.query)
response.send request.query
}
prefix(":a/:b") {
path(":c/:d") {
respond byMethod.
post {
response.send new LinkedHashMap(allPathTokens).toString()
}.
put {
response.send allPathTokens.collectEntries { [it.key.toUpperCase(), it.value.toUpperCase()] }.toString()
}
}
}
}
}
then:
getText("a/b/c?foo=baz") == "foo=baz"
resetRequest()
postText("1/2/3/4") == "[a:1, b:2, c:3, d:4]"
putText("5/6/7/8") == "[A:5, B:6, C:7, D:8]"
with(head("a/b/c?head")) {
statusCode == 200
getHeader("X-value") == "head"
asByteArray().length == 0
}
}
def "can use method chain"() {
when:
app {
handlers {
path("foo") {
def prefix = "common"
respond byMethod.
get {
response.send("$prefix: get")
}.
post {
response.send("$prefix: post")
}
}
}
}
then:
getText("foo") == "common: get"
postText("foo") == "common: post"
put("foo").statusCode == 405
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/path/PathParamsSpec.groovy
********************************************************************************
class PathParamsSpec extends RatpackGroovyDslSpec {
def "can parse url params"() {
when:
app {
handlers {
get(":a/:b/:c?") {
response.send new LinkedHashMap(pathTokens).toString()
}
}
}
then:
getText("1/2/3") == [a: 1, b: 2, c: 3].toString()
getText("1/2") == [a: 1, b: 2].toString()
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/path/PathRoutingSpec.groovy
********************************************************************************
class PathRoutingSpec extends RatpackGroovyDslSpec {
def "can route by prefix"() {
when:
app {
handlers {
prefix("abc") {
handler {
response.send(get(PathBinding).boundTo)
}
}
}
}
then:
getText("abc/def") == "abc"
getText("abc/ghi") == "abc"
getText("abc") == "abc"
get("ab/def").statusCode == NOT_FOUND.code()
}
def "can route by nested prefix"() {
when:
app {
handlers {
prefix("abc") {
prefix("def") {
handler {
response.send(get(PathBinding).pastBinding)
}
}
}
}
}
then:
getText("abc/def/ghi") == "ghi"
getText("abc/def/jkl") == "jkl"
getText("abc/def") == ""
get("abc/de/ghi").statusCode == NOT_FOUND.code()
}
def "can route by prefix with tokens"() {
when:
app {
handlers {
prefix(":a/:b/:c") {
handler {
def binding = get(PathBinding)
response.send("$binding.tokens - $binding.pastBinding")
}
}
}
}
then:
getText("1/2/3/4/5") == "[a:1, b:2, c:3] - 4/5"
}
def "can route by nested prefix with tokens"() {
when:
app {
handlers {
prefix(":a/:b") {
prefix(":d/:e") {
handler {
def binding = get(PathBinding)
response.send("$binding.tokens - $binding.allTokens - $binding.pastBinding")
}
}
}
}
}
then:
getText("1/2/3/4/5/6") == "[d:3, e:4] - [a:1, b:2, d:3, e:4] - 5/6"
}
def "can route by exact path"() {
when:
app {
handlers {
path("abc") {
response.send(get(PathBinding).boundTo)
}
}
}
then:
getText("abc") == "abc"
get("abc/def").statusCode == NOT_FOUND.code()
get("ab").statusCode == NOT_FOUND.code()
}
def "can route by nested exact path"() {
when:
app {
handlers {
prefix("abc") {
path("def") {
response.send(get(PathBinding).boundTo)
}
}
}
}
then:
getText("abc/def") == "def"
get("abc/def/ghi").statusCode == NOT_FOUND.code()
get("abc/de").statusCode == NOT_FOUND.code()
get("abc").statusCode == NOT_FOUND.code()
}
def "can route by exact path with tokens"() {
when:
app {
handlers {
path(":a/:b/:c") {
def binding = get(PathBinding)
response.send("$binding.tokens - $binding.pastBinding")
}
}
}
then:
get("1/2/3/4/5").statusCode == NOT_FOUND.code()
getText("1/2/3") == "[a:1, b:2, c:3] - "
}
def "can route by nested exact path with tokens"() {
when:
app {
handlers {
prefix(":a/:b") {
path(":d/:e") {
def binding = get(PathBinding)
response.send("$binding.tokens - $binding.allTokens - $binding.pastBinding")
}
}
}
}
then:
getText("1/2/3/4") == "[d:3, e:4] - [a:1, b:2, d:3, e:4] - "
get("1/2/3/4/5/6").statusCode == NOT_FOUND.code()
}
def "can use get handler"() {
when:
app {
handlers {
get {
response.send("root")
}
get("a") {
response.send("a")
}
}
}
then:
getText() == "root"
getText("a") == "a"
get("a/b/c").statusCode == NOT_FOUND.code()
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/registry/ServiceRegistryToStringSpec.groovy
********************************************************************************
class ServiceRegistryToStringSpec extends Specification {
def "registry to strings show chain"() {
when:
def root = new RootRegistry(ImmutableList.of(1))
def object1 = new ObjectHoldingChildRegistry(root, 2)
def object2 = new ObjectHoldingChildRegistry(object1, 3)
def lazy = new LazyChildRegistry(object2, Integer, { 4 } as org.ratpackframework.util.Factory<Integer>)
then:
lazy.toString() == "LazyChildRegistry{${Integer.name}} -> ObjectServiceRegistry{3} -> ObjectServiceRegistry{2} -> RootRegistry{[1]}"
}
}
********************************************************************************
ratpack/ratpack-core/src/test/groovy/org/ratpackframework/server/BindAddressInContextSpec.groovy
********************************************************************************
class BindAddressInContextSpec extends RatpackGroovyDslSpec {
def "bind address is available in context"() {
when:
app {
handlers {
get("port") { BindAddress bindAddress ->
response.send bindAddress.port.toString()
}
get("host") { BindAddress bindAddress ->
response.send bindAddress.host
}
}
}
then:
getText("port") == server.bindPort.toString()
getText("host") == server.bindHost
}
}
********************************************************************************
ratpack/ratpack-gradle/src/test/groovy/org/ratpackframework/gradle/functional/FunctionalSpec.groovy
********************************************************************************
abstract class FunctionalSpec extends Specification {
@Rule TemporaryFolder dir
static class ExecutedTask {
Task task
TaskState state
}
List<ExecutedTask> executedTasks = []
GradleLauncher launcher(String... args) {
StartParameter startParameter = GradleLauncher.createStartParameter(args)
startParameter.setProjectDir(dir.root)
GradleLauncher launcher = GradleLauncher.newInstance(startParameter)
executedTasks.clear()
launcher.addListener(new TaskExecutionListener() {
void beforeExecute(Task task) {
getExecutedTasks() << new ExecutedTask(task: task)
}
void afterExecute(Task task, TaskState taskState) {
getExecutedTasks().last().state = taskState
taskState.metaClass.upToDate = taskState.skipMessage == "UP-TO-DATE"
}
})
launcher
}
BuildResult run(String... args) {
def launcher = launcher(*args)
def result = launcher.run()
result.rethrowFailure()
result
}
File getBuildFile() {
makeFile("build.gradle")
}
File makeFile(String path) {
def f = file(path)
if (!f.exists()) {
def parts = path.split("/")
if (parts.size() > 1) {
dir.newFolder(*parts[0..-2])
}
dir.newFile(path)
}
f
}
File file(String path) {
def file = new File(dir.root, path)
assert file.parentFile.mkdirs() || file.parentFile.exists()
file
}
ExecutedTask task(String name) {
executedTasks.find { it.task.name == name }
}
def setup() {
file("settings.gradle") << "rootProject.name = 'test-app'"
buildFile << """
ext.RatpackGroovyPlugin = project.class.classLoader.loadClass('${RatpackGroovyPlugin.name}')
apply plugin: RatpackGroovyPlugin
archivesBaseName = "functional-test"
version = "1.0"
repositories {
maven { url "file://${localRepo.absolutePath}" }
jcenter()
}
"""
}
def unzip(File source, File destination) {
def project = ProjectBuilder.builder().withProjectDir(dir.root).build()
project.copy {
from project.zipTree(source)
into destination
}
}
File getWarFile() {
def f = file("build/libs/functional-test-1.0.war")
assert f.exists()
f
}
def unpackedWarFile(path) {
def unpacked = file("build/unpacked-war")
if (!unpacked.exists()) {
unzip(warFile, unpacked)
}
new File(unpacked, path)
}
File getLocalRepo() {
def rootRelative = new File("build/localrepo")
rootRelative.directory ? rootRelative : new File(new File(System.getProperty("user.dir")).parentFile, "build/localrepo")
}
}
********************************************************************************
ratpack/ratpack-gradle/src/test/groovy/org/ratpackframework/gradle/functional/InstallAppSpec.groovy
********************************************************************************
class InstallAppSpec extends FunctionalSpec {
def "everything goes in the right place"() {
given:
file("src/ratpack/ratpack.groovy") << """
import static org.ratpackframework.groovy.RatpackScript.ratpack
ratpack {
handlers {
get("") {
response.send "foo"
}
}
}
"""
when:
run "installApp"
def process = new ProcessBuilder().directory(file("build/install/test-app")).command("bin/test-app").start()
process.consumeProcessOutput(System.out, System.err)
then:
new PollingConditions().within(10) {
try {
urlText("") == "foo"
} catch (ConnectException ignore) {
false
}
}
cleanup:
process?.destroy()
process?.waitFor()
}
HttpURLConnection url(String path = "") {
new URL("http://localhost:5050/$path").openConnection() as HttpURLConnection
}
String urlText(String path = "") {
new URL("http://localhost:5050/$path").text
}
}
********************************************************************************
ratpack/ratpack-groovy/src/test/groovy/org/ratpackframework/groovy/ExampleRestSpec.groovy
********************************************************************************
class ExampleRestSpec extends RatpackGroovyScriptAppSpec {
def "get by id"() {
given:
script """
ratpack {
handlers {
get {
response.send "Yeah!"
}
}
}
"""
when:
get()
then:
response.statusCode == 200
response.body.asString() == "Yeah!"
}
def "can verify json"() {
given:
script """
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
ratpack {
handlers {
post {
if (!request.contentType.json) {
clientError(415)
return
}
respond byContent.
type("application/json") {
def json = new JsonSlurper().parseText(request.text)
def value = json.value
response.send "application/json", JsonOutput.toJson([value: value * 2])
}
}
}
}
"""
when:
request {
contentType "text/plain"
body "foo"
}
post()
then:
response.statusCode == 415
when:
request {
header "Accept", "application/json"
contentType "application/json"
body '{"value": 1}'
}
post()
then:
response.statusCode == 200
response.jsonPath().value == 2
}
def "can read request body"() {
given:
script """
ratpack {
handlers {
post {
response.send(request.text)
}
}
}
"""
when:
request {
body "foo"
}
post()
then:
response.statusCode == 200
response.body.asString() == "foo"
}
}
********************************************************************************
ratpack/ratpack-groovy/src/test/groovy/org/ratpackframework/groovy/StandaloneScriptSpec.groovy
********************************************************************************
class StandaloneScriptSpec extends RatpackGroovyScriptAppSpec {
class ScriptBackedService extends AbstractIdleService implements RatpackService {
RatpackServer server
@Override
protected void startUp() throws Exception {
def shell = new GroovyShell(getClass().classLoader)
def script = shell.parse(StandaloneScriptSpec.this.ratpackFile)
StandaloneScriptBacking.captureNext(Util.delegatingAction {
server = it
})
Thread.start {
script.run()
}
def stopAt = System.currentTimeMillis() + 10000
while (System.currentTimeMillis() < stopAt) {
if (server != null) {
break
}
sleep 100
}
if (!server) {
throw new IllegalStateException("Server did not start")
}
server.start()
}
@Override
protected void shutDown() throws Exception {
server?.stop()
}
@Override
int getBindPort() {
server.bindPort
}
@Override
String getBindHost() {
server.bindHost
}
}
@Override
File getRatpackFile() {
file("custom.groovy")
}
@Override
RatpackServer createServer() {
def service = new ScriptBackedService()
new ServiceBackedServer(service, LaunchConfigBuilder.baseDir(ratpackFile.parentFile).build(new GroovyScriptHandlerFactory()))
}
def "can execute plain script and reload"() {
when:
app {
script """
ratpack {
handlers {
get {
response.send "foo"
}
}
}
"""
}
then:
getText() == "foo"
when:
script """
ratpack {
handlers {
get {
response.send "bar"
}
}
}
"""
then:
getText() == "bar"
}
}
********************************************************************************
ratpack/ratpack-groovy/src/test/groovy/org/ratpackframework/groovy/handling/BasicGroovyDslSpec.groovy
********************************************************************************
class BasicGroovyDslSpec extends RatpackGroovyDslSpec {
def "can use special Groovy dsl"() {
given:
file("public/foo.txt") << "bar"
when:
app {
handlers {
get("a") {
response.send "a handler"
}
post("b") {
response.send "b handler"
}
prefix(":first") {
get(":second") {
response.send new LinkedHashMap<>(allPathTokens).toString()
}
path("c/:second") {
response.send new LinkedHashMap<>(allPathTokens).toString()
}
}
assets("public")
}
}
then:
getText("a") == "a handler"
get("b").statusCode == 405
postText("b") == "b handler"
getText("1/2") == "[first:1, second:2]"
getText("foo/c/bar") == "[first:foo, second:bar]"
getText("foo.txt") == "bar"
}
def "can use file method to access file contextually"() {
given:
file("foo/file.txt") << "foo"
file("bar/file.txt") << "bar"
when:
app {
handlers {
fileSystem("foo") {
get("foo") {
response.send file("file.txt").text
}
}
fileSystem("bar") {
get("bar") {
response.send file("file.txt").text
}
}
}
}
then:
getText("foo") == 'foo'
getText("bar") == 'bar'
}
def "can use method chain"() {
when:
app {
handlers {
path("foo") {
def prefix = "common"
respond byMethod.
get {
response.send("$prefix: get")
}.
post {
response.send("$prefix: post")
}
}
}
}
then:
getText("foo") == "common: get"
postText("foo") == "common: post"
put("foo").statusCode == 405
}
def "can inject services via closure params"() {
when:
app {
handlers {
get("p1") { FileSystemBinding fileSystemBinding ->
response.send fileSystemBinding.class.name
}
get("p2") { FileSystemBinding fileSystemBinding, ServerErrorHandler serverErrorHandler ->
response.send serverErrorHandler.class.name
}
}
}
then:
getText("p1") == DefaultFileSystemBinding.class.name
getText("p2") == DefaultServerErrorHandler.class.name
}
}
********************************************************************************
ratpack/ratpack-groovy/src/test/groovy/org/ratpackframework/groovy/handling/BasicGroovyScriptAppSpec.groovy
********************************************************************************
class BasicGroovyScriptAppSpec extends RatpackGroovyScriptAppSpec {
def "can use script app"() {
given:
compileStatic = true
when:
app {
script """
ratpack {
handlers {
get {
getResponse().send("foo")
}
}
}
"""
}
then:
getText() == "foo"
}
}
********************************************************************************
ratpack/ratpack-groovy/src/test/groovy/org/ratpackframework/groovy/handling/ChainSubclassParitySpec.groovy
********************************************************************************
class ChainSubclassParitySpec extends Specification {
def javaType = org.ratpackframework.handling.Chain
def groovyType = Chain
def "groovy chain subclass overrides universally overrides return type"() {
/*
We are testing here that the Groovy subclass overrides all of the Java methods
in order to specify that it returns the Groovy type
*/
given:
def javaChainReturningMethods = javaType.getDeclaredMethods().findAll { it.returnType.equals(javaType) }
expect:
javaChainReturningMethods.each {
try {
def override = groovyType.getDeclaredMethod(it.name, it.parameterTypes)
assert override.returnType == groovyType
} catch (NoSuchMethodException ignore) {
throw new AssertionError("Chain method $it is not overridden in groovy subclass")
}
}
}
def "groovy chain subclass has closure overloads for all handler methods"() {
given:
def javaChainHandlerAsLastArgMethods = javaType.declaredMethods.findAll {
it.parameterTypes.size() > 0 && it.parameterTypes.last() == Handler
}
expect:
javaChainHandlerAsLastArgMethods.each {
try {
def paramList = it.parameterTypes.toList()
paramList[paramList.size() - 1] = Closure
def override = groovyType.getDeclaredMethod(it.name, (Class[]) paramList.toArray())
assert override.returnType == Chain
assert override.returnType == groovyType
} catch (NoSuchMethodException ignore) {
throw new AssertionError("Chain method $it is not overridden in groovy subclass")
}
}
}
}
********************************************************************************
ratpack/ratpack-groovy/src/test/groovy/org/ratpackframework/groovy/handling/ClosureServiceParametersSpec.groovy
********************************************************************************
class ClosureServiceParametersSpec extends RatpackGroovyAppSpec {
static class Thing {}
static class ThingModule extends AbstractModule {
protected void configure() {
bind(Thing)
}
}
def "can have global services"() {
when:
file("templates/foo.html") << "bar"
app {
modules {
register(new ThingModule())
}
handlers { Thing thing ->
get {
response.send thing.class.name
}
}
}
then:
text == Thing.class.name
}
}
********************************************************************************
ratpack/ratpack-groovy/src/test/groovy/org/ratpackframework/groovy/handling/DefaultGroovyErrorHandlingSpec.groovy
********************************************************************************
class DefaultGroovyErrorHandlingSpec extends RatpackGroovyAppSpec {
def "error handler is registered"() {
given:
app {
handlers {
get {
throw new Exception("!")
}
}
}
when:
get()
then:
response.statusCode == 500
response.body.asString().contains "html"
}
def "error handler is registered next() is not called"() {
given:
app {
handlers {
prefix("foo") {
handler {
throw new Exception("!")
}
}
}
}
when:
get("foo")
then:
response.statusCode == 500
response.body.asString().contains "html"
}
def "404 page is used"() {
given:
app {
handlers {
get("foo") {
response.send("foo")
}
}
}
when:
get("bar")
then:
response.statusCode == 404
response.body.asString().contains "html"
}
}
********************************************************************************
ratpack/ratpack-groovy/src/test/groovy/org/ratpackframework/groovy/templating/TemplateRenderingSpec.groovy
********************************************************************************
class TemplateRenderingSpec extends RatpackGroovyDslSpec {
def setup() {
modules << new TemplatingModule()
}
def "can render template"() {
given:
file("templates/foo.html") << "a \${model.value} b <% 3.times { %> a <% } %>"
when:
app {
handlers {
get {
render groovyTemplate("foo.html", value: "bar")
}
}
}
then:
text == "a bar b a a a "
}
def "off thread errors are rendered"() {
given:
when:
app {
handlers {
get {
withErrorHandling Thread.start {
throw new Exception("nested!")
}
}
}
}
then:
text.contains "<title>java.lang.Exception</title>"
}
def "can render inner template"() {
given:
file("templates/outer.html") << "outer: \${model.value}, <% render 'inner.html', value: 'inner' %>"
file("templates/inner.html") << "inner: \${model.value}"
when:
app {
handlers {
get {
render groovyTemplate("outer.html", value: "outer")
}
}
}
then:
text == "outer: outer, inner: inner"
}
def "can render inner, inner template"() {
given:
file("templates/outer.html") << "outer: \${model.value}, <% render 'inner.html', value: 'inner' %>"
file("templates/inner.html") << "inner: \${model.value}, <% render 'innerInner.html', value: 1 %>, <% render 'innerInner.html', value: 2 %>, <% render 'innerInner.html', value: 1 %>"
file("templates/innerInner.html") << "innerInner: \${model.value}"
when:
app {
handlers {
get {
render groovyTemplate("outer.html", value: "outer")
}
}
}
then:
text == "outer: outer, inner: inner, innerInner: 1, innerInner: 2, innerInner: 1"
}
def "inner template exceptions"() {
given:
file("templates/outer.html") << "outer: \${model.value}, <% render 'inner.html', value: 'inner' %>"
file("templates/inner.html") << "inner: \${model.value}, <% render 'innerInner.html', value: 1 %>, <% render 'innerInner.html', value: 2 %>, <% render 'innerInner.html', value: 1 %>"
file("templates/innerInner.html") << "\${throw new Exception(model.value.toString())}"
when:
app {
handlers {
get {
render groovyTemplate("outer.html", value: "outer")
}
}
}
then:
text.contains('[innerInner.html] template execution failed')
}
def "nested templates inherit the outer model"() {
given:
file("templates/outer.html") << "outer: \${model.a}\${model.b}, <% render 'inner.html', b: 'B' %>"
file("templates/inner.html") << "inner: \${model.a}\${model.b}, <% render 'innerInner.html', a: 'A' %>"
file("templates/innerInner.html") << "innerInner: \${model.a}\${model.b}"
when:
app {
handlers {
get {
render groovyTemplate("outer.html", a: "a", b: "b")
}
}
}
then:
text == "outer: ab, inner: aB, innerInner: AB"
}
@Unroll
"can use render in output section - #template"() {
given:
file("templates/outer.html") << template
file("templates/foo.html") << "foo"
when:
app {
handlers {
get {
render groovyTemplate("outer.html")
}
}
}
then:
text == "foo"
where:
template << ["\${render 'foo.html'}", "<%= render 'foo.html' %>"]
}
@Unroll
"can use render in output section in nested - #template"() {
given:
file("templates/outer.html") << "<% render 'inner.html' %>"
file("templates/inner.html") << template
file("templates/foo.html") << "foo"
when:
app {
handlers {
get {
render groovyTemplate("outer.html")
}
}
}
then:
text == "foo"
where:
template << ["\${render 'foo.html'}", "<%= render 'foo.html' %>"]
}
def "compile error in inner template"() {
given:
file("templates/outer.html") << "outer: \${model.value}, <% render 'inner.html', value: 'inner' %>"
file("templates/inner.html") << "inner: \${model.value.toInteger()}"
when:
app {
modules {
get(TemplatingModule).staticallyCompile = true
}
handlers {
get {
render groovyTemplate("outer.html", value: "outer")
}
}
}
then:
text.contains "[inner.html] compilation failure"
}
def "can get model object via type"() {
given:
file("templates/template.html") << "value: \${model.get('value', String).toInteger()}"
when:
app {
modules {
get(TemplatingModule).staticallyCompile = true
}
handlers {
get {
render groovyTemplate("template.html", value: "2")
}
}
}
then:
text.contains "value: 2"
}
def "client errors are rendered with the template renderer"() {
when:
app {
handlers {
handler {
clientError(404)
}
}
}
then:
text.contains "<title>Not Found</title>"
get().statusCode == 404
}
def "templates are reloadable in reload mode"() {
given:
reloadable = true
file("templates/t") << "1"
when:
app {
handlers {
get { render groovyTemplate("t") }
}
}
then:
text == "1"
when:
sleep 1000
file("templates/t").text = "2"
then:
text == "2"
}
def "templates are not reloadable in reload mode"() {
given:
reloadable = false
file("templates/t") << "1"
when:
app {
handlers {
get { render groovyTemplate("t") }
}
}
then:
text == "1"
when:
file("templates/t").text = "2"
then:
text == "1"
}
def "templates are reloadable if reloading is forced"() {
given:
file("templates/t") << "1"
when:
app {
modules {
get(TemplatingModule).reloadable = true
}
handlers {
get { render groovyTemplate("t") }
}
}
then:
text == "1"
when:
sleep 1000
file("templates/t").text = "2"
then:
text == "2"
}
def "content type by template extension"() {
when:
file("templates/t.html") << "1"
file("templates/t.xml") << "1"
file("templates/dir/t.html") << "1"
file("templates/dir/t.xml") << "1"
file("templates/dir/t") << "1"
app {
handlers {
handler {
render groovyTemplate(request.path, request.queryParams.type)
}
}
}
then:
get("t.html").contentType == "text/html;charset=UTF-8"
get("t.xml").contentType == "application/xml"
get("dir/t.html").contentType == "text/html;charset=UTF-8"
get("dir/t.xml").contentType == "application/xml"
get("dir/t").contentType == "application/octet-stream"
get("t.xml?type=foo/bar").contentType == "foo/bar"
get("dir/t.xml?type=foo/bar").contentType == "foo/bar"
}
}
********************************************************************************
ratpack/ratpack-groovy-test/src/test/groovy/org/ratpackframework/groovy/test/remote/RemoteControlUsageSpec.groovy
********************************************************************************
class RemoteControlUsageSpec extends RatpackGroovyDslSpec {
void setup() {
other.put("remoteControl.enabled", "true")
}
@javax.inject.Singleton
static class ValueHolder {
String value = "initial"
}
def "can access application internals"() {
when:
app {
modules {
register new RemoteControlModule()
bind ValueHolder
}
}
handlers {
get { ValueHolder valueHolder ->
response.send valueHolder.value
}
}
and:
def remote = new RemoteControl(applicationUnderTest)
then:
text == "initial"
when:
remote.exec { registry.get(ValueHolder).value = "changed" }
then:
text == "changed"
}
}
********************************************************************************
ratpack/ratpack-groovy-test/src/test/groovy/org/ratpackframework/groovy/test/remote/RemoteControlUsageSpec.groovy
********************************************************************************
class RemoteControlUsageSpec extends RatpackGroovyDslSpec {
void setup() {
other.put("remoteControl.enabled", "true")
}
@javax.inject.Singleton
static class ValueHolder {
String value = "initial"
}
def "can access application internals"() {
when:
app {
modules {
register new RemoteControlModule()
bind ValueHolder
}
}
handlers {
get { ValueHolder valueHolder ->
response.send valueHolder.value
}
}
and:
def remote = new RemoteControl(applicationUnderTest)
then:
text == "initial"
when:
remote.exec { registry.get(ValueHolder).value = "changed" }
then:
text == "changed"
}
}
********************************************************************************
ratpack/ratpack-guice/src/test/groovy/org/ratpackframework/guice/GuiceBootstrappingSpec.groovy
********************************************************************************
class GuiceBootstrappingSpec extends RatpackGroovyDslSpec {
static class ServiceOne {
String name
}
static class ServiceTwo {
String name
}
@Override
Transformer<Module, Injector> createInjectorFactory() {
Injector parentInjector = createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(ServiceOne).toInstance(new ServiceOne(name: "parent"))
}
})
Guice.childInjectorFactory(parentInjector)
}
def "parent injector objects are available"() {
when:
app {
modules {
bind ServiceTwo, new ServiceTwo(name: "child")
}
handlers {
get { ServiceOne one, ServiceTwo two ->
response.send "$one.name:$two.name"
}
}
}
then:
text == "parent:child"
}
}
********************************************************************************
ratpack/ratpack-guice/src/test/groovy/org/ratpackframework/guice/InjectedHandlersSpec.groovy
********************************************************************************
class InjectedHandlersSpec extends RatpackGroovyDslSpec {
static class Injectable {
String name
}
static class InjectedHandler implements Handler {
Injectable injectable
@Inject
InjectedHandler(Injectable injectable) {
this.injectable = injectable
}
@Override
void handle(Context exchange) {
exchange.response.send(injectable.name)
}
}
def "can use injected handlers"() {
given:
def nameValue = "foo"
when:
app {
modules {
assert launchConfig != null
register(new AbstractModule() {
@Override
protected void configure() {
bind(Injectable).toInstance(new Injectable(name: nameValue))
}
})
}
handlers {
handler registry.get(InjectedHandler)
}
}
then:
text == nameValue
}
}
********************************************************************************
ratpack/ratpack-guice/src/test/groovy/org/ratpackframework/guice/ModuleRegistryBindingsSpec.groovy
********************************************************************************
class ModuleRegistryBindingsSpec extends RatpackGroovyDslSpec {
static interface Type1 {}
static class Type1Impl1 implements Type1 {}
static class Type1Impl2 implements Type1 {}
static interface Type2 {}
static class Type2Impl1 implements Type2 {}
static class Type2Impl2 implements Type2 {}
static class Type2Provider implements Provider<Type2> {
Type2 get() {
new Type2Impl2()
}
}
static class SomeType {}
def "can bind via module registry"() {
when:
app {
modules {
// direct bindings always override module bindings
bind SomeType
bind Type1, Type1Impl2
provider Type2, Type2Provider
// regardless of module registration order
register new AbstractModule() {
protected void configure() {
bind(Type1).to(Type1Impl1)
bind(Type2).to(Type2Impl1)
}
}
}
handlers {
get("classDirect") { SomeType someType ->
response.send someType.class.name
}
get("publicType") { Type1 someType ->
response.send someType.class.name
}
get("provider") { Type2 someType ->
response.send someType.class.name
}
}
}
then:
getText("classDirect") == SomeType.name
getText("publicType") == Type1Impl2.name
getText("provider") == Type2Impl2.name
}
}
********************************************************************************
ratpack/ratpack-guice/src/test/groovy/org/ratpackframework/guice/RendererBindingsSpec.groovy
********************************************************************************
class RendererBindingsSpec extends RatpackGroovyDslSpec {
static class IntRenderer extends ByTypeRenderer<Integer> {
public IntRenderer() {
super(Integer)
}
@Override
void render(Context context, Integer object) {
context.response.send("text/integer", object.toString())
}
}
static class StringRenderer extends ByTypeRenderer<String> {
public StringRenderer() {
super(String)
}
@Override
void render(Context context, String object) {
context.response.send("text/string", object.toString())
}
}
def "bound renderers are usable"() {
when:
app {
modules {
register new AbstractModule() {
protected void configure() {
bind(IntRenderer)
bind(StringRenderer)
bind(ServerErrorHandler).to(PrintingServerErrorHandler)
}
}
}
handlers {
get("int") {
render 1
}
get("string") {
render "abc"
}
get("none") {
render new LinkedList()
}
}
}
then:
with(get("int")) {
body.asString() == "1"
contentType == "text/integer;charset=UTF-8"
}
with(get("string")) {
body.asString() == "abc"
contentType == "text/string;charset=UTF-8"
}
with(get("none")) {
statusCode == 500
body.asString().contains(NoSuchRendererException.name)
}
}
}
********************************************************************************
ratpack/ratpack-handlebars/src/test/groovy/org/ratpackframework/handlebars/HandlebarsTemplateRenderingSpec.groovy
********************************************************************************
class HandlebarsTemplateRenderingSpec extends RatpackGroovyDslSpec {
@Unroll
void 'can render a handlebars template from #scenario'() {
given:
other = otherConfig
file(filePath) << '{{key}}'
when:
app {
modules {
register new HandlebarsModule(templatesPath: templatesPath)
}
handlers {
get {
render handlebarsTemplate('simple', key: 'it works!')
}
}
}
then:
text == 'it works!'
where:
scenario | templatesPath | filePath | otherConfig
'default path' | null | 'handlebars/simple.hbs' | [:]
'path set in module' | 'custom' | 'custom/simple.hbs' | [:]
'path set in config' | null | 'fromConfig/simple.hbs' | ['handlebars.templatesPath': "fromConfig"]
}
@Unroll
void 'can configure loader suffix via #scenario'() {
given:
other = otherConfig
file('handlebars/simple.hbs') << '{{this}}'
when:
app {
modules {
register new HandlebarsModule(templatesSuffix: templatesSuffix)
}
handlers {
get {
render handlebarsTemplate('simple.hbs', 'it works!')
}
}
}
then:
text == 'it works!'
where:
scenario | templatesSuffix | otherConfig
'module' | '' | [:]
'config' | null | ['handlebars.templatesSuffix': '']
}
void 'missing templates are handled'() {
given:
file('handlebars').mkdir()
app {
modules {
register new HandlebarsModule()
}
handlers {
get {
render handlebarsTemplate('simple', key: 'it works!')
}
}
}
when:
get()
then:
response.statusCode == INTERNAL_SERVER_ERROR.code()
}
void 'helpers can be registered'() {
given:
file('handlebars/helper.hbs') << '{{test}}'
when:
app {
modules {
register new HandlebarsModule()
bind TestHelper
}
handlers {
get {
render handlebarsTemplate('helper')
}
}
}
then:
text == 'from helper'
}
void 'content types are based on file type but can be overriden'() {
given:
file('handlebars/simple.hbs') << '{{this}}'
file('handlebars/simple.json.hbs') << '{{this}}'
file('handlebars/simple.html.hbs') << '{{this}}'
when:
app {
modules {
register new HandlebarsModule()
}
handlers {
handler {
render handlebarsTemplate(request.path, 'content types', request.queryParams.type)
}
}
}
then:
get("simple").contentType == "application/octet-stream"
get("simple.json").contentType == "application/json"
get("simple.html").contentType == "text/html;charset=UTF-8"
get("simple.html?type=application/octet-stream").contentType == "application/octet-stream"
}
}
class TestHelper implements NamedHelper {
String name = 'test'
CharSequence apply(Object context, Options options) throws IOException {
'from helper'
}
}
********************************************************************************
ratpack/ratpack-jackson/src/test/groovy/org/ratpackframework/jackson/JacksonRenderingSpec.groovy
********************************************************************************
class JacksonRenderingSpec extends RatpackGroovyDslSpec {
static class User {
String username
String password
}
def "can render json"() {
when:
app {
modules {
register new JacksonModule()
}
handlers {
get {
render json(new User(username: "foo", password: "bar"))
}
}
}
then:
get().jsonPath().get("username") == "foo"
}
}
********************************************************************************
ratpack/ratpack-remote/src/test/groovy/org/ratpackframework/remote/RemoteControlSpec.groovy
********************************************************************************
class RemoteControlSpec extends RatpackGroovyDslSpec {
void appWithRemoteControl() {
app {
modules {
register new RemoteControlModule()
}
}
}
void appWithEnabledRemoteControl() {
other['remoteControl.enabled'] = 'true'
appWithRemoteControl()
}
RemoteControl getRemote() {
startServerIfNeeded()
new RemoteControl(new HttpTransport("http://localhost:${server.bindPort}/$DEFAULT_REMOTE_CONTROL_PATH"))
}
void 'by default the endpoint is not enabled'() {
given:
appWithRemoteControl()
expect:
post(DEFAULT_REMOTE_CONTROL_PATH).statusCode == NOT_FOUND.code()
}
void 'only posts are allowed'() {
given:
appWithEnabledRemoteControl()
expect:
get(DEFAULT_REMOTE_CONTROL_PATH).statusCode == METHOD_NOT_ALLOWED.code()
head(DEFAULT_REMOTE_CONTROL_PATH).statusCode == METHOD_NOT_ALLOWED.code()
}
void 'only requests that contain groovy-remote-control-command are allowed'() {
given:
appWithEnabledRemoteControl()
expect:
post(DEFAULT_REMOTE_CONTROL_PATH).statusCode == UNSUPPORTED_MEDIA_TYPE.code()
}
void 'only requests that accept groovy-remote-control-result are allowed'() {
given:
appWithEnabledRemoteControl()
when:
request.header(HttpHeaders.Names.CONTENT_TYPE, RemoteControlHandler.REQUEST_CONTENT_TYPE)
request.header(HttpHeaders.Names.ACCEPT, 'text/html')
then:
post(DEFAULT_REMOTE_CONTROL_PATH).statusCode == NOT_ACCEPTABLE.code()
}
@Unroll
void 'sending a simple command - #scenario'() {
given:
app {
modules {
register new RemoteControlModule(path: modulePath)
}
}
other = ['remoteControl.enabled': 'true'] + otherConfig
and:
startServerIfNeeded()
def remoteControl = new RemoteControl(new HttpTransport("http://localhost:${server.bindPort}/$path"))
expect:
remoteControl { 1 + 2 } == 3
where:
scenario | path | modulePath | otherConfig
'default path' | DEFAULT_REMOTE_CONTROL_PATH | null | [:]
'path set in module' | 'custom' | 'custom' | [:]
'path set in config' | 'fromConfig' | null | ['remoteControl.path': 'fromConfig']
}
void 'registry is available in command context'() {
given:
appWithEnabledRemoteControl()
other.test = 'it works'
expect:
//from guice
remote.exec { registry.get(LaunchConfig).other.test } == 'it works'
//from root registry
remote.exec { registry.get(DefaultFileSystemBinding) != null }
//created just in time
remote.exec { registry.get(FileRenderer) != null }
}
void 'endpoint is also enabled if reloading is enabled'() {
given:
reloadable = true
appWithRemoteControl()
expect:
remote.exec { 1 + 2 } == 3
}
}
********************************************************************************
ratpack/ratpack-session/src/test/groovy/org/ratpackframework/session/SessionSpec.groovy
********************************************************************************
class SessionSpec extends RatpackGroovyDslSpec {
def setup() {
modules << new SessionModule()
modules << new MapSessionsModule(10, 5)
}
def "can use session"() {
when:
app {
handlers {
get(":v") {
response.send get(Session).id
}
}
}
then:
getText("a") == getText("b")
}
def "can store session vars"() {
when:
app {
handlers {
get("") {
def store = get(SessionStorage)
response.send store.value.toString()
}
get("set/:value") {
def store = get(SessionStorage)
store.value = pathTokens.value
response.send store.value.toString()
}
}
}
and:
getText("set/foo") == "foo"
then:
getText() == "foo"
}
def "can invalidate session vars"() {
when:
app {
handlers {
get("") {
def store = get(SessionStorage)
response.send store.value ?: "null"
}
get("set/:value") {
def store = get(SessionStorage)
store.value = pathTokens.value
response.send store.value ?: "null"
}
get("invalidate") {
get(Session).terminate()
response.send()
}
get("size") {
response.send get(SessionStore).size().toString()
}
}
}
and:
getText("set/foo")
then:
getText() == "foo"
getText("size") == "1"
when:
getText("invalidate")
then:
getText() == "null"
getText("size") == "1"
}
def "sessions are created on demand"() {
when:
app {
handlers {
get {
response.send get(SessionStore).size().toString()
}
}
}
then:
getText() == "0"
when:
app {
handlers {
get {
get(SessionStorage)
response.send get(SessionStore).size().toString()
}
}
}
then:
getText() == "1"
}
def "session cookies are only set when needed"() {
when:
app {
handlers {
get("foo") {
response.send("foo")
}
get("bar") {
get(SessionStorage) // just retrieve
response.send("bar")
}
}
}
then:
get("foo").cookies().isEmpty()
get("bar").cookies().JSESSIONID != null
// null because the session id is already set
get("bar").cookies().JSESSIONID == null
}
}
********************************************************************************
ratpack/ratpack-site/src/test/groovy/org/ratpackframework/site/SiteSmokeSpec.groovy
********************************************************************************
class SiteSmokeSpec extends ScriptAppSpec {
def "Check Site Index"() {
when:
get("index.html")
then:
response.statusCode == 200
response.body.asString().contains('<title>Ratpack: A toolkit for JVM web applications</title>')
}
def "Check Site /"() {
when:
get("")
then:
response.statusCode == 200
response.body.asString().contains('<title>Ratpack: A toolkit for JVM web applications</title>')
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment