From 035207b8dbe3683eec59a4a803ab2b5939062231 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Tue, 24 Sep 2024 14:57:20 +0800 Subject: [PATCH 1/3] chore: remove unused code related to system configMap (#6695) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /area ui /kind cleanup /milestone 2.20.x #### What this PR does / why we need it: 清理 UI 中关于获取 system configmap 的无用代码,目前观察到已经没有任何地方在使用这个数据。 #### Does this PR introduce a user-facing change? ```release-note None ``` --- ui/console-src/main.ts | 5 ---- .../modules/system/settings/tabs/Setting.vue | 5 +--- ui/console-src/stores/system-configmap.ts | 29 ------------------- 3 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 ui/console-src/stores/system-configmap.ts diff --git a/ui/console-src/main.ts b/ui/console-src/main.ts index 5c5c7e566..e05d17c4d 100644 --- a/ui/console-src/main.ts +++ b/ui/console-src/main.ts @@ -19,7 +19,6 @@ import { setupCoreModules, setupPluginModules, } from "@console/setup/setupModules"; -import { useSystemConfigMapStore } from "@console/stores/system-configmap"; import { useThemeStore } from "@console/stores/theme"; const app = createApp(App); @@ -97,10 +96,6 @@ async function initApp() { console.error("Failed to load plugins", e); } - // load system configMap - const systemConfigMapStore = useSystemConfigMapStore(); - await systemConfigMapStore.fetchSystemConfigMap(); - if (globalInfoStore.globalInfo?.userInitialized) { await loadActivatedTheme(); } diff --git a/ui/console-src/modules/system/settings/tabs/Setting.vue b/ui/console-src/modules/system/settings/tabs/Setting.vue index a897b1170..e2df8c1fb 100644 --- a/ui/console-src/modules/system/settings/tabs/Setting.vue +++ b/ui/console-src/modules/system/settings/tabs/Setting.vue @@ -9,7 +9,6 @@ import { Toast, VButton } from "@halo-dev/components"; // hooks import { useGlobalInfoStore } from "@/stores/global-info"; import { useSettingFormConvert } from "@console/composables/use-setting-form"; -import { useSystemConfigMapStore } from "@console/stores/system-configmap"; import type { ConfigMap, Setting } from "@halo-dev/api-client"; import { coreApiClient } from "@halo-dev/api-client"; import { useQuery, useQueryClient } from "@tanstack/vue-query"; @@ -18,7 +17,6 @@ import { useI18n } from "vue-i18n"; const SYSTEM_CONFIGMAP_NAME = "system"; const { t } = useI18n(); -const systemConfigMapStore = useSystemConfigMapStore(); const queryClient = useQueryClient(); const group = inject>("activeTab", ref("basic")); @@ -52,7 +50,7 @@ const handleSaveConfigMap = async () => { return; } - const { data } = await coreApiClient.configMap.updateConfigMap({ + await coreApiClient.configMap.updateConfigMap({ name: SYSTEM_CONFIGMAP_NAME, configMap: configMapToUpdate, }); @@ -61,7 +59,6 @@ const handleSaveConfigMap = async () => { queryClient.invalidateQueries({ queryKey: ["system-configMap"] }); await useGlobalInfoStore().fetchGlobalInfo(); - systemConfigMapStore.configMap = data; saving.value = false; }; diff --git a/ui/console-src/stores/system-configmap.ts b/ui/console-src/stores/system-configmap.ts deleted file mode 100644 index 18ed50c5e..000000000 --- a/ui/console-src/stores/system-configmap.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { ConfigMap } from "@halo-dev/api-client"; -import { coreApiClient } from "@halo-dev/api-client"; -import { defineStore } from "pinia"; - -interface SystemConfigMapState { - configMap?: ConfigMap; -} - -export const useSystemConfigMapStore = defineStore({ - id: "system-configmap", - state: (): SystemConfigMapState => ({ - configMap: undefined, - }), - actions: { - async fetchSystemConfigMap() { - try { - const { data } = await coreApiClient.configMap.getConfigMap( - { - name: "system", - }, - { mute: true } - ); - this.configMap = data; - } catch (error) { - console.error("Failed to fetch system configMap", error); - } - }, - }, -}); From 86b95ccfd031fd6a3f42654020c6fc7edc191fff Mon Sep 17 00:00:00 2001 From: John Niang Date: Tue, 24 Sep 2024 15:01:22 +0800 Subject: [PATCH 2/3] Upgrade to Spring Boot 3.4.0-M3 (#6687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind cleanup /area core /milestone 2.20.x #### What this PR does / why we need it: This PR upgrades to [Spring Boot 3.4.0-M3](https://github.com/spring-projects/spring-boot/releases/tag/v3.4.0-M3). 1. Fix the compilation error of OptimalPropertyAccess because the class has been privated in [this commit](https://github.com/spring-projects/spring-framework/commit/b4315940212b8e955215ffe09bafde228daa6eac). 2. Fix exception `org.mockito.exceptions.misusing.UnnecessaryStubbingException` for some unit tests after upgrading. 3. Replace deprecated annotations `@MockBean` and `@SpyBean` with `@MockitoBean` and `@MockitoSpyBean` respectively. #### Does this PR introduce a user-facing change? ```release-note 升级 Spring Boot 至 3.4.0-M3 ``` --- .../dialect/EvaluationContextEnhancer.java | 3 +- .../config/ExtensionConfigurationTest.java | 4 +- .../halo/app/config/WebFluxConfigTest.java | 4 +- .../content/CategoryPostCountUpdaterTest.java | 4 +- .../app/content/PostIntegrationTests.java | 4 +- .../CommentServiceImplIntegrationTest.java | 6 +- .../comment/CommentServiceImplTest.java | 65 ++++++++----------- .../ReplyServiceImplIntegrationTest.java | 6 +- .../console/EmailVerificationCodeTest.java | 48 ++++++++------ .../console/UserEndpointIntegrationTest.java | 4 +- ...ltPluginApplicationContextFactoryTest.java | 4 +- .../app/plugin/DefaultSettingFetcherTest.java | 4 +- .../security/AuthProviderServiceImplTest.java | 19 +++--- .../security/SuperAdminInitializerTest.java | 4 +- .../authorization/AuthorizationTest.java | 8 +-- .../halo/app/theme/ThemeIntegrationTest.java | 4 +- .../halo/app/theme/ViewNameResolverTest.java | 26 ++++---- .../dialect/GeneratorMetaProcessorTest.java | 6 +- .../CommentPublicQueryServiceImplTest.java | 14 ++-- .../ThemeMessageResolverIntegrationTest.java | 6 +- .../router/PreviewRouterFunctionTest.java | 35 ++++++---- build.gradle | 2 +- 22 files changed, 141 insertions(+), 139 deletions(-) diff --git a/application/src/main/java/run/halo/app/theme/dialect/EvaluationContextEnhancer.java b/application/src/main/java/run/halo/app/theme/dialect/EvaluationContextEnhancer.java index 2d01e48b6..36a390d37 100644 --- a/application/src/main/java/run/halo/app/theme/dialect/EvaluationContextEnhancer.java +++ b/application/src/main/java/run/halo/app/theme/dialect/EvaluationContextEnhancer.java @@ -12,6 +12,7 @@ import org.springframework.expression.MethodExecutor; import org.springframework.expression.MethodResolver; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypedValue; +import org.springframework.expression.spel.CompilablePropertyAccessor; import org.springframework.expression.spel.support.ReflectivePropertyAccessor; import org.springframework.integration.json.JsonPropertyAccessor; import org.springframework.lang.Nullable; @@ -122,7 +123,7 @@ public class EvaluationContextEnhancer extends AbstractTemplateBoundariesProcess public PropertyAccessor createOptimalAccessor(EvaluationContext context, Object target, String name) { var optimalAccessor = delegate.createOptimalAccessor(context, target, name); - if (optimalAccessor instanceof OptimalPropertyAccessor optimalPropertyAccessor) { + if (optimalAccessor instanceof CompilablePropertyAccessor optimalPropertyAccessor) { if (ReactiveUtils.isReactiveType(optimalPropertyAccessor.getPropertyType())) { return this; } diff --git a/application/src/test/java/run/halo/app/config/ExtensionConfigurationTest.java b/application/src/test/java/run/halo/app/config/ExtensionConfigurationTest.java index 9cc8a8140..3c3078fc4 100644 --- a/application/src/test/java/run/halo/app/config/ExtensionConfigurationTest.java +++ b/application/src/test/java/run/halo/app/config/ExtensionConfigurationTest.java @@ -19,10 +19,10 @@ 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.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.core.publisher.Flux; import run.halo.app.core.extension.Role; @@ -47,7 +47,7 @@ class ExtensionConfigurationTest { @Autowired SchemeManager schemeManager; - @MockBean + @MockitoBean RoleService roleService; @BeforeEach diff --git a/application/src/test/java/run/halo/app/config/WebFluxConfigTest.java b/application/src/test/java/run/halo/app/config/WebFluxConfigTest.java index 70f2db955..40030c643 100644 --- a/application/src/test/java/run/halo/app/config/WebFluxConfigTest.java +++ b/application/src/test/java/run/halo/app/config/WebFluxConfigTest.java @@ -13,10 +13,10 @@ 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.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.socket.WebSocketHandler; import org.springframework.web.reactive.socket.WebSocketMessage; @@ -38,7 +38,7 @@ class WebFluxConfigTest { @Autowired WebTestClient webClient; - @SpyBean + @MockitoSpyBean RoleService roleService; @LocalServerPort diff --git a/application/src/test/java/run/halo/app/content/CategoryPostCountUpdaterTest.java b/application/src/test/java/run/halo/app/content/CategoryPostCountUpdaterTest.java index 8b0fab57c..7ee6fae5b 100644 --- a/application/src/test/java/run/halo/app/content/CategoryPostCountUpdaterTest.java +++ b/application/src/test/java/run/halo/app/content/CategoryPostCountUpdaterTest.java @@ -12,8 +12,8 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -46,7 +46,7 @@ class CategoryPostCountUpdaterTest { @Autowired private SchemeManager schemeManager; - @SpyBean + @MockitoSpyBean private ExtensionClient client; @Autowired diff --git a/application/src/test/java/run/halo/app/content/PostIntegrationTests.java b/application/src/test/java/run/halo/app/content/PostIntegrationTests.java index 6eb47e1c4..76e301d35 100644 --- a/application/src/test/java/run/halo/app/content/PostIntegrationTests.java +++ b/application/src/test/java/run/halo/app/content/PostIntegrationTests.java @@ -12,9 +12,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.core.publisher.Flux; import run.halo.app.core.extension.Role; @@ -39,7 +39,7 @@ public class PostIntegrationTests { @Autowired private WebTestClient webTestClient; - @MockBean + @MockitoBean RoleService roleService; @BeforeEach diff --git a/application/src/test/java/run/halo/app/content/comment/CommentServiceImplIntegrationTest.java b/application/src/test/java/run/halo/app/content/comment/CommentServiceImplIntegrationTest.java index 0b1600551..ce77779e0 100644 --- a/application/src/test/java/run/halo/app/content/comment/CommentServiceImplIntegrationTest.java +++ b/application/src/test/java/run/halo/app/content/comment/CommentServiceImplIntegrationTest.java @@ -14,8 +14,8 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -48,7 +48,7 @@ class CommentServiceImplIntegrationTest { @Autowired private SchemeManager schemeManager; - @SpyBean + @MockitoSpyBean private ReactiveExtensionClient reactiveClient; @Autowired @@ -57,7 +57,7 @@ class CommentServiceImplIntegrationTest { @Autowired private IndexerFactory indexerFactory; - @SpyBean + @MockitoSpyBean private CommentServiceImpl commentService; Mono deleteImmediately(Extension extension) { diff --git a/application/src/test/java/run/halo/app/content/comment/CommentServiceImplTest.java b/application/src/test/java/run/halo/app/content/comment/CommentServiceImplTest.java index 3e706594d..f9a2f0bbb 100644 --- a/application/src/test/java/run/halo/app/content/comment/CommentServiceImplTest.java +++ b/application/src/test/java/run/halo/app/content/comment/CommentServiceImplTest.java @@ -2,7 +2,6 @@ package run.halo.app.content.comment; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -12,7 +11,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import org.json.JSONException; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -61,53 +59,25 @@ import run.halo.app.security.authorization.AuthorityUtils; class CommentServiceImplTest { @Mock - private SystemConfigurableEnvironmentFetcher environmentFetcher; + SystemConfigurableEnvironmentFetcher environmentFetcher; @Mock - private ReactiveExtensionClient client; + ReactiveExtensionClient client; @Mock - private UserService userService; + UserService userService; @Mock - private RoleService roleService; + RoleService roleService; @Mock - private ExtensionGetter extensionGetter; + ExtensionGetter extensionGetter; @InjectMocks - private CommentServiceImpl commentService; + CommentServiceImpl commentService; @Mock - private CounterService counterService; - - @BeforeEach - void setUp() { - SystemSetting.Comment commentSetting = getCommentSetting(); - lenient().when(environmentFetcher.fetchComment()).thenReturn(Mono.just(commentSetting)); - - ListResult comments = new ListResult<>(1, 10, 3, comments()); - when(client.listBy(eq(Comment.class), any(ListOptions.class), any(PageRequest.class))) - .thenReturn(Mono.just(comments)); - - when(userService.getUserOrGhost(eq("A-owner"))) - .thenReturn(Mono.just(createUser("A-owner"))); - when(userService.getUserOrGhost(eq("B-owner"))) - .thenReturn(Mono.just(createUser("B-owner"))); - when(client.fetch(eq(User.class), eq("C-owner"))) - .thenReturn(Mono.empty()); - - when(roleService.contains(Set.of("USER"), - Set.of(AuthorityUtils.COMMENT_MANAGEMENT_ROLE_NAME))) - .thenReturn(Mono.just(false)); - - PostCommentSubject postCommentSubject = Mockito.mock(PostCommentSubject.class); - when(extensionGetter.getExtensions(CommentSubject.class)) - .thenReturn(Flux.just(postCommentSubject)); - - when(postCommentSubject.supports(any())).thenReturn(true); - when(postCommentSubject.get(eq("fake-post"))).thenReturn(Mono.just(post())); - } + CounterService counterService; private static User createUser(String name) { User user = new User(); @@ -122,10 +92,21 @@ class CommentServiceImplTest { @Test void listComment() { + var comments = new ListResult(1, 10, 3, comments()); + when(client.listBy(eq(Comment.class), any(ListOptions.class), any(PageRequest.class))) + .thenReturn(Mono.just(comments)); + + PostCommentSubject postCommentSubject = Mockito.mock(PostCommentSubject.class); + when(extensionGetter.getExtensions(CommentSubject.class)) + .thenReturn(Flux.just(postCommentSubject)); + + when(postCommentSubject.supports(any())).thenReturn(true); + when(postCommentSubject.get(eq("fake-post"))).thenReturn(Mono.just(post())); + when(userService.getUserOrGhost(any())) .thenReturn(Mono.just(ghostUser())); - when(userService.getUserOrGhost("A-owner")) - .thenReturn(Mono.just(createUser("A-owner"))); + // when(userService.getUserOrGhost("A-owner")) + // .thenReturn(Mono.just(createUser("A-owner"))); when(userService.getUserOrGhost("B-owner")) .thenReturn(Mono.just(createUser("B-owner"))); @@ -170,6 +151,12 @@ class CommentServiceImplTest { @Test @WithMockUser(username = "B-owner") void create() throws JSONException { + var commentSetting = getCommentSetting(); + when(environmentFetcher.fetchComment()).thenReturn(Mono.just(commentSetting)); + when(roleService.contains(Set.of("USER"), + Set.of(AuthorityUtils.COMMENT_MANAGEMENT_ROLE_NAME))) + .thenReturn(Mono.just(false)); + CommentRequest commentRequest = new CommentRequest(); commentRequest.setRaw("fake-raw"); commentRequest.setContent("fake-content"); diff --git a/application/src/test/java/run/halo/app/content/comment/ReplyServiceImplIntegrationTest.java b/application/src/test/java/run/halo/app/content/comment/ReplyServiceImplIntegrationTest.java index c2365bdf7..01c4c435c 100644 --- a/application/src/test/java/run/halo/app/content/comment/ReplyServiceImplIntegrationTest.java +++ b/application/src/test/java/run/halo/app/content/comment/ReplyServiceImplIntegrationTest.java @@ -14,8 +14,8 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -56,7 +56,7 @@ class ReplyServiceImplIntegrationTest { @Autowired private SchemeManager schemeManager; - @SpyBean + @MockitoSpyBean private ReactiveExtensionClient reactiveClient; @Autowired @@ -65,7 +65,7 @@ class ReplyServiceImplIntegrationTest { @Autowired private IndexerFactory indexerFactory; - @SpyBean + @MockitoSpyBean private ReplyServiceImpl replyService; Mono deleteImmediately(Extension extension) { diff --git a/application/src/test/java/run/halo/app/core/endpoint/console/EmailVerificationCodeTest.java b/application/src/test/java/run/halo/app/core/endpoint/console/EmailVerificationCodeTest.java index 683e6552c..e5691d09c 100644 --- a/application/src/test/java/run/halo/app/core/endpoint/console/EmailVerificationCodeTest.java +++ b/application/src/test/java/run/halo/app/core/endpoint/console/EmailVerificationCodeTest.java @@ -1,15 +1,11 @@ package run.halo.app.core.endpoint.console; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf; +import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity; import io.github.resilience4j.ratelimiter.RateLimiterConfig; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; -import io.github.resilience4j.reactor.ratelimiter.operator.RateLimiterOperator; import java.time.Duration; import java.util.Map; import org.junit.jupiter.api.BeforeEach; @@ -39,47 +35,49 @@ import run.halo.app.extension.ReactiveExtensionClient; @ExtendWith(SpringExtension.class) @WithMockUser(username = "fake-user", password = "fake-password") class EmailVerificationCodeTest { + WebTestClient webClient; + @Mock ReactiveExtensionClient client; + @Mock EmailVerificationService emailVerificationService; @Mock UserService userService; + @Mock + RateLimiterRegistry rateLimiterRegistry; + @InjectMocks UserEndpoint endpoint; @BeforeEach void setUp() { - var spyUserEndpoint = spy(endpoint); + webClient = WebTestClient.bindToRouterFunction(endpoint.endpoint()) + .apply(springSecurity()) + .build(); + } + + @Test + void sendEmailVerificationCode() { var config = RateLimiterConfig.custom() .limitRefreshPeriod(Duration.ofSeconds(10)) .limitForPeriod(1) .build(); var sendCodeRateLimiter = RateLimiterRegistry.of(config) .rateLimiter("send-email-verification-code-fake-user:hi@halo.run"); - doReturn(RateLimiterOperator.of(sendCodeRateLimiter)).when(spyUserEndpoint) - .sendEmailVerificationCodeRateLimiter(eq("fake-user"), eq("hi@halo.run")); + when(rateLimiterRegistry.rateLimiter( + "send-email-verification-code-fake-user:hi@halo.run", + "send-email-verification-code") + ).thenReturn(sendCodeRateLimiter); - var verifyEmailRateLimiter = RateLimiterRegistry.of(config) - .rateLimiter("verify-email-fake-user"); - doReturn(RateLimiterOperator.of(verifyEmailRateLimiter)).when(spyUserEndpoint) - .verificationEmailRateLimiter(eq("fake-user")); - - webClient = WebTestClient.bindToRouterFunction(spyUserEndpoint.endpoint()).build() - .mutateWith(csrf()); - } - - @Test - void sendEmailVerificationCode() { var user = new User(); user.setMetadata(new Metadata()); user.getMetadata().setName("fake-user"); user.setSpec(new User.UserSpec()); user.getSpec().setEmail("hi@halo.run"); - when(client.get(eq(User.class), eq("fake-user"))).thenReturn(Mono.just(user)); when(emailVerificationService.sendVerificationCode(anyString(), anyString())) .thenReturn(Mono.empty()); webClient.post() @@ -100,6 +98,16 @@ class EmailVerificationCodeTest { @Test void verifyEmail() { + var config = RateLimiterConfig.custom() + .limitRefreshPeriod(Duration.ofSeconds(10)) + .limitForPeriod(1) + .build(); + + var verifyEmailRateLimiter = RateLimiterRegistry.of(config) + .rateLimiter("verify-email-fake-user"); + when(rateLimiterRegistry.rateLimiter("verify-email-fake-user", "verify-email")) + .thenReturn(verifyEmailRateLimiter); + when(emailVerificationService.verify(anyString(), anyString())) .thenReturn(Mono.empty()); when(userService.confirmPassword(anyString(), anyString())) diff --git a/application/src/test/java/run/halo/app/core/endpoint/console/UserEndpointIntegrationTest.java b/application/src/test/java/run/halo/app/core/endpoint/console/UserEndpointIntegrationTest.java index 29cfae324..db061b28e 100644 --- a/application/src/test/java/run/halo/app/core/endpoint/console/UserEndpointIntegrationTest.java +++ b/application/src/test/java/run/halo/app/core/endpoint/console/UserEndpointIntegrationTest.java @@ -14,9 +14,9 @@ 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.boot.test.mock.mockito.MockBean; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -37,7 +37,7 @@ public class UserEndpointIntegrationTest { @Autowired ReactiveExtensionClient client; - @MockBean + @MockitoBean RoleService roleService; @BeforeEach diff --git a/application/src/test/java/run/halo/app/plugin/DefaultPluginApplicationContextFactoryTest.java b/application/src/test/java/run/halo/app/plugin/DefaultPluginApplicationContextFactoryTest.java index 0f37a595e..bf2e4a494 100644 --- a/application/src/test/java/run/halo/app/plugin/DefaultPluginApplicationContextFactoryTest.java +++ b/application/src/test/java/run/halo/app/plugin/DefaultPluginApplicationContextFactoryTest.java @@ -9,13 +9,13 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.pf4j.PluginWrapper; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import run.halo.app.search.SearchService; @SpringBootTest class DefaultPluginApplicationContextFactoryTest { - @SpyBean + @MockitoSpyBean SpringPluginManager pluginManager; DefaultPluginApplicationContextFactory factory; diff --git a/application/src/test/java/run/halo/app/plugin/DefaultSettingFetcherTest.java b/application/src/test/java/run/halo/app/plugin/DefaultSettingFetcherTest.java index 5e7f0d2ee..2a70af35b 100644 --- a/application/src/test/java/run/halo/app/plugin/DefaultSettingFetcherTest.java +++ b/application/src/test/java/run/halo/app/plugin/DefaultSettingFetcherTest.java @@ -23,11 +23,11 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.skyscreamer.jsonassert.JSONAssert; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.concurrent.ConcurrentMapCache; import org.springframework.context.ApplicationContext; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import reactor.core.publisher.Mono; import run.halo.app.core.extension.Plugin; import run.halo.app.extension.ConfigMap; @@ -55,7 +55,7 @@ class DefaultSettingFetcherTest { @Mock private CacheManager cacheManager; - @MockBean + @MockitoBean private final PluginContext pluginContext = PluginContext.builder() .name("fake") .configMapName("fake-config") diff --git a/application/src/test/java/run/halo/app/security/AuthProviderServiceImplTest.java b/application/src/test/java/run/halo/app/security/AuthProviderServiceImplTest.java index 6487ee375..5b41cce60 100644 --- a/application/src/test/java/run/halo/app/security/AuthProviderServiceImplTest.java +++ b/application/src/test/java/run/halo/app/security/AuthProviderServiceImplTest.java @@ -37,11 +37,12 @@ import run.halo.app.infra.utils.JsonUtils; */ @ExtendWith(SpringExtension.class) class AuthProviderServiceImplTest { + @Mock - private ReactiveExtensionClient client; + ReactiveExtensionClient client; @InjectMocks - private AuthProviderServiceImpl authProviderService; + AuthProviderServiceImpl authProviderService; @Test void testEnable() { @@ -57,14 +58,12 @@ class AuthProviderServiceImplTest { when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG))) .thenReturn(Mono.just(configMap)); - AuthProvider local = createAuthProvider("local"); - local.getMetadata().getLabels().put(AuthProvider.PRIVILEGED_LABEL, "true"); - when(client.list(eq(AuthProvider.class), any(), any())).thenReturn(Flux.just(local)); - // Call the method being tested - Mono result = authProviderService.enable("github"); + authProviderService.enable("github") + .as(StepVerifier::create) + .expectNext(authProvider) + .verifyComplete(); - assertEquals(authProvider, result.block()); ConfigMap value = captor.getValue(); String providerSettingStr = value.getData().get(SystemSetting.AuthProvider.GROUP); Set enabled = @@ -84,7 +83,7 @@ class AuthProviderServiceImplTest { AuthProvider local = createAuthProvider("local"); local.getMetadata().getLabels().put(AuthProvider.PRIVILEGED_LABEL, "true"); - when(client.list(eq(AuthProvider.class), any(), any())).thenReturn(Flux.just(local)); + // when(client.list(eq(AuthProvider.class), any(), any())).thenReturn(Flux.just(local)); ArgumentCaptor captor = ArgumentCaptor.forClass(ConfigMap.class); when(client.update(captor.capture())).thenReturn(Mono.empty()); @@ -155,7 +154,7 @@ class AuthProviderServiceImplTest { "supportsBinding": false, "privileged": false },{ - + "name": "gitee", "displayName": "gitee", "enabled": false, diff --git a/application/src/test/java/run/halo/app/security/SuperAdminInitializerTest.java b/application/src/test/java/run/halo/app/security/SuperAdminInitializerTest.java index 909bee4f7..f646ad040 100644 --- a/application/src/test/java/run/halo/app/security/SuperAdminInitializerTest.java +++ b/application/src/test/java/run/halo/app/security/SuperAdminInitializerTest.java @@ -10,8 +10,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.web.reactive.server.WebTestClient; import run.halo.app.core.extension.Role; import run.halo.app.core.extension.RoleBinding; @@ -28,7 +28,7 @@ import run.halo.app.extension.ReactiveExtensionClient; @AutoConfigureTestDatabase class SuperAdminInitializerTest { - @SpyBean + @MockitoSpyBean ReactiveExtensionClient client; @Autowired diff --git a/application/src/test/java/run/halo/app/security/authorization/AuthorizationTest.java b/application/src/test/java/run/halo/app/security/authorization/AuthorizationTest.java index 21a6758d7..49912b316 100644 --- a/application/src/test/java/run/halo/app/security/authorization/AuthorizationTest.java +++ b/application/src/test/java/run/halo/app/security/authorization/AuthorizationTest.java @@ -17,7 +17,6 @@ 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.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; @@ -26,6 +25,7 @@ import org.springframework.security.core.userdetails.ReactiveUserDetailsPassword import org.springframework.security.core.userdetails.ReactiveUserDetailsService; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; @@ -49,13 +49,13 @@ class AuthorizationTest { @Autowired WebTestClient webClient; - @SpyBean + @MockitoSpyBean ReactiveUserDetailsService userDetailsService; - @SpyBean + @MockitoSpyBean ReactiveUserDetailsPasswordService userDetailsPasswordService; - @SpyBean + @MockitoSpyBean RoleService roleService; @Autowired diff --git a/application/src/test/java/run/halo/app/theme/ThemeIntegrationTest.java b/application/src/test/java/run/halo/app/theme/ThemeIntegrationTest.java index fd0505382..32f90d23c 100644 --- a/application/src/test/java/run/halo/app/theme/ThemeIntegrationTest.java +++ b/application/src/test/java/run/halo/app/theme/ThemeIntegrationTest.java @@ -11,11 +11,11 @@ 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.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; @@ -36,7 +36,7 @@ public class ThemeIntegrationTest { @Autowired WebTestClient webClient; - @MockBean + @MockitoBean InitializationStateGetter initializationStateGetter; @Autowired diff --git a/application/src/test/java/run/halo/app/theme/ViewNameResolverTest.java b/application/src/test/java/run/halo/app/theme/ViewNameResolverTest.java index 2c8c6021b..14c656a64 100644 --- a/application/src/test/java/run/halo/app/theme/ViewNameResolverTest.java +++ b/application/src/test/java/run/halo/app/theme/ViewNameResolverTest.java @@ -2,14 +2,13 @@ package run.halo.app.theme; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; -import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; +import java.nio.file.Path; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -35,40 +34,41 @@ import reactor.test.StepVerifier; class ViewNameResolverTest { @Mock - private ThemeResolver themeResolver; + ThemeResolver themeResolver; @Mock - private ThymeleafProperties thymeleafProperties; + ThymeleafProperties thymeleafProperties; @InjectMocks - private DefaultViewNameResolver viewNameResolver; + DefaultViewNameResolver viewNameResolver; @TempDir - private File themePath; + Path themePath; @BeforeEach void setUp() throws IOException { when(thymeleafProperties.getSuffix()).thenReturn(ThymeleafProperties.DEFAULT_SUFFIX); + } - var templatesPath = themePath.toPath().resolve("templates"); + @Test + void resolveViewNameOrDefault() throws URISyntaxException, IOException { + var templatesPath = themePath.resolve("templates"); if (!Files.exists(templatesPath)) { Files.createDirectory(templatesPath); } Files.createFile(templatesPath.resolve("post_news.html")); Files.createFile(templatesPath.resolve("post_docs.html")); - when(themeResolver.getTheme(any())) + + var exchange = Mockito.mock(ServerWebExchange.class); + when(themeResolver.getTheme(exchange)) .thenReturn(Mono.fromSupplier(() -> ThemeContext.builder() .name("fake-theme") - .path(themePath.toPath()) + .path(themePath) .active(true) .build()) ); - } - @Test - void resolveViewNameOrDefault() throws URISyntaxException { - ServerWebExchange exchange = Mockito.mock(ServerWebExchange.class); MockServerRequest request = MockServerRequest.builder() .uri(new URI("/")).method(HttpMethod.GET) .exchange(exchange) diff --git a/application/src/test/java/run/halo/app/theme/dialect/GeneratorMetaProcessorTest.java b/application/src/test/java/run/halo/app/theme/dialect/GeneratorMetaProcessorTest.java index a732d7aec..92d2bf7a1 100644 --- a/application/src/test/java/run/halo/app/theme/dialect/GeneratorMetaProcessorTest.java +++ b/application/src/test/java/run/halo/app/theme/dialect/GeneratorMetaProcessorTest.java @@ -11,7 +11,7 @@ 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.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.ResourceUtils; import org.springframework.web.server.ServerWebExchange; @@ -27,10 +27,10 @@ class GeneratorMetaProcessorTest { @Autowired WebTestClient webClient; - @MockBean + @MockitoBean InitializationStateGetter initializationStateGetter; - @MockBean + @MockitoBean ThemeResolver themeResolver; @BeforeEach diff --git a/application/src/test/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImplTest.java b/application/src/test/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImplTest.java index 0a4a8060d..54fbf17ea 100644 --- a/application/src/test/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImplTest.java +++ b/application/src/test/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImplTest.java @@ -44,21 +44,19 @@ import run.halo.app.metrics.CounterService; class CommentPublicQueryServiceImplTest { @Mock - private ReactiveExtensionClient client; - @Mock - private UserService userService; + ReactiveExtensionClient client; @Mock - private CounterService counterService; + UserService userService; + + @Mock + CounterService counterService; @InjectMocks - private CommentPublicQueryServiceImpl commentPublicQueryService; + CommentPublicQueryServiceImpl commentPublicQueryService; @BeforeEach void setUp() { - User ghost = createUser(); - ghost.getMetadata().setName("ghost"); - when(userService.getUserOrGhost(eq("ghost"))).thenReturn(Mono.just(ghost)); when(userService.getUserOrGhost(eq("fake-user"))).thenReturn(Mono.just(createUser())); } diff --git a/application/src/test/java/run/halo/app/theme/message/ThemeMessageResolverIntegrationTest.java b/application/src/test/java/run/halo/app/theme/message/ThemeMessageResolverIntegrationTest.java index 4c461ff6b..ad7c7b7cf 100644 --- a/application/src/test/java/run/halo/app/theme/message/ThemeMessageResolverIntegrationTest.java +++ b/application/src/test/java/run/halo/app/theme/message/ThemeMessageResolverIntegrationTest.java @@ -13,9 +13,9 @@ 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.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.context.annotation.Bean; import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.ResourceUtils; import org.springframework.web.reactive.function.server.RequestPredicates; @@ -38,14 +38,14 @@ import run.halo.app.theme.ThemeResolver; @AutoConfigureWebTestClient public class ThemeMessageResolverIntegrationTest { - @SpyBean + @MockitoSpyBean private ThemeResolver themeResolver; private URL defaultThemeUrl; private URL otherThemeUrl; - @SpyBean + @MockitoSpyBean private InitializationStateGetter initializationStateGetter; @Autowired diff --git a/application/src/test/java/run/halo/app/theme/router/PreviewRouterFunctionTest.java b/application/src/test/java/run/halo/app/theme/router/PreviewRouterFunctionTest.java index 683401a1d..baed96cb4 100644 --- a/application/src/test/java/run/halo/app/theme/router/PreviewRouterFunctionTest.java +++ b/application/src/test/java/run/halo/app/theme/router/PreviewRouterFunctionTest.java @@ -43,36 +43,40 @@ import run.halo.app.theme.finders.vo.SinglePageVo; @ExtendWith(SpringExtension.class) class PreviewRouterFunctionTest { @Mock - private ReactiveExtensionClient client; + ReactiveExtensionClient client; @Mock - private PostPublicQueryService postPublicQueryService; + PostPublicQueryService postPublicQueryService; @Mock - private ViewNameResolver viewNameResolver; + ViewNameResolver viewNameResolver; @Mock - private ViewResolver viewResolver; + ViewResolver viewResolver; @Mock - private PostService postService; + PostService postService; @Mock - private SinglePageConversionService singlePageConversionService; + SinglePageConversionService singlePageConversionService; @InjectMocks - private PreviewRouterFunction previewRouterFunction; + PreviewRouterFunction previewRouterFunction; - private WebTestClient webTestClient; + WebTestClient webTestClient; @BeforeEach - public void setUp() { + void setUp() { webTestClient = WebTestClient.bindToRouterFunction(previewRouterFunction.previewRouter()) .handlerStrategies(HandlerStrategies.builder() .viewResolver(viewResolver) .build()) .build(); + } + @Test + @WithMockUser(username = "testuser") + void previewPost() { when(viewResolver.resolveViewName(any(), any())) .thenReturn(Mono.just(new EmptyView() { @Override @@ -81,11 +85,7 @@ class PreviewRouterFunctionTest { return super.render(model, contentType, exchange); } })); - } - @Test - @WithMockUser(username = "testuser") - public void previewPost() { Post post = new Post(); post.setMetadata(new Metadata()); post.getMetadata().setName("post1"); @@ -123,6 +123,15 @@ class PreviewRouterFunctionTest { @Test @WithMockUser(username = "testuser") public void previewSinglePage() { + when(viewResolver.resolveViewName(any(), any())) + .thenReturn(Mono.just(new EmptyView() { + @Override + public Mono render(Map model, MediaType contentType, + ServerWebExchange exchange) { + return super.render(model, contentType, exchange); + } + })); + SinglePage singlePage = new SinglePage(); singlePage.setMetadata(new Metadata()); singlePage.getMetadata().setName("page1"); diff --git a/build.gradle b/build.gradle index ec0f11424..e94ecb0b4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '3.3.3' apply false + id 'org.springframework.boot' version '3.4.0-M3' apply false id 'io.spring.dependency-management' version '1.1.6' apply false id "com.gorylenko.gradle-git-properties" version "2.4.1" apply false id "de.undercouch.download" version "5.6.0" apply false From f6409a0cb0574c49d4fb9da0f73fa7deca4e0e97 Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:59:25 +0800 Subject: [PATCH 3/3] fix: correct file mime type validation parameter to restore functionality (#6673) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind bug /area core /milestone 2.20.x #### What this PR does / why we need it: 修复文件上传时类型校验失效的问题 此问题由 #6390 导致 #### Does this PR introduce a user-facing change? ```release-note 修复文件上传时类型校验失效的问题 ``` --- .../LocalAttachmentUploadHandler.java | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/run/halo/app/core/attachment/endpoint/LocalAttachmentUploadHandler.java b/application/src/main/java/run/halo/app/core/attachment/endpoint/LocalAttachmentUploadHandler.java index f13408cea..bb48574d4 100644 --- a/application/src/main/java/run/halo/app/core/attachment/endpoint/LocalAttachmentUploadHandler.java +++ b/application/src/main/java/run/halo/app/core/attachment/endpoint/LocalAttachmentUploadHandler.java @@ -6,6 +6,7 @@ import static run.halo.app.infra.utils.FileUtils.checkDirectoryTraversal; import static run.halo.app.infra.utils.FileUtils.deleteFileSilently; import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.FileAlreadyExistsException; @@ -25,6 +26,7 @@ import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.MediaType; import org.springframework.http.codec.multipart.FilePart; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -156,19 +158,14 @@ class LocalAttachmentUploadHandler implements AttachmentHandler { var typeValidator = file.content() .next() .handle((dataBuffer, sink) -> { - var mimeType = "Unknown"; - try { - mimeType = FileTypeDetectUtils.detectMimeType(dataBuffer.asInputStream()); - var isAllow = setting.getAllowedFileTypes() - .stream() - .map(FileCategoryMatcher::of) - .anyMatch(matcher -> matcher.match(file.filename())); - if (isAllow) { - sink.next(dataBuffer); - return; - } - } catch (IOException e) { - log.warn("Failed to detect file type", e); + var mimeType = detectMimeType(dataBuffer.asInputStream()); + var isAllow = setting.getAllowedFileTypes() + .stream() + .map(FileCategoryMatcher::of) + .anyMatch(matcher -> matcher.match(mimeType)); + if (isAllow) { + sink.next(dataBuffer); + return; } sink.error(new FileTypeNotAllowedException("File type is not allowed", "problemDetail.attachment.upload.fileTypeNotSupported", @@ -180,6 +177,16 @@ class LocalAttachmentUploadHandler implements AttachmentHandler { return Mono.when(validations); } + @NonNull + private String detectMimeType(InputStream inputStream) { + try { + return FileTypeDetectUtils.detectMimeType(inputStream); + } catch (IOException e) { + log.warn("Failed to detect file type", e); + return "Unknown"; + } + } + @Override public Mono delete(DeleteContext deleteContext) { return Mono.just(deleteContext)