Public Access
Added tests
This commit is contained in:
1 parent
fb3a5140b8
commit
b8b427b9f9
6 files changed
+187
No files matched your search
@@ -0,0 +1,102 @@
|
||||
package core_service.architecture;
|
||||
|
||||
import core_service.shared.test.AbstractArchitectureTest;
|
||||
import com.tngtech.archunit.core.domain.JavaClass;
|
||||
import com.tngtech.archunit.core.importer.ImportOption;
|
||||
import com.tngtech.archunit.junit.AnalyzeClasses;
|
||||
import com.tngtech.archunit.junit.ArchTest;
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import com.tngtech.archunit.library.dependencies.SlicesRuleDefinition;
|
||||
|
||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
|
||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
|
||||
|
||||
/**
|
||||
* ArchUnit tests to enforce package boundaries in core-service.
|
||||
*
|
||||
* Notes on Core-specific structure (differs from auth-service):
|
||||
* - JPA entities live in `..domain..` (not `..persistence..`).
|
||||
* - Repository interfaces live in `..repo..`.
|
||||
* - `steam_adapter` is an outbound port consumed by feature modules
|
||||
* (e.g. `library`), so strict slice independence is relaxed for it.
|
||||
*/
|
||||
@AnalyzeClasses(packages = "core_service", importOptions = ImportOption.DoNotIncludeTests.class)
|
||||
class ArchitectureTest extends AbstractArchitectureTest {
|
||||
|
||||
// ===================================================================================
|
||||
// 1. MODULE INDEPENDENCE (Slices)
|
||||
// ===================================================================================
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule modules_should_not_depend_on_each_other = SlicesRuleDefinition.slices()
|
||||
.matching("core_service.modules.(*)..")
|
||||
.should().notDependOnEachOther()
|
||||
.ignoreDependency(
|
||||
JavaClass.Predicates.resideInAPackage("core_service.modules.library.."),
|
||||
JavaClass.Predicates.resideInAPackage("core_service.modules.steam_adapter.."))
|
||||
.because("Feature modules should be isolated; steam_adapter is an explicit shared port.");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule no_cyclic_dependencies = SlicesRuleDefinition.slices()
|
||||
.matching("core_service.(*)..")
|
||||
.should().beFreeOfCycles()
|
||||
.because("Cyclic dependencies make the code unmaintainable and tightly coupled.");
|
||||
|
||||
// ===================================================================================
|
||||
// 2. LAYER DEPENDENCY DIRECTION
|
||||
// ===================================================================================
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule apis_should_not_depend_on_repos = noClasses()
|
||||
.that().resideInAPackage("..api..")
|
||||
.should().dependOnClassesThat().resideInAPackage("..repo..")
|
||||
.because("Controllers must delegate to Services and not access repositories directly.");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule repos_should_not_depend_on_apis = noClasses()
|
||||
.that().resideInAPackage("..repo..")
|
||||
.should().dependOnClassesThat().resideInAPackage("..api..")
|
||||
.because("Persistence layer must be independent of the API layer.");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule repos_should_be_interfaces_named_repository = classes()
|
||||
.that().resideInAPackage("..repo..")
|
||||
.should().haveSimpleNameEndingWith("Repository")
|
||||
.because("Repository interfaces are the persistence boundary; naming makes them explicit.");
|
||||
|
||||
// ===================================================================================
|
||||
// 3. SHARED / CONFIGURATION RULES
|
||||
// ===================================================================================
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule shared_should_not_depend_on_modules = noClasses()
|
||||
.that().resideInAPackage("core_service.shared..")
|
||||
.should().dependOnClassesThat().resideInAPackage("core_service.modules..")
|
||||
.because("Shared classes should be independent of specific modules.");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule modules_should_not_depend_on_global_configuration = noClasses()
|
||||
.that().resideInAPackage("core_service.modules..")
|
||||
.should().dependOnClassesThat().resideInAPackage("core_service.configuration..")
|
||||
.because("Module logic should not depend on global Spring configuration classes.");
|
||||
|
||||
// ===================================================================================
|
||||
// 4. SPRING ANNOTATION CONVENTIONS
|
||||
// ===================================================================================
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule services_should_be_annotated = classes()
|
||||
.that().resideInAPackage("..modules..service..")
|
||||
.and().haveSimpleNameEndingWith("Service")
|
||||
.should().beAnnotatedWith(org.springframework.stereotype.Service.class)
|
||||
.because("Classes named *Service in modules should be registered as Spring @Service beans.");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule controllers_should_be_annotated = classes()
|
||||
.that().resideInAPackage("..api..")
|
||||
.and().haveSimpleNameEndingWith("Controller")
|
||||
.should().beAnnotatedWith(org.springframework.web.bind.annotation.RestController.class)
|
||||
.orShould().beAnnotatedWith(org.springframework.web.bind.annotation.ControllerAdvice.class)
|
||||
.orShould().beAnnotatedWith(org.springframework.web.bind.annotation.RestControllerAdvice.class)
|
||||
.because("Classes in 'api' package named *Controller should be Spring REST controllers.");
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package core_service.shared.test;
|
||||
|
||||
import org.junit.jupiter.api.Tag;
|
||||
|
||||
/**
|
||||
* Base class for ArchUnit tests.
|
||||
*/
|
||||
@Tag("ArchitectureTest")
|
||||
public abstract class AbstractArchitectureTest {
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package core_service.shared.test;
|
||||
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
/**
|
||||
* Base class for integration tests.
|
||||
* Full Spring context with Testcontainers.
|
||||
*/
|
||||
@Tag("IntegrationTest")
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles("dev")
|
||||
@Testcontainers
|
||||
public abstract class AbstractIntegrationTest {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:17-alpine");
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package core_service.shared.test;
|
||||
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
|
||||
import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
/**
|
||||
* Base class for persistence tests.
|
||||
* Slices only the persistence layer with Testcontainers.
|
||||
*/
|
||||
@Tag("PersistenceTest")
|
||||
@DataJpaTest
|
||||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
||||
@Testcontainers
|
||||
public abstract class AbstractPersistenceTest {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:17-alpine");
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package core_service.shared.test;
|
||||
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
/**
|
||||
* Base class for unit tests.
|
||||
* Isolated from Spring context.
|
||||
*/
|
||||
@Tag("UnitTest")
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public abstract class AbstractUnitTest {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package core_service.shared.test;
|
||||
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
|
||||
|
||||
/**
|
||||
* Base class for WebMvc tests.
|
||||
* Slices the web layer.
|
||||
*/
|
||||
@Tag("WebMvcTest")
|
||||
@WebMvcTest
|
||||
public abstract class AbstractWebMvcTest {
|
||||
}
|
||||
Reference in new issue
Block a user