private Mono<ServerResponse> signUp(ServerRequest request) {
        return request.bodyToMono(SignUpRequest.class)
            .doOnNext(signUpRequest -> signUpRequest.user().getSpec().setEmailVerified(false))
            .flatMap(signUpRequest -> environmentFetcher.fetch(SystemSetting.User.GROUP,
                    SystemSetting.User.class)
                .map(user -> BooleanUtils.isTrue(user.getMustVerifyEmailOnRegistration()))
                .defaultIfEmpty(false)
                .flatMap(mustVerifyEmailOnRegistration -> {
                    if (!mustVerifyEmailOnRegistration) {
                        return Mono.just(signUpRequest);
                    }
                    if (!StringUtils.isNumeric(signUpRequest.verifyCode)) {
                        return Mono.error(new EmailVerificationFailed());
                    }
                    return emailVerificationService.verifyRegisterVerificationCode(
                            signUpRequest.user().getSpec().getEmail(),
                            signUpRequest.verifyCode)
                        .flatMap(verified -> {
                            if (BooleanUtils.isNotTrue(verified)) {
                                return Mono.error(new EmailVerificationFailed());
                            }
                            signUpRequest.user().getSpec().setEmailVerified(true);
                            return Mono.just(signUpRequest);
                        });
                })
            )
            .flatMap(signUpRequest ->
                userService.signUp(signUpRequest.user(), signUpRequest.password())
            )
            .flatMap(user -> authenticate(user.getMetadata().getName(), request.exchange())
                .thenReturn(user)
            )
            .flatMap(user -> ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(user)
            )
            .transformDeferred(getRateLimiterForSignUp(request.exchange()))
            .onErrorMap(RequestNotPermitted.class, RateLimitExceededException::new);
    }
@Test
    void signUp() {
        User user = new User();
        user.setMetadata(new Metadata());
        user.getMetadata().setName("fake-user");
        user.setSpec(new User.UserSpec());
        user.getSpec().setDisplayName("hello");
        user.getSpec().setBio("bio");

        when(userService.signUp(any(User.class), anyString())).thenReturn(Mono.just(user));
        when(securityContextRepository.save(any(), any())).thenReturn(Mono.empty());
        when(reactiveUserDetailsService.findByUsername(anyString())).thenReturn(Mono.just(
            org.springframework.security.core.userdetails.User.withUsername("fake-user")
                .password("123456")
                .authorities("test-role")
                .build()));
        SystemSetting.User userSetting = mock(SystemSetting.User.class);
        when(environmentFetcher.fetch(SystemSetting.User.GROUP, SystemSetting.User.class))
            .thenReturn(Mono.just(userSetting));

        when(rateLimiterRegistry.rateLimiter("signup-from-ip-127.0.0.1", "signup"))
            .thenReturn(RateLimiter.ofDefaults("signup"));

        webClient.post()
            .uri("/users/-/signup")
            .header("X-Forwarded-For", "127.0.0.1")
            .bodyValue(new PublicUserEndpoint.SignUpRequest(user, "fake-password", ""))
            .exchange()
            .expectStatus().isOk();

        verify(userService).signUp(any(User.class), anyString());
        verify(securityContextRepository).save(any(), any());
        verify(reactiveUserDetailsService).findByUsername(eq("fake-user"));
    }