I wrote a simple spring boot application which uses OAuth2 using Firebase.
Here's the configuration
@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity security) throws Exception {
security
.cors()
.and()
.csrf().disable()
.authorizeHttpRequests()
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer()
.jwt();
return security.build();
}
}
I have a controller which I want to test using MockMvc
Here's the test file
@WebMvcTest(CodeController.class)
@WebAppConfiguration
@ContextConfiguration(classes = SecurityConfig.class)
public class CodeControllerTests {
@MockBean
private CodeExecutionService codeExecutionService;
@MockBean
private ProblemService problemService;
// @MockBean
// private ProblemRepo problemRepo;
@MockBean
private TestCaseValidationService validationService;
// @MockBean
// private ProblemRepo problemRepo;
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@BeforeEach
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
@Test
void runTestCode() throws Exception {
RunCodeDTO runCodeDTO = new RunCodeDTO("python", "something", "two-sum");
Problem problem = ProblemUtils.getTwoSum();
UserCode userCode = new UserCode(runCodeDTO.code(), runCodeDTO.language());
userCode.mergeWithStub(problem.getCodeRunStub());
List<TestResult> testResults = problem.getRunTestCases()
.stream()
.map(testCase -> new TestResult(testCase, Status.SUCCESS, ""))
.toList();
List<TestOutput> testOutputs = testResults
.stream()
.map(result -> new TestOutput(result.testCase(), new ValidationResult(Status.SUCCESS, "Test Case Passed")))
.toList();
when(problemService.getProblem(runCodeDTO.problemId())).thenReturn(Optional.of(problem));
when(codeExecutionService.executeAllTestCases(problem.getRunTestCases(), userCode)).thenReturn(testResults);
when(validationService.validateAllTestResults(testResults, problem.getOutputType(), problem.getValidationType())).thenReturn(testOutputs);
mockMvc
.perform(
MockMvcRequestBuilders.post("/code/test")
.contentType(MediaType.APPLICATION_JSON)
.content("")
.with(SecurityMockMvcRequestPostProcessors.oauth2Login())
)
.andExpect(status().isOk());
}
}
I'm trying to mock authorization using the SecurityMockMvcRequestPostProcessors.oauth2Login()
but I get a NoClassDefFoundError - org/springframework/security/oauth2/client/registration/ClientRegistration
.
However, the actual application works without any issue. It's only the test where I get this error
@m-denium is right in his comment, plus you are trying to build a client Authentication
implementation (oauth2Login()
instantiates an OAuth2AuthenticationToken
) when testing a resource-server (with resource-server dependencies, reason for classes under org/springframework/security/oauth2/client/
not being found).
As your conf is .oauth2ResourceServer().jwt()
, populate your test security context with a JwtAuthenticationToken
using SecurityMockMvcRequestPostProcessors.jwt()
.
Your test should be no more complicated than that:
@WebMvcTest(CodeController.class)
@Import({ SecurityConfig.class })
class CodeControllerTests {
@MockBean
private CodeExecutionService codeExecutionService;
@MockBean
private ProblemService problemService;
@MockBean
private TestCaseValidationService validationService;
@Autowired
MockMvc mockMvc;
@Test
void runTestCode() throws Exception {
RunCodeDTO runCodeDTO = new RunCodeDTO("python", "something", "two-sum");
Problem problem = ProblemUtils.getTwoSum();
UserCode userCode = new UserCode(runCodeDTO.code(), runCodeDTO.language());
userCode.mergeWithStub(problem.getCodeRunStub());
List<TestResult> testResults = problem.getRunTestCases()
.stream()
.map(testCase -> new TestResult(testCase, Status.SUCCESS, ""))
.toList();
List<TestOutput> testOutputs = testResults
.stream()
.map(result -> new TestOutput(result.testCase(), new ValidationResult(Status.SUCCESS, "Test Case Passed")))
.toList();
when(problemService.getProblem(runCodeDTO.problemId())).thenReturn(Optional.of(problem));
when(codeExecutionService.executeAllTestCases(problem.getRunTestCases(), userCode)).thenReturn(testResults);
when(validationService.validateAllTestResults(testResults, problem.getOutputType(), problem.getValidationType())).thenReturn(testOutputs);
mockMvc
.perform(MockMvcRequestBuilders.post("/code/test")
.contentType(MediaType.APPLICATION_JSON)
.content("")
.with(SecurityMockMvcRequestPostProcessors.jwt()
.jwt(jwt -> jwt.claim(StandardClaimNames.SUB, "Tonton Pirate"))
.authorities(List.of(new SimpleGrantedAuthority("NICE"), new SimpleGrantedAuthority("AUTHOR")))))
.andExpect(status().isOk());
}
}
Instead of SecurityMockMvcRequestPostProcessors.jwt()
, you could also use @WithMockJwtAuth
available from spring-addons-oauth2-test with sources available on this repo of mine (repo which also contains plenty of resource-server samples, each with access-control unit-tests)