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