feat(reporting): compose role dashboards from feature services
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
||||
package com.lab.labtimesheet.feature.reporting.controller;
|
||||
|
||||
import com.lab.labtimesheet.feature.reporting.exception.DashboardAccessDeniedException;
|
||||
import com.lab.labtimesheet.feature.reporting.service.DashboardService;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Controller
|
||||
public class DashboardController {
|
||||
|
||||
private final DashboardService dashboardService;
|
||||
|
||||
public DashboardController(DashboardService dashboardService) {
|
||||
this.dashboardService = dashboardService;
|
||||
}
|
||||
|
||||
@GetMapping("/dashboard")
|
||||
public String dashboard(Authentication authentication, Model model) {
|
||||
String email = authentication.getName();
|
||||
if (hasRole(authentication, "ROLE_ADMIN")) {
|
||||
model.addAttribute("dashboard", dashboardService.admin(email));
|
||||
return "dashboard/admin";
|
||||
}
|
||||
if (hasRole(authentication, "ROLE_MENTOR")) {
|
||||
model.addAttribute("dashboard", dashboardService.mentor(email));
|
||||
return "dashboard/mentor";
|
||||
}
|
||||
if (hasRole(authentication, "ROLE_INTERN")) {
|
||||
model.addAttribute("dashboard", dashboardService.intern(email));
|
||||
return "dashboard/intern";
|
||||
}
|
||||
throw new DashboardAccessDeniedException("Dashboard access requires a supported global role");
|
||||
}
|
||||
|
||||
private boolean hasRole(Authentication authentication, String role) {
|
||||
return authentication.getAuthorities().stream().anyMatch(authority -> authority.getAuthority().equals(role));
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.lab.labtimesheet.feature.reporting.exception;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||
public class DashboardAccessDeniedException extends AccessDeniedException {
|
||||
|
||||
public DashboardAccessDeniedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.lab.labtimesheet.feature.reporting.service;
|
||||
|
||||
import com.lab.labtimesheet.feature.account.model.AccountStatus;
|
||||
import com.lab.labtimesheet.feature.account.model.GlobalRole;
|
||||
import com.lab.labtimesheet.feature.account.model.dto.AccountIdentity;
|
||||
import com.lab.labtimesheet.feature.account.model.dto.AccountSummary;
|
||||
import com.lab.labtimesheet.feature.account.service.AccountService;
|
||||
import com.lab.labtimesheet.feature.attendance.exception.AttendanceException;
|
||||
import com.lab.labtimesheet.feature.attendance.model.dto.AttendanceCurrentState;
|
||||
import com.lab.labtimesheet.feature.attendance.service.AttendanceApplicationService;
|
||||
import com.lab.labtimesheet.feature.reporting.exception.DashboardAccessDeniedException;
|
||||
import com.lab.labtimesheet.feature.reporting.model.dto.DashboardView;
|
||||
import com.lab.labtimesheet.feature.reporting.model.dto.DashboardView.AssignedTask;
|
||||
import com.lab.labtimesheet.feature.reporting.model.dto.DashboardView.AttendanceState;
|
||||
import com.lab.labtimesheet.feature.project.model.dto.ProjectDashboardSummary;
|
||||
import com.lab.labtimesheet.feature.project.service.ProjectQueryService;
|
||||
import com.lab.labtimesheet.feature.task.model.dto.TaskDashboardView;
|
||||
import com.lab.labtimesheet.feature.task.service.TaskDashboardService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
public class DashboardService {
|
||||
|
||||
private final AccountService accounts;
|
||||
private final ProjectQueryService projects;
|
||||
private final TaskDashboardService tasks;
|
||||
private final AttendanceApplicationService attendance;
|
||||
|
||||
public DashboardService(
|
||||
AccountService accounts,
|
||||
ProjectQueryService projects,
|
||||
TaskDashboardService tasks,
|
||||
AttendanceApplicationService attendance) {
|
||||
this.accounts = accounts;
|
||||
this.projects = projects;
|
||||
this.tasks = tasks;
|
||||
this.attendance = attendance;
|
||||
}
|
||||
|
||||
public DashboardView.Admin admin(String email) {
|
||||
AccountIdentity admin = activeAccount(email, GlobalRole.ADMIN);
|
||||
AccountSummary accountSummary = accounts.summary();
|
||||
ProjectDashboardSummary projectSummary = projects.dashboardSummary(admin.id());
|
||||
return new DashboardView.Admin(
|
||||
accountSummary.activeAccounts(),
|
||||
accountSummary.pendingActivations(),
|
||||
accountSummary.activeInternships(),
|
||||
projectSummary.activeProjectCount());
|
||||
}
|
||||
|
||||
public DashboardView.Mentor mentor(String email) {
|
||||
AccountIdentity mentor = activeAccount(email, GlobalRole.MENTOR);
|
||||
ProjectDashboardSummary projectSummary = projects.dashboardSummary(mentor.id());
|
||||
TaskDashboardView taskSummary = tasks.dashboard(email);
|
||||
return new DashboardView.Mentor(
|
||||
mentor.displayName(),
|
||||
projectSummary.activeProjectCount(),
|
||||
projectSummary.distinctActiveMemberCount(),
|
||||
taskSummary.blockedTaskCount());
|
||||
}
|
||||
|
||||
public DashboardView.Intern intern(String email) {
|
||||
AccountIdentity intern = activeAccount(email, GlobalRole.INTERN);
|
||||
AttendanceCurrentState attendanceState;
|
||||
try {
|
||||
attendanceState = attendance.currentState(intern.id());
|
||||
} catch (AttendanceException exception) {
|
||||
throw new DashboardAccessDeniedException("Active Intern account and internship required");
|
||||
}
|
||||
ProjectDashboardSummary projectSummary = projects.dashboardSummary(intern.id());
|
||||
TaskDashboardView taskSummary = tasks.dashboard(email);
|
||||
return new DashboardView.Intern(
|
||||
intern.displayName(),
|
||||
AttendanceState.valueOf(attendanceState.name()),
|
||||
projectSummary.activeProjectCount(),
|
||||
taskSummary.assignedTaskCount(),
|
||||
taskSummary.priorityTasks().stream()
|
||||
.map(task -> new AssignedTask(
|
||||
task.title(), task.projectName(), task.status().name(), task.dueDate()))
|
||||
.toList());
|
||||
}
|
||||
|
||||
private AccountIdentity activeAccount(String email, GlobalRole role) {
|
||||
AccountIdentity account;
|
||||
try {
|
||||
account = accounts.requireIdentityByEmail(email);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw new DashboardAccessDeniedException("Active " + role + " account required");
|
||||
}
|
||||
if (account.status() != AccountStatus.ACTIVE || account.role() != role) {
|
||||
throw new DashboardAccessDeniedException("Active " + role + " account required");
|
||||
}
|
||||
return account;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,25 @@
|
||||
package com.lab.labtimesheet.feature.reporting;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
class ReportingArchitectureTest {
|
||||
|
||||
@Test
|
||||
void reportingUsesFeaturePackageWithoutGlobalLayersOrPlaceholderBoundary() throws Exception {
|
||||
Class<?> view = Class.forName("com.lab.labtimesheet.feature.reporting.model.dto.DashboardView");
|
||||
Class<?> controller = Class.forName("com.lab.labtimesheet.feature.reporting.controller.DashboardController");
|
||||
Class<?> service = Class.forName("com.lab.labtimesheet.feature.reporting.service.DashboardService");
|
||||
Class<?> exception = Class.forName("com.lab.labtimesheet.feature.reporting.exception.DashboardAccessDeniedException");
|
||||
assertTrue(view.isSealed());
|
||||
assertFalse(view.getPackageName().startsWith("com.lab.labtimesheet.model"));
|
||||
assertFalse(usesJdbcTemplate(controller, service, exception));
|
||||
|
||||
assertMissing("com.lab.labtimesheet.controller.DashboardController");
|
||||
assertMissing("com.lab.labtimesheet.dto.DashboardView");
|
||||
@@ -22,6 +29,15 @@ class ReportingArchitectureTest {
|
||||
assertMissing("com.lab.labtimesheet.service.DashboardService");
|
||||
assertMissing("com.lab.labtimesheet.reporting.ModuleBoundary");
|
||||
assertMissing("com.lab.labtimesheet.feature.reporting.ModuleBoundary");
|
||||
assertAll(
|
||||
() -> assertMissing("com.lab.labtimesheet.feature.reporting.model.DashboardAccount"),
|
||||
() -> assertMissing("com.lab.labtimesheet.feature.reporting.repository.DashboardRepository"));
|
||||
}
|
||||
|
||||
private boolean usesJdbcTemplate(Class<?>... types) {
|
||||
return Arrays.stream(types)
|
||||
.flatMap(type -> Arrays.stream(type.getDeclaredFields()))
|
||||
.anyMatch(field -> field.getType().equals(JdbcTemplate.class));
|
||||
}
|
||||
|
||||
private void assertMissing(String className) {
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package com.lab.labtimesheet.feature.reporting.controller;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.lab.labtimesheet.config.TestcontainersConfiguration;
|
||||
import com.lab.labtimesheet.feature.account.service.BootstrapService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Import(TestcontainersConfiguration.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class AdminDashboardWebTest {
|
||||
|
||||
private final MockMvc mvc;
|
||||
private final BootstrapService bootstrap;
|
||||
|
||||
@Autowired
|
||||
AdminDashboardWebTest(MockMvc mvc, BootstrapService bootstrap) {
|
||||
this.mvc = mvc;
|
||||
this.bootstrap = bootstrap;
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "admin@example.test", roles = "ADMIN")
|
||||
void adminDashboardUsesAccountAndProjectServiceSummaries() throws Exception {
|
||||
bootstrap();
|
||||
|
||||
mvc.perform(get("/dashboard"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("System overview")))
|
||||
.andExpect(content().string(containsString("Active accounts</div><div class=\"metric-value\">1")))
|
||||
.andExpect(content().string(containsString("Pending activation</div><div class=\"metric-value\">0")))
|
||||
.andExpect(content().string(containsString("Active internships</div><div class=\"metric-value\">0")))
|
||||
.andExpect(content().string(containsString("Active Projects</div><div class=\"metric-value\">0")))
|
||||
.andExpect(content().string(containsString("Create account")))
|
||||
.andExpect(content().string(containsString("No pending activations")))
|
||||
.andExpect(content().string(not(containsString("Create Project"))))
|
||||
.andExpect(content().string(not(containsString("Check in"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "intern@example.test", roles = "ADMIN")
|
||||
void adminAuthorityDoesNotAuthorizeUnknownAccount() throws Exception {
|
||||
bootstrap();
|
||||
|
||||
mvc.perform(get("/dashboard"))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
void dashboardRequiresAuthentication() throws Exception {
|
||||
bootstrap();
|
||||
mvc.perform(get("/dashboard"))
|
||||
.andExpect(status().is3xxRedirection());
|
||||
}
|
||||
|
||||
private void bootstrap() {
|
||||
bootstrap.bootstrap("admin@example.test", "An Admin", "correct-horse-battery-staple");
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.lab.labtimesheet.feature.reporting.controller;
|
||||
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
|
||||
|
||||
import com.lab.labtimesheet.feature.reporting.model.dto.DashboardView;
|
||||
import com.lab.labtimesheet.feature.reporting.service.DashboardService;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@WebMvcTest(DashboardController.class)
|
||||
class DashboardControllerWebTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mvc;
|
||||
|
||||
@MockitoBean
|
||||
private DashboardService dashboards;
|
||||
|
||||
@Test
|
||||
void adminRendersAdminDashboardForAuthenticatedIdentity() throws Exception {
|
||||
var dashboard = new DashboardView.Admin(2, 1, 1, 3);
|
||||
given(dashboards.admin("admin@example.test")).willReturn(dashboard);
|
||||
|
||||
mvc.perform(get("/dashboard").with(user("admin@example.test").roles("ADMIN")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(view().name("dashboard/admin"))
|
||||
.andExpect(model().attribute("dashboard", dashboard));
|
||||
|
||||
verify(dashboards).admin("admin@example.test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mentorRendersMentorDashboardForAuthenticatedIdentity() throws Exception {
|
||||
var dashboard = new DashboardView.Mentor("Mentor", 2, 4, 1);
|
||||
given(dashboards.mentor("mentor@example.test")).willReturn(dashboard);
|
||||
|
||||
mvc.perform(get("/dashboard").with(user("mentor@example.test").roles("MENTOR")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(view().name("dashboard/mentor"))
|
||||
.andExpect(model().attribute("dashboard", dashboard));
|
||||
|
||||
verify(dashboards).mentor("mentor@example.test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void internRendersInternDashboardWithoutClientSuppliedBusinessDate() throws Exception {
|
||||
var dashboard = new DashboardView.Intern(
|
||||
"Intern", DashboardView.AttendanceState.NOT_CHECKED_IN, 1, 0, List.of());
|
||||
given(dashboards.intern("intern@example.test")).willReturn(dashboard);
|
||||
|
||||
mvc.perform(get("/dashboard").with(user("intern@example.test").roles("INTERN")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(view().name("dashboard/intern"))
|
||||
.andExpect(model().attribute("dashboard", dashboard));
|
||||
|
||||
verify(dashboards).intern("intern@example.test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsupportedRoleIsForbiddenWithoutCallingDashboardServices() throws Exception {
|
||||
mvc.perform(get("/dashboard").with(user("user@example.test").roles("USER")))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
verifyNoInteractions(dashboards);
|
||||
}
|
||||
|
||||
@Test
|
||||
void dashboardRequiresAuthentication() throws Exception {
|
||||
mvc.perform(get("/dashboard"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
verifyNoInteractions(dashboards);
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
package com.lab.labtimesheet.feature.reporting.service;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
import com.lab.labtimesheet.feature.account.model.AccountStatus;
|
||||
import com.lab.labtimesheet.feature.account.model.GlobalRole;
|
||||
import com.lab.labtimesheet.feature.account.model.dto.AccountIdentity;
|
||||
import com.lab.labtimesheet.feature.account.model.dto.AccountSummary;
|
||||
import com.lab.labtimesheet.feature.account.service.AccountService;
|
||||
import com.lab.labtimesheet.feature.attendance.model.dto.AttendanceCurrentState;
|
||||
import com.lab.labtimesheet.feature.attendance.exception.AttendanceException;
|
||||
import com.lab.labtimesheet.feature.attendance.exception.AttendanceRejection;
|
||||
import com.lab.labtimesheet.feature.attendance.service.AttendanceApplicationService;
|
||||
import com.lab.labtimesheet.feature.project.model.dto.ProjectDashboardSummary;
|
||||
import com.lab.labtimesheet.feature.project.service.ProjectQueryService;
|
||||
import com.lab.labtimesheet.feature.reporting.exception.DashboardAccessDeniedException;
|
||||
import com.lab.labtimesheet.feature.reporting.model.dto.DashboardView;
|
||||
import com.lab.labtimesheet.feature.task.model.TaskStatus;
|
||||
import com.lab.labtimesheet.feature.task.model.dto.TaskDashboardView;
|
||||
import com.lab.labtimesheet.feature.task.model.dto.TaskPriorityView;
|
||||
import com.lab.labtimesheet.feature.task.service.TaskDashboardService;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class DashboardServiceTest {
|
||||
|
||||
private final AccountService accounts = mock(AccountService.class);
|
||||
private final ProjectQueryService projects = mock(ProjectQueryService.class);
|
||||
private final TaskDashboardService tasks = mock(TaskDashboardService.class);
|
||||
private final AttendanceApplicationService attendance = mock(AttendanceApplicationService.class);
|
||||
private DashboardService dashboards;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
dashboards = new DashboardService(accounts, projects, tasks, attendance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminDashboardCombinesAccountAndProjectSummaries() {
|
||||
given(accounts.requireIdentityByEmail("admin@example.test"))
|
||||
.willReturn(identity(1L, "Admin", GlobalRole.ADMIN, AccountStatus.ACTIVE));
|
||||
given(accounts.summary()).willReturn(new AccountSummary(8, 2, 3));
|
||||
given(projects.dashboardSummary(1L)).willReturn(new ProjectDashboardSummary(4, 0));
|
||||
|
||||
assertThat(dashboards.admin("admin@example.test"))
|
||||
.isEqualTo(new DashboardView.Admin(8, 2, 3, 4));
|
||||
|
||||
verifyNoInteractions(tasks, attendance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void mentorDashboardCombinesOwnedProjectAndTaskSummaries() {
|
||||
given(accounts.requireIdentityByEmail("mentor@example.test"))
|
||||
.willReturn(identity(2L, "Minh Mentor", GlobalRole.MENTOR, AccountStatus.ACTIVE));
|
||||
given(projects.dashboardSummary(2L)).willReturn(new ProjectDashboardSummary(3, 7));
|
||||
given(tasks.dashboard("mentor@example.test")).willReturn(new TaskDashboardView(5, 0, List.of()));
|
||||
|
||||
assertThat(dashboards.mentor("mentor@example.test"))
|
||||
.isEqualTo(new DashboardView.Mentor("Minh Mentor", 3, 7, 5));
|
||||
|
||||
verifyNoInteractions(attendance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void internDashboardCombinesAttendanceProjectAndTaskViews() {
|
||||
var dueDate = LocalDate.of(2026, 8, 20);
|
||||
given(accounts.requireIdentityByEmail("intern@example.test"))
|
||||
.willReturn(identity(3L, "Mai Intern", GlobalRole.INTERN, AccountStatus.ACTIVE));
|
||||
given(attendance.currentState(3L)).willReturn(AttendanceCurrentState.CHECKED_IN);
|
||||
given(projects.dashboardSummary(3L)).willReturn(new ProjectDashboardSummary(2, 0));
|
||||
given(tasks.dashboard("intern@example.test")).willReturn(new TaskDashboardView(
|
||||
0, 6, List.of(new TaskPriorityView("Draft report", "Portal", TaskStatus.IN_PROGRESS, dueDate))));
|
||||
|
||||
assertThat(dashboards.intern("intern@example.test"))
|
||||
.isEqualTo(new DashboardView.Intern(
|
||||
"Mai Intern",
|
||||
DashboardView.AttendanceState.CHECKED_IN,
|
||||
2,
|
||||
6,
|
||||
List.of(new DashboardView.AssignedTask("Draft report", "Portal", "IN_PROGRESS", dueDate))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void roleAndActiveStatusComeFromTheAccountServiceRatherThanGrantedAuthorities() {
|
||||
given(accounts.requireIdentityByEmail("intern@example.test"))
|
||||
.willReturn(identity(3L, "Mai Intern", GlobalRole.INTERN, AccountStatus.ACTIVE));
|
||||
given(accounts.requireIdentityByEmail("locked@example.test"))
|
||||
.willReturn(identity(4L, "Locked Mentor", GlobalRole.MENTOR, AccountStatus.LOCKED));
|
||||
|
||||
assertThatThrownBy(() -> dashboards.admin("intern@example.test"))
|
||||
.isInstanceOf(DashboardAccessDeniedException.class);
|
||||
assertThatThrownBy(() -> dashboards.mentor("locked@example.test"))
|
||||
.isInstanceOf(DashboardAccessDeniedException.class);
|
||||
|
||||
verifyNoInteractions(projects, tasks, attendance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingAccountIsReportedAsDashboardAccessDenied() {
|
||||
given(accounts.requireIdentityByEmail("missing@example.test"))
|
||||
.willThrow(new IllegalArgumentException("Account not found"));
|
||||
|
||||
assertThatThrownBy(() -> dashboards.intern("missing@example.test"))
|
||||
.isInstanceOf(DashboardAccessDeniedException.class);
|
||||
|
||||
verifyNoInteractions(projects, tasks, attendance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ineligibleInternIsReportedAsDashboardAccessDenied() {
|
||||
given(accounts.requireIdentityByEmail("intern@example.test"))
|
||||
.willReturn(identity(3L, "Mai Intern", GlobalRole.INTERN, AccountStatus.ACTIVE));
|
||||
given(attendance.currentState(3L))
|
||||
.willThrow(new AttendanceException(AttendanceRejection.INACTIVE_INTERN));
|
||||
|
||||
assertThatThrownBy(() -> dashboards.intern("intern@example.test"))
|
||||
.isInstanceOf(DashboardAccessDeniedException.class);
|
||||
|
||||
verifyNoInteractions(projects, tasks);
|
||||
}
|
||||
|
||||
private static AccountIdentity identity(
|
||||
long id, String displayName, GlobalRole role, AccountStatus status) {
|
||||
return new AccountIdentity(id, role.name().toLowerCase() + "@example.test", displayName, role, status);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user