From e5639c146180a68e0f5d91094d91a3a32dad9b7a Mon Sep 17 00:00:00 2001 From: sechmachine <97589681+sechmachine727@users.noreply.github.com> Date: Sat, 15 Aug 2026 11:37:02 +0700 Subject: [PATCH 1/3] refactor(project): replace mechanical constructors with Lombok --- docs/tests/unit/lombok-project-boilerplate.md | 115 ++++++++++++++++++ .../project/controller/ProjectController.java | 13 +- .../exception/ProjectControllerAdvice.java | 4 - .../project/model/entity/ProjectEntity.java | 7 +- .../entity/ProjectLeadershipTermEntity.java | 7 +- .../model/entity/ProjectMembershipEntity.java | 7 +- .../project/service/ProjectQueryService.java | 13 +- .../project/service/ProjectService.java | 24 +--- .../ProjectLombokBoilerplateTest.java | 77 ++++++++++++ 9 files changed, 207 insertions(+), 60 deletions(-) create mode 100644 docs/tests/unit/lombok-project-boilerplate.md create mode 100644 src/test/java/com/lab/labtimesheet/feature/project/repository/ProjectLombokBoilerplateTest.java diff --git a/docs/tests/unit/lombok-project-boilerplate.md b/docs/tests/unit/lombok-project-boilerplate.md new file mode 100644 index 0000000..51699cc --- /dev/null +++ b/docs/tests/unit/lombok-project-boilerplate.md @@ -0,0 +1,115 @@ +# Test Evidence: Targeted Project Lombok boilerplate + +- **Test type:** Unit +- **Requirement IDs:** `ARC-002`, `ARC-005`, `ARC-006`, `TST-001` +- **Scenario IDs:** `AC-TST-001` +- **Test class/method:** `com.lab.labtimesheet.feature.project.repository.ProjectLombokBoilerplateTest#eligibleConstructorsUseTargetedLombokWithoutChangingDomainApis` +- **Implementation commit:** `pending` + +## Protected behavior + +Project Spring components use targeted required-argument constructor generation only when their +constructors assign required final dependencies. Project JPA entities use only protected no-arg +constructor generation. Records remain records, and entity identity, lazy associations, explicit +domain accessors, aggregate constructors, and mutation methods do not gain broad generated APIs. + +## Test method + +The source-contract test inspects only `feature.project` production sources. It requires +`@RequiredArgsConstructor` on the three injection-only components, removes the stateless advice's +handwritten no-arg constructor, and requires protected `@NoArgsConstructor` on the three JPA +entities. It also rejects broad entity Lombok annotations, confirms representative explicit domain +APIs remain, and verifies every Project immutable DTO/value type remains a Java record. + +## Hand-derived expected result + +Seven handwritten constructors are mechanical and eligible for removal: three dependency-assignment +constructors, one empty advice constructor, and three empty protected JPA constructors. The three +entity domain constructors, all aggregate mutation methods, defensive-copy accessors, derived +membership/leadership accessors, validation constructors, exception constructors, and all thirteen +records must remain explicit or remain records because they carry behavior or preserve the existing +API shape. + +## RED + +**Command** + +```text +export JAVA_HOME=/opt/homebrew/opt/openjdk@25 +export PATH="/opt/homebrew/opt/node@24/bin:$JAVA_HOME/bin:$PATH" +./mvnw -Dtest=ProjectLombokBoilerplateTest test +``` + +**Observed result** + +```text +[INFO] Running com.lab.labtimesheet.feature.project.repository.ProjectLombokBoilerplateTest +[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 +ProjectLombokBoilerplateTest.eligibleConstructorsUseTargetedLombokWithoutChangingDomainApis +expected ProjectController.java to contain import lombok.RequiredArgsConstructor; and +@RequiredArgsConstructor, but neither was present and the handwritten assignment-only constructor +remained. +[INFO] BUILD FAILURE +``` + +## GREEN + +**Command** + +```text +export JAVA_HOME=/opt/homebrew/opt/openjdk@25 +export PATH="/opt/homebrew/opt/node@24/bin:$JAVA_HOME/bin:$PATH" +./mvnw -Dtest=ProjectLombokBoilerplateTest test +``` + +**Observed result** + +```text +[INFO] Running com.lab.labtimesheet.feature.project.repository.ProjectLombokBoilerplateTest +[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 +[INFO] BUILD SUCCESS +``` + +## Affected suite + +**Command and result** + +```text +export JAVA_HOME=/opt/homebrew/opt/openjdk@25 +export PATH="/opt/homebrew/opt/node@24/bin:$JAVA_HOME/bin:$PATH" +./mvnw -DskipTests compile +[INFO] BUILD SUCCESS + +./mvnw -Dtest=ProjectLombokBoilerplateTest,ProjectEntityTest,ProjectPersistenceStructureTest,ProjectTaskMutationContextTest test +[INFO] Tests run: 9, Failures: 0, Errors: 0, Skipped: 0 +[INFO] BUILD SUCCESS + +./mvnw -Dtest=ProjectControllerTest test +[INFO] Tests run: 16, Failures: 0, Errors: 0, Skipped: 0 +[INFO] BUILD SUCCESS + +export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock +./mvnw -Dtest=ProjectServiceIntegrationTest test +[INFO] Tests run: 7, Failures: 0, Errors: 0, Skipped: 0 +[INFO] BUILD SUCCESS + +./mvnw -Dtest='Project*Test' test +[INFO] Tests run: 45, Failures: 0, Errors: 0, Skipped: 0 +[INFO] BUILD SUCCESS + +./mvnw -DskipTests -Dmaven.javadoc.failOnWarnings=true -Ddoclint=all \ + -Dsubpackages=com.lab.labtimesheet.feature.project javadoc:javadoc +[INFO] BUILD SUCCESS + +git diff --check +(no output; exit 0) +``` + +## External-test boundaries + +The source audit does not by itself prove Lombok annotation processing, Spring constructor +injection, Hibernate materialization, PostgreSQL mappings, Thymeleaf behavior, Project authorization, +locking, or aggregate lifecycle rules. Those boundaries are covered by the compile, scoped +Javadoc/doclint, Project unit/web, and PostgreSQL 18.4 integration gates above. The first sandboxed +unit-suite attempt could not attach Mockito's Byte Buddy agent; the unchanged command passed after +approved execution outside that sandbox. Browser E2E behavior remains outside this unit milestone. diff --git a/src/main/java/com/lab/labtimesheet/feature/project/controller/ProjectController.java b/src/main/java/com/lab/labtimesheet/feature/project/controller/ProjectController.java index e11f370..feacced 100644 --- a/src/main/java/com/lab/labtimesheet/feature/project/controller/ProjectController.java +++ b/src/main/java/com/lab/labtimesheet/feature/project/controller/ProjectController.java @@ -8,6 +8,7 @@ import com.lab.labtimesheet.feature.project.service.ProjectQueryService; import com.lab.labtimesheet.feature.project.service.ProjectService; import jakarta.validation.Valid; import java.security.Principal; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -27,22 +28,12 @@ import org.springframework.web.bind.annotation.RequestMapping; */ @Controller @RequestMapping("/projects") +@RequiredArgsConstructor public class ProjectController { private final ProjectQueryService pages; private final ProjectService projects; - /** - * Creates the MVC adapter for Project queries and mutations. - * - * @param pages authorized Project read operations - * @param projects transactional Project mutation operations - */ - public ProjectController(ProjectQueryService pages, ProjectService projects) { - this.pages = pages; - this.projects = projects; - } - /** * Lists only Projects visible to the authenticated actor and exposes Project creation only * to Mentors. diff --git a/src/main/java/com/lab/labtimesheet/feature/project/exception/ProjectControllerAdvice.java b/src/main/java/com/lab/labtimesheet/feature/project/exception/ProjectControllerAdvice.java index 3fa2f45..3730d30 100644 --- a/src/main/java/com/lab/labtimesheet/feature/project/exception/ProjectControllerAdvice.java +++ b/src/main/java/com/lab/labtimesheet/feature/project/exception/ProjectControllerAdvice.java @@ -17,10 +17,6 @@ import org.springframework.web.servlet.ModelAndView; @ControllerAdvice(assignableTypes = ProjectController.class) public class ProjectControllerAdvice { - /** Creates the stateless Project exception-to-view adapter. */ - public ProjectControllerAdvice() { - } - /** * Hides whether a requested Project or nested resource exists. * diff --git a/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectEntity.java b/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectEntity.java index 6a3ed22..877a309 100644 --- a/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectEntity.java +++ b/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectEntity.java @@ -22,6 +22,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Set; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; /** * JPA aggregate root for Project lifecycle, membership intervals, and leadership intervals. @@ -32,6 +34,7 @@ import java.util.Set; */ @Entity @Table(name = "projects") +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class ProjectEntity { @Id @@ -75,10 +78,6 @@ public class ProjectEntity { @Version private long version; - /** Constructor reserved for JPA materialization. */ - protected ProjectEntity() { - } - private ProjectEntity( long mentorUserId, String name, diff --git a/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectLeadershipTermEntity.java b/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectLeadershipTermEntity.java index dbfd7ec..367f51f 100644 --- a/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectLeadershipTermEntity.java +++ b/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectLeadershipTermEntity.java @@ -11,6 +11,8 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import java.time.Instant; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; /** * JPA leadership interval attached to an active same-Project membership. @@ -20,6 +22,7 @@ import java.time.Instant; */ @Entity @Table(name = "project_leadership_terms") +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class ProjectLeadershipTermEntity { @Id @@ -46,10 +49,6 @@ public class ProjectLeadershipTermEntity { @Column(name = "ended_by_mentor_user_id") private Long endedByMentorUserId; - /** Constructor reserved for JPA materialization. */ - protected ProjectLeadershipTermEntity() { - } - ProjectLeadershipTermEntity( ProjectEntity project, ProjectMembershipEntity membership, diff --git a/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectMembershipEntity.java b/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectMembershipEntity.java index 3240d17..de75340 100644 --- a/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectMembershipEntity.java +++ b/src/main/java/com/lab/labtimesheet/feature/project/model/entity/ProjectMembershipEntity.java @@ -11,6 +11,8 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.Version; import java.time.Instant; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; /** * JPA membership interval linking one Intern to one Project. @@ -20,6 +22,7 @@ import java.time.Instant; */ @Entity @Table(name = "project_memberships") +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class ProjectMembershipEntity { @Id @@ -48,10 +51,6 @@ public class ProjectMembershipEntity { @Version private long version; - /** Constructor reserved for JPA materialization. */ - protected ProjectMembershipEntity() { - } - ProjectMembershipEntity(ProjectEntity project, long internUserId, Instant joinedAt, long addedByUserId) { this.project = project; this.internUserId = internUserId; 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 8bc861c..2d11096 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 @@ -15,6 +15,7 @@ import com.lab.labtimesheet.feature.project.model.dto.ProjectTaskMemberView; import com.lab.labtimesheet.feature.project.model.entity.ProjectEntity; import com.lab.labtimesheet.feature.project.repository.ProjectRepository; import java.util.List; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,22 +27,12 @@ import org.springframework.transaction.annotation.Transactional; * members but expose no current Leader or active-member context. */ @Service +@RequiredArgsConstructor public class ProjectQueryService { private final ProjectRepository projects; private final AccountService accounts; - /** - * Creates the Project read service. - * - * @param projects Project aggregate repository - * @param accounts public Account identity and internship-eligibility boundary - */ - public ProjectQueryService(ProjectRepository projects, AccountService accounts) { - this.projects = projects; - this.accounts = accounts; - } - /** * Resolves an active authenticated account to its stable user identifier. * diff --git a/src/main/java/com/lab/labtimesheet/feature/project/service/ProjectService.java b/src/main/java/com/lab/labtimesheet/feature/project/service/ProjectService.java index 8295e2e..c20489c 100644 --- a/src/main/java/com/lab/labtimesheet/feature/project/service/ProjectService.java +++ b/src/main/java/com/lab/labtimesheet/feature/project/service/ProjectService.java @@ -11,6 +11,7 @@ import com.lab.labtimesheet.feature.task.service.TaskQueryService; import java.time.Clock; import java.util.Set; import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional; * imports their repositories or entities. */ @Service +@RequiredArgsConstructor public class ProjectService { private final ProjectRepository projects; @@ -30,28 +32,6 @@ public class ProjectService { private final TaskQueryService taskQueries; private final Clock clock; - /** - * Creates the Project mutation service. - * - * @param projects Project aggregate repository - * @param accounts public Account identity and eligibility boundary - * @param queries DTO-only Project query boundary reused for locked Task context - * @param taskQueries public Task activation-guard boundary - * @param clock server clock supplying persisted mutation instants - */ - public ProjectService( - ProjectRepository projects, - AccountService accounts, - ProjectQueryService queries, - TaskQueryService taskQueries, - Clock clock) { - this.projects = projects; - this.accounts = accounts; - this.queries = queries; - this.taskQueries = taskQueries; - this.clock = clock; - } - /** * Atomically creates a planned Mentor-owned Project, eligible initial membership, and first * leadership term. {@code saveAndFlush} exposes database invariant violations before commit. diff --git a/src/test/java/com/lab/labtimesheet/feature/project/repository/ProjectLombokBoilerplateTest.java b/src/test/java/com/lab/labtimesheet/feature/project/repository/ProjectLombokBoilerplateTest.java new file mode 100644 index 0000000..c5e5c7d --- /dev/null +++ b/src/test/java/com/lab/labtimesheet/feature/project/repository/ProjectLombokBoilerplateTest.java @@ -0,0 +1,77 @@ +package com.lab.labtimesheet.feature.project.repository; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import org.junit.jupiter.api.Test; + +class ProjectLombokBoilerplateTest { + + private static final Path PROJECT_SOURCE = + Path.of("src/main/java/com/lab/labtimesheet/feature/project"); + + @Test + void eligibleConstructorsUseTargetedLombokWithoutChangingDomainApis() throws IOException { + assertRequiredArgsConstructor("controller/ProjectController.java", "ProjectController"); + assertRequiredArgsConstructor("service/ProjectService.java", "ProjectService"); + assertRequiredArgsConstructor("service/ProjectQueryService.java", "ProjectQueryService"); + + String advice = source("exception/ProjectControllerAdvice.java"); + assertThat(advice).doesNotContain("public ProjectControllerAdvice()"); + + assertProtectedJpaConstructor("model/entity/ProjectEntity.java", "ProjectEntity"); + assertProtectedJpaConstructor( + "model/entity/ProjectLeadershipTermEntity.java", "ProjectLeadershipTermEntity"); + assertProtectedJpaConstructor( + "model/entity/ProjectMembershipEntity.java", "ProjectMembershipEntity"); + + assertThat(source("model/entity/ProjectEntity.java")) + .contains("public static ProjectEntity plan(", "public ProjectMembershipEntity addMember(") + .doesNotContain("@Getter", "@Setter", "@Data", "@EqualsAndHashCode", "@ToString"); + assertThat(source("model/entity/ProjectLeadershipTermEntity.java")) + .contains("public long internUserId()", "Instant end(Instant at, long mentorUserId)") + .doesNotContain("@Getter", "@Setter", "@Data", "@EqualsAndHashCode", "@ToString"); + assertThat(source("model/entity/ProjectMembershipEntity.java")) + .contains("public long internUserId()", "public boolean isCurrent()") + .doesNotContain("@Getter", "@Setter", "@Data", "@EqualsAndHashCode", "@ToString"); + + for (String recordSource : List.of( + "model/ProjectInternEligibility.java", + "model/ProjectLeaderChange.java", + "model/dto/ProjectActorView.java", + "model/dto/ProjectCreateCommand.java", + "model/dto/ProjectCreateForm.java", + "model/dto/ProjectDashboardSummary.java", + "model/dto/ProjectDetail.java", + "model/dto/ProjectLeadershipTermView.java", + "model/dto/ProjectMemberForm.java", + "model/dto/ProjectMemberView.java", + "model/dto/ProjectSummary.java", + "model/dto/ProjectTaskContext.java", + "model/dto/ProjectTaskMemberView.java")) { + assertThat(source(recordSource)).contains(" record "); + } + } + + private static void assertRequiredArgsConstructor(String relativePath, String typeName) throws IOException { + assertThat(source(relativePath)) + .contains("import lombok.RequiredArgsConstructor;", "@RequiredArgsConstructor") + .doesNotContain("public " + typeName + "("); + } + + private static void assertProtectedJpaConstructor(String relativePath, String typeName) throws IOException { + assertThat(source(relativePath)) + .contains( + "import lombok.AccessLevel;", + "import lombok.NoArgsConstructor;", + "@NoArgsConstructor(access = AccessLevel.PROTECTED)") + .doesNotContain("protected " + typeName + "()"); + } + + private static String source(String relativePath) throws IOException { + return Files.readString(PROJECT_SOURCE.resolve(relativePath)); + } +} From 4b1e5721e4c6c45e3d4391a8447c278669782f53 Mon Sep 17 00:00:00 2001 From: sechmachine <97589681+sechmachine727@users.noreply.github.com> Date: Sat, 15 Aug 2026 11:37:29 +0700 Subject: [PATCH 2/3] docs(project): pin Lombok cleanup evidence --- docs/tests/unit/lombok-project-boilerplate.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tests/unit/lombok-project-boilerplate.md b/docs/tests/unit/lombok-project-boilerplate.md index 51699cc..75c5454 100644 --- a/docs/tests/unit/lombok-project-boilerplate.md +++ b/docs/tests/unit/lombok-project-boilerplate.md @@ -4,7 +4,7 @@ - **Requirement IDs:** `ARC-002`, `ARC-005`, `ARC-006`, `TST-001` - **Scenario IDs:** `AC-TST-001` - **Test class/method:** `com.lab.labtimesheet.feature.project.repository.ProjectLombokBoilerplateTest#eligibleConstructorsUseTargetedLombokWithoutChangingDomainApis` -- **Implementation commit:** `pending` +- **Implementation commit:** `e5639c1` ## Protected behavior From 587127100d348281041fe7b3ef28453d190e971d Mon Sep 17 00:00:00 2001 From: sechmachine <97589681+sechmachine727@users.noreply.github.com> Date: Sat, 15 Aug 2026 11:46:01 +0700 Subject: [PATCH 3/3] test(project): remove brittle Lombok source audit --- docs/tests/unit/lombok-project-boilerplate.md | 30 ++++---- .../ProjectLombokBoilerplateTest.java | 77 ------------------- 2 files changed, 16 insertions(+), 91 deletions(-) delete mode 100644 src/test/java/com/lab/labtimesheet/feature/project/repository/ProjectLombokBoilerplateTest.java diff --git a/docs/tests/unit/lombok-project-boilerplate.md b/docs/tests/unit/lombok-project-boilerplate.md index 75c5454..6fd7bef 100644 --- a/docs/tests/unit/lombok-project-boilerplate.md +++ b/docs/tests/unit/lombok-project-boilerplate.md @@ -1,9 +1,9 @@ # Test Evidence: Targeted Project Lombok boilerplate -- **Test type:** Unit +- **Test type:** Temporary source audit (removed after GREEN) - **Requirement IDs:** `ARC-002`, `ARC-005`, `ARC-006`, `TST-001` - **Scenario IDs:** `AC-TST-001` -- **Test class/method:** `com.lab.labtimesheet.feature.project.repository.ProjectLombokBoilerplateTest#eligibleConstructorsUseTargetedLombokWithoutChangingDomainApis` +- **Test class/method:** N/A; the temporary source audit was removed after its RED/GREEN cycle - **Implementation commit:** `e5639c1` ## Protected behavior @@ -13,13 +13,15 @@ constructors assign required final dependencies. Project JPA entities use only p constructor generation. Records remain records, and entity identity, lazy associations, explicit domain accessors, aggregate constructors, and mutation methods do not gain broad generated APIs. -## Test method +## Temporary RED/GREEN method -The source-contract test inspects only `feature.project` production sources. It requires -`@RequiredArgsConstructor` on the three injection-only components, removes the stateless advice's -handwritten no-arg constructor, and requires protected `@NoArgsConstructor` on the three JPA -entities. It also rejects broad entity Lombok annotations, confirms representative explicit domain -APIs remain, and verifies every Project immutable DTO/value type remains a Java record. +The temporary source-contract test inspected only `feature.project` production sources. It required +`@RequiredArgsConstructor` on the three injection-only components, removal of the stateless advice's +handwritten no-arg constructor, and protected `@NoArgsConstructor` on the three JPA entities. It also +rejected broad entity Lombok annotations, confirmed representative explicit domain APIs remained, +and verified every Project immutable DTO/value type remained a Java record. It was deleted after +preserving the historical RED/GREEN below because exact imports, annotation spelling, and source +substrings are implementation details rather than a durable public contract. ## Hand-derived expected result @@ -80,8 +82,8 @@ export PATH="/opt/homebrew/opt/node@24/bin:$JAVA_HOME/bin:$PATH" ./mvnw -DskipTests compile [INFO] BUILD SUCCESS -./mvnw -Dtest=ProjectLombokBoilerplateTest,ProjectEntityTest,ProjectPersistenceStructureTest,ProjectTaskMutationContextTest test -[INFO] Tests run: 9, Failures: 0, Errors: 0, Skipped: 0 +./mvnw -Dtest=ProjectEntityTest,ProjectPersistenceStructureTest,ProjectTaskMutationContextTest test +[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0 [INFO] BUILD SUCCESS ./mvnw -Dtest=ProjectControllerTest test @@ -94,7 +96,7 @@ export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock [INFO] BUILD SUCCESS ./mvnw -Dtest='Project*Test' test -[INFO] Tests run: 45, Failures: 0, Errors: 0, Skipped: 0 +[INFO] Tests run: 44, Failures: 0, Errors: 0, Skipped: 0 [INFO] BUILD SUCCESS ./mvnw -DskipTests -Dmaven.javadoc.failOnWarnings=true -Ddoclint=all \ @@ -107,9 +109,9 @@ git diff --check ## External-test boundaries -The source audit does not by itself prove Lombok annotation processing, Spring constructor -injection, Hibernate materialization, PostgreSQL mappings, Thymeleaf behavior, Project authorization, -locking, or aggregate lifecycle rules. Those boundaries are covered by the compile, scoped +The removed source audit did not prove Lombok annotation processing, Spring constructor injection, +Hibernate materialization, PostgreSQL mappings, Thymeleaf behavior, Project authorization, locking, +or aggregate lifecycle rules. Those durable boundaries are covered by the compile, scoped Javadoc/doclint, Project unit/web, and PostgreSQL 18.4 integration gates above. The first sandboxed unit-suite attempt could not attach Mockito's Byte Buddy agent; the unchanged command passed after approved execution outside that sandbox. Browser E2E behavior remains outside this unit milestone. diff --git a/src/test/java/com/lab/labtimesheet/feature/project/repository/ProjectLombokBoilerplateTest.java b/src/test/java/com/lab/labtimesheet/feature/project/repository/ProjectLombokBoilerplateTest.java deleted file mode 100644 index c5e5c7d..0000000 --- a/src/test/java/com/lab/labtimesheet/feature/project/repository/ProjectLombokBoilerplateTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.lab.labtimesheet.feature.project.repository; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import org.junit.jupiter.api.Test; - -class ProjectLombokBoilerplateTest { - - private static final Path PROJECT_SOURCE = - Path.of("src/main/java/com/lab/labtimesheet/feature/project"); - - @Test - void eligibleConstructorsUseTargetedLombokWithoutChangingDomainApis() throws IOException { - assertRequiredArgsConstructor("controller/ProjectController.java", "ProjectController"); - assertRequiredArgsConstructor("service/ProjectService.java", "ProjectService"); - assertRequiredArgsConstructor("service/ProjectQueryService.java", "ProjectQueryService"); - - String advice = source("exception/ProjectControllerAdvice.java"); - assertThat(advice).doesNotContain("public ProjectControllerAdvice()"); - - assertProtectedJpaConstructor("model/entity/ProjectEntity.java", "ProjectEntity"); - assertProtectedJpaConstructor( - "model/entity/ProjectLeadershipTermEntity.java", "ProjectLeadershipTermEntity"); - assertProtectedJpaConstructor( - "model/entity/ProjectMembershipEntity.java", "ProjectMembershipEntity"); - - assertThat(source("model/entity/ProjectEntity.java")) - .contains("public static ProjectEntity plan(", "public ProjectMembershipEntity addMember(") - .doesNotContain("@Getter", "@Setter", "@Data", "@EqualsAndHashCode", "@ToString"); - assertThat(source("model/entity/ProjectLeadershipTermEntity.java")) - .contains("public long internUserId()", "Instant end(Instant at, long mentorUserId)") - .doesNotContain("@Getter", "@Setter", "@Data", "@EqualsAndHashCode", "@ToString"); - assertThat(source("model/entity/ProjectMembershipEntity.java")) - .contains("public long internUserId()", "public boolean isCurrent()") - .doesNotContain("@Getter", "@Setter", "@Data", "@EqualsAndHashCode", "@ToString"); - - for (String recordSource : List.of( - "model/ProjectInternEligibility.java", - "model/ProjectLeaderChange.java", - "model/dto/ProjectActorView.java", - "model/dto/ProjectCreateCommand.java", - "model/dto/ProjectCreateForm.java", - "model/dto/ProjectDashboardSummary.java", - "model/dto/ProjectDetail.java", - "model/dto/ProjectLeadershipTermView.java", - "model/dto/ProjectMemberForm.java", - "model/dto/ProjectMemberView.java", - "model/dto/ProjectSummary.java", - "model/dto/ProjectTaskContext.java", - "model/dto/ProjectTaskMemberView.java")) { - assertThat(source(recordSource)).contains(" record "); - } - } - - private static void assertRequiredArgsConstructor(String relativePath, String typeName) throws IOException { - assertThat(source(relativePath)) - .contains("import lombok.RequiredArgsConstructor;", "@RequiredArgsConstructor") - .doesNotContain("public " + typeName + "("); - } - - private static void assertProtectedJpaConstructor(String relativePath, String typeName) throws IOException { - assertThat(source(relativePath)) - .contains( - "import lombok.AccessLevel;", - "import lombok.NoArgsConstructor;", - "@NoArgsConstructor(access = AccessLevel.PROTECTED)") - .doesNotContain("protected " + typeName + "()"); - } - - private static String source(String relativePath) throws IOException { - return Files.readString(PROJECT_SOURCE.resolve(relativePath)); - } -}