Skip to content

Commit 27c4ffd

Browse files
committed
test(mfa): rename tests to follow naming convention and add enforcement test
Rename 11 test methods across MfaConfigurationTest and MfaFeatureEnabledIntegrationTest to follow the project's should[ExpectedBehavior]When[Condition] naming convention. Add integration test verifying that the DelegatingMissingAuthorityAccessDeniedHandler redirects to the password entry-point URI when a user is missing the PASSWORD factor authority.
1 parent f1ce6e6 commit 27c4ffd

2 files changed

Lines changed: 23 additions & 11 deletions

File tree

src/test/java/com/digitalsanctuary/spring/user/api/MfaFeatureEnabledIntegrationTest.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
55
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
66
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
7+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;
78
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
89
import java.util.Set;
910
import java.util.stream.Collectors;
@@ -47,7 +48,7 @@ void shouldRegisterMfaBeansAndMappingsWhenEnabled() {
4748

4849
@Test
4950
@DisplayName("should return MFA status for authenticated user")
50-
void shouldReturnMfaStatusForAuthenticatedUser() throws Exception {
51+
void shouldReturnMfaStatusWhenUserIsAuthenticated() throws Exception {
5152
mockMvc.perform(get("/user/mfa/status").with(user("user@test.com").roles("USER")))
5253
.andExpect(status().isOk())
5354
.andExpect(jsonPath("$.success").value(true))
@@ -58,7 +59,7 @@ void shouldReturnMfaStatusForAuthenticatedUser() throws Exception {
5859

5960
@Test
6061
@DisplayName("should return MFA status for unauthenticated request")
61-
void shouldReturnMfaStatusForUnauthenticatedRequest() throws Exception {
62+
void shouldReturnMfaStatusWhenRequestIsUnauthenticated() throws Exception {
6263
// The MFA status endpoint should be accessible without full authentication
6364
// since it's added to unprotected URIs
6465
mockMvc.perform(get("/user/mfa/status"))
@@ -70,12 +71,23 @@ void shouldReturnMfaStatusForUnauthenticatedRequest() throws Exception {
7071

7172
@Test
7273
@DisplayName("should report PASSWORD as missing factor when user has no password authority")
73-
void shouldReportPasswordAsMissingFactor() throws Exception {
74+
void shouldReportPasswordAsMissingFactorWhenUserLacksAuthority() throws Exception {
7475
// A standard user login via MockMvc's .with(user()) does not add FactorGrantedAuthority,
7576
// so PASSWORD should be reported as missing
7677
mockMvc.perform(get("/user/mfa/status").with(user("user@test.com").roles("USER")))
7778
.andExpect(status().isOk())
7879
.andExpect(jsonPath("$.data.missingFactors[0]").value("PASSWORD"))
7980
.andExpect(jsonPath("$.data.fullyAuthenticated").value(false));
8081
}
82+
83+
@Test
84+
@DisplayName("should redirect to password entry point when user is missing password factor authority")
85+
void shouldRedirectToPasswordEntryPointWhenMissingFactorAuthority() throws Exception {
86+
// A user with ROLE_USER but no FactorGrantedAuthority hits a protected endpoint.
87+
// The DelegatingMissingAuthorityAccessDeniedHandler should redirect to the
88+
// password entry-point URI since the PASSWORD factor is not satisfied.
89+
mockMvc.perform(get("/protected.html").with(user("user@test.com").roles("USER")))
90+
.andExpect(status().is3xxRedirection())
91+
.andExpect(redirectedUrlPattern(mfaConfigProperties.getPasswordEntryPointUri() + "**"));
92+
}
8193
}

src/test/java/com/digitalsanctuary/spring/user/security/MfaConfigurationTest.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,21 @@ class MapFactorToAuthority {
3131

3232
@Test
3333
@DisplayName("should map PASSWORD to FactorGrantedAuthority.PASSWORD_AUTHORITY")
34-
void shouldMapPasswordFactor() {
34+
void shouldMapToPasswordAuthorityWhenPasswordFactorGiven() {
3535
assertThat(MfaConfiguration.mapFactorToAuthority("PASSWORD"))
3636
.isEqualTo(FactorGrantedAuthority.PASSWORD_AUTHORITY);
3737
}
3838

3939
@Test
4040
@DisplayName("should map WEBAUTHN to FactorGrantedAuthority.WEBAUTHN_AUTHORITY")
41-
void shouldMapWebauthnFactor() {
41+
void shouldMapToWebauthnAuthorityWhenWebauthnFactorGiven() {
4242
assertThat(MfaConfiguration.mapFactorToAuthority("WEBAUTHN"))
4343
.isEqualTo(FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
4444
}
4545

4646
@Test
4747
@DisplayName("should be case-insensitive")
48-
void shouldBeCaseInsensitive() {
48+
void shouldMapCorrectlyWhenFactorNameIsLowercase() {
4949
assertThat(MfaConfiguration.mapFactorToAuthority("password"))
5050
.isEqualTo(FactorGrantedAuthority.PASSWORD_AUTHORITY);
5151
assertThat(MfaConfiguration.mapFactorToAuthority("webauthn"))
@@ -54,7 +54,7 @@ void shouldBeCaseInsensitive() {
5454

5555
@Test
5656
@DisplayName("should return null for unknown factor")
57-
void shouldReturnNullForUnknownFactor() {
57+
void shouldReturnNullWhenFactorIsUnknown() {
5858
assertThat(MfaConfiguration.mapFactorToAuthority("UNKNOWN")).isNull();
5959
}
6060
}
@@ -65,7 +65,7 @@ class ResolveFactorAuthorities {
6565

6666
@Test
6767
@DisplayName("should resolve configured factors to authority strings")
68-
void shouldResolveConfiguredFactors() {
68+
void shouldResolveAuthoritiesWhenFactorsConfigured() {
6969
mfaConfigProperties.setFactors(List.of("PASSWORD", "WEBAUTHN"));
7070
List<String> authorities = mfaConfiguration.resolveFactorAuthorities();
7171
assertThat(authorities).containsExactly(
@@ -75,14 +75,14 @@ void shouldResolveConfiguredFactors() {
7575

7676
@Test
7777
@DisplayName("should return empty list for empty factors")
78-
void shouldReturnEmptyForEmptyFactors() {
78+
void shouldReturnEmptyListWhenNoFactorsConfigured() {
7979
mfaConfigProperties.setFactors(List.of());
8080
assertThat(mfaConfiguration.resolveFactorAuthorities()).isEmpty();
8181
}
8282

8383
@Test
8484
@DisplayName("should skip unknown factors")
85-
void shouldSkipUnknownFactors() {
85+
void shouldSkipUnknownWhenMixedFactorsConfigured() {
8686
mfaConfigProperties.setFactors(List.of("PASSWORD", "UNKNOWN"));
8787
List<String> authorities = mfaConfiguration.resolveFactorAuthorities();
8888
assertThat(authorities).containsExactly(FactorGrantedAuthority.PASSWORD_AUTHORITY);
@@ -144,7 +144,7 @@ void shouldNotThrowWhenWebauthnFactorAndWebauthnEnabled() {
144144

145145
@Test
146146
@DisplayName("should not throw for PASSWORD-only configuration")
147-
void shouldNotThrowForPasswordOnly() {
147+
void shouldNotThrowWhenPasswordOnlyConfigured() {
148148
mfaConfigProperties.setEnabled(true);
149149
mfaConfigProperties.setFactors(List.of("PASSWORD"));
150150
// Should not throw (but will log a warning)

0 commit comments

Comments
 (0)