diff --git a/src/main/java/com/lab/labtimesheet/feature/project/service/ProjectQueryService.java b/src/main/java/com/lab/labtimesheet/feature/project/service/ProjectQueryService.java index 1867eda..3a3cf45 100644 --- a/src/main/java/com/lab/labtimesheet/feature/project/service/ProjectQueryService.java +++ b/src/main/java/com/lab/labtimesheet/feature/project/service/ProjectQueryService.java @@ -71,7 +71,9 @@ public class ProjectQueryService { @Transactional(readOnly = true) public List members(long actorUserId, long projectId) { var project = visibleProject(actorUserId, projectId); - var leaderUserId = project.currentLeader().internUserId(); + Long leaderUserId = project.status() == ProjectStatus.COMPLETED + ? null + : project.currentLeader().internUserId(); return project.memberships().stream() .map(membership -> new ProjectMemberView( membership.id(), @@ -79,7 +81,9 @@ public class ProjectQueryService { displayName(membership.internUserId()), membership.joinedAt(), membership.leftAt(), - membership.isCurrent() && membership.internUserId() == leaderUserId)) + membership.isCurrent() + && leaderUserId != null + && membership.internUserId() == leaderUserId)) .toList(); } @@ -103,6 +107,16 @@ public class ProjectQueryService { ProjectTaskContext taskContext(long actorUserId, ProjectEntity project) { requireVisibleProject(actorUserId, project); + if (project.status() == ProjectStatus.COMPLETED) { + return new ProjectTaskContext( + project.id(), + project.mentorUserId(), + project.status().name(), + project.startDate(), + project.endDate(), + null, + List.of()); + } var activeMembers = project.memberships().stream() .filter(membership -> membership.isCurrent() && isEligibleIntern(membership.internUserId())) .map(membership -> new ProjectTaskMemberView( diff --git a/src/test/java/com/lab/labtimesheet/feature/project/service/ProjectServiceIntegrationTest.java b/src/test/java/com/lab/labtimesheet/feature/project/service/ProjectServiceIntegrationTest.java index a353e45..d98b3f7 100644 --- a/src/test/java/com/lab/labtimesheet/feature/project/service/ProjectServiceIntegrationTest.java +++ b/src/test/java/com/lab/labtimesheet/feature/project/service/ProjectServiceIntegrationTest.java @@ -1,6 +1,7 @@ package com.lab.labtimesheet.feature.project.service; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -178,6 +179,42 @@ class ProjectServiceIntegrationTest { assertEquals(1, projectPages.dashboardSummary(mentorId).distinctActiveMemberCount()); } + @Test + void completedProjectQueriesReturnHistoricalMembersWithoutRequiringACurrentLeader() { + long mentorId = user("mentor-history@example.test", "MENTOR"); + long leaderId = intern("leader-history@example.test", "I012"); + long memberId = intern("member-history@example.test", "I013"); + long projectId = createProject(mentorId, leaderId, "Completed history"); + projectService.addMember(mentorId, projectId, memberId); + var activatedAt = dbTime(NOW.plusSeconds(30)); + var completedAt = dbTime(NOW.plusSeconds(60)); + jdbc.update(""" + update project_leadership_terms + set ended_at = ?, ended_by_mentor_user_id = ? + where project_id = ? and ended_at is null + """, completedAt, mentorId, projectId); + jdbc.update(""" + update project_memberships + set left_at = ?, removed_by_mentor_user_id = ?, updated_at = ? + where project_id = ? and left_at is null + """, completedAt, mentorId, completedAt, projectId); + jdbc.update(""" + update projects + set status = 'COMPLETED', activated_at = ?, completed_at = ?, updated_at = ? + where id = ? + """, activatedAt, completedAt, completedAt, projectId); + entityManager.clear(); + + var taskContext = projectPages.taskContext(memberId, projectId); + assertEquals("COMPLETED", taskContext.status()); + assertNull(taskContext.currentLeaderMembershipId()); + assertEquals(List.of(), taskContext.activeMembers()); + var members = projectPages.members(memberId, projectId); + assertEquals(2, members.size()); + assertTrue(members.stream().allMatch(member -> member.leftAt() != null)); + assertTrue(members.stream().noneMatch(member -> member.currentLeader())); + } + private long createProject(long mentorId, long leaderId, String name) { return projectService.create( mentorId,