test(attendance): cover form-authenticated calendar access
This commit is contained in:
+154
@@ -0,0 +1,154 @@
|
||||
package com.lab.labtimesheet.feature.attendance.controller;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.lab.labtimesheet.config.TestcontainersConfiguration;
|
||||
import com.lab.labtimesheet.feature.account.model.GlobalRole;
|
||||
import com.lab.labtimesheet.feature.account.model.dto.CreateAccountCommand;
|
||||
import com.lab.labtimesheet.feature.account.service.AccountService;
|
||||
import com.lab.labtimesheet.feature.integration.model.SecurityMode;
|
||||
import com.lab.labtimesheet.feature.integration.model.dto.SmtpConnection;
|
||||
import com.lab.labtimesheet.feature.integration.model.dto.SmtpDraft;
|
||||
import com.lab.labtimesheet.feature.integration.service.SmtpConfigurationService;
|
||||
import com.lab.labtimesheet.feature.integration.service.SmtpProbe;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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.context.TestConfiguration;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@Import({TestcontainersConfiguration.class, CalendarAuthorizationWebIntegrationTest.MailProbeConfiguration.class})
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@ActiveProfiles("test")
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
class CalendarAuthorizationWebIntegrationTest {
|
||||
private static final String PASSWORD = "correct horse battery staple";
|
||||
private static final String ADMIN_EMAIL = "admin@example.test";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private AccountService accounts;
|
||||
|
||||
@Autowired
|
||||
private SmtpConfigurationService smtp;
|
||||
|
||||
@Autowired
|
||||
private RecordingSmtpProbe mail;
|
||||
|
||||
@Test
|
||||
void formAuthenticatedAdminCanOpenCalendarWhileMentorAndInternAreDenied() throws Exception {
|
||||
bootstrapFirstAdminThroughTheForm();
|
||||
MockHttpSession adminSession = login(ADMIN_EMAIL, PASSWORD, "ADMIN");
|
||||
|
||||
mockMvc.perform(get("/attendance/calendar").session(adminSession))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
long adminId = accounts.requireActiveAdminId(ADMIN_EMAIL);
|
||||
configureSmtp(adminId);
|
||||
createAndActivate(adminId, new CreateAccountCommand(
|
||||
"mentor@example.test", "Mentor", GlobalRole.MENTOR, null, null, null));
|
||||
createAndActivate(adminId, new CreateAccountCommand(
|
||||
"intern@example.test",
|
||||
"Intern",
|
||||
GlobalRole.INTERN,
|
||||
"INT-001",
|
||||
LocalDate.of(2026, 8, 1),
|
||||
LocalDate.of(2026, 12, 31)));
|
||||
|
||||
mockMvc.perform(get("/attendance/calendar").session(login("mentor@example.test", PASSWORD, "MENTOR")))
|
||||
.andExpect(status().isForbidden());
|
||||
mockMvc.perform(get("/attendance/calendar").session(login("intern@example.test", PASSWORD, "INTERN")))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
private void bootstrapFirstAdminThroughTheForm() throws Exception {
|
||||
mockMvc.perform(post("/bootstrap")
|
||||
.with(csrf())
|
||||
.param("email", ADMIN_EMAIL)
|
||||
.param("displayName", "Admin")
|
||||
.param("password", PASSWORD))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("/admin/smtp?onboarding"));
|
||||
}
|
||||
|
||||
private MockHttpSession login(String email, String password, String role) throws Exception {
|
||||
var result = mockMvc.perform(post("/login")
|
||||
.with(csrf())
|
||||
.param("username", email)
|
||||
.param("password", password))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(authenticated().withUsername(email))
|
||||
.andExpect(authenticated().withRoles(role))
|
||||
.andReturn();
|
||||
return (MockHttpSession) result.getRequest().getSession(false);
|
||||
}
|
||||
|
||||
private void configureSmtp(long adminId) {
|
||||
long draftId = smtp.saveDraft(adminId, new SmtpDraft(
|
||||
"mailpit", 1025, SecurityMode.NONE, null, null, ADMIN_EMAIL, "Lab Timesheet"));
|
||||
smtp.testDraft(draftId, adminId, ADMIN_EMAIL);
|
||||
smtp.activate(draftId, adminId);
|
||||
mail.clear();
|
||||
}
|
||||
|
||||
private void createAndActivate(long adminId, CreateAccountCommand command) {
|
||||
var creation = accounts.create(command, adminId);
|
||||
assertThat(creation.deliverySucceeded()).isTrue();
|
||||
assertThat(accounts.activate(mail.activationTokenFor(command.email()), PASSWORD)).isTrue();
|
||||
}
|
||||
|
||||
@TestConfiguration(proxyBeanMethods = false)
|
||||
static class MailProbeConfiguration {
|
||||
@Bean
|
||||
@Primary
|
||||
RecordingSmtpProbe recordingSmtpProbe() {
|
||||
return new RecordingSmtpProbe();
|
||||
}
|
||||
}
|
||||
|
||||
static final class RecordingSmtpProbe implements SmtpProbe {
|
||||
private final List<Message> messages = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void send(SmtpConnection connection, String recipient, String subject, String body) {
|
||||
messages.add(new Message(recipient, body));
|
||||
}
|
||||
|
||||
void clear() {
|
||||
messages.clear();
|
||||
}
|
||||
|
||||
String activationTokenFor(String recipient) {
|
||||
String body = messages.stream()
|
||||
.filter(message -> message.recipient().equals(recipient))
|
||||
.findFirst()
|
||||
.orElseThrow()
|
||||
.body();
|
||||
int tokenStart = body.indexOf("token=");
|
||||
assertThat(tokenStart).isGreaterThanOrEqualTo(0);
|
||||
return body.substring(tokenStart + "token=".length()).trim();
|
||||
}
|
||||
}
|
||||
|
||||
record Message(String recipient, String body) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user