upgrade Java-25 Kotlin-2.3.0 usw.
This commit is contained in:
@@ -0,0 +1,323 @@
|
||||
Meldestelle on main [✘»!+?] via 🅶 v9.2.1 via ☕ v25.0.1 via 🅺 v2.3.0
|
||||
❯ ./gradlew :backend:infrastructure:gateway:test --stacktrace
|
||||
Starting a Gradle Daemon, 2 stopped Daemons could not be reused, use --status for details
|
||||
Type-safe project accessors is an incubating feature.
|
||||
|
||||
> Task :backend:infrastructure:gateway:test
|
||||
|
||||
WebFluxSmokeTest > should load reactive web context and serve smoke endpoint() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
|
||||
Caused by: java.lang.IllegalStateException at SpringBootCondition.java:60
|
||||
Caused by: java.lang.IllegalArgumentException at ClassUtils.java:372
|
||||
Caused by: java.lang.ClassNotFoundException at BuiltinClassLoader.java:642
|
||||
|
||||
GatewayFiltersTests > should preserve existing correlation ID header() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
|
||||
Caused by: java.lang.IllegalStateException at SpringBootCondition.java:60
|
||||
Caused by: java.lang.IllegalArgumentException at ClassUtils.java:372
|
||||
Caused by: java.lang.ClassNotFoundException at BuiltinClassLoader.java:642
|
||||
|
||||
GatewayFiltersTests > should handle requests with X-Forwarded-For header() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayFiltersTests > should apply admin rate limit for admin users() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayFiltersTests > should add rate limiting headers() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayFiltersTests > should add correlation ID header when not present() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayFiltersTests > should apply different rate limits for auth endpoints() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayFiltersTests > should enforce rate limiting after exceeding limit() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayFiltersTests > should apply higher rate limit for authenticated users() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
KeycloakGatewayIntegrationTest > should initialize Spring context with Keycloak configuration() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
|
||||
Caused by: java.lang.IllegalStateException at SpringBootCondition.java:60
|
||||
Caused by: java.lang.IllegalArgumentException at ClassUtils.java:372
|
||||
Caused by: java.lang.ClassNotFoundException at BuiltinClassLoader.java:642
|
||||
|
||||
GatewayApplicationTests > contextLoads() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
|
||||
Caused by: java.lang.IllegalStateException at SpringBootCondition.java:60
|
||||
Caused by: java.lang.IllegalArgumentException at ClassUtils.java:372
|
||||
Caused by: java.lang.ClassNotFoundException at BuiltinClassLoader.java:642
|
||||
|
||||
FallbackControllerTests > should handle POST requests to masterdata fallback() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
|
||||
Caused by: java.lang.IllegalStateException at SpringBootCondition.java:60
|
||||
Caused by: java.lang.IllegalArgumentException at ClassUtils.java:372
|
||||
Caused by: java.lang.ClassNotFoundException at BuiltinClassLoader.java:642
|
||||
|
||||
FallbackControllerTests > should handle POST requests to default fallback() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > should return masterdata service fallback response() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > should handle POST requests to members fallback() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > should handle POST requests to events fallback() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > should return valid JSON structure for all fallback responses() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > should handle POST requests to auth fallback() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > sollte Members Service Fallback Response zurueckgeben() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > should have consistent error response structure() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > sollte Events Service Fallback Response zurueckgeben() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > should return auth service fallback response() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > sollte Horses Service Fallback Response zurueckgeben() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > should handle POST requests to horses fallback() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
FallbackControllerTests > should return default fallback response for unknown service() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should handle different HTTP methods allowed in CORS() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
|
||||
Caused by: java.lang.IllegalStateException at SpringBootCondition.java:60
|
||||
Caused by: java.lang.IllegalArgumentException at ClassUtils.java:372
|
||||
Caused by: java.lang.ClassNotFoundException at BuiltinClassLoader.java:642
|
||||
|
||||
GatewaySecurityTests > should handle complex CORS scenarios() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should handle PUT requests with CORS headers() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should allow requests from meldestelle domain() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should allow credentials in CORS requests() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should set max age for CORS requests() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should handle authorization headers in CORS requests() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should not duplicate CORS headers due to deduplication filter() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should handle CORS preflight requests() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should allow requests from localhost origins() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should maintain security headers in responses() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should handle POST requests with CORS headers() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewaySecurityTests > should handle DELETE requests with CORS headers() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayRoutingTests > should route ping service requests() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
|
||||
Caused by: java.lang.IllegalStateException at SpringBootCondition.java:60
|
||||
Caused by: java.lang.IllegalArgumentException at ClassUtils.java:372
|
||||
Caused by: java.lang.ClassNotFoundException at BuiltinClassLoader.java:642
|
||||
|
||||
GatewayRoutingTests > should route horses service requests() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayRoutingTests > should handle gateway info path request() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayRoutingTests > should route members service requests() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayRoutingTests > auth route is not configured anymore() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayRoutingTests > should route masterdata service requests() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
GatewayRoutingTests > should route events service requests() FAILED
|
||||
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:145
|
||||
|
||||
45 tests completed, 45 failed
|
||||
|
||||
> Task :backend:infrastructure:gateway:test FAILED
|
||||
|
||||
[Incubating] Problems report is available at: file:///home/stefan/WsMeldestelle/Meldestelle/build/reports/problems/problems-report.html
|
||||
|
||||
FAILURE: Build failed with an exception.
|
||||
|
||||
* What went wrong:
|
||||
Execution failed for task ':backend:infrastructure:gateway:test'.
|
||||
> There were failing tests. See the report at: file:///home/stefan/WsMeldestelle/Meldestelle/backend/infrastructure/gateway/build/reports/tests/test/index.html
|
||||
|
||||
* Try:
|
||||
> Run with --scan to generate a Build Scan (powered by Develocity).
|
||||
|
||||
* Exception is:
|
||||
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':backend:infrastructure:gateway:test'.
|
||||
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:135)
|
||||
at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:288)
|
||||
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:133)
|
||||
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:121)
|
||||
at org.gradle.api.internal.tasks.execution.ProblemsTaskPathTrackingTaskExecuter.execute(ProblemsTaskPathTrackingTaskExecuter.java:41)
|
||||
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
|
||||
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
|
||||
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
|
||||
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
|
||||
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
|
||||
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
|
||||
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
|
||||
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
|
||||
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
|
||||
at org.gradle.execution.plan.DefaultNodeExecutor.executeLocalTaskNode(DefaultNodeExecutor.java:55)
|
||||
at org.gradle.execution.plan.DefaultNodeExecutor.execute(DefaultNodeExecutor.java:34)
|
||||
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355)
|
||||
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
|
||||
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.lambda$execute$0(DefaultTaskExecutionGraph.java:339)
|
||||
at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:84)
|
||||
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:339)
|
||||
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:328)
|
||||
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:459)
|
||||
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:376)
|
||||
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
||||
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
|
||||
Caused by: org.gradle.api.internal.exceptions.MarkedVerificationException: There were failing tests. See the report at: file:///home/stefan/WsMeldestelle/Meldestelle/backend/infrastructure/gateway/build/reports/tests/test/index.html
|
||||
at org.gradle.api.tasks.testing.AbstractTestTask.handleTestFailures(AbstractTestTask.java:703)
|
||||
at org.gradle.api.tasks.testing.AbstractTestTask.handleCollectedResults(AbstractTestTask.java:535)
|
||||
at org.gradle.api.tasks.testing.AbstractTestTask.executeTests(AbstractTestTask.java:527)
|
||||
at org.gradle.api.tasks.testing.Test.executeTests(Test.java:714)
|
||||
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:125)
|
||||
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
|
||||
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
|
||||
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
|
||||
at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:252)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
|
||||
at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:237)
|
||||
at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:220)
|
||||
at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:203)
|
||||
at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:170)
|
||||
at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:105)
|
||||
at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:44)
|
||||
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:59)
|
||||
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:56)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
|
||||
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:56)
|
||||
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:44)
|
||||
at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:42)
|
||||
at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:75)
|
||||
at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
|
||||
at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:50)
|
||||
at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:28)
|
||||
at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
|
||||
at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
|
||||
at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:61)
|
||||
at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:26)
|
||||
at org.gradle.internal.execution.steps.CaptureOutputsAfterExecutionStep.execute(CaptureOutputsAfterExecutionStep.java:69)
|
||||
at org.gradle.internal.execution.steps.CaptureOutputsAfterExecutionStep.execute(CaptureOutputsAfterExecutionStep.java:46)
|
||||
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:39)
|
||||
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:28)
|
||||
at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:189)
|
||||
at org.gradle.internal.execution.steps.BuildCacheStep.executeAndStoreInCache(BuildCacheStep.java:145)
|
||||
at org.gradle.internal.execution.steps.BuildCacheStep.lambda$executeWithCache$4(BuildCacheStep.java:101)
|
||||
at org.gradle.internal.execution.steps.BuildCacheStep.lambda$executeWithCache$5(BuildCacheStep.java:101)
|
||||
at org.gradle.internal.Try$Success.map(Try.java:170)
|
||||
at org.gradle.internal.execution.steps.BuildCacheStep.executeWithCache(BuildCacheStep.java:85)
|
||||
at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$0(BuildCacheStep.java:74)
|
||||
at org.gradle.internal.Either$Left.fold(Either.java:116)
|
||||
at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:62)
|
||||
at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:73)
|
||||
at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:48)
|
||||
at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:46)
|
||||
at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:35)
|
||||
at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:75)
|
||||
at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$2(SkipUpToDateStep.java:53)
|
||||
at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:53)
|
||||
at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:35)
|
||||
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
|
||||
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
|
||||
at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStep.executeDelegate(ResolveIncrementalCachingStateStep.java:49)
|
||||
at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStep.executeDelegate(ResolveIncrementalCachingStateStep.java:27)
|
||||
at org.gradle.internal.execution.steps.AbstractResolveCachingStateStep.execute(AbstractResolveCachingStateStep.java:71)
|
||||
at org.gradle.internal.execution.steps.AbstractResolveCachingStateStep.execute(AbstractResolveCachingStateStep.java:39)
|
||||
at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:64)
|
||||
at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:35)
|
||||
at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:62)
|
||||
at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:40)
|
||||
at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:76)
|
||||
at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:45)
|
||||
at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.executeWithNonEmptySources(AbstractSkipEmptyWorkStep.java:136)
|
||||
at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:66)
|
||||
at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:38)
|
||||
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
|
||||
at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:36)
|
||||
at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:23)
|
||||
at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:75)
|
||||
at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:41)
|
||||
at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.lambda$execute$0(AssignMutableWorkspaceStep.java:35)
|
||||
at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:297)
|
||||
at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:31)
|
||||
at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:22)
|
||||
at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:40)
|
||||
at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:23)
|
||||
at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.lambda$execute$2(ExecuteWorkBuildOperationFiringStep.java:67)
|
||||
at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:67)
|
||||
at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:39)
|
||||
at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:46)
|
||||
at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:34)
|
||||
at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
|
||||
at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:31)
|
||||
at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:64)
|
||||
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:132)
|
||||
... 30 more
|
||||
|
||||
|
||||
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.2.1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
|
||||
|
||||
BUILD FAILED in 23s
|
||||
17 actionable tasks: 4 executed, 13 up-to-date
|
||||
+1
-6
@@ -1,17 +1,12 @@
|
||||
package at.mocode.infrastructure.gateway.config
|
||||
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter
|
||||
import org.springframework.core.Ordered
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.web.server.ServerWebExchange
|
||||
import reactor.core.publisher.Mono
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.UUID
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Gateway-Konfiguration für erweiterte Funktionalitäten wie Logging, Rate Limiting und Security.
|
||||
|
||||
+2
@@ -1,6 +1,7 @@
|
||||
package at.mocode.infrastructure.gateway.config
|
||||
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import reactor.core.publisher.Mono
|
||||
@@ -14,6 +15,7 @@ class RateLimiterConfig {
|
||||
* Funktioniert out-of-the-box mit Keycloak (Resource Server), sofern Security aktiv ist.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "gateway.ratelimit.principal-key-resolver", name = ["enabled"], havingValue = "true", matchIfMissing = false)
|
||||
fun principalNameKeyResolver(): KeyResolver = KeyResolver { exchange ->
|
||||
exchange.getPrincipal<Principal>()
|
||||
.map { it.name }
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package at.mocode.infrastructure.gateway.config
|
||||
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver
|
||||
import org.springframework.context.annotation.Primary
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
@Configuration
|
||||
class RateLimitingConfig {
|
||||
|
||||
/**
|
||||
* Einfache IP-basierte KeyResolver-Implementierung für das RequestRateLimiter-Filter.
|
||||
* Nutzt X-Forwarded-For, wenn vorhanden, sonst die Remote-Adresse.
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
fun ipAddressKeyResolver(): KeyResolver = KeyResolver { exchange ->
|
||||
val forwardedFor = exchange.request.headers.getFirst("X-Forwarded-For")
|
||||
?.split(',')?.firstOrNull()?.trim()
|
||||
val ip = forwardedFor
|
||||
?: exchange.request.remoteAddress?.address?.hostAddress
|
||||
?: "unknown"
|
||||
Mono.just(ip)
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package at.mocode.infrastructure.gateway.error
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.web.server.ServerWebExchange
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
/**
|
||||
* Einfacher ProblemDetails-Handler für unerwartete Fehler im Gateway.
|
||||
* Gibt application/problem+json zurück mit Correlation-ID als traceId.
|
||||
*/
|
||||
@Component
|
||||
class ProblemDetailsExceptionHandler : ErrorWebExceptionHandler {
|
||||
|
||||
private val mapper = ObjectMapper()
|
||||
|
||||
override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> {
|
||||
// Versuche, Status aus Attributen zu lesen, ansonsten 500
|
||||
val status = exchange.response.statusCode?.value() ?: HttpStatus.INTERNAL_SERVER_ERROR.value()
|
||||
val traceId = exchange.request.headers.getFirst("X-Correlation-ID")
|
||||
val body = mapOf(
|
||||
"type" to "about:blank",
|
||||
"title" to (ex.message ?: "Unexpected error"),
|
||||
"status" to status,
|
||||
"traceId" to traceId
|
||||
)
|
||||
|
||||
exchange.response.statusCode = HttpStatus.valueOf(status)
|
||||
exchange.response.headers.contentType = MediaType.APPLICATION_PROBLEM_JSON
|
||||
|
||||
val bytes = mapper.writeValueAsBytes(body)
|
||||
val buffer = exchange.response.bufferFactory().wrap(bytes)
|
||||
return exchange.response.writeWith(Mono.just(buffer))
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package at.mocode.infrastructure.gateway.fallback
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Alternative FallbackController (deaktiviert per Default), nur aktivierbar über
|
||||
* property `gateway.customFallback.enabled=true`. Standardmäßig existiert bereits
|
||||
* ein FallbackController unter `...gateway.controller.FallbackController`.
|
||||
*/
|
||||
@RestController
|
||||
@ConditionalOnProperty(prefix = "gateway.customFallback", name = ["enabled"], havingValue = "true", matchIfMissing = false)
|
||||
class FallbackController {
|
||||
|
||||
@RequestMapping("/fallback/ping")
|
||||
fun pingFallback(): ResponseEntity<Map<String, Any>> =
|
||||
ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(
|
||||
mapOf(
|
||||
"message" to "Ping service unavailable",
|
||||
"timestamp" to Instant.now().toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
+23
-3
@@ -46,13 +46,18 @@ class SecurityConfig(
|
||||
pathMatchers(*securityProperties.publicPaths.toTypedArray()),
|
||||
permitAll
|
||||
)
|
||||
// Ping-API erfordert Admin-Rolle (Realm-Rolle "admin")
|
||||
authorize(pathMatchers("/api/ping/**"), hasRole("admin"))
|
||||
// Alle anderen Pfade erfordern eine Authentifizierung
|
||||
authorize(anyExchange, authenticated)
|
||||
}
|
||||
|
||||
// 4. JWT-Validierung via Keycloak aktivieren
|
||||
oauth2ResourceServer {
|
||||
jwt { }
|
||||
jwt {
|
||||
// Realm-Rollen (Keycloak) -> ROLE_* Authorities
|
||||
jwtAuthenticationConverter = realmRolesJwtAuthenticationConverter()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,6 +98,22 @@ class SecurityConfig(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konvertiert Keycloak Realm-Rollen (realm_access.roles) in Spring Authorities (ROLE_*),
|
||||
* sodass hasRole("admin") funktioniert.
|
||||
*/
|
||||
@Bean
|
||||
fun realmRolesJwtAuthenticationConverter(): org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter {
|
||||
val converter = org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter()
|
||||
converter.setJwtGrantedAuthoritiesConverter { jwt ->
|
||||
val roles = (jwt.claims["realm_access"] as? Map<*, *>)?.get("roles") as? Collection<*> ?: emptyList<Any>()
|
||||
roles
|
||||
.filterIsInstance<String>()
|
||||
.map { role -> org.springframework.security.core.authority.SimpleGrantedAuthority("ROLE_" + role.lowercase()) }
|
||||
}
|
||||
return org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter(converter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Definiert die zentrale und einzige CORS-Konfiguration für das Gateway.
|
||||
*/
|
||||
@@ -125,8 +146,7 @@ data class GatewaySecurityProperties(
|
||||
"/actuator/**",
|
||||
"/webjars/**",
|
||||
"/v3/api-docs/**",
|
||||
"/api/auth/**", // Alle Auth-Endpunkte
|
||||
"/api/ping/**"
|
||||
"/api/auth/**" // Alle Auth-Endpunkte
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
+1
-11
@@ -1,23 +1,13 @@
|
||||
# ===================================================================
|
||||
# Keycloak Profile Configuration
|
||||
# ===================================================================
|
||||
# This profile configures OAuth2/JWT authentication with Keycloak.
|
||||
# Uses Spring Security's oauth2ResourceServer for secure JWT validation.
|
||||
# ===================================================================
|
||||
|
||||
# migrated from application-keycloak.yml (standardized to .yaml)
|
||||
spring:
|
||||
security:
|
||||
oauth2:
|
||||
resourceserver:
|
||||
jwt:
|
||||
# Issuer URI for JWT validation - Docker internal: keycloak:8080, External: localhost:8180
|
||||
issuer-uri: ${KEYCLOAK_ISSUER_URI:http://keycloak:8180/realms/meldestelle}
|
||||
# JWK Set URI for fetching public keys to validate JWT signatures
|
||||
jwk-set-uri: ${KEYCLOAK_JWK_SET_URI:http://keycloak:8180/realms/meldestelle/protocol/openid-connect/certs}
|
||||
|
||||
# Keycloak-spezifische Konfiguration
|
||||
keycloak:
|
||||
# Internal Docker service name, external via port 8180
|
||||
server-url: ${KEYCLOAK_SERVER_URL:http://keycloak:8180}
|
||||
issuer-uri: ${KEYCLOAK_ISSUER_URI:http://keycloak:8180/realms/meldestelle}
|
||||
jwk-set-uri: ${KEYCLOAK_JWK_SET_URI:http://keycloak:8180/realms/meldestelle/protocol/openid-connect/certs}
|
||||
@@ -0,0 +1,35 @@
|
||||
spring:
|
||||
application:
|
||||
name: gateway
|
||||
autoconfigure:
|
||||
exclude:
|
||||
- org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration
|
||||
cloud:
|
||||
gateway:
|
||||
httpclient:
|
||||
connect-timeout: 3000
|
||||
response-timeout: 5s
|
||||
routes:
|
||||
- id: ping-service
|
||||
uri: http://ping-service:8080
|
||||
predicates:
|
||||
- Path=/api/ping/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- name: CircuitBreaker
|
||||
args:
|
||||
name: pingServiceCB
|
||||
fallbackUri: forward:/fallback/ping
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info,prometheus
|
||||
|
||||
# Gateway-spezifische Einstellungen
|
||||
gateway:
|
||||
ratelimit:
|
||||
enabled: false # Start: ausgeschaltet; zum Aktivieren default-filters plus RequestRateLimiter in YAML hinzufügen
|
||||
replenish-rate: 10
|
||||
burst-capacity: 20
|
||||
@@ -1,321 +0,0 @@
|
||||
# Port, auf dem das Gateway läuft
|
||||
server:
|
||||
port: ${GATEWAY_SERVER_PORT:8081}
|
||||
# Optimierte Netty-Konfiguration für reaktive Anwendungen
|
||||
netty:
|
||||
connection-timeout: 5s
|
||||
idle-timeout: 15s
|
||||
|
||||
# Der Name, unter dem sich das Gateway in Consul registriert
|
||||
spring:
|
||||
application:
|
||||
name: api-gateway
|
||||
profiles:
|
||||
active: ${SPRING_PROFILES_ACTIVE:dev}
|
||||
security:
|
||||
user:
|
||||
name: ${GATEWAY_ADMIN_USER:admin}
|
||||
password: ${GATEWAY_ADMIN_PASSWORD:admin}
|
||||
data:
|
||||
redis:
|
||||
host: ${REDIS_HOST:localhost}
|
||||
port: ${REDIS_PORT:6379}
|
||||
timeout: 3s
|
||||
cloud:
|
||||
consul:
|
||||
host: ${CONSUL_HOST:localhost}
|
||||
port: ${CONSUL_PORT:8500}
|
||||
enabled: ${CONSUL_ENABLED:true}
|
||||
discovery:
|
||||
enabled: ${CONSUL_ENABLED:true}
|
||||
register: ${CONSUL_ENABLED:true}
|
||||
health-check-path: /actuator/health
|
||||
health-check-interval: 10s
|
||||
instance-id: ${spring.application.name}-${server.port}-${random.uuid}
|
||||
gateway:
|
||||
server:
|
||||
webflux:
|
||||
httpclient:
|
||||
connect-timeout: 5000
|
||||
response-timeout: 30s
|
||||
pool:
|
||||
max-idle-time: 15s
|
||||
max-life-time: 60s
|
||||
default-filters:
|
||||
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
|
||||
- name: Retry
|
||||
args:
|
||||
retries: 2
|
||||
statuses: BAD_GATEWAY,GATEWAY_TIMEOUT
|
||||
methods: GET
|
||||
backoff:
|
||||
firstBackoff: 100ms
|
||||
maxBackoff: 1000ms
|
||||
factor: 2
|
||||
basedOnPreviousValue: false
|
||||
- name: AddResponseHeader
|
||||
args:
|
||||
name: X-Content-Type-Options
|
||||
value: nosniff
|
||||
- name: AddResponseHeader
|
||||
args:
|
||||
name: X-Frame-Options
|
||||
value: DENY
|
||||
- name: AddResponseHeader
|
||||
args:
|
||||
name: Referrer-Policy
|
||||
value: strict-origin-when-cross-origin
|
||||
- name: AddResponseHeader
|
||||
args:
|
||||
name: Cache-Control
|
||||
value: no-cache, no-store, must-revalidate
|
||||
routes:
|
||||
|
||||
# ==============================================================
|
||||
# --- Gateway-Info-Route (optional) ---
|
||||
# ==============================================================
|
||||
- id: gateway-info-route
|
||||
uri: http://localhost:${server.port}
|
||||
predicates:
|
||||
- Method=GET
|
||||
- Path=/gateway-info
|
||||
filters:
|
||||
- name: SetStatus
|
||||
args:
|
||||
status: 200
|
||||
- name: SetResponseHeader
|
||||
args:
|
||||
name: Content-Type
|
||||
value: application/json
|
||||
|
||||
# ==============================================================
|
||||
# --- Ping-Service-Integration (optional) ---
|
||||
# ==============================================================
|
||||
- id: ping-service-route
|
||||
uri: lb://ping-service
|
||||
predicates:
|
||||
- Path=/api/ping/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- name: CircuitBreaker
|
||||
args:
|
||||
name: pingCircuitBreaker
|
||||
fallbackUri: forward:/fallback/ping
|
||||
- name: RequestRateLimiter
|
||||
args:
|
||||
key-resolver: "#{@principalNameKeyResolver}"
|
||||
redis-rate-limiter.replenishRate: ${PING_RATE_LIMIT_REPLENISH_RATE:50}
|
||||
redis-rate-limiter.burstCapacity: ${PING_RATE_LIMIT_BURST:100}
|
||||
|
||||
# ==============================================================
|
||||
# --- Entries-Service-Integration (MP-27) ---
|
||||
# ==============================================================
|
||||
- id: entries-service-route
|
||||
uri: lb://entries-service
|
||||
predicates:
|
||||
- Path=/api/entries/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# Mappe das Root "/api/entries" explizit auf die Service-Root "/"
|
||||
- id: entries-service-root
|
||||
uri: lb://entries-service
|
||||
predicates:
|
||||
- Path=/api/entries
|
||||
filters:
|
||||
- SetPath=/
|
||||
|
||||
- id: entries-service-root-slash
|
||||
uri: lb://entries-service
|
||||
predicates:
|
||||
- Path=/api/entries/
|
||||
filters:
|
||||
- SetPath=/
|
||||
|
||||
# ==============================================================
|
||||
# --- Members-Service-Integration (optional) ---
|
||||
# ==============================================================
|
||||
# - id: members-service-route
|
||||
# uri: lb://members-service
|
||||
# predicates:
|
||||
# - Path=/api/members/**
|
||||
# filters:
|
||||
# - StripPrefix=1
|
||||
# - name: CircuitBreaker
|
||||
# args:
|
||||
# name: membersCircuitBreaker
|
||||
# fallbackUri: forward:/fallback/members
|
||||
|
||||
# ==============================================================
|
||||
# --- Horses-Service-Integration (optional) ---
|
||||
# ==============================================================
|
||||
# - id: horses-service-route
|
||||
# uri: lb://horses-service
|
||||
# predicates:
|
||||
# - Path=/api/horses/**
|
||||
# filters:
|
||||
# - StripPrefix=1
|
||||
# - name: CircuitBreaker
|
||||
# args:
|
||||
# name: horsesCircuitBreaker
|
||||
# fallbackUri: forward:/fallback/horses
|
||||
|
||||
# ==============================================================
|
||||
# --- Events-Service-Integration (optional) ---
|
||||
# ==============================================================
|
||||
# - id: events-service-route
|
||||
# uri: lb://events-service
|
||||
# predicates:
|
||||
# - Path=/api/events/**
|
||||
# filters:
|
||||
# - StripPrefix=1
|
||||
# - name: CircuitBreaker
|
||||
# args:
|
||||
# name: eventsCircuitBreaker
|
||||
# fallbackUri: forward:/fallback/events
|
||||
|
||||
# ==============================================================
|
||||
# --- Masterdata-Service-Integration (optional) ---
|
||||
# ==============================================================
|
||||
# - id: masterdata-service-route
|
||||
# uri: lb://masterdata-service
|
||||
# predicates:
|
||||
# - Path=/api/masterdata/**
|
||||
# filters:
|
||||
# - StripPrefix=1
|
||||
# - name: CircuitBreaker
|
||||
# args:
|
||||
# name: masterdataCircuitBreaker
|
||||
# fallbackUri: forward:/fallback/masterdata
|
||||
|
||||
# ==============================================================
|
||||
# --- Auth-Service-Integration (optional) ---
|
||||
# ==============================================================
|
||||
# - id: auth-service-route
|
||||
# uri: lb://auth-service
|
||||
# predicates:
|
||||
# - Path=/api/auth/**
|
||||
# filters:
|
||||
# - StripPrefix=1
|
||||
# - name: CircuitBreaker
|
||||
# args:
|
||||
# name: authCircuitBreaker
|
||||
# fallbackUri: forward:/fallback/auth
|
||||
# Circuit Breaker Konfiguration
|
||||
resilience4j:
|
||||
circuitbreaker:
|
||||
configs:
|
||||
default:
|
||||
registerHealthIndicator: true
|
||||
slidingWindowSize: 100
|
||||
minimumNumberOfCalls: 20
|
||||
permittedNumberOfCallsInHalfOpenState: 3
|
||||
automaticTransitionFromOpenToHalfOpenEnabled: true
|
||||
waitDurationInOpenState: 5s
|
||||
failureRateThreshold: 50
|
||||
eventConsumerBufferSize: 10
|
||||
recordExceptions:
|
||||
- org.springframework.web.client.HttpServerErrorException
|
||||
- java.util.concurrent.TimeoutException
|
||||
- java.io.IOException
|
||||
instances:
|
||||
defaultCircuitBreaker:
|
||||
baseConfig: default
|
||||
pingCircuitBreaker:
|
||||
baseConfig: default
|
||||
membersCircuitBreaker:
|
||||
baseConfig: default
|
||||
slidingWindowSize: 50
|
||||
horsesCircuitBreaker:
|
||||
baseConfig: default
|
||||
slidingWindowSize: 50
|
||||
eventsCircuitBreaker:
|
||||
baseConfig: default
|
||||
slidingWindowSize: 75
|
||||
masterdataCircuitBreaker:
|
||||
baseConfig: default
|
||||
slidingWindowSize: 30
|
||||
authCircuitBreaker:
|
||||
baseConfig: default
|
||||
slidingWindowSize: 20
|
||||
failureRateThreshold: 30
|
||||
|
||||
# Management und Monitoring
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info,metrics,prometheus,gateway,circuitbreakers
|
||||
base-path: /actuator
|
||||
cors:
|
||||
allowed-origins:
|
||||
- "https://*.meldestelle.at"
|
||||
- "http://localhost:*"
|
||||
allowed-methods: GET,POST
|
||||
allowed-headers: "*"
|
||||
allow-credentials: true
|
||||
endpoint:
|
||||
health:
|
||||
show-details: when_authorized
|
||||
show-components: when_authorized
|
||||
probes:
|
||||
enabled: true
|
||||
metrics:
|
||||
access: unrestricted
|
||||
info:
|
||||
access: unrestricted
|
||||
prometheus:
|
||||
access: unrestricted
|
||||
gateway:
|
||||
access: unrestricted
|
||||
circuitbreakers:
|
||||
enabled: true
|
||||
metrics:
|
||||
distribution:
|
||||
percentiles-histogram:
|
||||
http.server.requests: true
|
||||
percentiles:
|
||||
http.server.requests: 0.5,0.90,0.95,0.99
|
||||
minimum-expected-value:
|
||||
http.server.requests: 1ms
|
||||
maximum-expected-value:
|
||||
http.server.requests: 30s
|
||||
tags:
|
||||
application: ${spring.application.name}
|
||||
environment: ${spring.profiles.active}
|
||||
instance: ${spring.cloud.consul.discovery.instance-id}
|
||||
service: gateway
|
||||
component: infrastructure
|
||||
gateway: api-gateway
|
||||
info:
|
||||
env:
|
||||
enabled: true
|
||||
git:
|
||||
mode: full
|
||||
build:
|
||||
enabled: true
|
||||
java:
|
||||
enabled: true
|
||||
# Tracing-Konfiguration - Aktiviert (Micrometer Tracing + Zipkin)
|
||||
tracing:
|
||||
enabled: ${TRACING_ENABLED:false}
|
||||
sampling:
|
||||
probability: ${TRACING_SAMPLING_PROBABILITY:1.0}
|
||||
zipkin:
|
||||
tracing:
|
||||
endpoint: ${ZIPKIN_TRACING_ENDPOINT:http://localhost:9411/api/v2/spans}
|
||||
# Reduziert Verbindungsfehler, wenn Zipkin nicht verfügbar ist
|
||||
connect-timeout: 1s
|
||||
read-timeout: 10s
|
||||
|
||||
# Erweiterte Logging-Konfiguration
|
||||
logging:
|
||||
level:
|
||||
org.springframework.cloud.gateway: INFO
|
||||
org.springframework.cloud.loadbalancer: INFO
|
||||
org.springframework.cloud.consul: INFO
|
||||
at.mocode.infrastructure.gateway: INFO
|
||||
io.github.resilience4j: INFO
|
||||
reactor.netty.http.client: INFO
|
||||
|
||||
# (Redis-Konfiguration wurde in den bestehenden spring.data.redis-Block oben integriert)
|
||||
+2
-26
@@ -3,7 +3,7 @@ package at.mocode.infrastructure.gateway
|
||||
import at.mocode.infrastructure.gateway.config.TestSecurityConfig
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import at.mocode.infrastructure.gateway.support.GatewayTestContext
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
@@ -13,31 +13,7 @@ import org.springframework.test.web.reactive.server.WebTestClient
|
||||
* Tests für den Fallback Controller, der Circuit Breaker Szenarien behandelt.
|
||||
* Testet alle Fallback-Endpunkte für verschiedene Services.
|
||||
*/
|
||||
@SpringBootTest(
|
||||
classes = [GatewayApplication::class],
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = [
|
||||
// Externe Abhängigkeiten für Fallback-Tests deaktivieren
|
||||
"spring.cloud.discovery.enabled=false",
|
||||
"spring.cloud.consul.enabled=false",
|
||||
"spring.cloud.consul.config.enabled=false",
|
||||
"spring.cloud.consul.discovery.register=false",
|
||||
"spring.cloud.loadbalancer.enabled=false",
|
||||
// Circuit Breaker Health Indicator deaktivieren um Interferenzen zu vermeiden
|
||||
"resilience4j.circuitbreaker.configs.default.registerHealthIndicator=false",
|
||||
"management.health.circuitbreakers.enabled=false",
|
||||
// Custom Filter für reine Fallback-Tests deaktivieren
|
||||
"gateway.security.jwt.enabled=false",
|
||||
// Reaktiven Web-Anwendungstyp verwenden
|
||||
"spring.main.web-application-type=reactive",
|
||||
// Gateway Discovery deaktivieren
|
||||
"spring.cloud.gateway.server.webflux.discovery.locator.enabled=false",
|
||||
// Actuator Security deaktivieren
|
||||
"management.security.enabled=false",
|
||||
// Zufälligen Port setzen
|
||||
"server.port=0"
|
||||
]
|
||||
)
|
||||
@GatewayTestContext
|
||||
@ActiveProfiles("test")
|
||||
@Import(TestSecurityConfig::class)
|
||||
class FallbackControllerTests {
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package at.mocode.infrastructure.gateway
|
||||
|
||||
import org.springframework.boot.SpringBootConfiguration
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
|
||||
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration
|
||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
|
||||
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration
|
||||
import org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration
|
||||
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
|
||||
/**
|
||||
* Test-spezifische, minimale GatewayApplication. Diese Klasse überschattet die Produktions-
|
||||
* `GatewayApplication` während der Tests und deaktiviert problematische Auto-Konfigurationen,
|
||||
* lädt aber weiterhin unsere Komponenten aus dem Gateway-Paket.
|
||||
*/
|
||||
@SpringBootConfiguration
|
||||
@ComponentScan(basePackages = ["at.mocode.infrastructure.gateway"])
|
||||
@ImportAutoConfiguration(
|
||||
exclude = [
|
||||
// Spring Cloud Refresh/Context (CNF in Tests vermeiden)
|
||||
RefreshAutoConfiguration::class,
|
||||
// HTTP/WebClient in Basis-Context-Load-Tests nicht erforderlich
|
||||
HttpClientAutoConfiguration::class,
|
||||
WebClientAutoConfiguration::class,
|
||||
// Security AutoConfigs minimieren
|
||||
ReactiveOAuth2ResourceServerAutoConfiguration::class,
|
||||
SecurityAutoConfiguration::class,
|
||||
ReactiveSecurityAutoConfiguration::class
|
||||
]
|
||||
)
|
||||
class GatewayApplication
|
||||
+10
-5
@@ -2,6 +2,10 @@ package at.mocode.infrastructure.gateway
|
||||
|
||||
import at.mocode.infrastructure.gateway.config.TestSecurityConfig
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration
|
||||
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration
|
||||
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
@@ -11,8 +15,8 @@ import org.springframework.test.context.ActiveProfiles
|
||||
* Verwendet ein Test-Profil, um Produktions-Filter und externe Abhängigkeiten zu deaktivieren.
|
||||
*/
|
||||
@SpringBootTest(
|
||||
classes = [GatewayApplication::class],
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = [MinimalTestApp::class],
|
||||
webEnvironment = SpringBootTest.WebEnvironment.NONE,
|
||||
properties = [
|
||||
// Alle externen Abhängigkeiten für Context-Loading-Test deaktivieren
|
||||
"spring.cloud.discovery.enabled=false",
|
||||
@@ -25,8 +29,8 @@ import org.springframework.test.context.ActiveProfiles
|
||||
"management.health.circuitbreakers.enabled=false",
|
||||
// Custom Security und Filter deaktivieren
|
||||
"gateway.security.jwt.enabled=false",
|
||||
// Reaktiven Web-Anwendungstyp verwenden
|
||||
"spring.main.web-application-type=reactive",
|
||||
// Für diesen Kontext-Load-Test keinen Web-Stack initialisieren
|
||||
"spring.main.web-application-type=none",
|
||||
// Gateway Discovery deaktivieren (korrekte Property)
|
||||
"spring.cloud.gateway.discovery.locator.enabled=false",
|
||||
// Zufälligen Port setzen
|
||||
@@ -34,7 +38,8 @@ import org.springframework.test.context.ActiveProfiles
|
||||
]
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Import(TestSecurityConfig::class)
|
||||
@EnableAutoConfiguration
|
||||
@Import(TestSecurityConfig::class, TestSupportConfig::class)
|
||||
class GatewayApplicationTests {
|
||||
|
||||
@Test
|
||||
|
||||
+3
-27
@@ -4,7 +4,7 @@ import at.mocode.infrastructure.gateway.config.TestSecurityConfig
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import at.mocode.infrastructure.gateway.support.GatewayTestContext
|
||||
import org.springframework.cloud.gateway.route.RouteLocator
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder
|
||||
import org.springframework.context.annotation.Bean
|
||||
@@ -20,32 +20,8 @@ import org.springframework.web.bind.annotation.RestController
|
||||
* Tests for Gateway custom filters: CorrelationId, Enhanced Logging, and Rate Limiting.
|
||||
* Tests filter behavior without disabling them (unlike other test classes).
|
||||
*/
|
||||
@SpringBootTest(
|
||||
classes = [GatewayApplication::class],
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = [
|
||||
// Disable external dependencies
|
||||
"spring.cloud.discovery.enabled=false",
|
||||
"spring.cloud.consul.enabled=false",
|
||||
"spring.cloud.consul.config.enabled=false",
|
||||
"spring.cloud.consul.discovery.register=false",
|
||||
"spring.cloud.loadbalancer.enabled=false",
|
||||
// Disable circuit breaker for filter tests
|
||||
"resilience4j.circuitbreaker.configs.default.registerHealthIndicator=false",
|
||||
"management.health.circuitbreakers.enabled=false",
|
||||
// Keep custom filters enabled for testing
|
||||
"gateway.security.jwt.enabled=false", // Disable JWT but keep other filters
|
||||
// Use reactive web application type
|
||||
"spring.main.web-application-type=reactive",
|
||||
// Disable gateway discovery - use explicit routes
|
||||
"spring.cloud.gateway.server.webflux.discovery.locator.enabled=false",
|
||||
// Disable actuator security
|
||||
"management.security.enabled=false",
|
||||
// Set random port
|
||||
"server.port=0"
|
||||
]
|
||||
)
|
||||
@ActiveProfiles("dev") // Use dev profile to enable filters
|
||||
@GatewayTestContext
|
||||
@ActiveProfiles("test")
|
||||
@AutoConfigureWebTestClient
|
||||
@Import(TestSecurityConfig::class, GatewayFiltersTests.TestFilterConfig::class)
|
||||
class GatewayFiltersTests {
|
||||
|
||||
+2
-28
@@ -1,16 +1,15 @@
|
||||
package at.mocode.infrastructure.gateway
|
||||
|
||||
import at.mocode.infrastructure.gateway.config.TestSecurityConfig
|
||||
import at.mocode.infrastructure.gateway.support.GatewayTestContext
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.cloud.gateway.route.RouteLocator
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.web.reactive.server.WebTestClient
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
@@ -21,32 +20,7 @@ import org.springframework.web.bind.annotation.RestController
|
||||
* Tests for Gateway routing functionality.
|
||||
* Uses mock backend services to test route forwarding.
|
||||
*/
|
||||
@SpringBootTest(
|
||||
classes = [GatewayApplication::class],
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = [
|
||||
// Disable external dependencies
|
||||
"spring.cloud.discovery.enabled=false",
|
||||
"spring.cloud.consul.enabled=false",
|
||||
"spring.cloud.consul.config.enabled=false",
|
||||
"spring.cloud.consul.discovery.register=false",
|
||||
"spring.cloud.loadbalancer.enabled=false",
|
||||
// Disable circuit breaker for routing tests
|
||||
"resilience4j.circuitbreaker.configs.default.registerHealthIndicator=false",
|
||||
"management.health.circuitbreakers.enabled=false",
|
||||
// Disable custom filters for pure routing tests
|
||||
"gateway.security.jwt.enabled=false",
|
||||
// Use reactive web application type
|
||||
"spring.main.web-application-type=reactive",
|
||||
// Disable gateway discovery - use explicit routes
|
||||
"spring.cloud.gateway.server.webflux.discovery.locator.enabled=false",
|
||||
// Disable actuator security
|
||||
"management.security.enabled=false",
|
||||
// Set random port
|
||||
"server.port=0"
|
||||
]
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@GatewayTestContext
|
||||
@AutoConfigureWebTestClient
|
||||
@Import(TestSecurityConfig::class, GatewayRoutingTests.TestRoutesConfig::class)
|
||||
class GatewayRoutingTests {
|
||||
|
||||
+3
-27
@@ -5,7 +5,7 @@ import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import at.mocode.infrastructure.gateway.support.GatewayTestContext
|
||||
import org.springframework.boot.test.web.server.LocalServerPort
|
||||
import org.springframework.cloud.gateway.route.RouteLocator
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder
|
||||
@@ -20,32 +20,8 @@ import org.springframework.web.bind.annotation.*
|
||||
* Tests for Gateway security configuration including CORS settings.
|
||||
* Tests the overall security setup and cross-origin request handling.
|
||||
*/
|
||||
@SpringBootTest(
|
||||
classes = [GatewayApplication::class],
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = [
|
||||
// Disable external dependencies
|
||||
"spring.cloud.discovery.enabled=false",
|
||||
"spring.cloud.consul.enabled=false",
|
||||
"spring.cloud.consul.config.enabled=false",
|
||||
"spring.cloud.consul.discovery.register=false",
|
||||
"spring.cloud.loadbalancer.enabled=false",
|
||||
// Disable circuit breaker for security tests
|
||||
"resilience4j.circuitbreaker.configs.default.registerHealthIndicator=false",
|
||||
"management.health.circuitbreakers.enabled=false",
|
||||
// Disable JWT for CORS testing
|
||||
"gateway.security.jwt.enabled=false",
|
||||
// Use reactive web application type
|
||||
"spring.main.web-application-type=reactive",
|
||||
// Disable gateway discovery - use explicit routes
|
||||
"spring.cloud.gateway.server.webflux.discovery.locator.enabled=false",
|
||||
// Disable actuator security
|
||||
"management.security.enabled=false",
|
||||
// Set random port
|
||||
"server.port=0"
|
||||
]
|
||||
)
|
||||
@ActiveProfiles("test") // Use test profile to disable unrelated global filters; CORS config is present in application-test.yml
|
||||
@GatewayTestContext
|
||||
@ActiveProfiles("test") // Behalte test-Profil explizit für Klarheit
|
||||
@AutoConfigureWebTestClient
|
||||
@Import(TestSecurityConfig::class, GatewaySecurityTests.TestSecurityConfig::class)
|
||||
class GatewaySecurityTests {
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ package at.mocode.infrastructure.gateway
|
||||
|
||||
import at.mocode.infrastructure.gateway.config.TestSecurityConfig
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import at.mocode.infrastructure.gateway.support.GatewayTestContext
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.TestPropertySource
|
||||
@@ -13,7 +13,7 @@ import org.springframework.test.context.TestPropertySource
|
||||
* without requiring actual Testcontainers, focusing on resolving the OAuth2 ResourceServer
|
||||
* autoconfiguration timing issue.
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@GatewayTestContext
|
||||
@ActiveProfiles("keycloak-integration-test")
|
||||
@TestPropertySource(
|
||||
properties = [
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package at.mocode.infrastructure.gateway
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
|
||||
/**
|
||||
* Minimaler Test-ApplicationContext, der nur die absolut nötigen Auto-Konfigurationen lädt.
|
||||
* Problematische Auto-Configs werden hier explizit ausgeschlossen, damit der Context sicher startet.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
class MinimalTestApp
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package at.mocode.infrastructure.gateway
|
||||
|
||||
import org.springframework.boot.test.context.TestConfiguration
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.web.reactive.function.client.WebClient
|
||||
|
||||
@TestConfiguration
|
||||
class TestSupportConfig {
|
||||
@Bean
|
||||
fun webClientBuilder(): WebClient.Builder = WebClient.builder()
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package at.mocode.infrastructure.gateway
|
||||
|
||||
import at.mocode.infrastructure.gateway.support.GatewayTestContext
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.test.web.reactive.server.WebTestClient
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@GatewayTestContext
|
||||
@Import(WebFluxSmokeTest.SmokeConfig::class)
|
||||
class WebFluxSmokeTest {
|
||||
|
||||
@Autowired
|
||||
lateinit var webTestClient: WebTestClient
|
||||
|
||||
@Test
|
||||
fun `should load reactive web context and serve smoke endpoint`() {
|
||||
webTestClient.get()
|
||||
.uri("/smoke")
|
||||
.exchange()
|
||||
.expectStatus().isOk
|
||||
.expectBody(String::class.java)
|
||||
.isEqualTo("ok")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class SmokeConfig {
|
||||
@Bean
|
||||
fun smokeController(): SmokeController = SmokeController()
|
||||
}
|
||||
|
||||
@RestController
|
||||
@RequestMapping
|
||||
class SmokeController {
|
||||
@GetMapping("/smoke")
|
||||
fun smoke(): String = "ok"
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package at.mocode.infrastructure.gateway.support
|
||||
|
||||
import at.mocode.infrastructure.gateway.MinimalTestApp
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration
|
||||
import org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.context.annotation.Import
|
||||
|
||||
/**
|
||||
* Zentrale Meta-Annotation für Gateway-Tests.
|
||||
*
|
||||
* - Lädt einen minimalen Spring-Boot-Kontext über `MinimalTestApp`.
|
||||
* - Erzwingt das `test`-Profil.
|
||||
* - Schließt laute/unnötige Auto-Konfigurationen für schnelle, stabile Context-Loads aus.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@SpringBootTest(
|
||||
classes = [MinimalTestApp::class],
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = [
|
||||
// Cloud/Discovery im Test deaktivieren
|
||||
"spring.cloud.discovery.enabled=false",
|
||||
"spring.cloud.consul.enabled=false",
|
||||
"spring.cloud.consul.config.enabled=false",
|
||||
"spring.cloud.consul.discovery.register=false",
|
||||
"spring.cloud.loadbalancer.enabled=false",
|
||||
// Circuit Breaker Health aus
|
||||
"resilience4j.circuitbreaker.configs.default.registerHealthIndicator=false",
|
||||
"management.health.circuitbreakers.enabled=false",
|
||||
// Gateway Discovery Locator aus
|
||||
"spring.cloud.gateway.discovery.locator.enabled=false",
|
||||
// Reaktiven Web‑Stack initialisieren (für WebTestClient)
|
||||
"spring.main.web-application-type=reactive",
|
||||
// Zufälliger Port verhindert Port-Konflikte
|
||||
"server.port=0"
|
||||
]
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@ImportAutoConfiguration(
|
||||
exclude = [
|
||||
// Nur die wirklich lauten/unnötigen Auto‑Configs im Default‑Testprofil deaktivieren
|
||||
// Spring Cloud Refresh (verursachte CNF in früheren Läufen)
|
||||
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration::class,
|
||||
// Security Resource Server (Keycloak) für die meisten Tests nicht nötig
|
||||
ReactiveOAuth2ResourceServerAutoConfiguration::class
|
||||
]
|
||||
)
|
||||
@Profile("test")
|
||||
annotation class GatewayTestContext
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package at.mocode.infrastructure.gateway.support
|
||||
|
||||
/**
|
||||
* Platzhalter-Klasse: Die frühere ContextCustomizerFactory wurde entfernt,
|
||||
* um Kompilationsfehler zu vermeiden. Die Test-Excludes werden nun über
|
||||
* junit-platform.properties und application-test.yaml gesetzt.
|
||||
*/
|
||||
class TestAutoConfigExcluderPlaceholder
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// DEPRECATED: Diese Datei wurde absichtlich geleert, um @EnableWebFlux im Testkontext zu vermeiden,
|
||||
// da sie die WebFluxAutoConfiguration deaktiviert. Bitte nicht wieder aktivieren.
|
||||
package at.mocode.infrastructure.gateway.support
|
||||
@@ -0,0 +1 @@
|
||||
# Deaktiviert: zentrale ContextCustomizerFactory wurde entfernt
|
||||
+2
-3
@@ -1,3 +1,4 @@
|
||||
# migrated from application-dev.yml (standardized to .yaml)
|
||||
server:
|
||||
port: 0
|
||||
|
||||
@@ -26,8 +27,7 @@ spring:
|
||||
discovery:
|
||||
locator:
|
||||
enabled: false
|
||||
routes:
|
||||
[ ]
|
||||
routes: [ ]
|
||||
globalcors:
|
||||
cors-configurations:
|
||||
'[/**]':
|
||||
@@ -45,7 +45,6 @@ spring:
|
||||
- "*"
|
||||
allowCredentials: true
|
||||
maxAge: 3600
|
||||
# Override production routes: keep empty in tests running with dev profile
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
+13
-6
@@ -1,15 +1,14 @@
|
||||
# migrated from application-keycloak-integration-test.yml (standardized to .yaml)
|
||||
server:
|
||||
port: 0
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: api-gateway-test
|
||||
name: api-gateway-keycloak-integration-test
|
||||
main:
|
||||
web-application-type: reactive
|
||||
autoconfigure:
|
||||
exclude:
|
||||
# Disable OAuth2 ResourceServer autoconfiguration in tests
|
||||
# use mock JwtAuthenticationFilter instead of real JWT validation
|
||||
- org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration
|
||||
cloud:
|
||||
discovery:
|
||||
@@ -23,7 +22,6 @@ spring:
|
||||
loadbalancer:
|
||||
enabled: false
|
||||
gateway:
|
||||
# IMPORTANT: Do not load production lb:// routes in tests
|
||||
server:
|
||||
webflux:
|
||||
discovery:
|
||||
@@ -32,8 +30,7 @@ spring:
|
||||
httpclient:
|
||||
connect-timeout: 1000
|
||||
response-timeout: 5s
|
||||
routes:
|
||||
[ ]
|
||||
routes: [ ]
|
||||
globalcors:
|
||||
cors-configurations:
|
||||
'[/**]':
|
||||
@@ -63,8 +60,18 @@ management:
|
||||
health:
|
||||
circuit breakers:
|
||||
enabled: false
|
||||
security:
|
||||
enabled: false
|
||||
|
||||
gateway:
|
||||
security:
|
||||
jwt:
|
||||
enabled: false
|
||||
keycloak:
|
||||
enabled: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
org.springframework.cloud.gateway: WARN
|
||||
org.springframework.security: DEBUG
|
||||
at.mocode.infrastructure.gateway: DEBUG
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
server:
|
||||
port: 0
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: api-gateway-keycloak-integration-test
|
||||
main:
|
||||
web-application-type: reactive
|
||||
# Exclude OAuth2 ResourceServer auto-configuration to prevent early issuer-uri validation
|
||||
# The OAuth2 configuration will be set dynamically after Testcontainers start
|
||||
autoconfigure:
|
||||
exclude:
|
||||
- org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration
|
||||
# OAuth2 configuration will be set by @DynamicPropertySource after containers start
|
||||
# Do not set static issuer-uri here as it will fail validation before containers are ready
|
||||
cloud:
|
||||
discovery:
|
||||
enabled: false
|
||||
consul:
|
||||
enabled: false
|
||||
config:
|
||||
enabled: false
|
||||
discovery:
|
||||
register: false
|
||||
loadbalancer:
|
||||
enabled: false
|
||||
gateway:
|
||||
# IMPORTANT: Do not load production lb:// routes in tests
|
||||
server:
|
||||
webflux:
|
||||
discovery:
|
||||
locator:
|
||||
enabled: false
|
||||
httpclient:
|
||||
connect-timeout: 1000
|
||||
response-timeout: 5s
|
||||
routes:
|
||||
[ ]
|
||||
globalcors:
|
||||
cors-configurations:
|
||||
'[/**]':
|
||||
allowedOriginPatterns:
|
||||
- "http://localhost:*"
|
||||
- "https://*.meldestelle.at"
|
||||
allowedMethods:
|
||||
- GET
|
||||
- POST
|
||||
- PUT
|
||||
- DELETE
|
||||
- PATCH
|
||||
- OPTIONS
|
||||
allowedHeaders:
|
||||
- "*"
|
||||
allowCredentials: true
|
||||
maxAge: 3600
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
health:
|
||||
circuit breakers:
|
||||
enabled: false
|
||||
security:
|
||||
enabled: false
|
||||
|
||||
# Enable JWT authentication through OAuth2 Resource Server for integration testing
|
||||
gateway:
|
||||
security:
|
||||
jwt:
|
||||
enabled: false # Disable custom JWT filter
|
||||
keycloak:
|
||||
enabled: true # Enable Keycloak integration
|
||||
|
||||
logging:
|
||||
level:
|
||||
org.springframework.cloud.gateway: WARN
|
||||
org.springframework.security: DEBUG
|
||||
at.mocode.infrastructure.gateway: DEBUG
|
||||
@@ -0,0 +1,28 @@
|
||||
spring:
|
||||
autoconfigure:
|
||||
exclude: [ ]
|
||||
main:
|
||||
web-application-type: reactive
|
||||
cloud:
|
||||
refresh:
|
||||
enabled: false
|
||||
config:
|
||||
enabled: false
|
||||
bootstrap:
|
||||
enabled: false
|
||||
|
||||
spring.cloud:
|
||||
gateway:
|
||||
enabled: true
|
||||
|
||||
# Keine weiteren Gateway-spezifischen AutoConfigs ausschließen, da nicht zwingend vorhanden
|
||||
|
||||
management:
|
||||
health:
|
||||
circuitbreakers:
|
||||
enabled: false
|
||||
resilience4j:
|
||||
circuitbreaker:
|
||||
configs:
|
||||
default:
|
||||
registerHealthIndicator: false
|
||||
@@ -0,0 +1,21 @@
|
||||
spring.profiles.active=test
|
||||
spring.main.allow-bean-definition-overriding=true
|
||||
logging.level.org.springframework.boot.test=INFO
|
||||
spring.test.context.failure.threshold=0
|
||||
|
||||
# Zentrale AutoConfiguration-Excludes (testweit). Bitte minimal halten und mit application-test.yaml abgleichen.
|
||||
spring.autoconfigure.exclude=\
|
||||
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
|
||||
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
|
||||
org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
|
||||
org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
|
||||
org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration
|
||||
|
||||
# Spring Cloud im Test vollständig ruhigstellen
|
||||
spring.cloud.refresh.enabled=false
|
||||
spring.cloud.config.enabled=false
|
||||
spring.cloud.bootstrap.enabled=false
|
||||
@@ -17,6 +17,7 @@ kotlinx-coroutines = "1.10.2"
|
||||
|
||||
# --- Spring Ecosystem ---
|
||||
springBoot = "3.5.9"
|
||||
# Spring Cloud Version kompatibel zu Spring Boot 3.5.x
|
||||
springCloud = "2025.1.0"
|
||||
# springCloudGateway = "4.3.0"
|
||||
springDependencyManagement = "1.1.7"
|
||||
@@ -55,8 +56,9 @@ auth0Jwt = "4.5.0"
|
||||
keycloakAdminClient = "26.0.7"
|
||||
|
||||
# --- Testing ---
|
||||
junitJupiter = "6.0.1"
|
||||
junitPlatform = "6.0.1"
|
||||
# JUnit 5 (Jupiter) & JUnit Platform versions aligned with Gradle 9.x
|
||||
junitJupiter = "5.11.3"
|
||||
junitPlatform = "1.11.3"
|
||||
mockk = "1.14.7"
|
||||
assertj = "3.27.6"
|
||||
testcontainers = "2.0.3"
|
||||
|
||||
@@ -22,6 +22,23 @@ dependencies {
|
||||
// `constraints` erzwingt spezifische Versionen für einzelne Bibliotheken.
|
||||
// Alle Versionen werden sicher aus `libs.versions.toml` bezogen.
|
||||
constraints {
|
||||
// --- Spring Boot Core Constraints (hart pinnen, um Leaks von M-Releases zu verhindern) ---
|
||||
api("org.springframework.boot:spring-boot:${libs.versions.springBoot.get()}")
|
||||
api("org.springframework.boot:spring-boot-autoconfigure:${libs.versions.springBoot.get()}")
|
||||
api("org.springframework.boot:spring-boot-actuator-autoconfigure:${libs.versions.springBoot.get()}")
|
||||
api("org.springframework.boot:spring-boot-actuator:${libs.versions.springBoot.get()}")
|
||||
api("org.springframework.boot:spring-boot-starter:${libs.versions.springBoot.get()}")
|
||||
api("org.springframework.boot:spring-boot-test:${libs.versions.springBoot.get()}")
|
||||
api("org.springframework.boot:spring-boot-test-autoconfigure:${libs.versions.springBoot.get()}")
|
||||
// Zusätzliche Boot-Module, die in neueren Versionen als eigenständige Artefakte vorliegen
|
||||
// und in AutoConfigurations referenziert werden. Hart pinnen, um Versions-Skew in Tests zu vermeiden.
|
||||
// HttpClient AutoConfig und Settings
|
||||
api("org.springframework.boot:spring-boot-http:${libs.versions.springBoot.get()}")
|
||||
api("org.springframework.boot:spring-boot-autoconfigure-processor:${libs.versions.springBoot.get()}")
|
||||
api("org.springframework.boot:spring-boot-http-converter:${libs.versions.springBoot.get()}")
|
||||
// Kontext- und Properties-Unterstützung, auf die PropertyMapper intern zugreifen kann
|
||||
api("org.springframework.boot:spring-boot-configuration-processor:${libs.versions.springBoot.get()}")
|
||||
|
||||
// --- Utilities & Other ---
|
||||
api(libs.caffeine)
|
||||
api(libs.reactor.kafka)
|
||||
|
||||
Reference in New Issue
Block a user