Merge commit '8b48e281f7e860af435ae35b16c4edeb139286dc' into work/reports-ui
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# Test Evidence: Attendance PostgreSQL persistence and calendar rules
|
||||
|
||||
- **Test type:** Integration
|
||||
- **Requirement IDs:** `ATT-002`, `ATT-005`, `ATT-007`, `ATT-008`, `ATT-010`, `CAL-001`, `CAL-006`, `CAL-007`, `CAL-009`, `AUTH-003`, `RPT-004`
|
||||
- **Scenario IDs:** `AC-ATT-003`, `AC-ATT-004`, `AC-CAL-003`, `AC-CAL-004`
|
||||
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.service.AttendancePersistenceIntegrationTest`
|
||||
- **Implementation commit:** `pending (committed with this evidence)`
|
||||
|
||||
## Protected behavior
|
||||
|
||||
PostgreSQL stores server-time punches with the seeded applied-policy foreign key,
|
||||
enforces one row per Intern/date, and returns the attached policy in history.
|
||||
Admin-only manual calendar changes affect check-in, past events are immutable,
|
||||
stale edits are rejected, and Mentor/Admin/own-history scopes are enforced.
|
||||
|
||||
## Test method
|
||||
|
||||
A Spring Boot integration test migrates a real PostgreSQL 18.4 Testcontainer,
|
||||
creates and activates a valid Intern exclusively through public account and SMTP
|
||||
service/DTO boundaries, invokes the transactional attendance services, and
|
||||
asserts persisted rows and denied state transitions.
|
||||
|
||||
## Hand-derived expected result
|
||||
|
||||
The 1970 seed has ID 1 and a 30-minute checkout grace. An event created for
|
||||
2026-08-14 while server business date is 2026-08-13 blocks check-in on that
|
||||
date. After business date advances to 2026-08-15, that event cannot change.
|
||||
An update from version 0 advances the row, so a second version-0 edit is stale.
|
||||
|
||||
## RED
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||
./mvnw -Dtest=AttendancePersistenceIntegrationTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
[ERROR] cannot find symbol: class AttendanceApplicationService
|
||||
[ERROR] cannot find symbol: class CalendarApplicationService
|
||||
[INFO] 8 errors
|
||||
[INFO] BUILD FAILURE
|
||||
Process exited 1 before Testcontainers startup because the required persistence/application services did not exist.
|
||||
```
|
||||
|
||||
The optimistic-edit assertion was separately observed RED:
|
||||
|
||||
```text
|
||||
./mvnw -Dtest=AttendancePersistenceIntegrationTest test
|
||||
[ERROR] method updateManual ... actual and formal argument lists differ in length
|
||||
[INFO] 4 errors
|
||||
[INFO] BUILD FAILURE
|
||||
Process exited 1 because update did not yet accept an expected version.
|
||||
```
|
||||
|
||||
## GREEN
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||
./mvnw -Dtest=AttendancePersistenceIntegrationTest,AttendanceControllerTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
PostgreSQL 18.4 container started and Flyway applied V1.
|
||||
AttendancePersistenceIntegrationTest: Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## Affected suite
|
||||
|
||||
**Command and result**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||
./mvnw -Dtest='*Attendance*Test' test
|
||||
Tests run: 26, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## External-test boundaries
|
||||
|
||||
This test does not prove cross-request check-in races, production authentication
|
||||
configuration, shared-shell integration, HolidayAPI, leave creation/decision,
|
||||
corrections, schedulers, or later policy scheduling.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Test Evidence: Current business-date attendance state
|
||||
|
||||
- **Test type:** Unit
|
||||
- **Requirement IDs:** `ATT-005`, `I1-UI-03`
|
||||
- **Scenario IDs:** `I1-ATT-03`, `I1-ATT-04`
|
||||
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.service.AttendanceApplicationServiceTest`
|
||||
- **Implementation commit:** `pending (committed with this evidence)`
|
||||
|
||||
## Protected behavior
|
||||
|
||||
The public attendance service reports an eligible Intern's current business-date
|
||||
state as not checked in, checked in, or checked out without exposing attendance
|
||||
repositories/entities to dashboard consumers. Ineligible Interns are rejected.
|
||||
|
||||
## Test method
|
||||
|
||||
A fixed Clock, seeded policy, and mocked Spring Data/account boundaries drive the
|
||||
real application service through all three persisted-record shapes. A separate
|
||||
case makes account eligibility false and asserts the attendance rejection.
|
||||
|
||||
## Hand-derived expected result
|
||||
|
||||
No record means `NOT_CHECKED_IN`; a record without checkout means `CHECKED_IN`;
|
||||
a record with checkout means `CHECKED_OUT`. An ineligible user produces
|
||||
`INACTIVE_INTERN` instead of a state.
|
||||
|
||||
## RED
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
./mvnw -Dtest=AttendancePersistenceIntegrationTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
cannot find symbol: class AttendanceCurrentState
|
||||
Tests did not run because the requested public DTO/service behavior did not exist.
|
||||
BUILD FAILURE
|
||||
Process exited 1.
|
||||
```
|
||||
|
||||
## GREEN
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
./mvnw -Dtest=AttendanceApplicationServiceTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## Affected suite
|
||||
|
||||
**Command and result**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||
./mvnw -Dtest='*Attendance*Test' test
|
||||
Tests run: 26, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## External-test boundaries
|
||||
|
||||
The unit test does not prove PostgreSQL persistence, account fixture creation,
|
||||
Spring transaction behavior, MVC rendering, or dashboard composition.
|
||||
@@ -0,0 +1,101 @@
|
||||
# Test Evidence: Attendance feature package and JPA boundaries
|
||||
|
||||
- **Test type:** Unit
|
||||
- **Requirement IDs:** `ARC-005`, `OPS-020`
|
||||
- **Scenario IDs:** `I1-ATT-01` through `I1-ATT-05` structural gate
|
||||
- **Test class/method:** `com.lab.labtimesheet.architecture.AttendanceLayerStructureTest`
|
||||
- **Implementation commit:** `pending (committed with this evidence)`
|
||||
|
||||
## Protected behavior
|
||||
|
||||
Attendance/calendar code lives under one `feature.attendance` boundary with
|
||||
controller, model, model.dto, model.entity, repository, service, and exception
|
||||
layers. The superseded feature-first and global-layer classes are absent, and
|
||||
application services do not depend on `JdbcTemplate`.
|
||||
Attendance does not map or expose the account feature's `app_users` or
|
||||
`intern_profiles` tables.
|
||||
|
||||
## Test method
|
||||
|
||||
Plain JUnit loads the required public classes by authoritative package name,
|
||||
proves superseded class names are absent, verifies the query repository is a
|
||||
Spring Data repository, reflects over application-service dependencies, and
|
||||
proves that attendance-owned account entities/repositories cannot be loaded.
|
||||
|
||||
## Hand-derived expected result
|
||||
|
||||
Seven representative classes load from `feature.attendance` internal layers;
|
||||
the old `attendance.AttendanceService` and global `controller.AttendanceController`
|
||||
do not load; query access implements Spring Data `Repository`; no checked
|
||||
application service has a `JdbcTemplate` field.
|
||||
The four forbidden attendance-owned account entity/repository class names do
|
||||
not load.
|
||||
|
||||
## RED
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
./mvnw -Dtest=AttendanceLayerStructureTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
ClassNotFoundException: com.lab.labtimesheet.feature.attendance.controller.AttendanceController
|
||||
ClassNotFoundException: com.lab.labtimesheet.feature.attendance.repository.AttendanceQueryRepository
|
||||
Tests run: 2, Failures: 0, Errors: 2, Skipped: 0
|
||||
BUILD FAILURE
|
||||
Process exited 1 because the implementation still used the superseded package layout.
|
||||
```
|
||||
|
||||
The account-boundary assertion was separately observed RED after the final
|
||||
feature package move:
|
||||
|
||||
```text
|
||||
./mvnw -Dtest=AttendanceLayerStructureTest test
|
||||
AttendanceLayerStructureTest.attendanceDoesNotMapOrExposeAccountFeatureTables:
|
||||
Expecting code to raise a throwable.
|
||||
Tests run: 3, Failures: 1, Errors: 0, Skipped: 0
|
||||
BUILD FAILURE
|
||||
Process exited 1 because attendance still owned shadow AppUser/InternProfile entity and repository types.
|
||||
```
|
||||
|
||||
## GREEN
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
./mvnw -Dtest=AttendanceLayerStructureTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## Affected suite
|
||||
|
||||
**Command and result**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||
./mvnw -Dtest='*Attendance*Test' test
|
||||
Tests run: 26, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## External-test boundaries
|
||||
|
||||
This test proves source/package and dependency shape, not Spring context startup,
|
||||
PostgreSQL queries, MVC behavior, or the final platform account-service wiring.
|
||||
@@ -0,0 +1,77 @@
|
||||
# Test Evidence: Attendance policy defaults and boundaries
|
||||
|
||||
- **Test type:** Unit
|
||||
- **Requirement IDs:** `ATT-001`, `ATT-002`, `ATT-003`, `ATT-004`
|
||||
- **Scenario IDs:** `AC-ATT-001`
|
||||
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.model.AttendancePolicyTest`
|
||||
- **Implementation commit:** `pending (committed with this evidence)`
|
||||
|
||||
## Protected behavior
|
||||
|
||||
The seeded policy applies from 1970-01-01 with the required timezone, schedule,
|
||||
workdays, grace values, leave quota, and penalty. Grace outside 0..720 or a
|
||||
checkout cutoff at midnight is rejected.
|
||||
|
||||
## Test method
|
||||
|
||||
Plain JUnit constructs the immutable policy and timeline directly, resolves two
|
||||
dates, and exercises the validation boundary without Spring or persistence.
|
||||
|
||||
## Hand-derived expected result
|
||||
|
||||
08:30 plus 30 minutes makes the inclusive on-time boundary 09:00. 15:30 plus
|
||||
30 minutes makes the inclusive checkout boundary 16:00. A 23:30 end plus 30
|
||||
minutes reaches midnight and is invalid.
|
||||
|
||||
## RED
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
./mvnw -Dtest=AttendancePolicyTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
[ERROR] AttendancePolicyTest.java:[51,20] cannot find symbol
|
||||
symbol: class AttendancePolicy
|
||||
[INFO] BUILD FAILURE
|
||||
Process exited 1. The test reached compilation and failed because the required policy domain did not exist.
|
||||
```
|
||||
|
||||
## GREEN
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
./mvnw -Dtest=AttendancePolicyTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## Affected suite
|
||||
|
||||
**Command and result**
|
||||
|
||||
```text
|
||||
./mvnw -Dtest='*Attendance*Test' test
|
||||
Tests run: 26, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## External-test boundaries
|
||||
|
||||
This unit test does not prove the platform-owned Flyway seed, PostgreSQL policy
|
||||
loading, policy-management authorization, or web rendering.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Test Evidence: Attendance punch boundaries
|
||||
|
||||
- **Test type:** Unit
|
||||
- **Requirement IDs:** `GOV-011`, `GOV-012`, `ATT-005`, `ATT-007`, `ATT-008`, `ATT-009`, `ATT-010`, `ATT-011`, `ATT-012`, `ATT-016`
|
||||
- **Scenario IDs:** `AC-ATT-002`, `AC-ATT-003`, `AC-ATT-004`, `AC-ATT-005`
|
||||
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.service.AttendanceServiceTest`
|
||||
- **Implementation commit:** `pending (committed with this evidence)`
|
||||
|
||||
## Protected behavior
|
||||
|
||||
Clock-controlled server time determines the local work date and raw punches.
|
||||
Check-in rejects inactive, non-workday, day-off, leave, and duplicate attempts.
|
||||
Exact grace/cutoff instants succeed; later checkout never writes raw checkout;
|
||||
a missed checkout is not also an early departure.
|
||||
|
||||
## Test method
|
||||
|
||||
Plain JUnit uses a fixed `Clock`, the production domain service, and a minimal
|
||||
in-memory repository port. Assertions cover stored state as well as rejection
|
||||
codes, including non-overwrite behavior.
|
||||
|
||||
## Hand-derived expected result
|
||||
|
||||
Asia/Ho_Chi_Minh is UTC+07 for the tested date: 09:00 local is 02:00Z,
|
||||
15:30 local is 08:30Z, and 16:00 local is 09:00Z. Equality is accepted;
|
||||
adding one millisecond crosses each strict-later boundary.
|
||||
|
||||
## RED
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
./mvnw -Dtest=AttendanceServiceTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
[ERROR] AttendanceServiceTest.java:[3,46] cannot find symbol
|
||||
symbol: class AttendanceRejection
|
||||
[ERROR] AttendanceServiceTest.java:[136,20] cannot find symbol
|
||||
symbol: class AttendanceService
|
||||
[INFO] 29 errors
|
||||
[INFO] BUILD FAILURE
|
||||
Process exited 1. The test reached compilation and failed because the required attendance domain did not exist.
|
||||
```
|
||||
|
||||
## GREEN
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
./mvnw -Dtest=AttendanceServiceTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## Affected suite
|
||||
|
||||
**Command and result**
|
||||
|
||||
```text
|
||||
./mvnw -Dtest='*Attendance*Test' test
|
||||
Tests run: 26, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## External-test boundaries
|
||||
|
||||
This unit test does not prove transaction isolation, PostgreSQL uniqueness,
|
||||
platform account/intern-state queries, approved-leave persistence, Spring
|
||||
Security, controller routing, or Thymeleaf rendering.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Test Evidence: Attendance and global-calendar web authorization
|
||||
|
||||
- **Test type:** Web
|
||||
- **Requirement IDs:** `AUTH-001`, `AUTH-002`, `AUTH-003`, `ATT-007`, `ATT-010`, `CAL-001`, `CAL-007`, `RPT-004`
|
||||
- **Scenario IDs:** `AC-ATT-003`, `AC-ATT-004`, `AC-CAL-004`
|
||||
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.controller.AttendanceControllerTest`
|
||||
- **Implementation commit:** `pending (committed with this evidence)`
|
||||
|
||||
## Protected behavior
|
||||
|
||||
Authenticated Intern punch routes use the server-resolved user ID, own history
|
||||
renders attached policy details, Mentor inspection routes preserve the target
|
||||
scope, and calendar management rejects non-Admin access. Calendar updates carry
|
||||
the submitted optimistic version.
|
||||
|
||||
## Test method
|
||||
|
||||
`@WebMvcTest` runs Spring Security filters, CSRF protection, MVC binding, route
|
||||
selection, controller authorization, Thymeleaf rendering, and service-call
|
||||
arguments while mocking only application-service and current-user boundaries.
|
||||
|
||||
## Hand-derived expected result
|
||||
|
||||
An Intern authenticated as user 42 can punch only ID 42. A Mentor can inspect
|
||||
target 42 but receives HTTP 403 for Admin calendar management. Attached policy
|
||||
grace renders as `30 min`. An event form with version 3 calls update with 3.
|
||||
|
||||
## RED
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
./mvnw -Dtest=AttendanceControllerTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
[ERROR] cannot find symbol: class AttendanceCurrentUserService
|
||||
[ERROR] cannot find symbol: class AttendanceController
|
||||
[ERROR] cannot find symbol: class CalendarController
|
||||
[INFO] 3 errors
|
||||
[INFO] BUILD FAILURE
|
||||
Process exited 1 because the required authenticated web endpoints did not exist.
|
||||
```
|
||||
|
||||
## GREEN
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
./mvnw -Dtest=AttendanceControllerTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## Affected suite
|
||||
|
||||
**Command and result**
|
||||
|
||||
```text
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||
./mvnw -Dtest='*Attendance*Test' test
|
||||
Tests run: 26, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
Process exited 0.
|
||||
```
|
||||
|
||||
## External-test boundaries
|
||||
|
||||
This MVC slice does not prove the platform's production login/session setup,
|
||||
shared shell and navigation, browser layout, or accessibility beyond semantic
|
||||
labels, table headers, status roles, CSRF, and route authorization.
|
||||
Reference in New Issue
Block a user