spring-projects / spring-modulith

Modular applications with Spring Boot
https://spring.io/projects/spring-modulith
Apache License 2.0
811 stars 139 forks source link

Invalid application module test bootstrap results in hard to diagnose exception message #838

Closed xenoterracide closed 1 month ago

xenoterracide commented 1 month ago

So my @SpringBootTest with an empty contextLoads, runs without error, but my modulith test is failing with some spring data jpa issues, but the root cause isn't obvious to me as I'm just using the basic spring-data-jpa autoconfiguration that loads from spring-boot.

// © Copyright 2024 Caleb Cushing
// SPDX-License-Identifier: AGPL-3.0-or-later

package com.xenoterracide;

import org.junit.jupiter.api.Test;
import org.springframework.modulith.core.ApplicationModules;
import org.springframework.modulith.test.ApplicationModuleTest;

@ApplicationModuleTest(ApplicationModuleTest.BootstrapMode.ALL_DEPENDENCIES)
class ModulithTest {

  @Test
  void modules() {
    ApplicationModules.of(Application.class).verify();
  }
}
 INFO 1906545 - .ModuleContextCustomizerFactory$ModuleContextCustomizer : Bootstrapping @org.springframework.modulith.test.ApplicationModuleTest for Xenoterracide in mode ALL_DEPENDENCIES (class com.xenoterracide.Application)…  at org.springframework.modulith.test.ModuleContextCustomizerFactory$ModuleContextCustomizer.logModules(ModuleContextCustomizerFactory.java:104)
 INFO 1906545 - .ModuleContextCustomizerFactory$ModuleContextCustomizer :                                                                                                                           at org.springframework.modulith.test.ModuleContextCustomizerFactory$ModuleContextCustomizer.logModules(ModuleContextCustomizerFactory.java:105)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : # Xenoterracide                                                                                                           at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : > Logical name: root:com.xenoterracide                                                                                    at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : > Base package: com.xenoterracide                                                                                         at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : > Direct module dependencies: none                                                                                        at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : > Spring beans:                                                                                                           at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     :   + ….Application                                                                                                         at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .ModuleContextCustomizerFactory$ModuleContextCustomizer :                                                                                                                           at org.springframework.modulith.test.ModuleContextCustomizerFactory$ModuleContextCustomizer.logHeadline(ModuleContextCustomizerFactory.java:181)
 INFO 1906545 - .ModuleContextCustomizerFactory$ModuleContextCustomizer : Shared modules:                                                                                                           at org.springframework.modulith.test.ModuleContextCustomizerFactory$ModuleContextCustomizer.logHeadline(ModuleContextCustomizerFactory.java:182)
 INFO 1906545 - .ModuleContextCustomizerFactory$ModuleContextCustomizer : > security                                                                                                                at org.springframework.modulith.test.ModuleContextCustomizerFactory$ModuleContextCustomizer.logModules(ModuleContextCustomizerFactory.java:125)
 INFO 1906545 - .ModuleContextCustomizerFactory$ModuleContextCustomizer :                                                                                                                           at org.springframework.modulith.test.ModuleContextCustomizerFactory$ModuleContextCustomizer.logHeadline(ModuleContextCustomizerFactory.java:181)
 INFO 1906545 - .ModuleContextCustomizerFactory$ModuleContextCustomizer : Included dependencies:                                                                                                    at org.springframework.modulith.test.ModuleContextCustomizerFactory$ModuleContextCustomizer.logHeadline(ModuleContextCustomizerFactory.java:182)
 INFO 1906545 - .ModuleContextCustomizerFactory$ModuleContextCustomizer :                                                                                                                           at org.springframework.modulith.test.ModuleContextCustomizerFactory$ModuleContextCustomizer.lambda$logModules$2(ModuleContextCustomizerFactory.java:141)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : # Security                                                                                                                at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : > Logical name: security                                                                                                  at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : > Base package: com.xenoterracide.security                                                                                at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : > Named interfaces:                                                                                                       at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     :   + NamedInterface: name=<<UNNAMED>>, types=[]                                                                            at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     :   + NamedInterface: name=SecurityController, types=[ c.x.s.c.RegistrationCtrlr ]                                          at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     :   + NamedInterface: name=SecurityRoot, types=[ c.x.s.RevEntity, c.x.s.RevEntity_ ]                                        at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     :   + NamedInterface: name=User, types=[ c.x.s.u.IdentityProviderUser, c.x.s.u.IdP, c.x.s.u.IdentityProviderUserId, c.x.s.u.IdentityProviderUserBuilder, c.x.s.u.IdentityProviderUser_, c.x.s.u.User, c.x.s.u.UserId, c.x.s.u.UserBuilder, c.x.s.u.UserRepository, c.x.s.u.User_ ]  at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : > Direct module dependencies: none                                                                                        at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     : > Spring beans:                                                                                                           at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     :   + ….controller.RegistrationCtrlr                                                                                        at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .util.Spliterators$ArraySpliterator                     :   + ….user.UserRepository                                                                                                 at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
 INFO 1906545 - .ModuleContextCustomizerFactory$ModuleContextCustomizer :                                                                                                                           at org.springframework.modulith.test.ModuleContextCustomizerFactory$ModuleContextCustomizer.logModules(ModuleContextCustomizerFactory.java:146)
 INFO 1906545 - .spri.boot.StartupInfoLogger                            : Starting ModulithTest using Java 21.0.2 with PID 1906545 (started by xeno in /home/xeno/IdeaProjects/spring-app-commons/app-core) at org.springframework.boot.StartupInfoLogger.logStarting(StartupInfoLogger.java:50)
DEBUG 1906545 - .spri.boot.StartupInfoLogger                            : Running with Spring Boot v3.3.4, Spring v6.1.13                                                                           at org.springframework.boot.StartupInfoLogger.logStarting(StartupInfoLogger.java:51)
 INFO 1906545 - .spri.boot.SpringApplication                            : No active profile set, falling back to 1 default profile: "default"                                                       at org.springframework.boot.SpringApplication.logStartupProfileInfo(SpringApplication.java:654)
 INFO 1906545 - uration$AutoConfigurationAndEntityScanPackageCustomizer : Re-configuring auto-configuration and entity scan packages to: com.xenoterracide, com.xenoterracide.security.             at org.springframework.modulith.test.ModuleTestAutoConfiguration$AutoConfigurationAndEntityScanPackageCustomizer.registerBeanDefinitions(ModuleTestAutoConfiguration.java:63)
 WARN 1906545 - .spri.cont.supp.AbstractApplicationContext              : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'userRepository' defined in com.xenoterracide.security.user.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Cannot register bean definition [Root bean: class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null; defined in com.xenoterracide.security.user.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration] for bean 'userRepository' since there is already [Root bean: class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null; defined in com.xenoterracide.security.user.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration] bound.  at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:633)
ERROR 1906545 - .spri.boot.diag.LoggingFailureAnalysisReporter          : 

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'userRepository', defined in com.xenoterracide.security.user.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration, could not be registered. A bean with that name has already been defined in com.xenoterracide.security.user.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
    at org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter.report(LoggingFailureAnalysisReporter.java:40)

============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:
-----------------

    None

Negative matches:
-----------------

    None

Exclusions:
-----------

    None

Unconditional classes:
----------------------

    None

 WARN 1906545 - .spri.test.cont.TestContextManager                      : Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener] to prepare test instance [com.xenoterracide.ModulithTest@53781df3]  at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:272)

java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@5e76eb5a testClass = com.xenoterracide.ModulithTest, locations = [], classes = [com.xenoterracide.Application], contextInitializerClasses = [], activeProfiles = [], propertySourceDescriptors = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.modulith.test.ModuleContextCustomizerFactory$ModuleContextCustomizer@6093d508, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@6093d527, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@4d499d65, [ImportsContextCustomizer@77fc3039 key = [org.springframework.modulith.test.ModuleTestAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@59b65dce, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@61ecbee9, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@593a6726, org.springframework.boot.test.web.reactor.netty.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory$DisableReactorResourceFactoryGlobalResourcesContextCustomizerCustomizer@1455154c, org.springframework.boot.test.context.SpringBootTestAnnotation@83e7f35b], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:180) ~[spring-test-6.1.13.jar:6.1.13]
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130) ~[spring-test-6.1.13.jar:6.1.13]
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191) ~[spring-test-6.1.13.jar:6.1.13]
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130) ~[spring-test-6.1.13.jar:6.1.13]
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) ~[spring-test-6.1.13.jar:6.1.13]
    at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163) ~[spring-test-6.1.13.jar:6.1.13]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:378) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:383) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$12(ClassBasedTestDescriptor.java:378) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[?:?]
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[?:?]
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) ~[?:?]
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[?:?]
    at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) ~[?:?]
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[?:?]
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[?:?]
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[?:?]
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[?:?]
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:?]
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[?:?]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:377) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$7(ClassBasedTestDescriptor.java:290) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:289) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:279) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at java.base/java.util.Optional.orElseGet(Optional.java:364) ~[?:?]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$6(ClassBasedTestDescriptor.java:278) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$before$3(ClassBasedTestDescriptor.java:204) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:203) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:85) ~[junit-jupiter-engine-5.11.0.jar:5.11.0]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:148) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) ~[?:?]
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) ~[junit-platform-engine-1.11.0.jar:1.11.0]
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:198) ~[junit-platform-launcher-1.11.0.jar:1.11.0]
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:169) ~[junit-platform-launcher-1.11.0.jar:1.11.0]
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:93) ~[junit-platform-launcher-1.11.0.jar:1.11.0]
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:58) ~[junit-platform-launcher-1.11.0.jar:1.11.0]
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:141) [junit-platform-launcher-1.11.0.jar:1.11.0]
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:57) [junit-platform-launcher-1.11.0.jar:1.11.0]
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:103) [junit-platform-launcher-1.11.0.jar:1.11.0]
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:85) [junit-platform-launcher-1.11.0.jar:1.11.0]
    at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) [junit-platform-launcher-1.11.0.jar:1.11.0]
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124) [gradle-testing-junit-platform-8.10.1.jar:8.10.1]
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99) [gradle-testing-junit-platform-8.10.1.jar:8.10.1]
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94) [gradle-testing-junit-platform-8.10.1.jar:8.10.1]
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63) [gradle-testing-base-infrastructure-8.10.1.jar:8.10.1]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?]
    at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?]
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) [gradle-messaging-8.10.1.jar:8.10.1]
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) [gradle-messaging-8.10.1.jar:8.10.1]
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) [gradle-messaging-8.10.1.jar:8.10.1]
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92) [gradle-messaging-8.10.1.jar:8.10.1]
    at jdk.proxy3/jdk.proxy3.$Proxy10.stop(Unknown Source) [?:?]
    at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200) [gradle-testing-base-infrastructure-8.10.1.jar:8.10.1]
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132) [gradle-testing-base-infrastructure-8.10.1.jar:8.10.1]
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103) [gradle-testing-base-infrastructure-8.10.1.jar:8.10.1]
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63) [gradle-testing-base-infrastructure-8.10.1.jar:8.10.1]
    at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) [gradle-worker-main-8.10.1.jar:8.10.1]
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121) [gradle-worker-main-8.10.1.jar:8.10.1]
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) [gradle-worker-main-8.10.1.jar:8.10.1]
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) [gradle-worker.jar:?]
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) [gradle-worker.jar:?]
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'userRepository' defined in com.xenoterracide.security.user.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Cannot register bean definition [Root bean: class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null; defined in com.xenoterracide.security.user.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration] for bean 'userRepository' since there is already [Root bean: class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null; defined in com.xenoterracide.security.user.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration] bound.
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:1017) ~[spring-beans-6.1.13.jar:6.1.13]

modulith work lives in this branch https://github.com/xenoterracide/spring-app-commons/tree/refactor/modulith

odrotbohm commented 1 month ago

The exception report is pretty clear isn't it?

Description:

The bean 'userRepository', defined in com.xenoterracide.security.user.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration, could not be registered. A bean with that name has already been defined in com.xenoterracide.security.user.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
    at org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter.report(LoggingFailureAnalysisReporter.java:40)

I cannot speak to that arrangement as it's user code. That said, to run the module verification, you don't need to bootstrap the application. I.e., the test case here doesn't need neither @SpringBootTest nor @ApplicationModuleTest either as is or in the more complex configuration setup you show.

What I am suspecting though is that your customized module setup is breaking the customizations we make to the component scanning. You have a module located at the root package, and one located in a sub-package of that. As each of the modules' base packages is added to the component scanning, adding com.acme and com.acme.foo will get foo scanned twice. That said, that misconfiguration is nothing the core container is aware of, so it can't detect the original cause of the problem.

xenoterracide commented 1 month ago

right com.xenoterracide.security.user.UserRepository is the only userRepository ... I've left it to be "auto-registered" without any changes to auto-configuration. I don't know why it's trying to register it again. This doesn't make sense to me, why the results are different from a normal @SpringBootTest. The suggested remedy doesn't feel like the correct answer as I'm not trying to redefine a bean.

That said, to run the module verification, you don't need to bootstrap the application.

good to know.

What I am suspecting though is that your customized module setup is breaking the customizations we make to the component scanning. You have a module located at the root package, and one located in a sub-package of that. As each of the modules' base packages is added to the component scanning, adding com.acme and com.acme.foo will get foo scanned twice. That said, that misconfiguration is nothing the core container is aware of, so it can't detect the original cause of the problem.

possible. I'm doing spring.modulith.detection-strategy = explicitly-annotated at the time of this test, even then I'm not certain my setup is as explicit really supports.

Perhaps you can offer a recommendation to my structure. It's possible modulith simply won't work.

The goal is roughly 3 (horizontal) layers, infrastructure, domain, controller, with feature (vertical) slicing. Using packages and jpms to limit access to code. I'm equating my JPMS to be a real module as far as DDD is concerned. This particular repository is for me to develop this theory and use it for highly generic components such as security based models.

my current thought for the domain layer is myorg.<domain bounded context/module>.<aggregate>

previously I had thought myorg.<domain bounded context/module>.model but then it occurred to me the additional model wasn't a part of my domain and didn't seem useful. DDD often suggests not including things that aren't part of your domain. I suspect this is more compatible with what you have

From a compile time standpoint myorg.<domain bounded context/module>.model.<aggreate> might be useful as it prevents different aggregates from referencing each others package level internals.

At the controller layer I was simply going to use myorg.<domain bounded context/module>.controller this of course sits in a separate jar. In some cases I question the necessity of even breaking this up by the bounded context since each controller really should be self isolating.

There's also the problem that this isn't even a real application, it's just library implementations for whatever applications I decide to create. So then there's perhaps the concerning effect of myorg.security but what happens when I need myorg.<myapp>.<something> where something might even be security. Perhaps <myorg>.commons.<context/module>.model.<aggregate> is what I should have.

Oh, and Application doesn't actually depend on anything. Before I added security as a shared module, nothing was picked up on it because it just lives at the org root.

Thoughts?

odrotbohm commented 1 month ago

The root of the problem in your example above is that, for some reason, the package containing the application class also contains the other, related modules. Spring's component scanning can only work for package trees. You essentially get your nested package scanned twice, and thus the repository detected twice. Avoiding getting the root package considered an application module base package should solve the problem.

xenoterracide commented 1 month ago

Avoiding getting the root package considered an application module base package should solve the problem.

How does one do that? In spring boot the Application class is supposed to be at your root, right? and in your examples you're supposed to put @Modulith on said Application class, right?

 src/main/java
   ├─  example
   |  └─  Application.java
   ├─  example.inventory
   |  ├─  InventoryManagement.java
   |  └─  SomethingInventoryInternal.java
   ├─  example.order
   |  └─  OrderManagement.java
   └─  example.order.internal
      └─  SomethingOrderInternal.java

at the very bottom you have (btw, you might want to rename this class to simply be Application instead of MyApplication to avoid confusion, although now I see it calls DemoApplication which is extra confusing as then this class doesn't compile)

package example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.modulith.Modulithic;

@Modulithic
@SpringBootApplication
class MyApplication {

  public static void main(String... args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

from what I'm seeing, at least from the perspective of the Application class, it lives at the root as the example shows. If I don't add the shared module

@Modulith(sharedModules = "security")

Then security isn't printed at all, like it doesn't exist, as Application technically depends on nothing. Honestly reading this It's not even clear how the dependencies are supposed to be declared outside of "magic".

odrotbohm commented 1 month ago

I can – just as at the other tickets you filed – only reiterate that this is a bug tracker, not a personal consulting forum. So this will be my last reply regarding your setup questions:

You're right describing the example arrangements we provide (not surprising). Your arrangement differs from that, as it lists the package your application class resides in as a module in the log output (the very first few lines). I assume this is due to your customized module detection setup, which I don't know anything about. I don't know what security relates to in the context of you describing our defaults, nor what "outside 'magic'" is supposed to mean.

It feels as if you get lost in customizing and tweaking tons of things at the same time and all those customizations and specialties of your set up contributing some aspect to failure that you have a hard time describing coherently. The architecture verification test does not require an integration test setup in the first place. So this works as designed. As for your application module integration test, I suggest to simplify the arrangement to get to the gist of the issue.

xenoterracide commented 1 month ago

DDD tackling complexity in the heart of the domain, of course it is hard to articulate in twitter sized bites. Anything with any complexity and moving parts requires lots of explanation

I don't know what security relates to in the context of you describing our defaults,

it doesn't, please review the logs and it probably becomes clear.

You constantly criticize me. However I'm running off your documentation that as I just pointed out has code that doesn't compile. In jmolecules there's references to entire books worth documentation using terms that don't even exist in the book. I'm imperfect but pot kettle much.

I'll leave you alone.

xenoterracide commented 1 month ago

I don't know what security relates to in the context of you describing our defaults,

As I said (and included in my code), the only thing I customized was that I made it "explicit" there is no further customization. I included that in the code that I shared in the comments.