diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2018-08-08 15:53:37 +0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2018-08-08 18:24:18 +0800 |
commit | d85fd093539bdd7d8d432b058c2e2225eaa1ee2b (patch) | |
tree | 1b10e0cdcea08a51255152e259e446a75ec57ead /cask/test | |
parent | a5320694193fd86b639c53a91fa24fb7f8ea914e (diff) | |
download | cask-d85fd093539bdd7d8d432b058c2e2225eaa1ee2b.tar.gz cask-d85fd093539bdd7d8d432b058c2e2225eaa1ee2b.tar.bz2 cask-d85fd093539bdd7d8d432b058c2e2225eaa1ee2b.zip |
Properly roll back transactions when endpoints fail in TodoMvcDb
Diffstat (limited to 'cask/test')
-rw-r--r-- | cask/test/src/test/cask/Decorated.scala | 9 | ||||
-rw-r--r-- | cask/test/src/test/cask/ExampleTests.scala | 86 | ||||
-rw-r--r-- | cask/test/src/test/cask/FailureTests.scala | 3 | ||||
-rw-r--r-- | cask/test/src/test/cask/MinimalApplication2.scala | 16 | ||||
-rw-r--r-- | cask/test/src/test/cask/TodoMvcDb.scala | 42 |
5 files changed, 93 insertions, 63 deletions
diff --git a/cask/test/src/test/cask/Decorated.scala b/cask/test/src/test/cask/Decorated.scala index 9fac78a..d7cb6b8 100644 --- a/cask/test/src/test/cask/Decorated.scala +++ b/cask/test/src/test/cask/Decorated.scala @@ -1,21 +1,16 @@ package test.cask -import cask.internal.Router -import cask.model.{ParamContext, Response} - object Decorated extends cask.MainRoutes{ class User{ override def toString = "[haoyi]" } class loggedIn extends cask.Decorator { - def wrapMethodOutput(ctx: ParamContext, - delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = { + def wrapFunction(ctx: cask.ParamContext, delegate: Delegate): Returned = { delegate(Map("user" -> new User())) } } class withExtra extends cask.Decorator { - def wrapMethodOutput(ctx: ParamContext, - delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = { + def wrapFunction(ctx: cask.ParamContext, delegate: Delegate): Returned = { delegate(Map("extra" -> 31337)) } } diff --git a/cask/test/src/test/cask/ExampleTests.scala b/cask/test/src/test/cask/ExampleTests.scala index 6858051..36e0387 100644 --- a/cask/test/src/test/cask/ExampleTests.scala +++ b/cask/test/src/test/cask/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.server.handlers.BlockingHandler import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.MainRoutes)(f: String => T): T = { + def test[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(new BlockingHandler(example.defaultHandler)) @@ -23,25 +23,37 @@ object ExampleTests extends TestSuite{ success.text() ==> "Hello World!" success.statusCode ==> 200 - requests.get(host + "/doesnt-exist").statusCode ==> 404 + requests.get(s"$host/doesnt-exist").statusCode ==> 404 - requests.post(host + "/do-thing", data = "hello").text() ==> "olleh" + requests.post(s"$host/do-thing", data = "hello").text() ==> "olleh" - requests.get(host + "/do-thing").statusCode ==> 404 + requests.get(s"$host/do-thing").statusCode ==> 404 + } + 'MinimalApplication2 - test(MinimalMain){ host => + val success = requests.get(host) + + success.text() ==> "Hello World!" + success.statusCode ==> 200 + + requests.get(s"$host/doesnt-exist").statusCode ==> 404 + + requests.post(s"$host/do-thing", data = "hello").text() ==> "olleh" + + requests.get(s"$host/do-thing").statusCode ==> 404 } 'VariableRoutes - test(VariableRoutes){ host => val noIndexPage = requests.get(host) noIndexPage.statusCode ==> 404 - requests.get(host + "/user/lihaoyi").text() ==> "User lihaoyi" + requests.get(s"$host/user/lihaoyi").text() ==> "User lihaoyi" - requests.get(host + "/user").statusCode ==> 404 + requests.get(s"$host/user").statusCode ==> 404 - requests.get(host + "/post/123?param=xyz¶m=abc").text() ==> + requests.get(s"$host/post/123?param=xyz¶m=abc").text() ==> "Post 123 ArrayBuffer(xyz, abc)" - requests.get(host + "/post/123").text() ==> + requests.get(s"$host/post/123").text() ==> """Missing argument: (param: Seq[String]) | |Arguments provided did not match expected signature: @@ -52,33 +64,33 @@ object ExampleTests extends TestSuite{ | |""".stripMargin - requests.get(host + "/path/one/two/three").text() ==> + requests.get(s"$host/path/one/two/three").text() ==> "Subpath List(one, two, three)" } 'StaticFiles - test(StaticFiles){ host => - requests.get(host + "/static/example.txt").text() ==> + requests.get(s"$host/static/example.txt").text() ==> "the quick brown fox jumps over the lazy dog" } 'RedirectAbort - test(RedirectAbort){ host => - val resp = requests.get(host + "/") + val resp = requests.get(s"$host/") resp.statusCode ==> 401 resp.history.get.statusCode ==> 301 } 'FormJsonPost - test(FormJsonPost){ host => - requests.post(host + "/json", data = """{"value1": true, "value2": [3]}""").text() ==> + requests.post(s"$host/json", data = """{"value1": true, "value2": [3]}""").text() ==> "OK true Vector(3)" requests.post( - host + "/form", + s"$host/form", data = Seq("value1" -> "hello", "value2" -> "1", "value2" -> "2") ).text() ==> "OK FormValue(hello,null) List(1, 2)" val resp = requests.post( - host + "/upload", + s"$host/upload", data = requests.MultiPart( requests.MultiItem("image", "...", "my-best-image.txt") ) @@ -86,61 +98,61 @@ object ExampleTests extends TestSuite{ resp.text() ==> "my-best-image.txt" } 'Decorated - test(Decorated){ host => - requests.get(host + "/hello/woo").text() ==> "woo31337" - requests.get(host + "/internal/boo").text() ==> "boo[haoyi]" - requests.get(host + "/internal-extra/goo").text() ==> "goo[haoyi]31337" + requests.get(s"$host/hello/woo").text() ==> "woo31337" + requests.get(s"$host/internal/boo").text() ==> "boo[haoyi]" + requests.get(s"$host/internal-extra/goo").text() ==> "goo[haoyi]31337" } 'TodoMvcApi - test(TodoMvcApi){ host => - requests.get(host + "/list/all").text() ==> + requests.get(s"$host/list/all").text() ==> """[{"checked":true,"text":"Get started with Cask"},{"checked":false,"text":"Profit!"}]""" - requests.get(host + "/list/active").text() ==> + requests.get(s"$host/list/active").text() ==> """[{"checked":false,"text":"Profit!"}]""" - requests.get(host + "/list/completed").text() ==> + requests.get(s"$host/list/completed").text() ==> """[{"checked":true,"text":"Get started with Cask"}]""" - requests.post(host + "/toggle/1") + requests.post(s"$host/toggle/1") - requests.get(host + "/list/all").text() ==> + requests.get(s"$host/list/all").text() ==> """[{"checked":true,"text":"Get started with Cask"},{"checked":true,"text":"Profit!"}]""" - requests.get(host + "/list/active").text() ==> + requests.get(s"$host/list/active").text() ==> """[]""" - requests.post(host + "/add", data = "new Task") + requests.post(s"$host/add", data = "new Task") - requests.get(host + "/list/active").text() ==> + requests.get(s"$host/list/active").text() ==> """[{"checked":false,"text":"new Task"}]""" - requests.post(host + "/delete/0") + requests.post(s"$host/delete/0") - requests.get(host + "/list/active").text() ==> + requests.get(s"$host/list/active").text() ==> """[]""" } 'TodoMvcDb - test(TodoMvcDb){ host => - requests.get(host + "/list/all").text() ==> + requests.get(s"$host/list/all").text() ==> """[{"id":1,"checked":true,"text":"Get started with Cask"},{"id":2,"checked":false,"text":"Profit!"}]""" - requests.get(host + "/list/active").text() ==> + requests.get(s"$host/list/active").text() ==> """[{"id":2,"checked":false,"text":"Profit!"}]""" - requests.get(host + "/list/completed").text() ==> + requests.get(s"$host/list/completed").text() ==> """[{"id":1,"checked":true,"text":"Get started with Cask"}]""" - requests.post(host + "/toggle/2") + requests.post(s"$host/toggle/2") - requests.get(host + "/list/all").text() ==> + requests.get(s"$host/list/all").text() ==> """[{"id":1,"checked":true,"text":"Get started with Cask"},{"id":2,"checked":true,"text":"Profit!"}]""" - requests.get(host + "/list/active").text() ==> + requests.get(s"$host/list/active").text() ==> """[]""" - requests.post(host + "/add", data = "new Task") + requests.post(s"$host/add", data = "new Task") - requests.get(host + "/list/active").text() ==> + requests.get(s"$host/list/active").text() ==> """[{"id":3,"checked":false,"text":"new Task"}]""" - requests.post(host + "/delete/3") + requests.post(s"$host/delete/3") - requests.get(host + "/list/active").text() ==> + requests.get(s"$host/list/active").text() ==> """[]""" } } diff --git a/cask/test/src/test/cask/FailureTests.scala b/cask/test/src/test/cask/FailureTests.scala index de3b438..9e28c0b 100644 --- a/cask/test/src/test/cask/FailureTests.scala +++ b/cask/test/src/test/cask/FailureTests.scala @@ -6,8 +6,7 @@ import utest._ object FailureTests extends TestSuite { class myDecorator extends cask.Decorator { - def wrapMethodOutput(ctx: ParamContext, - delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = { + def wrapFunction(ctx: ParamContext, delegate: Delegate): Returned = { delegate(Map("extra" -> 31337)) } } diff --git a/cask/test/src/test/cask/MinimalApplication2.scala b/cask/test/src/test/cask/MinimalApplication2.scala new file mode 100644 index 0000000..924b00f --- /dev/null +++ b/cask/test/src/test/cask/MinimalApplication2.scala @@ -0,0 +1,16 @@ +package test.cask + +object MinimalRoutes extends cask.Routes{ + @cask.get("/") + def hello() = { + "Hello World!" + } + + @cask.post("/do-thing") + def doThing(request: cask.Request) = { + new String(request.data.readAllBytes()).reverse + } + + initialize() +} +object MinimalMain extends cask.Main(MinimalRoutes)
\ No newline at end of file diff --git a/cask/test/src/test/cask/TodoMvcDb.scala b/cask/test/src/test/cask/TodoMvcDb.scala index c6f8191..b352d1a 100644 --- a/cask/test/src/test/cask/TodoMvcDb.scala +++ b/cask/test/src/test/cask/TodoMvcDb.scala @@ -1,32 +1,39 @@ package test.cask + import cask.internal.Router -import cask.model.{ParamContext, Response} import com.typesafe.config.ConfigFactory -import io.getquill._ +import io.getquill.{SqliteJdbcContext, SnakeCase} object TodoMvcDb extends cask.MainRoutes{ - case class Todo(id: Int, checked: Boolean, text: String) - object Todo{ - implicit def todoRW = upickle.default.macroRW[Todo] - } + val tmpDb = java.nio.file.Files.createTempDirectory("todo-cask-sqlite") + object ctx extends SqliteJdbcContext( SnakeCase, ConfigFactory.parseString( s"""{"driverClassName":"org.sqlite.JDBC","jdbcUrl":"jdbc:sqlite:$tmpDb/file.db"}""" ) ) - val tmpDb = java.nio.file.Files.createTempDirectory("todo-cask-sqlite") - - import ctx._ class transactional extends cask.Decorator{ - def wrapMethodOutput(pctx: ParamContext, - delegate: Map[String, Input] => Router.Result[Response]): Router.Result[Response] = { - ctx.transaction(delegate(Map("ctx" -> ctx))) + class TransactionFailed(val value: Router.Result.Error) extends Exception + def wrapFunction(pctx: cask.ParamContext, delegate: Delegate): Returned = { + try ctx.transaction( + delegate(Map()) match{ + case Router.Result.Success(t) => Router.Result.Success(t) + case e: Router.Result.Error => throw new TransactionFailed(e) + } + ) + catch{case e: TransactionFailed => e.value} + } } + case class Todo(id: Int, checked: Boolean, text: String) + object Todo{ + implicit def todoRW = upickle.default.macroRW[Todo] + } + ctx.executeAction( """CREATE TABLE todo ( | id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -42,9 +49,11 @@ object TodoMvcDb extends cask.MainRoutes{ |""".stripMargin ) + import ctx._ + @transactional @cask.get("/list/:state") - def list(state: String)(ctx: SqliteJdbcContext[_]) = { + def list(state: String) = { val filteredTodos = state match{ case "all" => run(query[Todo]) case "active" => run(query[Todo].filter(!_.checked)) @@ -55,22 +64,21 @@ object TodoMvcDb extends cask.MainRoutes{ @transactional @cask.post("/add") - def add(request: cask.Request)(ctx: SqliteJdbcContext[_]) = { + def add(request: cask.Request) = { val body = new String(request.data.readAllBytes()) run(query[Todo].insert(_.checked -> lift(false), _.text -> lift(body)).returning(_.id)) } @transactional @cask.post("/toggle/:index") - def toggle(index: Int)(ctx: SqliteJdbcContext[_]) = { + def toggle(index: Int) = { run(query[Todo].filter(_.id == lift(index)).update(p => p.checked -> !p.checked)) } @transactional @cask.post("/delete/:index") - def delete(index: Int)(ctx: SqliteJdbcContext[_]) = { + def delete(index: Int) = { run(query[Todo].filter(_.id == lift(index)).delete) - } initialize() |