test(attendance): verify compiled lombok contracts
This commit is contained in:
@@ -1,28 +1,35 @@
|
|||||||
# Test Evidence: Attendance targeted Lombok boilerplate retrofit
|
# Test Evidence: Attendance targeted Lombok boilerplate retrofit
|
||||||
|
|
||||||
- **Test type:** Unit source-contract audit
|
- **Test type:** Unit compiled-contract audit
|
||||||
- **Requirement IDs:** `ATT-001`–`ATT-012`, `CAL-001`, `CAL-006`–`CAL-009`
|
- **Requirement IDs:** `ATT-001`–`ATT-012`, `CAL-001`, `CAL-006`–`CAL-009`
|
||||||
- **Scenario IDs:** `AC-ATT-001`–`AC-ATT-005`, `AC-CAL-003`, `AC-CAL-004`
|
- **Scenario IDs:** `AC-ATT-001`–`AC-ATT-005`, `AC-CAL-003`, `AC-CAL-004`
|
||||||
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.AttendanceLombokBoilerplateTest`
|
- **Test class/method:**
|
||||||
|
`com.lab.labtimesheet.feature.attendance.AttendanceLombokBoilerplateTest#generatedConstructorsPreserveParameterListsAndVisibility`,
|
||||||
|
`com.lab.labtimesheet.feature.attendance.AttendanceLombokBoilerplateTest#immutableModelsRemainRecordsWithTheirComponentContracts`,
|
||||||
|
`com.lab.labtimesheet.feature.attendance.AttendanceLombokBoilerplateTest#entitiesExposeOnlyIntentionalPublicAndProtectedDeclaredMethods`
|
||||||
- **Implementation commit:** `82ad8202fd31f77db8c3932a902dba07cee70894`
|
- **Implementation commit:** `82ad8202fd31f77db8c3932a902dba07cee70894`
|
||||||
|
|
||||||
## Protected behavior
|
## Protected behavior
|
||||||
|
|
||||||
Attendance uses the installed Lombok processor only for mechanical constructors while preserving package-level
|
Attendance uses the installed Lombok processor only for mechanical constructors while preserving the compiled API:
|
||||||
Spring injection access, protected JPA construction, immutable records, domain constructors and mutations, raw punch
|
package-level Spring injection, protected JPA construction, immutable record components, domain constructors and
|
||||||
and attached-policy history rules, composite-key identity, and existing public API names.
|
mutations, raw punch and attached-policy history rules, composite-key identity, and existing public method names.
|
||||||
|
|
||||||
## Test method
|
## Test method
|
||||||
|
|
||||||
The source-contract test inspects only `feature.attendance` production Java. It enumerates the exact injection-only
|
Reflection inspects compiled `feature.attendance` classes rather than source spelling. It verifies every constructor's
|
||||||
components and JPA/stateless no-argument constructors eligible for Lombok, rejects retained handwritten equivalents
|
parameter order and modifier, every immutable model's record components, and the exact public/protected declared method
|
||||||
and blanket `@Data`, and asserts that records and business-significant methods remain explicit.
|
surface of each Attendance entity. The entity surface prevents generated bean getters/setters or entity
|
||||||
|
`equals`/`hashCode`/`toString` widening while explicitly retaining `AttendanceRecordEntity#setCheckOutAt` and the
|
||||||
|
`LeaveRequestDayId` identity methods.
|
||||||
|
|
||||||
## Hand-derived expected result
|
## Hand-derived expected result
|
||||||
|
|
||||||
Five injection-only components use package-scoped `@RequiredArgsConstructor`; six JPA/embeddable types use protected
|
Five injection-only components expose only their package-scoped dependency constructors. Six JPA/embeddable types
|
||||||
`@NoArgsConstructor`; the stateless domain service uses a package-scoped `@NoArgsConstructor`. No record is replaced,
|
retain protected no-argument construction alongside their intentional domain constructors, and the stateless domain
|
||||||
and no validated constructor, state mutation, identity method, or stable domain-style accessor is generated away.
|
service remains package-scoped. Seven immutable models remain records with the same component order and types. Entity
|
||||||
|
method surfaces contain only intentional domain conversion/access/mutation methods; only the composite key owns
|
||||||
|
`equals` and `hashCode`, and no Attendance entity declares `toString`.
|
||||||
|
|
||||||
## RED
|
## RED
|
||||||
|
|
||||||
@@ -38,10 +45,10 @@ export PATH="$JAVA_HOME/bin:$PATH"
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
Tests run: 3, Failures: 2, Errors: 0, Skipped: 0
|
Tests run: 3, Failures: 2, Errors: 0, Skipped: 0
|
||||||
The injection-component assertion first failed on AttendanceController because the required package-scoped
|
The initial source audit first failed on AttendanceController because its mechanical dependency constructor remained,
|
||||||
@RequiredArgsConstructor and Lombok imports were absent. The JPA/stateless assertion first failed on
|
and on AttendancePolicyEntity because its mechanical protected JPA constructor remained. The immutable-record and
|
||||||
AttendancePolicyEntity because the protected @NoArgsConstructor and Lombok imports were absent. The record and
|
business-method retention guard passed. After this RED established the retrofit gap, the permanent regression was
|
||||||
business-method retention guard passed.
|
replaced with compiled reflection/API checks so formatting or annotation spelling cannot affect the result.
|
||||||
BUILD FAILURE
|
BUILD FAILURE
|
||||||
Process exited 1.
|
Process exited 1.
|
||||||
```
|
```
|
||||||
@@ -81,9 +88,9 @@ Process exited 0.
|
|||||||
|
|
||||||
## External-test boundaries
|
## External-test boundaries
|
||||||
|
|
||||||
The source audit does not replace compilation, reflection/JPA bootstrapping, MVC property access, PostgreSQL
|
The reflection audit does not replace Spring/JPA bootstrapping, MVC property access, PostgreSQL persistence, or
|
||||||
persistence, or Javadoc/doclint. Those checks are required as affected verification after the source contract turns
|
Javadoc/doclint. Those checks remain affected verification. No application behavior or public API is intentionally
|
||||||
GREEN. No application behavior or public API is intentionally changed by this retrofit.
|
changed by this retrofit.
|
||||||
|
|
||||||
Additional verification on the same source tree:
|
Additional verification on the same source tree:
|
||||||
|
|
||||||
@@ -99,7 +106,8 @@ BUILD SUCCESS
|
|||||||
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||||
PostgreSQL 18.4; BUILD SUCCESS
|
PostgreSQL 18.4; BUILD SUCCESS
|
||||||
|
|
||||||
javadoc -quiet -Xdoclint:all -d target/attendance-javadocs -classpath "target/classes:<Maven dependency classpath>" -sourcepath src/main/java -subpackages com.lab.labtimesheet.feature.attendance
|
./mvnw -q -DskipTests compile dependency:build-classpath -Dmdep.outputFile=target/attendance-javadoc-classpath.txt
|
||||||
|
javadoc -quiet -Xdoclint:all -d target/attendance-javadocs -classpath "target/classes:$(tr -d '\n' < target/attendance-javadoc-classpath.txt)" -sourcepath src/main/java -subpackages com.lab.labtimesheet.feature.attendance
|
||||||
Process exited 0. The source frontend reported seven generated-constructor missing-comment warnings because it does not
|
Process exited 0. The source frontend reported seven generated-constructor missing-comment warnings because it does not
|
||||||
expand Lombok constructors; repository policy exempts generated trivial constructors from duplicate Javadoc.
|
expand Lombok constructors; repository policy exempts generated trivial constructors from duplicate Javadoc.
|
||||||
```
|
```
|
||||||
|
|||||||
+254
-70
@@ -2,94 +2,278 @@ package com.lab.labtimesheet.feature.attendance;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import java.io.IOException;
|
import com.lab.labtimesheet.feature.account.service.AccountService;
|
||||||
import java.nio.file.Files;
|
import com.lab.labtimesheet.feature.attendance.controller.AttendanceController;
|
||||||
import java.nio.file.Path;
|
import com.lab.labtimesheet.feature.attendance.controller.CalendarController;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.AttendanceActor;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.AttendanceDayContext;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.AttendancePolicy;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.AttendanceRecord;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.AttendanceRole;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.AttendanceViolations;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.dto.AttendanceHistoryItem;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.dto.GlobalCalendarEvent;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.entity.AttendancePolicyEntity;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.entity.AttendanceRecordEntity;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.entity.GlobalCalendarEventEntity;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.entity.LeaveRequestDayEntity;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.entity.LeaveRequestDayId;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.model.entity.LeaveRequestEntity;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.repository.AttendancePolicyRepository;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.repository.AttendanceQueryRepository;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.repository.AttendanceRecordRepository;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.repository.GlobalCalendarEventRepository;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.service.AttendanceApplicationService;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.service.AttendanceCurrentUserService;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.service.AttendanceService;
|
||||||
|
import com.lab.labtimesheet.feature.attendance.service.CalendarApplicationService;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class AttendanceLombokBoilerplateTest {
|
class AttendanceLombokBoilerplateTest {
|
||||||
|
|
||||||
private static final Path SOURCE_ROOT =
|
private static final int PACKAGE_PRIVATE = 0;
|
||||||
Path.of("src/main/java/com/lab/labtimesheet/feature/attendance");
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void injectionOnlyComponentsUsePackageScopedRequiredArgsConstructors() throws IOException {
|
void generatedConstructorsPreserveParameterListsAndVisibility() {
|
||||||
for (String relativePath : List.of(
|
assertConstructors(
|
||||||
"controller/AttendanceController.java",
|
AttendanceController.class,
|
||||||
"controller/CalendarController.java",
|
constructor(
|
||||||
"service/AttendanceApplicationService.java",
|
PACKAGE_PRIVATE,
|
||||||
"service/AttendanceCurrentUserService.java",
|
AttendanceApplicationService.class,
|
||||||
"service/CalendarApplicationService.java")) {
|
AttendanceCurrentUserService.class));
|
||||||
String source = source(relativePath);
|
assertConstructors(
|
||||||
String typeName = typeName(relativePath);
|
CalendarController.class,
|
||||||
|
constructor(
|
||||||
|
PACKAGE_PRIVATE,
|
||||||
|
CalendarApplicationService.class,
|
||||||
|
AttendanceApplicationService.class,
|
||||||
|
AttendanceCurrentUserService.class));
|
||||||
|
assertConstructors(
|
||||||
|
AttendanceApplicationService.class,
|
||||||
|
constructor(
|
||||||
|
PACKAGE_PRIVATE,
|
||||||
|
Clock.class,
|
||||||
|
AttendancePolicyRepository.class,
|
||||||
|
AttendanceRecordRepository.class,
|
||||||
|
AttendanceQueryRepository.class,
|
||||||
|
AccountService.class,
|
||||||
|
CalendarApplicationService.class,
|
||||||
|
AttendanceService.class));
|
||||||
|
assertConstructors(
|
||||||
|
AttendanceCurrentUserService.class,
|
||||||
|
constructor(PACKAGE_PRIVATE, AccountService.class));
|
||||||
|
assertConstructors(
|
||||||
|
CalendarApplicationService.class,
|
||||||
|
constructor(
|
||||||
|
PACKAGE_PRIVATE,
|
||||||
|
Clock.class,
|
||||||
|
AttendancePolicyRepository.class,
|
||||||
|
GlobalCalendarEventRepository.class));
|
||||||
|
assertConstructors(AttendanceService.class, constructor(PACKAGE_PRIVATE));
|
||||||
|
|
||||||
assertThat(source)
|
assertConstructors(AttendancePolicyEntity.class, constructor(Modifier.PROTECTED));
|
||||||
.as(relativePath)
|
assertConstructors(
|
||||||
.contains(
|
AttendanceRecordEntity.class,
|
||||||
"import lombok.AccessLevel;",
|
constructor(Modifier.PROTECTED),
|
||||||
"import lombok.RequiredArgsConstructor;",
|
constructor(
|
||||||
"@RequiredArgsConstructor(access = AccessLevel.PACKAGE)")
|
Modifier.PUBLIC,
|
||||||
.doesNotContain(typeName + "(");
|
long.class,
|
||||||
}
|
LocalDate.class,
|
||||||
|
AttendancePolicyEntity.class,
|
||||||
|
Instant.class,
|
||||||
|
Instant.class));
|
||||||
|
assertConstructors(
|
||||||
|
GlobalCalendarEventEntity.class,
|
||||||
|
constructor(Modifier.PROTECTED),
|
||||||
|
constructor(
|
||||||
|
Modifier.PUBLIC,
|
||||||
|
LocalDate.class,
|
||||||
|
String.class,
|
||||||
|
boolean.class,
|
||||||
|
long.class));
|
||||||
|
assertConstructors(
|
||||||
|
LeaveRequestDayEntity.class,
|
||||||
|
constructor(Modifier.PROTECTED),
|
||||||
|
constructor(
|
||||||
|
PACKAGE_PRIVATE,
|
||||||
|
LeaveRequestEntity.class,
|
||||||
|
LocalDate.class,
|
||||||
|
AttendancePolicyEntity.class,
|
||||||
|
int.class));
|
||||||
|
assertConstructors(
|
||||||
|
LeaveRequestDayId.class,
|
||||||
|
constructor(Modifier.PROTECTED),
|
||||||
|
constructor(Modifier.PUBLIC, long.class, LocalDate.class));
|
||||||
|
assertConstructors(
|
||||||
|
LeaveRequestEntity.class,
|
||||||
|
constructor(Modifier.PROTECTED),
|
||||||
|
constructor(
|
||||||
|
PACKAGE_PRIVATE,
|
||||||
|
long.class,
|
||||||
|
LocalDate.class,
|
||||||
|
LocalDate.class,
|
||||||
|
String.class,
|
||||||
|
Instant.class,
|
||||||
|
Instant.class,
|
||||||
|
long.class,
|
||||||
|
Instant.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void jpaTypesAndStatelessServiceUseTargetedNoArgsConstructors() throws IOException {
|
void immutableModelsRemainRecordsWithTheirComponentContracts() {
|
||||||
for (String relativePath : List.of(
|
assertRecordComponents(
|
||||||
"model/entity/AttendancePolicyEntity.java",
|
AttendanceActor.class,
|
||||||
"model/entity/AttendanceRecordEntity.java",
|
component("userId", long.class),
|
||||||
"model/entity/GlobalCalendarEventEntity.java",
|
component("role", AttendanceRole.class));
|
||||||
"model/entity/LeaveRequestDayEntity.java",
|
assertRecordComponents(
|
||||||
"model/entity/LeaveRequestDayId.java",
|
AttendanceDayContext.class,
|
||||||
"model/entity/LeaveRequestEntity.java")) {
|
component("activeIntern", boolean.class),
|
||||||
String source = source(relativePath);
|
component("globalDayOff", boolean.class),
|
||||||
String typeName = typeName(relativePath);
|
component("approvedLeave", boolean.class));
|
||||||
|
assertRecordComponents(
|
||||||
assertThat(source)
|
AttendancePolicy.class,
|
||||||
.as(relativePath)
|
component("id", long.class),
|
||||||
.contains(
|
component("effectiveFrom", LocalDate.class),
|
||||||
"import lombok.AccessLevel;",
|
component("zoneId", ZoneId.class),
|
||||||
"import lombok.NoArgsConstructor;",
|
component("scheduledStart", LocalTime.class),
|
||||||
"@NoArgsConstructor(access = AccessLevel.PROTECTED)")
|
component("scheduledEnd", LocalTime.class),
|
||||||
.doesNotContain("protected " + typeName + "()");
|
component("checkInGraceMinutes", int.class),
|
||||||
}
|
component("checkoutGraceMinutes", int.class),
|
||||||
|
component("monthlyLeaveQuota", int.class),
|
||||||
assertThat(source("service/AttendanceService.java"))
|
component("violationPenalty", BigDecimal.class),
|
||||||
.contains(
|
component("workdays", Set.class));
|
||||||
"import lombok.AccessLevel;",
|
assertRecordComponents(
|
||||||
"import lombok.NoArgsConstructor;",
|
AttendanceRecord.class,
|
||||||
"@NoArgsConstructor(access = AccessLevel.PACKAGE)")
|
component("internId", long.class),
|
||||||
.doesNotContain("AttendanceService()");
|
component("workDate", LocalDate.class),
|
||||||
|
component("policy", AttendancePolicy.class),
|
||||||
|
component("checkInAt", Instant.class),
|
||||||
|
component("checkOutAt", Instant.class));
|
||||||
|
assertRecordComponents(
|
||||||
|
AttendanceViolations.class,
|
||||||
|
component("late", boolean.class),
|
||||||
|
component("earlyDeparture", boolean.class),
|
||||||
|
component("missingCheckout", boolean.class));
|
||||||
|
assertRecordComponents(
|
||||||
|
AttendanceHistoryItem.class,
|
||||||
|
component("workDate", LocalDate.class),
|
||||||
|
component("checkInAt", Instant.class),
|
||||||
|
component("checkOutAt", Instant.class),
|
||||||
|
component("policy", AttendancePolicy.class),
|
||||||
|
component("violations", AttendanceViolations.class));
|
||||||
|
assertRecordComponents(
|
||||||
|
GlobalCalendarEvent.class,
|
||||||
|
component("id", long.class),
|
||||||
|
component("date", LocalDate.class),
|
||||||
|
component("name", String.class),
|
||||||
|
component("dayOff", boolean.class),
|
||||||
|
component("version", long.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void auditRetainsRecordsAndExplicitBusinessMethodsWithoutBlanketData() throws IOException {
|
void entitiesExposeOnlyIntentionalPublicAndProtectedDeclaredMethods() {
|
||||||
try (var sources = Files.walk(SOURCE_ROOT)) {
|
assertMethodSurface(
|
||||||
for (Path sourcePath : sources.filter(path -> path.toString().endsWith(".java")).toList()) {
|
AttendancePolicyEntity.class,
|
||||||
assertThat(Files.readString(sourcePath)).as(sourcePath.toString()).doesNotContain("@Data");
|
method(Modifier.PUBLIC, "toDomain", AttendancePolicy.class));
|
||||||
|
assertMethodSurface(
|
||||||
|
AttendanceRecordEntity.class,
|
||||||
|
method(Modifier.PUBLIC, "toDomain", AttendanceRecord.class),
|
||||||
|
method(Modifier.PUBLIC, "setCheckOutAt", void.class, Instant.class),
|
||||||
|
method(Modifier.PUBLIC, "workDate", LocalDate.class));
|
||||||
|
assertMethodSurface(
|
||||||
|
GlobalCalendarEventEntity.class,
|
||||||
|
method(
|
||||||
|
Modifier.PUBLIC,
|
||||||
|
"update",
|
||||||
|
void.class,
|
||||||
|
LocalDate.class,
|
||||||
|
String.class,
|
||||||
|
boolean.class,
|
||||||
|
long.class),
|
||||||
|
method(Modifier.PUBLIC, "toDomain", GlobalCalendarEvent.class),
|
||||||
|
method(Modifier.PUBLIC, "calendarDate", LocalDate.class),
|
||||||
|
method(Modifier.PUBLIC, "version", long.class));
|
||||||
|
assertMethodSurface(LeaveRequestDayEntity.class);
|
||||||
|
assertMethodSurface(
|
||||||
|
LeaveRequestDayId.class,
|
||||||
|
method(Modifier.PUBLIC, "equals", boolean.class, Object.class),
|
||||||
|
method(Modifier.PUBLIC, "hashCode", int.class));
|
||||||
|
assertMethodSurface(LeaveRequestEntity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertConstructors(
|
||||||
|
Class<?> type, ConstructorContract... expectedConstructors) {
|
||||||
|
List<ConstructorContract> actual = Arrays.stream(type.getDeclaredConstructors())
|
||||||
|
.map(ConstructorContract::from)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
assertThat(actual).as(type.getName()).containsExactlyInAnyOrder(expectedConstructors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertRecordComponents(
|
||||||
|
Class<?> type, RecordComponentContract... expectedComponents) {
|
||||||
|
assertThat(type.isRecord()).as(type.getName()).isTrue();
|
||||||
|
assertThat(Arrays.stream(type.getRecordComponents())
|
||||||
|
.map(component -> new RecordComponentContract(component.getName(), component.getType()))
|
||||||
|
.toList())
|
||||||
|
.as(type.getName())
|
||||||
|
.containsExactly(expectedComponents);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertMethodSurface(Class<?> type, MethodContract... expectedMethods) {
|
||||||
|
List<MethodContract> actual = Arrays.stream(type.getDeclaredMethods())
|
||||||
|
.filter(method -> Modifier.isPublic(method.getModifiers())
|
||||||
|
|| Modifier.isProtected(method.getModifiers()))
|
||||||
|
.map(MethodContract::from)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
assertThat(actual).as(type.getName()).containsExactlyInAnyOrder(expectedMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConstructorContract constructor(int modifiers, Class<?>... parameterTypes) {
|
||||||
|
return new ConstructorContract(modifiers, List.of(parameterTypes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RecordComponentContract component(String name, Class<?> type) {
|
||||||
|
return new RecordComponentContract(name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodContract method(
|
||||||
|
int modifiers, String name, Class<?> returnType, Class<?>... parameterTypes) {
|
||||||
|
return new MethodContract(modifiers, name, returnType, List.of(parameterTypes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private record ConstructorContract(int modifiers, List<Class<?>> parameterTypes) {
|
||||||
|
|
||||||
|
private static ConstructorContract from(Constructor<?> constructor) {
|
||||||
|
return new ConstructorContract(
|
||||||
|
constructor.getModifiers(), List.of(constructor.getParameterTypes()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(source("model/AttendancePolicy.java")).contains("public record AttendancePolicy(");
|
private record RecordComponentContract(String name, Class<?> type) {}
|
||||||
assertThat(source("model/AttendanceRecord.java"))
|
|
||||||
.contains("public record AttendanceRecord(", "public AttendanceRecord checkOut(Instant at)");
|
|
||||||
assertThat(source("model/dto/AttendanceHistoryItem.java"))
|
|
||||||
.contains("public record AttendanceHistoryItem(");
|
|
||||||
assertThat(source("model/entity/GlobalCalendarEventEntity.java"))
|
|
||||||
.contains("public void update(", "public LocalDate calendarDate()", "public long version()");
|
|
||||||
assertThat(source("model/entity/AttendanceRecordEntity.java"))
|
|
||||||
.contains("public void setCheckOutAt(", "public LocalDate workDate()");
|
|
||||||
assertThat(source("model/entity/LeaveRequestDayId.java"))
|
|
||||||
.contains("public boolean equals(", "public int hashCode()");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String source(String relativePath) throws IOException {
|
private record MethodContract(
|
||||||
return Files.readString(SOURCE_ROOT.resolve(relativePath));
|
int modifiers, String name, Class<?> returnType, List<Class<?>> parameterTypes) {
|
||||||
}
|
|
||||||
|
|
||||||
private static String typeName(String relativePath) {
|
private static MethodContract from(Method method) {
|
||||||
String fileName = Path.of(relativePath).getFileName().toString();
|
return new MethodContract(
|
||||||
return fileName.substring(0, fileName.length() - ".java".length());
|
method.getModifiers(),
|
||||||
|
method.getName(),
|
||||||
|
method.getReturnType(),
|
||||||
|
List.of(method.getParameterTypes()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user