From e2b206c27e494fbcd0cc3ed99ff8c2c470285f9e Mon Sep 17 00:00:00 2001 From: sechmachine <97589681+sechmachine727@users.noreply.github.com> Date: Sat, 15 Aug 2026 11:32:59 +0700 Subject: [PATCH] refactor(reporting): use Lombok injection constructors --- .../unit/lombok-reporting-boilerplate.md | 99 +++++++++++++++++++ .../controller/DashboardController.java | 11 +-- .../reporting/service/DashboardService.java | 21 +--- 3 files changed, 103 insertions(+), 28 deletions(-) create mode 100644 docs/tests/unit/lombok-reporting-boilerplate.md diff --git a/docs/tests/unit/lombok-reporting-boilerplate.md b/docs/tests/unit/lombok-reporting-boilerplate.md new file mode 100644 index 0000000..6bdcf62 --- /dev/null +++ b/docs/tests/unit/lombok-reporting-boilerplate.md @@ -0,0 +1,99 @@ +# Test Evidence: Reporting Lombok boilerplate boundary + +- **Test type:** Unit +- **Requirement IDs:** `ARC-005`, `OPS-019`, `OPS-021` +- **Scenario IDs:** `AC-OPS-002` +- **Test class/method:** Temporary executable source audit plus stable Reporting compile/behavior/architecture suites +- **Implementation commit:** `pending` + +## Protected behavior + +Reporting uses Lombok only for constructors that mechanically assign required Spring dependencies. Immutable dashboard DTOs remain records, the access-denied exception keeps its explicit superclass constructor, and the attendance-state presentation contract keeps its explicit fluent `label()` method. + +## Test method + +The narrow source audit reads only the four Reporting production sources. It requires `@RequiredArgsConstructor` and removal of the two injection-only constructors while positively checking the deliberately retained records, exception constructor, and presentation method. A temporary JUnit source test established RED and passed GREEN, then was removed because retaining exact source-string assertions would couple the suite to implementation details. Existing unit, MockMvc, and architecture tests compile and exercise the generated constructor API. + +## Hand-derived expected result + +`DashboardController` and `DashboardService` each contain final injected dependencies and constructors that only assign those fields, so both are eligible for `@RequiredArgsConstructor`. `DashboardAccessDeniedException(String)` must call its superclass and exposes a documented public error contract. The dashboard projections are already concise immutable records. `AttendanceState.label()` intentionally exposes a fluent presentation API rather than Lombok's default `getLabel()` 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=ReportingLombokBoilerplateTest test +``` + +**Observed result** + +```text +Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 +ReportingLombokBoilerplateTest failed because DashboardController did not contain +import lombok.RequiredArgsConstructor; and still had its handwritten injection constructor. +BUILD FAILURE +Total time: 4.638 s +``` + +## 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=ReportingLombokBoilerplateTest test + +Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 +BUILD SUCCESS +Total time: 4.528 s + +test "$(rg -l '@RequiredArgsConstructor' \ + src/main/java/com/lab/labtimesheet/feature/reporting/controller/DashboardController.java \ + src/main/java/com/lab/labtimesheet/feature/reporting/service/DashboardService.java | wc -l | tr -d ' ')" = "2" +! rg -n 'public (DashboardController|DashboardService)\\(' \ + src/main/java/com/lab/labtimesheet/feature/reporting/controller/DashboardController.java \ + src/main/java/com/lab/labtimesheet/feature/reporting/service/DashboardService.java +``` + +**Observed result** + +```text +The temporary source audit passed 1/1 after both conversions. The final shell source audit exited 0: both eligible classes carry `@RequiredArgsConstructor`, and neither handwritten injection-only constructor remains. +``` + +## 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" +export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock + +./mvnw -Dtest=ReportingArchitectureTest,DashboardServiceTest,DashboardControllerWebTest test +Tests run: 13, Failures: 0, Errors: 0, Skipped: 0 +BUILD SUCCESS +Total time: 5.952 s + +./mvnw -Dtest=ReportingArchitectureTest,AccountTemplateIntegrationTest,AdminDashboardWebTest,AttendanceTemplateIntegrationTest,DashboardControllerWebTest,DashboardTemplateWebTest,ProjectTaskFormAccessibilityWebTest,ProjectTaskShellContractTest,RoleDashboardWebIntegrationTest,SharedErrorTemplateWebTest,DashboardServiceTest test +PostgreSQL 18.4 via Testcontainers +Tests run: 42, Failures: 0, Errors: 0, Skipped: 0 +BUILD SUCCESS +Total time: 20.781 s + +./mvnw -DskipTests compile +BUILD SUCCESS +Total time: 0.655 s + +./mvnw -DskipTests -Ddoclint=all javadoc:javadoc +BUILD SUCCESS +Total time: 1.208 s +``` + +## External-test boundaries + +The source audit proves annotation scope and deliberate retention but does not alone prove generated bytecode, Spring injection, dashboard behavior, or Thymeleaf property access. Compile, Reporting service tests, MockMvc dashboard tests, architecture tests, PostgreSQL role-dashboard integration, and Javadoc/doclint provide those gates. Reporting owns no JPA entity or mutable bean, so JPA accessor/mapping checks are outside this feature's retrofit scope. Java 25 reports Lombok's known `sun.misc.Unsafe` annotation-processor warning, and Mockito reports its dynamic-agent warning; neither changed or failed the executed gates. diff --git a/src/main/java/com/lab/labtimesheet/feature/reporting/controller/DashboardController.java b/src/main/java/com/lab/labtimesheet/feature/reporting/controller/DashboardController.java index 6d1710f..76de760 100644 --- a/src/main/java/com/lab/labtimesheet/feature/reporting/controller/DashboardController.java +++ b/src/main/java/com/lab/labtimesheet/feature/reporting/controller/DashboardController.java @@ -2,6 +2,7 @@ package com.lab.labtimesheet.feature.reporting.controller; import com.lab.labtimesheet.feature.reporting.exception.DashboardAccessDeniedException; import com.lab.labtimesheet.feature.reporting.service.DashboardService; +import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -14,19 +15,11 @@ import org.springframework.web.bind.annotation.GetMapping; * reloads and revalidates the persisted account role and lifecycle before returning any data. */ @Controller +@RequiredArgsConstructor public class DashboardController { private final DashboardService dashboardService; - /** - * Creates the dashboard endpoint backed by the reporting composition service. - * - * @param dashboardService service that authorizes and assembles role-scoped dashboard data - */ - public DashboardController(DashboardService dashboardService) { - this.dashboardService = dashboardService; - } - /** * Renders the dashboard permitted by the caller's authenticated global role. * diff --git a/src/main/java/com/lab/labtimesheet/feature/reporting/service/DashboardService.java b/src/main/java/com/lab/labtimesheet/feature/reporting/service/DashboardService.java index 0bce619..c0281e6 100644 --- a/src/main/java/com/lab/labtimesheet/feature/reporting/service/DashboardService.java +++ b/src/main/java/com/lab/labtimesheet/feature/reporting/service/DashboardService.java @@ -16,6 +16,7 @@ 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 lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -28,6 +29,7 @@ import org.springframework.transaction.annotation.Transactional; */ @Service @Transactional(readOnly = true) +@RequiredArgsConstructor public class DashboardService { private final AccountService accounts; @@ -35,25 +37,6 @@ public class DashboardService { private final TaskDashboardService tasks; private final AttendanceApplicationService attendance; - /** - * Creates a reporting coordinator over the concrete feature query boundaries. - * - * @param accounts account identity and Admin summary boundary - * @param projects role-scoped Project summary boundary - * @param tasks role-scoped Task dashboard boundary - * @param attendance attendance state boundary using the active policy business date - */ - public DashboardService( - AccountService accounts, - ProjectQueryService projects, - TaskDashboardService tasks, - AttendanceApplicationService attendance) { - this.accounts = accounts; - this.projects = projects; - this.tasks = tasks; - this.attendance = attendance; - } - /** * Builds system-wide Admin counts after confirming an active persisted Admin identity. *