From 2dd36c2b792060577bbdd4280f870afa0bbb31a9 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Mon, 7 May 2018 15:02:58 +1000 Subject: [PATCH] Give more distinct names and migrate test style --- ...etTest.groovy => JettyServlet2Test.groovy} | 134 ++++++++++------ ...TestServlet.groovy => TestServlet2.groovy} | 6 +- ...etTest.groovy => JettyServlet3Test.groovy} | 146 +++++++++--------- ...TestServlet.groovy => TestServlet3.groovy} | 2 +- ...tTest.groovy => TomcatServlet3Test.groovy} | 138 +++++++++-------- .../trace/agent/test/SpanAssert.groovy | 1 + .../trace/agent/test/TagsAssert.groovy | 9 ++ 7 files changed, 247 insertions(+), 189 deletions(-) rename dd-java-agent/instrumentation/servlet-2/src/test/groovy/{JettyServletTest.groovy => JettyServlet2Test.groovy} (51%) rename dd-java-agent/instrumentation/servlet-2/src/test/groovy/{TestServlet.groovy => TestServlet2.groovy} (73%) rename dd-java-agent/instrumentation/servlet-3/src/test/groovy/{JettyServletTest.groovy => JettyServlet3Test.groovy} (50%) rename dd-java-agent/instrumentation/servlet-3/src/test/groovy/{TestServlet.groovy => TestServlet3.groovy} (98%) rename dd-java-agent/instrumentation/servlet-3/src/test/groovy/{TomcatServletTest.groovy => TomcatServlet3Test.groovy} (55%) diff --git a/dd-java-agent/instrumentation/servlet-2/src/test/groovy/JettyServletTest.groovy b/dd-java-agent/instrumentation/servlet-2/src/test/groovy/JettyServlet2Test.groovy similarity index 51% rename from dd-java-agent/instrumentation/servlet-2/src/test/groovy/JettyServletTest.groovy rename to dd-java-agent/instrumentation/servlet-2/src/test/groovy/JettyServlet2Test.groovy index c8b6b1f3f7..e8e20f1eb2 100644 --- a/dd-java-agent/instrumentation/servlet-2/src/test/groovy/JettyServletTest.groovy +++ b/dd-java-agent/instrumentation/servlet-2/src/test/groovy/JettyServlet2Test.groovy @@ -17,18 +17,20 @@ import java.lang.reflect.Field import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit -class JettyServletTest extends AgentTestRunner { +import static datadog.trace.agent.test.ListWriterAssert.assertTraces + +class JettyServlet2Test extends AgentTestRunner { static final int PORT = TestUtils.randomOpenPort() // Jetty needs this to ensure consistent ordering for async. - static CountDownLatch latch + CountDownLatch latch = new CountDownLatch(1) OkHttpClient client = new OkHttpClient.Builder() .addNetworkInterceptor(new Interceptor() { @Override Response intercept(Interceptor.Chain chain) throws IOException { def response = chain.proceed(chain.request()) - JettyServletTest.latch.await(10, TimeUnit.SECONDS) // don't block forever or test never fails. + JettyServlet2Test.this.latch.await(10, TimeUnit.SECONDS) // don't block forever or test never fails. return response } }) @@ -45,7 +47,7 @@ class JettyServletTest extends AgentTestRunner { @Override void write(final List trace) { add(trace) - JettyServletTest.latch.countDown() + JettyServlet2Test.this.latch.countDown() } } DDTracer tracer = new DDTracer(writer) @@ -54,7 +56,7 @@ class JettyServletTest extends AgentTestRunner { jettyServer = new Server(PORT) servletContext = new ServletContextHandler() - servletContext.addServlet(TestServlet.Sync, "/sync") + servletContext.addServlet(TestServlet2.Sync, "/sync") jettyServer.setHandler(servletContext) jettyServer.start() @@ -82,7 +84,6 @@ class JettyServletTest extends AgentTestRunner { @Unroll def "test #path servlet call"() { setup: - latch = new CountDownLatch(1) def request = new Request.Builder() .url("http://localhost:$PORT/$path") .get() @@ -91,26 +92,27 @@ class JettyServletTest extends AgentTestRunner { expect: response.body().string().trim() == expectedResponse - writer.waitForTraces(1) - writer.size() == 1 - def trace = writer.firstTrace() - trace.size() == 1 - def span = trace[0] - span.context().serviceName == "unnamed-java-app" - span.context().operationName == "servlet.request" - span.context().resourceName == "GET /$path" - span.context().spanType == DDSpanTypes.WEB_SERVLET - !span.context().getErrorFlag() - span.context().parentId == 0 - span.context().tags["http.url"] == "http://localhost:$PORT/$path" - span.context().tags["http.method"] == "GET" - span.context().tags["span.kind"] == "server" - span.context().tags["component"] == "java-web-servlet" - span.context().tags["http.status_code"] == null // sadly servlet 2.x doesn't expose it generically. - span.context().tags["thread.name"] != null - span.context().tags["thread.id"] != null - span.context().tags.size() == 7 + assertTraces(writer, 1) { + trace(0, 1) { + span(0) { + serviceName "unnamed-java-app" + operationName "servlet.request" + resourceName "GET /$path" + spanType DDSpanTypes.WEB_SERVLET + errored false + parent() + tags { + "http.url" "http://localhost:$PORT/$path" + "http.method" "GET" + "span.kind" "server" + "component" "java-web-servlet" + "span.type" DDSpanTypes.WEB_SERVLET + defaultTags() + } + } + } + } where: path | expectedResponse @@ -128,29 +130,67 @@ class JettyServletTest extends AgentTestRunner { expect: response.body().string().trim() != expectedResponse - writer.waitForTraces(1) - writer.size() == 1 - def trace = writer.firstTrace() - trace.size() == 1 - def span = trace[0] - span.context().operationName == "servlet.request" - span.context().resourceName == "GET /$path" - span.context().spanType == DDSpanTypes.WEB_SERVLET - span.context().getErrorFlag() - span.context().parentId == 0 - span.context().tags["http.url"] == "http://localhost:$PORT/$path" - span.context().tags["http.method"] == "GET" - span.context().tags["span.kind"] == "server" - span.context().tags["component"] == "java-web-servlet" - span.context().tags["http.status_code"] == null // sadly servlet 2.x doesn't expose it generically. - span.context().tags["thread.name"] != null - span.context().tags["thread.id"] != null - span.context().tags["error"] == true - span.context().tags["error.msg"] == "some $path error" - span.context().tags["error.type"] == RuntimeException.getName() - span.context().tags["error.stack"] != null - span.context().tags.size() == 11 + assertTraces(writer, 1) { + trace(0, 1) { + span(0) { + serviceName "unnamed-java-app" + operationName "servlet.request" + resourceName "GET /$path" + spanType DDSpanTypes.WEB_SERVLET + errored true + parent() + tags { + "http.url" "http://localhost:$PORT/$path" + "http.method" "GET" + "span.kind" "server" + "component" "java-web-servlet" + "span.type" DDSpanTypes.WEB_SERVLET + errorTags(RuntimeException, "some $path error") + defaultTags() + } + } + } + } + + where: + path | expectedResponse + "sync" | "Hello Sync" + } + + @Unroll + def "test #path non-throwing-error servlet call"() { + // This doesn't actually detect the error because we can't get the status code via the old servlet API. + setup: + def request = new Request.Builder() + .url("http://localhost:$PORT/$path?non-throwing-error=true") + .get() + .build() + def response = client.newCall(request).execute() + + expect: + response.body().string().trim() != expectedResponse + + assertTraces(writer, 1) { + trace(0, 1) { + span(0) { + serviceName "unnamed-java-app" + operationName "servlet.request" + resourceName "GET /$path" + spanType DDSpanTypes.WEB_SERVLET + errored false + parent() + tags { + "http.url" "http://localhost:$PORT/$path" + "http.method" "GET" + "span.kind" "server" + "component" "java-web-servlet" + "span.type" DDSpanTypes.WEB_SERVLET + defaultTags() + } + } + } + } where: path | expectedResponse diff --git a/dd-java-agent/instrumentation/servlet-2/src/test/groovy/TestServlet.groovy b/dd-java-agent/instrumentation/servlet-2/src/test/groovy/TestServlet2.groovy similarity index 73% rename from dd-java-agent/instrumentation/servlet-2/src/test/groovy/TestServlet.groovy rename to dd-java-agent/instrumentation/servlet-2/src/test/groovy/TestServlet2.groovy index 77b1f691c1..9ddcc8d92b 100644 --- a/dd-java-agent/instrumentation/servlet-2/src/test/groovy/TestServlet.groovy +++ b/dd-java-agent/instrumentation/servlet-2/src/test/groovy/TestServlet2.groovy @@ -3,7 +3,7 @@ import groovy.servlet.AbstractHttpServlet import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse -class TestServlet { +class TestServlet2 { static class Sync extends AbstractHttpServlet { @Override @@ -11,6 +11,10 @@ class TestServlet { if (req.getParameter("error") != null) { throw new RuntimeException("some sync error") } + if (req.getParameter("non-throwing-error") != null) { + resp.sendError(500, "some sync error") + return + } resp.writer.print("Hello Sync") } } diff --git a/dd-java-agent/instrumentation/servlet-3/src/test/groovy/JettyServletTest.groovy b/dd-java-agent/instrumentation/servlet-3/src/test/groovy/JettyServlet3Test.groovy similarity index 50% rename from dd-java-agent/instrumentation/servlet-3/src/test/groovy/JettyServletTest.groovy rename to dd-java-agent/instrumentation/servlet-3/src/test/groovy/JettyServlet3Test.groovy index dd940500a5..b3d407ed1c 100644 --- a/dd-java-agent/instrumentation/servlet-3/src/test/groovy/JettyServletTest.groovy +++ b/dd-java-agent/instrumentation/servlet-3/src/test/groovy/JettyServlet3Test.groovy @@ -17,18 +17,21 @@ import java.lang.reflect.Field import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit -class JettyServletTest extends AgentTestRunner { +import static datadog.trace.agent.test.ListWriterAssert.assertTraces + +class JettyServlet3Test extends AgentTestRunner { static final int PORT = TestUtils.randomOpenPort() // Jetty needs this to ensure consistent ordering for async. - static CountDownLatch latch + CountDownLatch latch = new CountDownLatch(1) + OkHttpClient client = new OkHttpClient.Builder() .addNetworkInterceptor(new Interceptor() { @Override Response intercept(Interceptor.Chain chain) throws IOException { def response = chain.proceed(chain.request()) - JettyServletTest.latch.await(10, TimeUnit.SECONDS) // don't block forever or test never fails. + JettyServlet3Test.this.latch.await(10, TimeUnit.SECONDS) // don't block forever or test never fails. return response } }) @@ -45,7 +48,7 @@ class JettyServletTest extends AgentTestRunner { @Override void write(final List trace) { add(trace) - JettyServletTest.latch.countDown() + JettyServlet3Test.this.latch.countDown() } } DDTracer tracer = new DDTracer(writer) @@ -54,8 +57,8 @@ class JettyServletTest extends AgentTestRunner { jettyServer = new Server(PORT) servletContext = new ServletContextHandler() - servletContext.addServlet(TestServlet.Sync, "/sync") - servletContext.addServlet(TestServlet.Async, "/async") + servletContext.addServlet(TestServlet3.Sync, "/sync") + servletContext.addServlet(TestServlet3.Async, "/async") jettyServer.setHandler(servletContext) jettyServer.start() @@ -83,7 +86,6 @@ class JettyServletTest extends AgentTestRunner { @Unroll def "test #path servlet call"() { setup: - latch = new CountDownLatch(1) def request = new Request.Builder() .url("http://localhost:$PORT/$path") .get() @@ -92,26 +94,28 @@ class JettyServletTest extends AgentTestRunner { expect: response.body().string().trim() == expectedResponse - writer.waitForTraces(1) - writer.size() == 1 - def trace = writer.firstTrace() - trace.size() == 1 - def span = trace[0] - span.context().serviceName == "unnamed-java-app" - span.context().operationName == "servlet.request" - span.context().resourceName == "GET /$path" - span.context().spanType == DDSpanTypes.WEB_SERVLET - !span.context().getErrorFlag() - span.context().parentId == 0 - span.context().tags["http.url"] == "http://localhost:$PORT/$path" - span.context().tags["http.method"] == "GET" - span.context().tags["span.kind"] == "server" - span.context().tags["component"] == "java-web-servlet" - span.context().tags["http.status_code"] == 200 - span.context().tags["thread.name"] != null - span.context().tags["thread.id"] != null - span.context().tags.size() == 8 + assertTraces(writer, 1) { + trace(0, 1) { + span(0) { + serviceName "unnamed-java-app" + operationName "servlet.request" + resourceName "GET /$path" + spanType DDSpanTypes.WEB_SERVLET + errored false + parent() + tags { + "http.url" "http://localhost:$PORT/$path" + "http.method" "GET" + "span.kind" "server" + "component" "java-web-servlet" + "span.type" DDSpanTypes.WEB_SERVLET + "http.status_code" 200 + defaultTags() + } + } + } + } where: path | expectedResponse @@ -130,30 +134,29 @@ class JettyServletTest extends AgentTestRunner { expect: response.body().string().trim() != expectedResponse - writer.waitForTraces(1) - writer.size() == 1 - def trace = writer.firstTrace() - trace.size() == 1 - def span = trace[0] - span.context().serviceName == "unnamed-java-app" - span.context().operationName == "servlet.request" - span.context().resourceName == "GET /$path" - span.context().spanType == DDSpanTypes.WEB_SERVLET - span.context().getErrorFlag() - span.context().parentId == 0 - span.context().tags["http.url"] == "http://localhost:$PORT/$path" - span.context().tags["http.method"] == "GET" - span.context().tags["span.kind"] == "server" - span.context().tags["component"] == "java-web-servlet" - span.context().tags["http.status_code"] == 500 - span.context().tags["thread.name"] != null - span.context().tags["thread.id"] != null - span.context().tags["error"] == true - span.context().tags["error.msg"] == "some $path error" - span.context().tags["error.type"] == RuntimeException.getName() - span.context().tags["error.stack"] != null - span.context().tags.size() == 12 + assertTraces(writer, 1) { + trace(0, 1) { + span(0) { + serviceName "unnamed-java-app" + operationName "servlet.request" + resourceName "GET /$path" + spanType DDSpanTypes.WEB_SERVLET + errored true + parent() + tags { + "http.url" "http://localhost:$PORT/$path" + "http.method" "GET" + "span.kind" "server" + "component" "java-web-servlet" + "span.type" DDSpanTypes.WEB_SERVLET + "http.status_code" 500 + errorTags(RuntimeException, "some $path error") + defaultTags() + } + } + } + } where: path | expectedResponse @@ -172,30 +175,29 @@ class JettyServletTest extends AgentTestRunner { expect: response.body().string().trim() != expectedResponse - writer.waitForTraces(1) - writer.size() == 1 - def trace = writer.firstTrace() - trace.size() == 1 - def span = trace[0] - span.context().serviceName == "unnamed-java-app" - span.context().operationName == "servlet.request" - span.context().resourceName == "GET /$path" - span.context().spanType == DDSpanTypes.WEB_SERVLET - span.context().getErrorFlag() - span.context().parentId == 0 - span.context().tags["http.url"] == "http://localhost:$PORT/$path" - span.context().tags["http.method"] == "GET" - span.context().tags["span.kind"] == "server" - span.context().tags["component"] == "java-web-servlet" - span.context().tags["http.status_code"] == 500 - span.context().tags["thread.name"] != null - span.context().tags["thread.id"] != null - span.context().tags["error"] == true - span.context().tags["error.msg"] == null - span.context().tags["error.type"] == null - span.context().tags["error.stack"] == null - span.context().tags.size() == 9 + assertTraces(writer, 1) { + trace(0, 1) { + span(0) { + serviceName "unnamed-java-app" + operationName "servlet.request" + resourceName "GET /$path" + spanType DDSpanTypes.WEB_SERVLET + errored true + parent() + tags { + "http.url" "http://localhost:$PORT/$path" + "http.method" "GET" + "span.kind" "server" + "component" "java-web-servlet" + "span.type" DDSpanTypes.WEB_SERVLET + "http.status_code" 500 + "error" true + defaultTags() + } + } + } + } where: path | expectedResponse diff --git a/dd-java-agent/instrumentation/servlet-3/src/test/groovy/TestServlet.groovy b/dd-java-agent/instrumentation/servlet-3/src/test/groovy/TestServlet3.groovy similarity index 98% rename from dd-java-agent/instrumentation/servlet-3/src/test/groovy/TestServlet.groovy rename to dd-java-agent/instrumentation/servlet-3/src/test/groovy/TestServlet3.groovy index cf62ea9896..e61251267a 100644 --- a/dd-java-agent/instrumentation/servlet-3/src/test/groovy/TestServlet.groovy +++ b/dd-java-agent/instrumentation/servlet-3/src/test/groovy/TestServlet3.groovy @@ -4,7 +4,7 @@ import javax.servlet.annotation.WebServlet import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse -class TestServlet { +class TestServlet3 { @WebServlet static class Sync extends AbstractHttpServlet { diff --git a/dd-java-agent/instrumentation/servlet-3/src/test/groovy/TomcatServletTest.groovy b/dd-java-agent/instrumentation/servlet-3/src/test/groovy/TomcatServlet3Test.groovy similarity index 55% rename from dd-java-agent/instrumentation/servlet-3/src/test/groovy/TomcatServletTest.groovy rename to dd-java-agent/instrumentation/servlet-3/src/test/groovy/TomcatServlet3Test.groovy index de3847778a..ef91bef0d7 100644 --- a/dd-java-agent/instrumentation/servlet-3/src/test/groovy/TomcatServletTest.groovy +++ b/dd-java-agent/instrumentation/servlet-3/src/test/groovy/TomcatServlet3Test.groovy @@ -15,7 +15,9 @@ import spock.lang.Unroll import java.lang.reflect.Field -class TomcatServletTest extends AgentTestRunner { +import static datadog.trace.agent.test.ListWriterAssert.assertTraces + +class TomcatServlet3Test extends AgentTestRunner { static final int PORT = TestUtils.randomOpenPort() OkHttpClient client = new OkHttpClient.Builder() @@ -52,10 +54,10 @@ class TomcatServletTest extends AgentTestRunner { } }) - Tomcat.addServlet(appContext, "syncServlet", new TestServlet.Sync()) + Tomcat.addServlet(appContext, "syncServlet", new TestServlet3.Sync()) appContext.addServletMappingDecoded("/sync", "syncServlet") - Tomcat.addServlet(appContext, "asyncServlet", new TestServlet.Async()) + Tomcat.addServlet(appContext, "asyncServlet", new TestServlet3.Async()) appContext.addServletMappingDecoded("/async", "asyncServlet") tomcatServer.start() @@ -91,26 +93,28 @@ class TomcatServletTest extends AgentTestRunner { expect: response.body().string().trim() == expectedResponse - writer.waitForTraces(1) - writer.size() == 1 - def trace = writer.firstTrace() - trace.size() == 1 - def span = trace[0] - span.context().serviceName == "unnamed-java-app" - span.context().operationName == "servlet.request" - span.context().resourceName == "GET /$path" - span.context().spanType == DDSpanTypes.WEB_SERVLET - !span.context().getErrorFlag() - span.context().parentId == 0 - span.context().tags["http.url"] == "http://localhost:$PORT/$path" - span.context().tags["http.method"] == "GET" - span.context().tags["span.kind"] == "server" - span.context().tags["component"] == "java-web-servlet" - span.context().tags["http.status_code"] == 200 - span.context().tags["thread.name"] != null - span.context().tags["thread.id"] != null - span.context().tags.size() == 8 + assertTraces(writer, 1) { + trace(0, 1) { + span(0) { + serviceName "unnamed-java-app" + operationName "servlet.request" + resourceName "GET /$path" + spanType DDSpanTypes.WEB_SERVLET + errored false + parent() + tags { + "http.url" "http://localhost:$PORT/$path" + "http.method" "GET" + "span.kind" "server" + "component" "java-web-servlet" + "span.type" DDSpanTypes.WEB_SERVLET + "http.status_code" 200 + defaultTags() + } + } + } + } where: path | expectedResponse @@ -129,30 +133,29 @@ class TomcatServletTest extends AgentTestRunner { expect: response.body().string().trim() != expectedResponse - writer.waitForTraces(1) - writer.size() == 1 - def trace = writer.firstTrace() - trace.size() == 1 - def span = trace[0] - span.context().serviceName == "unnamed-java-app" - span.context().operationName == "servlet.request" - span.context().resourceName == "GET /$path" - span.context().spanType == DDSpanTypes.WEB_SERVLET - span.context().getErrorFlag() - span.context().parentId == 0 - span.context().tags["http.url"] == "http://localhost:$PORT/$path" - span.context().tags["http.method"] == "GET" - span.context().tags["span.kind"] == "server" - span.context().tags["component"] == "java-web-servlet" - span.context().tags["http.status_code"] == 500 - span.context().tags["thread.name"] != null - span.context().tags["thread.id"] != null - span.context().tags["error"] == true - span.context().tags["error.msg"] == "some $path error" - span.context().tags["error.type"] == RuntimeException.getName() - span.context().tags["error.stack"] != null - span.context().tags.size() == 12 + assertTraces(writer, 1) { + trace(0, 1) { + span(0) { + serviceName "unnamed-java-app" + operationName "servlet.request" + resourceName "GET /$path" + spanType DDSpanTypes.WEB_SERVLET + errored true + parent() + tags { + "http.url" "http://localhost:$PORT/$path" + "http.method" "GET" + "span.kind" "server" + "component" "java-web-servlet" + "span.type" DDSpanTypes.WEB_SERVLET + "http.status_code" 500 + errorTags(RuntimeException, "some $path error") + defaultTags() + } + } + } + } where: path | expectedResponse @@ -171,30 +174,29 @@ class TomcatServletTest extends AgentTestRunner { expect: response.body().string().trim() != expectedResponse - writer.waitForTraces(1) - writer.size() == 1 - def trace = writer.firstTrace() - trace.size() == 1 - def span = trace[0] - span.context().serviceName == "unnamed-java-app" - span.context().operationName == "servlet.request" - span.context().resourceName == "GET /$path" - span.context().spanType == DDSpanTypes.WEB_SERVLET - span.context().getErrorFlag() - span.context().parentId == 0 - span.context().tags["http.url"] == "http://localhost:$PORT/$path" - span.context().tags["http.method"] == "GET" - span.context().tags["span.kind"] == "server" - span.context().tags["component"] == "java-web-servlet" - span.context().tags["http.status_code"] == 500 - span.context().tags["thread.name"] != null - span.context().tags["thread.id"] != null - span.context().tags["error"] == true - span.context().tags["error.msg"] == null - span.context().tags["error.type"] == null - span.context().tags["error.stack"] == null - span.context().tags.size() == 9 + assertTraces(writer, 1) { + trace(0, 1) { + span(0) { + serviceName "unnamed-java-app" + operationName "servlet.request" + resourceName "GET /$path" + spanType DDSpanTypes.WEB_SERVLET + errored true + parent() + tags { + "http.url" "http://localhost:$PORT/$path" + "http.method" "GET" + "span.kind" "server" + "component" "java-web-servlet" + "span.type" DDSpanTypes.WEB_SERVLET + "http.status_code" 500 + "error" true + defaultTags() + } + } + } + } where: path | expectedResponse diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/SpanAssert.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/SpanAssert.groovy index 3b44546af7..124d35ac70 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/SpanAssert.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/SpanAssert.groovy @@ -35,6 +35,7 @@ class SpanAssert { def spanType(String type) { assert span.spanType == type + assert span.tags["span.type"] == type } def parent() { diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/TagsAssert.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/TagsAssert.groovy index 2b36320265..3565daae73 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/TagsAssert.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/TagsAssert.groovy @@ -30,10 +30,19 @@ class TagsAssert { } def errorTags(Class errorType) { + errorTags(errorType, null) + } + + def errorTags(Class errorType, String message) { assertedTags.add("error") assertedTags.add("error.type") assertedTags.add("error.stack") + if (message != null) { + assertedTags.add("error.msg") + tags["error.msg"] == message + } + tags["error"] == true tags["error.type"] == errorType tags["error.stack"] instanceof String