Skip to content

feat(executions): enriched execution logs (JSON tree, grouping, navigation, display settings)#16580

Open
flcarre wants to merge 3 commits into
developfrom
feat/enriched-execution-logs-v2
Open

feat(executions): enriched execution logs (JSON tree, grouping, navigation, display settings)#16580
flcarre wants to merge 3 commits into
developfrom
feat/enriched-execution-logs-v2

Conversation

@flcarre

@flcarre flcarre commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

What

Enriches the execution Logs tab and the global /logs page so structured and repetitive logs become readable and navigable (BHP request).

Log line

  • Inline collapsible JSON tree for structured lines (heuristic detection, parse-on-expand, raw fallback, --ks-editor-* syntax highlight, no v-html).
  • Similar-line grouping: consecutive near-identical lines collapse into a ×N group, one line stays visible.
  • Redesigned line: level rail, metadata pills, message on its own row.
  • Search highlight: matches of the active search term are wrapped in <mark> (tokenized over the rendered HTML so it never corrupts tags/entities), styled with the Kestra purple tag colour.

Toolbar (both pages, DS 2.0)

  • Sticky toolbar; per-icon KsButton surfaces matching the filter bar (size, gaps, font).
  • Display settings popover: density (row-height), font size, pretty-JSON, expand-by-default, body line-clamp.
  • Download with a filter modal; copy with a toast confirmation.
  • Click-a-value popover on metadata (Filter for / Filter out / Copy value / Open page), wired to the log query; actionable values render as purple links.
  • Click a log's level to filter by it.

Level navigation

  • Execution page: per-level count + jump to next/prev with scroll-into-view.
  • Global page: per-level server-wide counts (an ERROR count shows even when page 1 holds none); clicking filters to that level. Auto-refresh / live-tail comes from the existing KsFilter "Periodic refresh".

Tests

Unit tests for the structured-log helpers (detectStructured, normalizeLogTemplate, grouping) and the click-a-value filter query builder. 23 unit tests pass; eslint and vue-tsc clean.

enriched-logs-demo-1080-trimmed.mp4

Issue

Closes https://github.com/kestra-io/kestra-ee/issues/8305
Ships the JSON-in-logs ask from #7978.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

🐋 Docker image

ghcr.io/kestra-io/kestra-pr:16580
docker run --pull=always --rm -it -p 8080:8080 --user=root -v /var/run/docker.sock:/var/run/docker.sock -v /tmp:/tmp ghcr.io/kestra-io/kestra-pr:16580 server local

📄 OpenAPI Spec Changes

❌ Failed to generate EE OpenAPI spec (EE branch: develop).

k/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/UserService.java:517: warning: [serial] serializable class UserForbiddenException has no definition of serialVersionUID
      public static class UserForbiddenException extends KestraRuntimeException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/UserService.java:523: warning: [serial] serializable class UserNotFoundException has no definition of serialVersionUID
      public static class UserNotFoundException extends NotFoundException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/UserService.java:529: warning: [serial] serializable class UserAlreadyExistException has no definition of serialVersionUID
      public static class UserAlreadyExistException extends ConflictException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/RBACService.java:1034: warning: [serial] serializable class UnauthorizedInvitationException has no definition of serialVersionUID
      public static class UnauthorizedInvitationException extends KestraRuntimeException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/RBACService.java:1040: warning: [serial] serializable class BindingNotFoundException has no definition of serialVersionUID
      public static class BindingNotFoundException extends NotFoundException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/RBACService.java:1046: warning: [serial] serializable class RoleNotFoundException has no definition of serialVersionUID
      public static class RoleNotFoundException extends NotFoundException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/RBACService.java:1052: warning: [serial] serializable class GroupNotFoundException has no definition of serialVersionUID
      public static class GroupNotFoundException extends NotFoundException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/RBACService.java:1058: warning: [serial] serializable class UserNotInGroupException has no definition of serialVersionUID
      public static class UserNotInGroupException extends ConflictException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/RBACService.java:1064: warning: [serial] serializable class UserAlreadyInGroupException has no definition of serialVersionUID
      public static class UserAlreadyInGroupException extends ConflictException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/RBACService.java:1070: warning: [serial] serializable class GroupAlreadyExistsException has no definition of serialVersionUID
      public static class GroupAlreadyExistsException extends ConflictException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/RBACService.java:1076: warning: [serial] serializable class TenantAccessAlreadyExistsException has no definition of serialVersionUID
      public static class TenantAccessAlreadyExistsException extends ConflictException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/ServiceAccountService.java:263: warning: [serial] serializable class ServiceAccountForbiddenException has no definition of serialVersionUID
      public static class ServiceAccountForbiddenException extends KestraRuntimeException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/services/ServiceAccountService.java:269: warning: [serial] serializable class ServiceAccountFoundException has no definition of serialVersionUID
      public static class ServiceAccountFoundException extends NotFoundException {
                    ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/license/internals/MaxTenantsExceededException.java:5: warning: [serial] serializable class MaxTenantsExceededException has no definition of serialVersionUID
  public class MaxTenantsExceededException extends KestraLicenseException {
         ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/license/internals/MaxWorkerGroupExceededException.java:5: warning: [serial] serializable class MaxWorkerGroupExceededException has no definition of serialVersionUID
  public class MaxWorkerGroupExceededException extends KestraLicenseException {
         ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/plugin/ee/core/log/PurgeAuditLogs.java:39: warning: [text-blocks] trailing white space will be removed
              code = """
                     ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/runners/RunContextInitializer.java:36: warning: [unchecked] unchecked conversion
                      newOutputs.put(key, rehydrateOutputs(map));
                                                           ^
    required: Map<String,Object>
    found:    Map
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/plugins/EEPluginRegistry.java:126: warning: [rawtypes] found raw type: Class
                  registered.allClass().stream().map(Class::getName)
                                                     ^
    missing type arguments for generic class Class<T>
    where T is a type-variable:
      T extends Object declared in class Class
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/runners/RunContextInitializer.java:30: warning: [rawtypes] found raw type: Map
              if (value instanceof Map map) {
                                   ^
    missing type arguments for generic class Map<K,V>
    where K,V are type-variables:
      K extends Object declared in interface Map
      V extends Object declared in interface Map
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/plugin/ee/assets/File.java:37: warning: [this-escape] possible 'this' escape before subclass is fully initialized
          this.setSystem(system);
                        ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/plugin/ee/assets/VM.java:38: warning: [this-escape] possible 'this' escape before subclass is fully initialized
          this.setProvider(provider);
                          ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/plugin/ee/assets/Dataset.java:38: warning: [this-escape] possible 'this' escape before subclass is fully initialized
          this.setSystem(system);
                        ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/plugin/ee/assets/Table.java:39: warning: [this-escape] possible 'this' escape before subclass is fully initialized
          this.setSystem(system);
                        ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/apps/internals/DefaultAppContext.java:68: warning: [this-escape] possible 'this' escape before subclass is fully initialized
          Optional.ofNullable(context).ifPresent(ctx -> ctx.inject(this));
                                                 ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/killswitch/EEKillSwitchService.java:49: warning: [this-escape] possible 'this' escape before subclass is fully initialized
          reloadKillSwitches();
                            ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/plugins/EEPluginRegistry.java:64: warning: [this-escape] possible 'this' escape before subclass is fully initialized
          init();
              ^
  /home/runner/work/kestra-ee/kestra-ee/core-ee/src/main/java/io/kestra/ee/plugins/RemotePluginManager.java:119: warning: [this-escape] possible 'this' escape before subclass is fully initialized
          this.queueSubscriber = clusterEventQueue.subscriber().subscribe(either ->
                                                                          ^
  Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
  1 error
  58 warnings

* Try:
> Check your code and dependencies to fix the compilation error(s)
> Run with --scan to get full insights from a Build Scan (powered by Develocity).

Deprecated Gradle features were used in this build, making it incompatible with Gradle 10.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/9.4.1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD FAILED in 4m 13s
13 actionable tasks: 11 executed, 2 from cache

🧪 Java Unit Tests

TestsPassed ✅Skipped ⚠️FailedTime ⏱
Java Tests Report6817 ran6797 ✅20 ⚠️0 ❌49m 1s 464ms

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Tests report quick summary:

success ✅ > tests: 6817, success: 6797, skipped: 20, failed: 0 (🔄 6817 executed, 📦 0 from cache)

unfold for details
Project Status Success Skipped Failed
cli success ✅ 🔄 54 1 0
core success ✅ 🔄 2649 6 0
executor success ✅ 🔄 51 3 0
jdbc success ✅ 🔄 30 6 0
jdbc-h2 success ✅ 🔄 900 1 0
jdbc-mysql success ✅ 🔄 891 2 0
jdbc-postgres success ✅ 🔄 905 0 0
processor success ✅ 🔄 7 0 0
queue success ✅ 🔄 39 0 0
runner-memory success ✅ 🔄 1 0 0
scheduler success ✅ 🔄 87 0 0
script success ✅ 🔄 37 0 0
storage-local success ✅ 🔄 66 0 0
tests success ✅ 🔄 2 0 0
webserver success ✅ 🔄 887 1 0
worker success ✅ 🔄 58 0 0
worker-controller success ✅ 🔄 133 0 0

Develocity build scan: https://develocity.kestra.io/s/nqbtbzezdhimu


Flaky tests report quick summary:

failed ❌ > tests: 14, success: 13, skipped: 0, failed: 1

Project Status Success Skipped Failed
cli success ✅ 2 0 0
jdbc-h2 success ✅ 1 0 0
jdbc-mysql success ✅ 2 0 0
jdbc-postgres success ✅ 1 0 0
script success ✅ 2 0 0
webserver failed ❌ 5 0 1

Failed tests:

webserver > io.kestra.webserver.controllers.api.MiscControllerTest > canTriggerAWebhookWithoutBasicAuth() failed ❌ in 30.526
java.lang.AssertionError: [can trigger this Flow webhook when not authenticated] &#10;Expecting code not to raise a throwable but caught&#10;  "io.micronaut.http.client.exceptions.HttpClientResponseException: Client '/': Internal Server Error&#10;&#9;at io.micronaut.http.client.netty.DefaultHttpClient.makeErrorFromRequestBody(DefaultHttpClient.java:2142)&#10;&#9;at io.micronaut.http.client.netty.DefaultHttpClient.handleExchangeResponse(DefaultHttpClient.java:960)&#10;&#9;at io.micronaut.http.client.netty.DefaultHttpClient.lambda$exchange$9(DefaultHttpClient.java:913)&#10;&#9;at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)&#10;&#9;at io.micronaut.http.client.netty.DefaultHttpClient.lambda$exchange$10(DefaultHttpClient.java:913)&#10;&#9;at io.micronaut.http.client.netty.DefaultHttpClient.lambda$sendRequestWithRedirectsNoFilter$44(DefaultHttpClient.java:1666)&#10;&#9;at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)&#10;&#9;at io.micronaut.core.execution.DelayedExecutionFlowImpl$FlatMap.apply(DelayedExecutionFlowImpl.java:359)&#10;&#9;at io.micronaut.core.execution.DelayedExecutionFlowImpl.work(DelayedExecutionFlowImpl.java:58)&#10;&#9;at io.micronaut.core.execution.DelayedExecutionFlowImpl.completeLazy(DelayedExecutionFlowImpl.java:80)&#10;&#9;at io.micronaut.core.execution.DelayedExecutionFlowImpl.complete(DelayedExecutionFlowImpl.java:99)&#10;&#9;at io.micronaut.http.client.netty.DefaultHttpClient$6.complete(DefaultHttpClient.java:1763)&#10;&#9;at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.complete(Http1ResponseHandler.java:226)&#10;&#9;at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.read(Http1ResponseHandler.java:187)&#10;&#9;at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.read(Http1ResponseHandler.java:161)&#10;&#9;at io.micronaut.http.client.netty.Http1ResponseHandler.channelReadInstrumented(Http1ResponseHandler.java:75)&#10;&#9;at io.micronaut.http.client.netty.Http1ResponseHandler.channelReadInstrumented(Http1ResponseHandler.java:50)&#10;&#9;at io.micronaut.http.client.netty.SimpleChannelInboundHandlerInstrumented.channelRead0(SimpleChannelInboundHandlerInstrumented.java:46)&#10;&#9;at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)&#10;&#9;at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)&#10;&#9;at io.netty.handler.codec.http.HttpContentDecoder.decode(HttpContentDecoder.java:170)&#10;&#9;at io.netty.handler.codec.http.HttpContentDecoder.decode(HttpContentDecoder.java:48)&#10;&#9;at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:91)&#10;&#9;at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)&#10;&#9;at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:434)&#10;&#9;at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:361)&#10;&#9;at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:325)&#10;&#9;at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:249)&#10;&#9;at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)&#10;&#9;at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:288)&#10;&#9;at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)&#10;&#9;at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1429)&#10;&#9;at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:918)&#10;&#9;at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:176)&#10;&#9;at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.handle(AbstractNioChannel.java:445)&#10;&#9;at io.netty.channel.nio.NioIoHandler$DefaultNioRegistration.handle(NioIoHandler.java:388)&#10;&#9;at io.netty.channel.nio.NioIoHandler.processSelectedKey(NioIoHandler.java:596)&#10;&#9;at io.netty.channel.nio.NioIoHandler.processSelectedKeysPlain(NioIoHandler.java:541)&#10;&#9;at io.netty.channel.nio.NioIoHandler.processSelectedKeys(NioIoHandler.java:514)&#10;&#9;at io.netty.channel.nio.NioIoHandler.run(NioIoHandler.java:484)&#10;&#9;at io.netty.channel.SingleThreadIoEventLoop.runIo(SingleThreadIoEventLoop.java:225)&#10;&#9;at io.netty.channel.SingleThreadIoEventLoop.run(SingleThreadIoEventLoop.java:196)&#10;&#9;at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:1195)&#10;&#9;at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)&#10;&#9;at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)&#10;&#9;at java.base/java.lang.Thread.run(Thread.java:1474)&#10;&#9;Suppressed: java.lang.Exception: #block terminated with an error&#10;&#9;&#9;at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:104)&#10;&#9;&#9;at reactor.core.publisher.Mono.block(Mono.java:1779)&#10;&#9;&#9;at io.micronaut.http.client.netty.DefaultHttpClient$1.exchange(DefaultHttpClient.java:612)&#10;&#9;&#9;at io.micronaut.http.client.netty.DefaultHttpClient$1.retrieve(DefaultHttpClient.java:620)&#10;&#9;&#9;at io.micronaut.http.client.BlockingHttpClient.retrieve(BlockingHttpClient.java:123)&#10;&#9;&#9;at io.micronaut.http.client.BlockingHttpClient.retrieve(BlockingHttpClient.java:172)&#10;&#9;&#9;at io.kestra.webserver.controllers.api.MiscControllerTest.lambda$canTriggerAWebhookWithoutBasicAuth$1(MiscControllerTest.java:220)&#10;&#9;&#9;at org.assertj.core.api.ThrowableAssert.catchThrowable(ThrowableAssert.java:66)&#10;&#9;&#9;at org.assertj.core.api.AssertionsForClassTypes.catchThrowable(AssertionsForClassTypes.java:908)&#10;&#9;&#9;at org.assertj.core.api.AssertionsForClassTypes.assertThatCode(AssertionsForClassTypes.java:879)&#10;&#9;&#9;at org.assertj.core.api.Assertions.assertThatCode(Assertions.java:1396)&#10;&#9;&#9;at io.kestra.webserver.controllers.api.MiscControllerTest.canTriggerAWebhookWithoutBasicAuth(MiscControllerTest.java:219)&#10;&#9;&#9;at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)&#10;&#9;&#9;at java.base/java.lang.reflect.Method.invoke(Method.java:565)&#10;&#9;&#9;at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:786)&#10;&#9;&#9;at org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:514)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)&#10;&#9;&#9;at io.micronaut.test.extensions.junit5.MicronautJunit5Extension$2.proceed(MicronautJunit5Extension.java:154)&#10;&#9;&#9;at io.micronaut.test.extensions.AbstractMicronautExtension.interceptEach(AbstractMicronautExtension.java:171)&#10;&#9;&#9;at io.micronaut.test.extensions.AbstractMicronautExtension.interceptTest(AbstractMicronautExtension.java:128)&#10;&#9;&#9;at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.interceptTestMethod(MicronautJunit5Extension.java:141)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)&#10;&#9;&#9;at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:161)&#10;&#9;&#9;at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:152)&#10;&#9;&#9;at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:91)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:93)&#10;&#9;&#9;at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:87)&#10;&#9;&#9;at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$4(TestMethodTestDescriptor.java:221)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)&#10;&#9;&#9;at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:217)&#10;&#9;&#9;at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:159)&#10;&#9;&#9;at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:157)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)&#10;&#9;&#9;at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)&#10;&#9;&#9;at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)&#10;&#9;&#9;at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)&#10;&#9;&#9;at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:230)&#10;&#9;&#9;at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:204)&#10;&#9;&#9;at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:172)&#10;&#9;&#9;at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:101)&#10;&#9;&#9;at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:64)&#10;&#9;&#9;at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:150)&#10;&#9;&#9;at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:63)&#10;&#9;&#9;at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:109)&#10;&#9;&#9;at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:91)&#10;&#9;&#9;at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)&#10;&#9;&#9;at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$1(InterceptingLauncher.java:39)&#10;&#9;&#9;at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25)&#10;&#9;&#9;at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:38)&#10;&#9;&#9;at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)&#10;&#9;&#9;at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor$CollectThenExecuteTestDefinitionConsumer.processAllTestDefinitions(JUnitPlatformTestDefinitionProcessor.java:179)&#10;&#9;&#9;at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor$CollectThenExecuteTestDefinitionConsumer.access$000(JUnitPlatformTestDefinitionProcessor.java:122)&#10;&#9;&#9;at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor.stop(JUnitPlatformTestDefinitionProcessor.java:116)&#10;&#9;&#9;at org.gradle.api.internal.tasks.testing.SuiteTestDefinitionProcessor.stop(SuiteTestDefinitionProcessor.java:63)&#10;&#9;&#9;at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)&#10;&#9;&#9;at java.base/java.lang.reflect.Method.invoke(Method.java:565)&#10;&#9;&#9;at org.gradle.internal.dispatch.MethodInvocation.invokeOn(MethodInvocation.java:77)&#10;&#9;&#9;at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:28)&#10;&#9;&#9;at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:19)&#10;&#9;&#9;at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)&#10;&#9;&#9;at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:88)&#10;&#9;&#9;at jdk.proxy2/jdk.proxy2.$Proxy6.stop(Unknown Source)&#10;&#9;&#9;at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:195)&#10;&#9;&#9;at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:126)&#10;&#9;&#9;at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103)&#10;&#9;&#9;at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63)&#10;&#9;&#9;at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)&#10;&#9;&#9;at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:122)&#10;&#9;&#9;at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:72)&#10;&#9;&#9;at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)&#10;&#9;&#9;at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)&#10;"

java.lang.AssertionError: [can trigger this Flow webhook when not authenticated] 
Expecting code not to raise a throwable but caught
  "io.micronaut.http.client.exceptions.HttpClientResponseException: Client '/': Internal Server Error
	at io.micronaut.http.client.netty.DefaultHttpClient.makeErrorFromRequestBody(DefaultHttpClient.java:2142)
	at io.micronaut.http.client.netty.DefaultHttpClient.handleExchangeResponse(DefaultHttpClient.java:960)
	at io.micronaut.http.client.netty.DefaultHttpClient.lambda$exchange$9(DefaultHttpClient.java:913)
	at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
	at io.micronaut.http.client.netty.DefaultHttpClient.lambda$exchange$10(DefaultHttpClient.java:913)
	at io.micronaut.http.client.netty.DefaultHttpClient.lambda$sendRequestWithRedirectsNoFilter$44(DefaultHttpClient.java:1666)
	at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl$FlatMap.apply(DelayedExecutionFlowImpl.java:359)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl.work(DelayedExecutionFlowImpl.java:58)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl.completeLazy(DelayedExecutionFlowImpl.java:80)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl.complete(DelayedExecutionFlowImpl.java:99)
	at io.micronaut.http.client.netty.DefaultHttpClient$6.complete(DefaultHttpClient.java:1763)
	at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.complete(Http1ResponseHandler.java:226)
	at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.read(Http1ResponseHandler.java:187)
	at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.read(Http1ResponseHandler.java:161)
	at io.micronaut.http.client.netty.Http1ResponseHandler.channelReadInstrumented(Http1ResponseHandler.java:75)
	at io.micronaut.http.client.netty.Http1ResponseHandler.channelReadInstrumented(Http1ResponseHandler.java:50)
	at io.micronaut.http.client.netty.SimpleChannelInboundHandlerInstrumented.channelRead0(SimpleChannelInboundHandlerInstrumented.java:46)
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.codec.http.HttpContentDecoder.decode(HttpContentDecoder.java:170)
	at io.netty.handler.codec.http.HttpContentDecoder.decode(HttpContentDecoder.java:48)
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:91)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:434)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:361)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:325)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:249)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:288)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1429)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:918)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:176)
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.handle(AbstractNioChannel.java:445)
	at io.netty.channel.nio.NioIoHandler$DefaultNioRegistration.handle(NioIoHandler.java:388)
	at io.netty.channel.nio.NioIoHandler.processSelectedKey(NioIoHandler.java:596)
	at io.netty.channel.nio.NioIoHandler.processSelectedKeysPlain(NioIoHandler.java:541)
	at io.netty.channel.nio.NioIoHandler.processSelectedKeys(NioIoHandler.java:514)
	at io.netty.channel.nio.NioIoHandler.run(NioIoHandler.java:484)
	at io.netty.channel.SingleThreadIoEventLoop.runIo(SingleThreadIoEventLoop.java:225)
	at io.netty.channel.SingleThreadIoEventLoop.run(SingleThreadIoEventLoop.java:196)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:1195)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:1474)
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:104)
		at reactor.core.publisher.Mono.block(Mono.java:1779)
		at io.micronaut.http.client.netty.DefaultHttpClient$1.exchange(DefaultHttpClient.java:612)
		at io.micronaut.http.client.netty.DefaultHttpClient$1.retrieve(DefaultHttpClient.java:620)
		at io.micronaut.http.client.BlockingHttpClient.retrieve(BlockingHttpClient.java:123)
		at io.micronaut.http.client.BlockingHttpClient.retrieve(BlockingHttpClient.java:172)
		at io.kestra.webserver.controllers.api.MiscControllerTest.lambda$canTriggerAWebhookWithoutBasicAuth$1(MiscControllerTest.java:220)
		at org.assertj.core.api.ThrowableAssert.catchThrowable(ThrowableAssert.java:66)
		at org.assertj.core.api.AssertionsForClassTypes.catchThrowable(AssertionsForClassTypes.java:908)
		at org.assertj.core.api.AssertionsForClassTypes.assertThatCode(AssertionsForClassTypes.java:879)
		at org.assertj.core.api.Assertions.assertThatCode(Assertions.java:1396)
		at io.kestra.webserver.controllers.api.MiscControllerTest.canTriggerAWebhookWithoutBasicAuth(MiscControllerTest.java:219)
		at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
		at java.base/java.lang.reflect.Method.invoke(Method.java:565)
		at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:786)
		at org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:514)
		at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
		at io.micronaut.test.extensions.junit5.MicronautJunit5Extension$2.proceed(MicronautJunit5Extension.java:154)
		at io.micronaut.test.extensions.AbstractMicronautExtension.interceptEach(AbstractMicronautExtension.java:171)
		at io.micronaut.test.extensions.AbstractMicronautExtension.interceptTest(AbstractMicronautExtension.java:128)
		at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.interceptTestMethod(MicronautJunit5Extension.java:141)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
		at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:161)
		at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:152)
		at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:91)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:93)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:87)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$4(TestMethodTestDescriptor.java:221)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:217)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:159)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:157)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)
		at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)
		at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
		at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)
		at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)
		at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
		at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)
		at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)
		at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
		at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
		at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:230)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:204)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:172)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:101)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:64)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:150)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:63)
		at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:109)
		at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:91)
		at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)
		at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$1(InterceptingLauncher.java:39)
		at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25)
		at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:38)
		at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)
		at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor$CollectThenExecuteTestDefinitionConsumer.processAllTestDefinitions(JUnitPlatformTestDefinitionProcessor.java:179)
		at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor$CollectThenExecuteTestDefinitionConsumer.access$000(JUnitPlatformTestDefinitionProcessor.java:122)
		at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor.stop(JUnitPlatformTestDefinitionProcessor.java:116)
		at org.gradle.api.internal.tasks.testing.SuiteTestDefinitionProcessor.stop(SuiteTestDefinitionProcessor.java:63)
		at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
		at java.base/java.lang.reflect.Method.invoke(Method.java:565)
		at org.gradle.internal.dispatch.MethodInvocation.invokeOn(MethodInvocation.java:77)
		at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:28)
		at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:19)
		at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
		at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:88)
		at jdk.proxy2/jdk.proxy2.$Proxy6.stop(Unknown Source)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:195)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:126)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63)
		at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
		at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:122)
		at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:72)
		at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
		at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
"
	at io.kestra.webserver.controllers.api.MiscControllerTest.canTriggerAWebhookWithoutBasicAuth(MiscControllerTest.java:230)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension$2.proceed(MicronautJunit5Extension.java:154)
	at io.micronaut.test.extensions.AbstractMicronautExtension.interceptEach(AbstractMicronautExtension.java:171)
	at io.micronaut.test.extensions.AbstractMicronautExtension.interceptTest(AbstractMicronautExtension.java:128)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.interceptTestMethod(MicronautJunit5Extension.java:141)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)

@flcarre flcarre force-pushed the feat/enriched-execution-logs-v2 branch 4 times, most recently from 151db20 to 112b1cf Compare June 5, 2026 14:17
@flcarre flcarre requested review from a team and elevatebart June 5, 2026 15:47
flcarre and others added 3 commits June 5, 2026 17:52
…gation, display settings

Make the execution Logs tab and the global /logs page structure-aware and
navigable (BHP request kestra-ee#8305; ships the JSON-in-logs ask #7978).

Log line:
- Inline collapsible JSON tree for structured lines (heuristic detection,
  parse-on-expand, raw fallback, --ks-editor-* highlight, no v-html).
- Collapse consecutive near-identical lines into a "xN similar" group.
- Redesigned line: level rail, metadata pills, message on its own row.
- Highlight search matches (tokenized over rendered HTML, entity/ReDoS-safe).

Toolbar (both pages, DS 2.0):
- Sticky bar with per-icon KsButton surfaces matching the filter bar.
- Display-settings popover: density (row height), font size, pretty JSON,
  expand-by-default, body line-clamp.
- Download with a filter modal; copy with a toast.
- Click-a-value popover (Filter for / out / Copy / Open page) wired to the
  log query; click a log's level to filter by it.

Level navigation:
- Execution: per-level count + jump to next/prev with scroll-into-view.
- Global: server-wide per-level counts (visible even when page 1 has none);
  clicking a level chip filters at-or-above that level.

Pure helpers (utils/logs.ts, logValueFilter.ts) covered by 19 unit tests.

Closes kestra-io/kestra-ee#8305

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- logValueFilter: strip all existing comparators for a field before adding
  the new one, preventing self-contradictory API queries (EQUALS + NOT_EQUALS
  coexisting on the same field after toggling filter direction)
- TaskRunDetails: clear expandedGroups when props.filter changes (log
  reindexing from 0 after filter shifts all group keys, leaving stale entries)
- TaskRunDetails: replace O(n²) array spreads in log-index accumulators
  with push() calls
- Logs.vue / LogsWrapper.vue: use :tooltip prop on icon-only KsButtons
  instead of wrapping in KsTooltip (DS convention)
- LogLine.vue: replace hardcoded px values with rem equivalents

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…onents

Cover all 4 new components introduced by the enriched-execution-logs-v2 PR:
JsonTree (6 stories), LogValueActions (4 stories, incl. click-to-open play test),
LogDisplaySettings (2 stories, incl. open play test), LogLevelNavigator (5 stories).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@flcarre flcarre force-pushed the feat/enriched-execution-logs-v2 branch from fadf696 to 1a87a02 Compare June 5, 2026 15:52
@flcarre

flcarre commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author
🤖 Review walkthrough — post-review fixes

Changes applied after code review

Two commits were added on top of the original feature:

fix(design-system) — 5 bugs addressed

File Issue Fix
logValueFilter.ts:24 Conflicting operators — filter-for + filter-out on the same field both survived in the URL, sending a self-contradictory query to the API Strip all filters[field][*] keys before inserting the new comparator
TaskRunDetails.vue Stale expandedGroups — log items are re-indexed from 0 after props.filter changes, making all Set keys stale; previously-expanded groups stayed "expanded" forever watch(() => props.filter, () => { expandedGroups.value = new Set() })
TaskRunDetails.vue O(n²) array spreads in currentTaskRunsLogIndicesByLevel and allLogIndicesByLevel computeds ([...acc, item] per iteration = ~50M copies at 10K logs) Replace with (acc[key] ??= []).push(item)
Logs.vue / LogsWrapper.vue DS violation — icon-only KsButtons wrapped in <KsTooltip> instead of using :tooltip prop Replaced with :tooltip prop directly on KsButton
LogLine.vue Hardcoded px values in CSS (gap: 3px, padding: 1px, margin: 2px) Converted to rem equivalents (0.1875rem, 0.0625rem, 0.125rem)

test(design-system) — 4 Storybook story files, 17 stories

New components introduced by this PR were missing stories entirely:

Component Stories added
JsonTree Object, Array, Collapsed, DeeplyNested, MixedTypes, Leaf
LogValueActions FilterableWithLink, FilterableNoLink, CopyOnly, OpensOnClick (with play interaction test)
LogDisplaySettings Default, Opened (with play interaction test)
LogLevelNavigator Navigation, NoSelection, FilterMode, AllLevels, AllLevelsFilterMode

All 17 stories pass. All 861 unit tests pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To review

Development

Successfully merging this pull request may close these issues.

1 participant