From b68f44ab017b83f30d72e60431570ddc108ada6a Mon Sep 17 00:00:00 2001 From: Francisco J Lopez-Pellicer Date: Wed, 21 Oct 2026 19:30:31 +0200 Subject: [PATCH] test: share the service-check slice configuration --- CHANGELOG.md | 1 + .../access/ServiceCheckDispatcherTest.java | 79 +++------------- .../service/AffectedApplicationsTest.java | 18 ++-- .../check/ServiceAccessSummaryTest.java | 19 ++-- .../check/ServiceCheckSliceConfiguration.java | 92 +++++++++++++++++++ .../service/check/ServiceCheckStoreTest.java | 13 +-- .../check/ServiceHealthFilterTest.java | 13 +-- 7 files changed, 134 insertions(+), 101 deletions(-) create mode 100644 src/test/java/org/sitmun/domain/service/check/ServiceCheckSliceConfiguration.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 09348c5f..13f1b8f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), - **Tests**: `UserResourceTest` stores a fixed bcrypt hash in setup. The password update still asserts the stored hash changes ([#321](https://github.com/sitmun/sitmun-backend-core/issues/321)). - **Tests**: `DashboardMetricsQueryTest` uses the suite database. Its connections do not autocommit, so the transactional slice removes its rows. The day bucket still checks year, month, and day, and its count rises by one. Per-application counts stay `1` or `0` on an `app-` plus UUID name, including the built-in public user ([#322](https://github.com/sitmun/sitmun-backend-core/issues/322)). - **Tests**: `UserResourceTest.createUser` checks that GET after delete returns 404. `UserResourceIntegrationTest` no longer repeats that create and delete on a real server ([#313](https://github.com/sitmun/sitmun-backend-core/issues/313)). +- **Tests**: `ServiceCheckStoreTest`, `AffectedApplicationsTest`, `ServiceHealthFilterTest`, `ServiceAccessSummaryTest`, and `ServiceCheckDispatcherTest` load `ServiceCheckSliceConfiguration` on the suite database, so Liquibase runs once for that slice. Connections do not autocommit, so the transactional slice removes its rows ([#317](https://github.com/sitmun/sitmun-backend-core/issues/317)). ### Security diff --git a/src/test/java/org/sitmun/administration/service/access/ServiceCheckDispatcherTest.java b/src/test/java/org/sitmun/administration/service/access/ServiceCheckDispatcherTest.java index a5cdf55d..9d9796e5 100644 --- a/src/test/java/org/sitmun/administration/service/access/ServiceCheckDispatcherTest.java +++ b/src/test/java/org/sitmun/administration/service/access/ServiceCheckDispatcherTest.java @@ -5,6 +5,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @@ -13,16 +14,12 @@ import java.time.Instant; import java.time.LocalDate; import java.time.ZoneId; -import java.time.ZoneOffset; import java.util.Date; import java.util.List; import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -35,27 +32,29 @@ import org.sitmun.domain.service.check.ServiceCheckRepository; import org.sitmun.domain.service.check.ServiceCheckSample; import org.sitmun.domain.service.check.ServiceCheckSampleRepository; +import org.sitmun.domain.service.check.ServiceCheckSliceConfiguration; +import org.sitmun.domain.service.check.ServiceCheckSliceConfiguration.ProbeExecutor; import org.sitmun.domain.service.check.ServiceCheckStore; import org.sitmun.domain.service.usage.ServiceUsage; import org.sitmun.domain.service.usage.ServiceUsageRepository; import org.sitmun.domain.service.usage.ServiceUsageStore; -import org.sitmun.infrastructure.persistence.type.i18n.I18nTestConfiguration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest; import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase; import org.springframework.boot.liquibase.autoconfigure.LiquibaseAutoConfiguration; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.bean.override.mockito.MockitoBean; @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ImportAutoConfiguration(LiquibaseAutoConfiguration.class) -@TestPropertySource(properties = "sitmun.service-check.enabled=true") +@Import(ServiceCheckSliceConfiguration.class) +@TestPropertySource( + properties = { + "sitmun.service-check.enabled=true", + "spring.datasource.hikari.auto-commit=false" + }) @DisplayName("Service check dispatcher") class ServiceCheckDispatcherTest { @@ -71,12 +70,13 @@ class ServiceCheckDispatcherTest { @Autowired private ServiceCheckSampleRepository sampleRepository; @Autowired private ServiceUsageRepository usageRepository; @Autowired private ApplicationRepository applicationRepository; - @MockitoBean private ServiceAccessCheckExecutor checks; + @Autowired private ServiceAccessCheckExecutor checks; private CountDownLatch releaseProbe; @BeforeEach void resetWorkers() { + reset(checks); workers.async(false); releaseProbe = new CountDownLatch(1); Instant fresh = clock.instant(); @@ -326,61 +326,4 @@ private ServiceCheckSample sample(Service service, Instant observedAt, String st .observedAt(observedAt) .build(); } - - @TestConfiguration - @EnableConfigurationProperties(ServiceCheckProperties.class) - @Import({ - I18nTestConfiguration.class, - ServiceCheckStore.class, - ServiceUsageStore.class, - ServiceCheckDueQuery.class, - ServiceCheckDispatcher.class - }) - static class Configuration { - - @Bean - Clock serviceCheckClock() { - return Clock.fixed(Instant.parse("2026-10-05T12:00:00Z"), ZoneOffset.UTC); - } - - @Bean - ServiceCheckInFlight serviceCheckInFlight(ServiceCheckProperties properties) { - return new ServiceCheckInFlight(properties.maxInFlight()); - } - - @Bean(destroyMethod = "close") - ProbeExecutor serviceCheckExecutor() { - return new ProbeExecutor(); - } - } - - static final class ProbeExecutor implements Executor, AutoCloseable { - - private final ExecutorService pool = - Executors.newCachedThreadPool( - runnable -> { - Thread thread = new Thread(runnable, "service-check-test"); - thread.setDaemon(true); - return thread; - }); - private final AtomicBoolean async = new AtomicBoolean(); - - void async(boolean enabled) { - async.set(enabled); - } - - @Override - public void execute(Runnable command) { - if (async.get()) { - pool.execute(command); - } else { - command.run(); - } - } - - @Override - public void close() { - pool.shutdownNow(); - } - } } diff --git a/src/test/java/org/sitmun/domain/service/AffectedApplicationsTest.java b/src/test/java/org/sitmun/domain/service/AffectedApplicationsTest.java index 95f18a78..58378d77 100644 --- a/src/test/java/org/sitmun/domain/service/AffectedApplicationsTest.java +++ b/src/test/java/org/sitmun/domain/service/AffectedApplicationsTest.java @@ -27,6 +27,7 @@ import org.sitmun.domain.cartography.permission.CartographyPermissionRepository; import org.sitmun.domain.role.Role; import org.sitmun.domain.role.RoleRepository; +import org.sitmun.domain.service.check.ServiceCheckSliceConfiguration; import org.sitmun.domain.service.check.ServiceCheckStore; import org.sitmun.domain.service.usage.ServiceUsage; import org.sitmun.domain.service.usage.ServiceUsageKey; @@ -38,21 +39,24 @@ import org.sitmun.domain.tree.TreeRepository; import org.sitmun.domain.tree.node.TreeNode; import org.sitmun.domain.tree.node.TreeNodeRepository; -import org.sitmun.infrastructure.persistence.type.i18n.I18nTestConfiguration; import org.sitmun.upstream.signal.ServiceStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest; import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase; import org.springframework.boot.liquibase.autoconfigure.LiquibaseAutoConfiguration; -import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Import; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.test.context.TestPropertySource; @DataJpaTest -@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ImportAutoConfiguration(LiquibaseAutoConfiguration.class) -@Transactional +@Import(ServiceCheckSliceConfiguration.class) +@TestPropertySource( + properties = { + "sitmun.service-check.enabled=true", + "spring.datasource.hikari.auto-commit=false" + }) @DisplayName("Affected applications") class AffectedApplicationsTest { @@ -275,8 +279,4 @@ private void recordUsage( row.setRequests(requests); usage.save(row); } - - @TestConfiguration - @Import({I18nTestConfiguration.class, ServiceCheckStore.class, AffectedApplications.class}) - static class Configuration {} } diff --git a/src/test/java/org/sitmun/domain/service/check/ServiceAccessSummaryTest.java b/src/test/java/org/sitmun/domain/service/check/ServiceAccessSummaryTest.java index 01d42c15..997ec03f 100644 --- a/src/test/java/org/sitmun/domain/service/check/ServiceAccessSummaryTest.java +++ b/src/test/java/org/sitmun/domain/service/check/ServiceAccessSummaryTest.java @@ -15,22 +15,26 @@ import org.sitmun.administration.service.access.ServiceAccessSummary; import org.sitmun.domain.service.Service; import org.sitmun.domain.service.ServiceRepository; -import org.sitmun.domain.service.usage.ServiceUsageQueries; -import org.sitmun.infrastructure.persistence.type.i18n.I18nTestConfiguration; import org.sitmun.upstream.signal.ServiceStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest; import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase; import org.springframework.boot.liquibase.autoconfigure.LiquibaseAutoConfiguration; -import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Import; import org.springframework.http.HttpStatus; +import org.springframework.test.context.TestPropertySource; import org.springframework.web.server.ResponseStatusException; @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ImportAutoConfiguration(LiquibaseAutoConfiguration.class) +@Import(ServiceCheckSliceConfiguration.class) +@TestPropertySource( + properties = { + "sitmun.service-check.enabled=true", + "spring.datasource.hikari.auto-commit=false" + }) @DisplayName("Service access summaries") class ServiceAccessSummaryTest { @@ -325,13 +329,4 @@ private Service save(String name) { .isProxied(false) .build()); } - - @TestConfiguration - @Import({ - I18nTestConfiguration.class, - ServiceCheckStore.class, - ServiceAccessSummaries.class, - ServiceUsageQueries.class - }) - static class Configuration {} } diff --git a/src/test/java/org/sitmun/domain/service/check/ServiceCheckSliceConfiguration.java b/src/test/java/org/sitmun/domain/service/check/ServiceCheckSliceConfiguration.java new file mode 100644 index 00000000..18a2e9e5 --- /dev/null +++ b/src/test/java/org/sitmun/domain/service/check/ServiceCheckSliceConfiguration.java @@ -0,0 +1,92 @@ +package org.sitmun.domain.service.check; + +import static org.mockito.Mockito.mock; + +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneOffset; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; +import org.sitmun.administration.service.access.ServiceAccessCheckExecutor; +import org.sitmun.administration.service.access.ServiceAccessSummaries; +import org.sitmun.administration.service.access.ServiceCheckDispatcher; +import org.sitmun.administration.service.access.ServiceCheckDueQuery; +import org.sitmun.administration.service.access.ServiceCheckInFlight; +import org.sitmun.administration.service.access.ServiceCheckProperties; +import org.sitmun.domain.service.AffectedApplications; +import org.sitmun.domain.service.ServiceHealthQuery; +import org.sitmun.domain.service.usage.ServiceUsageQueries; +import org.sitmun.domain.service.usage.ServiceUsageStore; +import org.sitmun.infrastructure.persistence.type.i18n.I18nTestConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@TestConfiguration +@EnableConfigurationProperties(ServiceCheckProperties.class) +@Import({ + I18nTestConfiguration.class, + ServiceCheckStore.class, + ServiceHealthQuery.class, + AffectedApplications.class, + ServiceAccessSummaries.class, + ServiceUsageQueries.class, + ServiceUsageStore.class, + ServiceCheckDueQuery.class, + ServiceCheckDispatcher.class +}) +public class ServiceCheckSliceConfiguration { + + @Bean + ServiceAccessCheckExecutor serviceAccessCheckExecutor() { + return mock(ServiceAccessCheckExecutor.class); + } + + @Bean + Clock serviceCheckClock() { + return Clock.fixed(Instant.parse("2026-10-05T12:00:00Z"), ZoneOffset.UTC); + } + + @Bean + ServiceCheckInFlight serviceCheckInFlight(ServiceCheckProperties properties) { + return new ServiceCheckInFlight(properties.maxInFlight()); + } + + @Bean(destroyMethod = "close") + ProbeExecutor serviceCheckExecutor() { + return new ProbeExecutor(); + } + + public static final class ProbeExecutor implements Executor, AutoCloseable { + + private final ExecutorService pool = + Executors.newCachedThreadPool( + runnable -> { + Thread thread = new Thread(runnable, "service-check-test"); + thread.setDaemon(true); + return thread; + }); + private final AtomicBoolean async = new AtomicBoolean(); + + public void async(boolean enabled) { + async.set(enabled); + } + + @Override + public void execute(Runnable command) { + if (async.get()) { + pool.execute(command); + } else { + command.run(); + } + } + + @Override + public void close() { + pool.shutdownNow(); + } + } +} diff --git a/src/test/java/org/sitmun/domain/service/check/ServiceCheckStoreTest.java b/src/test/java/org/sitmun/domain/service/check/ServiceCheckStoreTest.java index fdf1d8ef..ebf359ab 100644 --- a/src/test/java/org/sitmun/domain/service/check/ServiceCheckStoreTest.java +++ b/src/test/java/org/sitmun/domain/service/check/ServiceCheckStoreTest.java @@ -8,19 +8,24 @@ import org.junit.jupiter.api.Test; import org.sitmun.domain.service.Service; import org.sitmun.domain.service.ServiceRepository; -import org.sitmun.infrastructure.persistence.type.i18n.I18nTestConfiguration; import org.sitmun.upstream.signal.ServiceStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest; import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase; import org.springframework.boot.liquibase.autoconfigure.LiquibaseAutoConfiguration; -import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Import; +import org.springframework.test.context.TestPropertySource; @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ImportAutoConfiguration(LiquibaseAutoConfiguration.class) +@Import(ServiceCheckSliceConfiguration.class) +@TestPropertySource( + properties = { + "sitmun.service-check.enabled=true", + "spring.datasource.hikari.auto-commit=false" + }) @DisplayName("Service check store") class ServiceCheckStoreTest { @@ -84,8 +89,4 @@ void upsertReplacesLatestAndLeavesBlockedUnchanged() { Service reloaded = serviceRepository.findById(service.getId()).orElseThrow(); assertThat(reloaded.getBlocked()).isTrue(); } - - @TestConfiguration - @Import({I18nTestConfiguration.class, ServiceCheckStore.class}) - static class Configuration {} } diff --git a/src/test/java/org/sitmun/domain/service/check/ServiceHealthFilterTest.java b/src/test/java/org/sitmun/domain/service/check/ServiceHealthFilterTest.java index 5994984d..c85f030f 100644 --- a/src/test/java/org/sitmun/domain/service/check/ServiceHealthFilterTest.java +++ b/src/test/java/org/sitmun/domain/service/check/ServiceHealthFilterTest.java @@ -11,24 +11,29 @@ import org.sitmun.domain.service.Service; import org.sitmun.domain.service.ServiceHealthQuery; import org.sitmun.domain.service.ServiceRepository; -import org.sitmun.infrastructure.persistence.type.i18n.I18nTestConfiguration; import org.sitmun.upstream.signal.ServiceStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest; import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase; import org.springframework.boot.liquibase.autoconfigure.LiquibaseAutoConfiguration; -import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Import; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.http.HttpStatus; +import org.springframework.test.context.TestPropertySource; import org.springframework.web.server.ResponseStatusException; @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ImportAutoConfiguration(LiquibaseAutoConfiguration.class) +@Import(ServiceCheckSliceConfiguration.class) +@TestPropertySource( + properties = { + "sitmun.service-check.enabled=true", + "spring.datasource.hikari.auto-commit=false" + }) @DisplayName("Service health filter") class ServiceHealthFilterTest { @@ -138,8 +143,4 @@ private Service save(String name) { .isProxied(false) .build()); } - - @TestConfiguration - @Import({I18nTestConfiguration.class, ServiceCheckStore.class, ServiceHealthQuery.class}) - static class Configuration {} }