Compare commits
156
Commits
55a8ce675f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58a087b118 | ||
|
|
8878326839 | ||
|
|
200a17e5bd | ||
|
|
4dd9f96a23 | ||
|
|
d044277194 | ||
|
|
d13443e338 | ||
|
|
7a6139017f | ||
|
|
cea4378f56 | ||
|
|
b13c547068 | ||
|
|
a9fb948769 | ||
|
|
7bac998866 | ||
|
|
28ecfabe37 | ||
|
|
c8735c8ab8 | ||
|
|
6de463221b | ||
|
|
0a139d5470 | ||
|
|
f3ffdab48e | ||
|
|
5df9eff21e | ||
|
|
e5ff128502 | ||
|
|
169da1a9f4 | ||
|
|
64c9370aa0 | ||
|
|
32c8a2d315 | ||
|
|
c64ec659e7 | ||
|
|
445e4fedeb | ||
|
|
10196d55b0 | ||
|
|
5b8a58f520 | ||
|
|
bed8b502d4 | ||
|
|
90e412cffa | ||
|
|
207c0bfe89 | ||
|
|
f98e7f39ef | ||
|
|
9802d5d17f | ||
|
|
826054c3c5 | ||
|
|
97e991317d | ||
|
|
ef08717ce5 | ||
|
|
432389220f | ||
|
|
719e02ea90 | ||
|
|
d617769499 | ||
|
|
c8d4e9eecc | ||
|
|
e70159a81b | ||
|
|
46279f740b | ||
|
|
93ea47ae4a | ||
|
|
f013ad7707 | ||
|
|
159e634ffc | ||
|
|
8be1b754e1 | ||
|
|
bca30e6e4b | ||
|
|
531c607852 | ||
|
|
b764707716 | ||
|
|
6725f6a97d | ||
|
|
daa97aef71 | ||
|
|
73b57360a6 | ||
|
|
f93ced67b2 | ||
|
|
c949973282 | ||
|
|
5ddbd75f07 | ||
|
|
587127100d | ||
|
|
9023062477 | ||
|
|
469d7e274b | ||
|
|
41448903aa | ||
|
|
8500cb6eb7 | ||
|
|
4b1e5721e4 | ||
|
|
e5639c1461 | ||
|
|
baa2752466 | ||
|
|
382fa40101 | ||
|
|
7c1a26d77f | ||
|
|
abc5208d67 | ||
|
|
82ad8202fd | ||
|
|
1eb995500c | ||
|
|
e2b206c27e | ||
|
|
b9b150ff8c | ||
|
|
ef21c1bc78 | ||
|
|
6df04c1405 | ||
|
|
4212e9cbc2 | ||
|
|
039fe25c7c | ||
|
|
ddf688a533 | ||
|
|
ec99586393 | ||
|
|
cb76bea18e | ||
|
|
c81c0dfe65 | ||
|
|
1f6454ae53 | ||
|
|
e7dfae09e9 | ||
|
|
ba406a460c | ||
|
|
643d181b9d | ||
|
|
692b23e9b9 | ||
|
|
bf6f9af78b | ||
|
|
62a21132e2 | ||
|
|
06dba4fb13 | ||
|
|
98688dec7e | ||
|
|
8ff6ee3d87 | ||
|
|
17fa25bb09 | ||
|
|
01b8095e94 | ||
|
|
4c39df70e1 | ||
|
|
baa0695c60 | ||
|
|
af0eb3cabb | ||
|
|
e38e2cdea9 | ||
|
|
fb0ed7f12c | ||
|
|
8347b9e3a6 | ||
|
|
8388b4cc9b | ||
|
|
6181984cf8 | ||
|
|
c5c143c3cd | ||
|
|
cbdbd8ee13 | ||
|
|
4849e0ba3d | ||
|
|
401f67671a | ||
|
|
2a9a149520 | ||
|
|
b1c6b170d0 | ||
|
|
dbf12023c2 | ||
|
|
3f2f789905 | ||
|
|
213a889c8f | ||
|
|
b598631ce7 | ||
|
|
511ee81a91 | ||
|
|
c4656a8880 | ||
|
|
788d148d9c | ||
|
|
e1aa8eb062 | ||
|
|
3d954dd512 | ||
|
|
3064485007 | ||
|
|
f8db8a3e8b | ||
|
|
f9ddef6861 | ||
|
|
c6635fd744 | ||
|
|
c3f92d4e31 | ||
|
|
f48fc63775 | ||
|
|
19a3518f74 | ||
|
|
906ec6bea4 | ||
|
|
a18d8e1d3d | ||
|
|
7dd61b9dd4 | ||
|
|
69b81ddc71 | ||
|
|
61938875d3 | ||
|
|
8e786ba37b | ||
|
|
3d7135424f | ||
|
|
8b48e281f7 | ||
|
|
2f2573159c | ||
|
|
6fac593fcd | ||
|
|
c01c0089ac | ||
|
|
98a52a1ac2 | ||
|
|
a9ee99a6fe | ||
|
|
2fb73ff0f4 | ||
|
|
3343745632 | ||
|
|
7acd525205 | ||
|
|
2baf274747 | ||
|
|
25a855ea90 | ||
|
|
605eb2f449 | ||
|
|
6d30b426aa | ||
|
|
0015557b32 | ||
|
|
dd1f3ed05f | ||
|
|
1235204bf1 | ||
|
|
3fdfbb2bf2 | ||
|
|
5638286b90 | ||
|
|
1ed23f4de9 | ||
|
|
bc70db1d0d | ||
|
|
eadf6a7528 | ||
|
|
bac39812a3 | ||
|
|
597ebf1b49 | ||
|
|
20cdd1b9b3 | ||
|
|
71901d1670 | ||
|
|
c165e25d3f | ||
|
|
a28227db18 | ||
|
|
d2d3cc57c3 | ||
|
|
3483347bb8 | ||
|
|
17a3c5dc70 | ||
|
|
4b37f8fd05 | ||
|
|
5967f7f70d |
@@ -0,0 +1,417 @@
|
|||||||
|
# Lab Timesheet — Three-Iteration Delivery Plan
|
||||||
|
|
||||||
|
**Artifact purpose:** Local agent coordination and progress tracking
|
||||||
|
**Implementation branches:** `work/platform`, `work/projects`, `work/tasks`, `work/attendance`, `work/reports-ui`
|
||||||
|
**Requirements authority:** `labtimesheet-docs-hub/requirements-specification.md`
|
||||||
|
**SRS:** `labtimesheet-docs-hub/software-requirements-specification.md`
|
||||||
|
**Initial status:** Planning complete; implementation remains subject to requirements approval
|
||||||
|
|
||||||
|
This file divides the approved product scope across three iterations and five persistent work branches. It is a coordination artifact, not an alternative requirements source. When this plan and a numbered requirement disagree, the numbered requirement wins.
|
||||||
|
|
||||||
|
## 1. Progress rules
|
||||||
|
|
||||||
|
Use these exact status values:
|
||||||
|
|
||||||
|
| Status | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| `TODO` | No implementation work has started. |
|
||||||
|
| `IN_PROGRESS` | One named owner is actively working on the item. |
|
||||||
|
| `BLOCKED` | Work cannot continue; the tracker must name the evidence and required decision/dependency. |
|
||||||
|
| `DONE` | Required RED/GREEN evidence exists, affected tests pass, and the integrated behavior satisfies the requirement. |
|
||||||
|
|
||||||
|
Before editing production code, an agent shall:
|
||||||
|
|
||||||
|
1. Read the applicable numbered requirements and acceptance scenarios.
|
||||||
|
2. Claim one bounded tracker item by setting its status to `IN_PROGRESS` and recording owner/date.
|
||||||
|
3. Identify the test level and evidence file that will protect the behavior.
|
||||||
|
4. Write and run the failing test before production code.
|
||||||
|
5. Confirm the test fails because the required behavior is missing, not because the test or environment is broken.
|
||||||
|
|
||||||
|
When completing an item, the agent shall record:
|
||||||
|
|
||||||
|
- the exact RED command and expected failure;
|
||||||
|
- the exact GREEN and affected-suite commands;
|
||||||
|
- the evidence Markdown path under `docs/tests/`;
|
||||||
|
- the implementation commit or final local commit SHA;
|
||||||
|
- any remaining limitation that is explicitly allowed by the requirements.
|
||||||
|
|
||||||
|
No item becomes `DONE` based only on compilation, an isolated happy path, screenshots, or a verbal claim.
|
||||||
|
|
||||||
|
## 2. Branch ownership and conflict boundaries
|
||||||
|
|
||||||
|
| Branch | Sole or primary ownership |
|
||||||
|
|---|---|
|
||||||
|
| `work/platform` | Maven/application baseline, feature-package foundation, Flyway migration files, account/security/bootstrap, internship lifecycle, integration credential lifecycle, notification delivery infrastructure, Docker, and CI. |
|
||||||
|
| `work/projects` | Projects, membership intervals, invitations, membership-exit requests/readiness, leadership terms, Project lifecycle, Project-history authorization, transfer orchestration, and Project completion. |
|
||||||
|
| `work/tasks` | Tasks, generic creator/assignment actors, pending-exit assignment exclusion, member self-Task rules, comments, work logs, fixed status transitions, batch reassignment/direct-removal transfer operations, Task soft deletion, and Project Task-progress/history queries. |
|
||||||
|
| `work/attendance` | Attendance-policy versions/history, configured workdays, global calendar/history, HolidayAPI import interpretation, attendance, corrections, leave, deadline schedulers, and attendance/compliance metrics. |
|
||||||
|
| `work/reports-ui` | Shared Thymeleaf shell/fragments, Tailwind tokens/assets, dashboards, invitation/exit/transfer screens, Project and Admin-setting History tabs, shared report datasets, Chart.js presentation, XLSX/PDF exports, and cross-product UI/accessibility consistency. |
|
||||||
|
|
||||||
|
Conflict-prevention rules:
|
||||||
|
|
||||||
|
- `LabtimesheetApplication` shall remain in `com.lab.labtimesheet`, shared wiring in `config`, and business code in `feature.account`, `feature.integration`, `feature.project`, `feature.task`, `feature.attendance`, `feature.notification`, or `feature.reporting`. Each feature repeats only the controller/model/model.dto/model.entity/repository/service/exception layers it needs, and tests mirror that feature/layer shape.
|
||||||
|
- Cross-feature code may call another feature's service contract and DTOs but shall not import that feature's repository or JPA entity. Do not create empty `utils`, `common`, or `core` packages.
|
||||||
|
- Business persistence shall use Spring Data JPA repositories. Direct SQL is limited to Flyway migrations and schema/catalog verification; services shall not use `JdbcTemplate` or embed SQL.
|
||||||
|
|
||||||
|
- `work/platform` owns `src/main/resources/db/migration/**`, Maven/dependency configuration, Compose, container build files, and CI workflow files. Other branches request schema changes instead of independently allocating migration versions.
|
||||||
|
- `work/reports-ui` owns shared templates/fragments, shared design tokens, and general UI assets. Each domain branch owns its module-specific controllers and pages while consuming those shared fragments.
|
||||||
|
- A targeted repair shall use a clean, isolated `work/fix/<feature>/<what-fix>` branch and worktree from the taskmaster-verified current `main`. Do not use `work/<feature>/fix/<what-fix>`: the persistent `work/<feature>` ref already occupies that Git ref prefix.
|
||||||
|
- `work/tasks` exposes focused Task eligibility, batch-transfer, unfinished-count, and retained-history operations required by Project workflows. `work/projects` owns pending-exit readiness/approval, direct-removal orchestration, and Project completion transactions.
|
||||||
|
- `work/platform` owns HolidayAPI credential storage and the tested HTTP client. `work/attendance` owns preview interpretation, selection, deduplication, import, and day-off effects.
|
||||||
|
- Attendance time and Task work time remain separate. No branch may make one mutate or prove the other.
|
||||||
|
- Do not introduce a generic workflow engine, generic event-sourcing layer, multi-assignee Task model, Project-level day-off model, or speculative cross-module abstraction.
|
||||||
|
|
||||||
|
## 3. Iteration overview
|
||||||
|
|
||||||
|
| Iteration | Theme | Required demonstration | Status | Integration commit |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 1 | Working vertical slice | Bootstrap users, create/activate Project, assign/change a Task, and check in/out with role-correct UI. | `DONE` | `b9b150ff8ca9333e3b46d77537ec91875a970d57` |
|
||||||
|
| 2 | Complete business workflows | Policy/calendar changes, leave/corrections, leadership/member transfer, work logs, notifications, and full HTML workflows. | `TODO` | — |
|
||||||
|
| 3 | Hardening and delivery | Historical/concurrency proof, production security, HTML/XLSX/PDF parity, accessibility, containers, and CI publication boundary. | `TODO` | — |
|
||||||
|
|
||||||
|
## 4. Iteration 1 — Working vertical slice
|
||||||
|
|
||||||
|
### 4.1 `work/platform`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I1-PLAT-01 | Establish the Maven/Spring Boot modular baseline and root/config/feature package boundaries. | Context/load test; root-package and package-by-feature/layer check; no cross-feature repository/entity access or direct-SQL business services. | `DONE` | platform_agent / 2026-08-15 | Approved Platform head `692b23e9b9891d360882671d8247965b44920b2f`; integrated architecture/full-suite gates passed. |
|
||||||
|
| I1-PLAT-02 | Promote the reviewed 23-table/56-foreign-key PostgreSQL baseline into the platform-owned initial Flyway migration after explicit approval. | Fresh PostgreSQL 18.4 migration replay, catalog assertions, and invitation/exit same-Project constraints. | `DONE` | platform_agent / 2026-08-15 | Fresh local replay produced exactly 23 application tables and 56 foreign keys at the integrated head. |
|
||||||
|
| I1-PLAT-03 | Configure PostgreSQL Testcontainers and shared test-only encryption/clock facilities. | Affected integration tests require no developer database or SMTP. | `DONE` | platform_agent / 2026-08-15 | Taskmaster full PostgreSQL 18.4 Testcontainers suite passed 197/197. |
|
||||||
|
| I1-PLAT-04 | Implement atomic first-Admin bootstrap and permanent bootstrap closure. | Concurrent submissions create exactly one first Admin; restart keeps bootstrap closed. | `DONE` | platform_agent / 2026-08-15 | Bootstrap/restart/concurrency evidence approved at `692b23e9b9891d360882671d8247965b44920b2f`; fresh local bootstrap passed. |
|
||||||
|
| I1-PLAT-05 | Implement initial SMTP draft/test/active path sufficient for Mailpit onboarding. | Failed test cannot activate; tested revision supports delivery. | `DONE` | platform_agent / 2026-08-15 | Real local Mailpit draft, test delivery, activation, active-state, and health checks passed at the integrated head. |
|
||||||
|
| I1-PLAT-06 | Create Mentor/Intern accounts, deliver activation, set first password, and authenticate/logout. | SMTP gate, single-use token, expiry, normalized email, role/state access. | `DONE` | platform_agent / 2026-08-15 | Activation/authentication evidence and independent review approved at `692b23e9b9891d360882671d8247965b44920b2f`. |
|
||||||
|
| I1-PLAT-07 | Provide development Compose with PostgreSQL and Mailpit plus initial Gitea verification workflow. | Fresh developer start and branch/main verification. | `DEFERRED` | taskmaster / 2026-08-15 | Explicitly excluded from this exit gate; application containerization, Compose, and CI remain future work. |
|
||||||
|
|
||||||
|
### 4.2 `work/projects`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I1-PRJ-01 | Atomically create a Mentor-owned `PLANNED` Project, eligible initial Leader membership, and first leadership term. | No committed Project is empty or leaderless; non-owner, ineligible Leader, and guessed-ID access are denied. | `DONE` | projects_agent / 2026-08-15 | Approved Project head `baa0695c60153bc997ebf8b11adcfbdd2cbc1962`; Project suite and review passed. |
|
||||||
|
| I1-PRJ-02 | Directly add eligible Interns through owning-Mentor-controlled interval memberships. | Multiple concurrent Projects per Intern; duplicate active membership rejected; no acceptance step for direct add. | `DONE` | projects_agent / 2026-08-15 | Membership/eligibility/IDOR evidence approved at `baa0695c60153bc997ebf8b11adcfbdd2cbc1962`. |
|
||||||
|
| I1-PRJ-03 | Appoint and change one current Leader from active same-Project members. | One current Leader; non-member/ineligible selection rejected. | `DONE` | projects_agent / 2026-08-15 | Leadership-term invariants and completed-history behavior approved at `baa0695c60153bc997ebf8b11adcfbdd2cbc1962`. |
|
||||||
|
| I1-PRJ-04 | Activate a Project when initial member, Leader, date, and assignee guards pass. | Missing Leader/member or invalid assignee blocks activation. | `DONE` | projects_agent / 2026-08-15 | Task-bound activation guard approved at `baa0695c60153bc997ebf8b11adcfbdd2cbc1962`. |
|
||||||
|
| I1-PRJ-05 | Provide Project list/detail/member/leadership pages with owning-Mentor and member visibility. | MockMvc authorization plus direct-ID denial. | `DONE` | projects_agent / 2026-08-15 | Authorized pages, retained errors, and former-member history approved at `baa0695c60153bc997ebf8b11adcfbdd2cbc1962`. |
|
||||||
|
|
||||||
|
### 4.3 `work/attendance`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I1-ATT-01 | Resolve the seeded attendance policy, timezone, configured workdays, schedule, and separate check-in/checkout grace boundaries. | Both grace defaults are 30; values are 0–720; checkout cutoff must stay before local midnight. | `DONE` | attendance_agent / 2026-08-15 | Approved Attendance head `01b8095e9459417e2cf5bd1079c796d4f01ec549`; fresh seed verified both grace values at 30. |
|
||||||
|
| I1-ATT-02 | Manage manual future global calendar events and day-off decisions. | Admin-only mutation; past-event immutability; workday/day-off distinction. | `DONE` | attendance_agent / 2026-08-15 | Calendar authorization and day-off evidence approved at `01b8095e9459417e2cf5bd1079c796d4f01ec549`. |
|
||||||
|
| I1-ATT-03 | Check in once on an eligible day using server time and the effective policy. | Off-day, approved-leave, duplicate, lifecycle rejection, and inclusive 09:00 check-in-grace boundary. | `DONE` | attendance_agent / 2026-08-15 | Boundary, eligibility, leave-day, and real duplicate-race evidence approved at `01b8095e9459417e2cf5bd1079c796d4f01ec549`. |
|
||||||
|
| I1-ATT-04 | Check out once through the attached-policy checkout cutoff and derive basic daily classification. | Default 16:00 succeeds; first later instant and zero-grace late attempt fail; no checkout becomes only `MISSING_CHECKOUT` with raw checkout unchanged. | `DONE` | attendance_agent / 2026-08-15 | Historical-policy cutoff, eligibility recheck, and missing-checkout-only evidence approved at `01b8095e9459417e2cf5bd1079c796d4f01ec549`. |
|
||||||
|
| I1-ATT-05 | Provide own-attendance history and authorized Mentor/Admin inspection. | Own/global-view authorization and historical applied-policy display. | `DONE` | attendance_agent / 2026-08-15 | Policy-local history, all violations, and role authorization approved at `01b8095e9459417e2cf5bd1079c796d4f01ec549`. |
|
||||||
|
|
||||||
|
### 4.4 `work/tasks`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I1-TSK-01 | Create one-assignee Tasks in `PLANNED` or `ACTIVE`: any active member for self, current Leader for any active same-Project member. Store generic creator/assigner/assignee actors. | Self-Task actor equality, other-assignee denial for members, Leader allowance, and cross-Project actor rejection. | `DONE` | tasks_agent / 2026-08-15 | Approved Task head `e38e2cdea912160b183c65398c4e8d5682c1b00e`; actor/assignee/IDOR evidence passed review. |
|
||||||
|
| I1-TSK-02 | Validate optional due dates against Project dates and current global days off. | Boundary dates accepted; outside/day-off dates rejected. | `DONE` | tasks_agent / 2026-08-15 | Due-date/calendar validation and retained field-error behavior approved at `e38e2cdea912160b183c65398c4e8d5682c1b00e`. |
|
||||||
|
| I1-TSK-03 | Enforce the complete fixed Task status graph through current-assignee authorization. | Parameterized allowed/forbidden transition matrix and ID denial. | `DONE` | tasks_agent / 2026-08-15 | Server graph and legal UI choices approved at `e38e2cdea912160b183c65398c4e8d5682c1b00e`. |
|
||||||
|
| I1-TSK-04 | Add append-only Task comments for active members, current Leader, and owning Mentor. | Unauthorized/non-member and completed-Project mutation denial. | `DONE` | tasks_agent / 2026-08-15 | Comment authorization, lock order, and completed-history behavior approved at `e38e2cdea912160b183c65398c4e8d5682c1b00e`. |
|
||||||
|
| I1-TSK-05 | Show Task list/detail and initial DONE/non-deleted progress/status counts. | Empty Project renders `N/A`; soft/deleted data not yet exposed as current. | `DONE` | tasks_agent / 2026-08-15 | Progress/N/A, assignee display, visibility, and dashboard ordering approved at `e38e2cdea912160b183c65398c4e8d5682c1b00e`. |
|
||||||
|
|
||||||
|
### 4.5 `work/reports-ui`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I1-UI-01 | Establish Tailwind tokens and reusable Thymeleaf shell/fragments. | Fragment rendering, local assets, no unauthorized navigation items. | `DONE` | reports_ui_agent / 2026-08-15 | Shared local-asset shell and persistent SMTP restriction contract verified at `039fe25c7c2622a015c8962892dd99ff58be321d`. |
|
||||||
|
| I1-UI-02 | Implement the supported desktop sidebar/header, forms, tables, badges, alerts, confirmations, empty/error states, and theme bootstrap. | Keyboard labels/focus, no desktop page-level overflow, pre-paint theme application. | `DONE` | reports_ui_agent / 2026-08-15 | Edge/Chromium 1365x900 focus, collapse tooltip, theme pre-paint, and overflow gates verified at `039fe25c7c2622a015c8962892dd99ff58be321d`. |
|
||||||
|
| I1-UI-03 | Build basic Admin, Mentor, and Intern dashboards from real queries. | Role-correct metrics/actions; illustrative data never leaks into production paths. | `DONE` | reports_ui_agent / 2026-08-15 | Public service/DTO dashboards and role-correct navigation verified at `039fe25c7c2622a015c8962892dd99ff58be321d`. |
|
||||||
|
| I1-UI-04 | Integrate bootstrap, authentication, Project, Task, and attendance pages into the shared shell. | Critical MockMvc web flows and server-side authorization. | `DONE` | reports_ui_agent / 2026-08-15 | Integrated shell, SMTP five-step journey, forms, history, errors, and authorization verified at `039fe25c7c2622a015c8962892dd99ff58be321d`. |
|
||||||
|
|
||||||
|
### 4.6 Iteration 1 integration gate
|
||||||
|
|
||||||
|
Integration order:
|
||||||
|
|
||||||
|
1. `work/platform`
|
||||||
|
2. `work/projects`
|
||||||
|
3. `work/attendance`
|
||||||
|
4. `work/tasks`
|
||||||
|
5. `work/reports-ui`
|
||||||
|
|
||||||
|
Exit demonstration:
|
||||||
|
|
||||||
|
- First Admin bootstraps the installation.
|
||||||
|
- SMTP is tested through Mailpit.
|
||||||
|
- Admin creates and activates Mentor and Intern accounts.
|
||||||
|
- Mentor atomically creates a Project with its first Leader, directly adds another member, and activates it.
|
||||||
|
- Ordinary member creates a self-assigned Task; Leader creates and assigns another Task.
|
||||||
|
- Assignee changes Task status and comments.
|
||||||
|
- Intern checks in and checks out.
|
||||||
|
- Every role sees only authorized navigation, actions, and records.
|
||||||
|
- Full integrated tests pass at the iteration integration commit.
|
||||||
|
|
||||||
|
Taskmaster exit result (2026-08-15): `DONE` at main integration commit `b9b150ff8ca9333e3b46d77537ec91875a970d57`, incorporating reviewed candidate `039fe25c7c2622a015c8962892dd99ff58be321d` and the verified development configuration/documentation follow-up. The final merged-main PostgreSQL 18.4 suite passed 197/197; Flyway produced 23 application tables and 56 foreign keys; Node 24/Tailwind assets built successfully; compile and full Javadoc/doclint passed on the reviewed candidate. A real local Java 25 process completed fresh bootstrap, login, five-step SMTP deferral, persistent restriction recovery, and a separate real Mailpit draft/test/activate flow with aggregate health `UP`. Temporary exit databases and Mailpit were removed; the existing development PostgreSQL service remained intact. Project/Task/Attendance role flows are protected by the approved branch suites and real desktop E2E evidence. Application containerization, Compose, CI, and production liveness/readiness hardening remain deferred as recorded above.
|
||||||
|
|
||||||
|
## 5. Iteration 2 — Complete business workflows
|
||||||
|
|
||||||
|
Iteration 2 starts only after every continuing branch incorporates the integrated Iteration 1 `main`.
|
||||||
|
|
||||||
|
### 5.1 `work/platform`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I2-PLAT-01 | Complete account lock/unlock/deactivation and session invalidation. | State graph, authentication denial, retained attribution. | `TODO` | — | — |
|
||||||
|
| I2-PLAT-02 | Implement activation failure/resend and forgot/reset-password workflows. | Failed token invalidation, single-use/expiry, generic enumeration-safe responses. | `TODO` | — | — |
|
||||||
|
| I2-PLAT-03 | Implement internship start activation, completion, and withdrawal guards. | Scheduler plus request-time activation; Leader/unfinished-Task terminal guards. | `TODO` | — | — |
|
||||||
|
| I2-PLAT-04 | Complete encrypted SMTP and HolidayAPI draft/test/active revision lifecycles. | AES-GCM round trip, wrong-key failure, one active revision, no browser secret disclosure. | `TODO` | — | — |
|
||||||
|
| I2-PLAT-05 | Expose the tested VN HolidayAPI client to the attendance module. | Valid preview, invalid key/rate limit/unavailable responses without local-data outage. | `TODO` | — | — |
|
||||||
|
| I2-PLAT-06 | Persist in-app notifications and initial ordinary-email delivery states, including Project invitation and membership-exit created/resolved types. | Recipient deduplication, domain commit independent of SMTP, self-Task silence, and `UNAVAILABLE`/sent/failed behavior. | `TODO` | — | — |
|
||||||
|
|
||||||
|
### 5.2 `work/tasks`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I2-TSK-01 | Add dated 1–1440-minute Task work logs and author corrections. | Membership/date boundaries and author-only editing. | `TODO` | — | — |
|
||||||
|
| I2-TSK-02 | Enforce the combined 1440-minute daily total across all Projects. | PostgreSQL integration and concurrent over-allocation proof. | `TODO` | — | — |
|
||||||
|
| I2-TSK-03 | Reassign unfinished Tasks while preserving creator, state, comments, work logs, lifecycle timestamps, and assignment actor/time; exclude pending exit targets from new/self-assignment. | DONE requires assignee reopen; prior attribution remains; pending target keeps existing assignee rights but receives no new work. | `TODO` | — | — |
|
||||||
|
| I2-TSK-04 | Implement Task edit/soft deletion and authorized historical inspection for Leader/self-Task creator plus Project History projection. | Eligible creator controls only while current assignee; deleted/completed Tasks expose retained attribution without invented previous-assignee or edit/status timelines. | `TODO` | — | — |
|
||||||
|
| I2-TSK-05 | Provide repeatable multi-Task/one-recipient transfer batches, unfinished-count, direct-removal transfer, and completion-query operations to Projects. | Each batch is atomic; recipient/pending state rechecked; direct removal transfers all unfinished Tasks atomically; non-deleted/DONE counts remain correct. | `TODO` | — | — |
|
||||||
|
| I2-TSK-06 | Complete Project status counts, percentage, total minutes, and per-member work queries. | Empty `N/A`, authorization scopes, hand-checkable totals. | `TODO` | — | — |
|
||||||
|
|
||||||
|
### 5.3 `work/projects`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I2-PRJ-01 | Issue and revoke non-expiring Leader invitations while retaining the issuing leadership term. | Eligibility, one pending Project/Intern pair, Leader-own versus Mentor-any revocation, ordinary notification behavior. | `TODO` | — | — |
|
||||||
|
| I2-PRJ-02 | Let only the intended authenticated Intern accept or decline; support Mentor direct-add supersession. | Email is not a bearer join token; exactly one membership; terminal status/code and provenance retained. | `TODO` | — | — |
|
||||||
|
| I2-PRJ-03 | Create/cancel Leader-removal and member-leave requests, expose persistent readiness warnings, and keep existing rights while excluding the target from new/self-assignment. | Nonblank reason, same-Project/type shape, one pending request per target, requester-only cancellation, replacement/remaining/ready state for every authorized viewer. | `TODO` | — | — |
|
||||||
|
| I2-PRJ-04 | Let only the owning Mentor approve/reject exits after Leader-managed redistribution; preserve the direct-removal automatic-transfer shortcut. | Leader exit requires replacement first; repeated batches persist; cancel/reject keeps them and restores eligibility; approval waits for non-Leader/zero unfinished Tasks; closure/request resolution commit together. | `TODO` | — | — |
|
||||||
|
| I2-PRJ-05 | Retain leadership history, change Leader without moving assignments, and complete only when every non-deleted Task is `DONE`. | Exactly one current Leader in PLANNED/ACTIVE; completion closes intervals, revokes invitations, and supersedes exits. | `TODO` | — | — |
|
||||||
|
| I2-PRJ-06 | Provide one authorized Project History view for membership, leadership, invitation, exit decision, completed/soft-deleted Task, comment, work-log, and retained attribution data. | Admin all read-only; owning Mentor/current member on open Project; removed member denied until completion; no fabricated previous-assignee/status/edit timeline. | `TODO` | — | — |
|
||||||
|
|
||||||
|
### 5.4 `work/attendance`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I2-ATT-01 | Schedule future-month attendance-policy versions, including separate grace values, preserve effective history, and expose Admin-only Policy History. | First-of-future-month rule; effective immutability; old cutoff/report stability; safe version/actor/effective metadata only. | `TODO` | — | — |
|
||||||
|
| I2-ATT-02 | Preview/import VN HolidayAPI candidates with Admin selection, override, provenance, deduplication, and Admin-only Calendar History. | Public suggestion not authority; manual fallback; repeated import safety; past/current event metadata remains read-only and non-secret. | `TODO` | — | — |
|
||||||
|
| I2-ATT-03 | Materialize frozen full-day leave allocations and monthly/cross-month quota reservations. | Workday/day-off classification, policy snapshot, quota per month. | `TODO` | — | — |
|
||||||
|
| I2-ATT-04 | Implement leave submit/approve/reject/cancel and overlap protection. | Same-day boundary, pending/approved reservations, concurrent overlap/quota. | `TODO` | — | — |
|
||||||
|
| I2-ATT-05 | Implement missed-checkout correction submission and effective-checkout derivation. | Reject before/at checkout cutoff; accept afterward through scheduled end +24 hours; raw checkout remains null. | `TODO` | — | — |
|
||||||
|
| I2-ATT-06 | Implement Mentor approve/reject/revert and separate decision-window locking. | Valid state graph, concurrent decision, expired pending auto-rejection. | `TODO` | — | — |
|
||||||
|
| I2-ATT-07 | Add idempotent schedulers and equivalent request-time deadline guards. | Late/multiple scheduler invocation cannot duplicate transitions/notifications. | `TODO` | — | — |
|
||||||
|
|
||||||
|
### 5.5 `work/reports-ui`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I2-UI-01 | Complete Admin/Mentor/Intern/Leader dashboards and notification UI. | Authorization-correct actions and deadline/state summaries. | `TODO` | — | — |
|
||||||
|
| I2-UI-02 | Complete account, integration, Project, invitation, membership-exit/transfer, Task, policy/calendar, attendance, correction, leave, and History desktop workflows using shared fragments. | Persistent warning; Leader-only multi-Task/one-recipient drawer; Mentor readiness decision; Project History; Admin-only Policy/Calendar/SMTP/HolidayAPI History; keyboard operation, redaction, and clear conflicts. | `TODO` | — | — |
|
||||||
|
| I2-UI-03 | Build one authorized attendance/compliance HTML report dataset. | Date filters, detailed versus own scope, formulas and `N/A`. | `TODO` | — | — |
|
||||||
|
| I2-UI-04 | Build one authorized Project/Task HTML report dataset. | Project/member/status/date filters and per-member visibility rules. | `TODO` | — | — |
|
||||||
|
| I2-UI-05 | Add only meaningful Chart.js trends with adjacent text/table alternatives. | Accessible label, equivalent data, theme tokens, reduced motion. | `TODO` | — | — |
|
||||||
|
|
||||||
|
### 5.6 Iteration 2 integration gate
|
||||||
|
|
||||||
|
Integration order:
|
||||||
|
|
||||||
|
1. `work/platform`
|
||||||
|
2. `work/tasks`
|
||||||
|
3. `work/projects`
|
||||||
|
4. `work/attendance`
|
||||||
|
5. `work/reports-ui`
|
||||||
|
|
||||||
|
Exit demonstration:
|
||||||
|
|
||||||
|
- Admin schedules a future policy without changing historical output.
|
||||||
|
- Admin previews/imports holidays and overrides a suggested day-off decision.
|
||||||
|
- Intern submits cross-month leave and Mentor decides it.
|
||||||
|
- Intern submits a missed-checkout correction; Mentor decides and may revert it inside the window.
|
||||||
|
- Leader reassigns an unfinished Task while preserving creator, comments, work logs, and current assignment attribution.
|
||||||
|
- Leader invites an eligible Intern; the signed-in Intern accepts or declines; Mentor direct-add safely supersedes a pending invite.
|
||||||
|
- Mentor changes Leader without moving the former Leader's Tasks.
|
||||||
|
- Member requests to leave and Leader requests removal; all authorized viewers see readiness, replacement is appointed first when needed, Leader redistributes unfinished Tasks in repeated batches, cancellation/rejection keeps completed batches, and approval waits for zero unfinished Tasks.
|
||||||
|
- Mentor directly removes an ordinary member and a Leader in separate cases; automatic transfer remains atomic and completed Tasks keep the removed Intern's displayed name.
|
||||||
|
- Admin, owning Mentor, current member, and removed member before/after completion receive the exact Project History visibility defined by AUTH-006.
|
||||||
|
- Admin opens read-only Attendance Policy, Calendar, SMTP, and HolidayAPI History tabs; non-Admins are denied and no secret/internal retry data appears.
|
||||||
|
- Mentor completes a Project after all non-deleted Tasks are done.
|
||||||
|
- Ordinary domain actions retain in-app notifications when SMTP is unavailable.
|
||||||
|
- Full integrated tests pass at the iteration integration commit.
|
||||||
|
|
||||||
|
## 6. Iteration 3 — Hardening, reports, and delivery readiness
|
||||||
|
|
||||||
|
Iteration 3 starts only after every continuing branch incorporates the integrated Iteration 2 `main`.
|
||||||
|
|
||||||
|
### 6.1 `work/platform`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I3-PLAT-01 | Add normalized-email-plus-source-IP login throttling. | Five-in-15 and 15-minute throttle boundaries; successful-login clearing. | `TODO` | — | — |
|
||||||
|
| I3-PLAT-02 | Finish ordinary email retry schedule and terminal failure handling. | Exact 1m/5m/30m/2h/12h attempts; idempotent bounded worker. | `TODO` | — | — |
|
||||||
|
| I3-PLAT-03 | Add token cleanup and production-safe operational/status views. | Expired token behavior, no secret/stack/SQL disclosure. | `TODO` | — | — |
|
||||||
|
| I3-PLAT-04 | Enforce production HTTPS/origin/proxy/header/cookie/master-key readiness. | Prod fails unsafe configuration; dev/test relax only transport/origin controls. | `TODO` | — | — |
|
||||||
|
| I3-PLAT-05 | Produce the non-root application image and bundled/external PostgreSQL deployment modes. | Same immutable image becomes healthy in both configurations. | `TODO` | — | — |
|
||||||
|
| I3-PLAT-06 | Finalize Gitea verification, main-only OCI publication, and disabled SSH deployment/rollback template. | Work branches never publish; disabled deploy receives no secrets; exact-SHA flow is testable when enabled. | `TODO` | — | — |
|
||||||
|
|
||||||
|
### 6.2 `work/projects`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I3-PRJ-01 | Harden concurrent membership, invitation acceptance/direct-add, exit approval, and leadership operations. | One valid winner; locks/rechecks cover invitation, Project, membership, leadership, request, and unfinished Tasks; stale requests produce explicit conflict without partial history. | `TODO` | — | — |
|
||||||
|
| I3-PRJ-02 | Complete the global-role/context/ownership authorization matrix for direct membership, invitations, exits, and leadership. | Direct-ID, stale Leader, wrong invitee, cross-Mentor/member, requester, target, and decision-maker negative cases. | `TODO` | — | — |
|
||||||
|
| I3-PRJ-03 | Prove completed/historical read-only behavior and terminal Intern guards. | No mutation through UI or direct request after lifecycle closure. | `TODO` | — | — |
|
||||||
|
| I3-PRJ-04 | Verify Project list/progress query indexes and bounded performance. | Explain plan/catalog evidence for actual report paths. | `TODO` | — | — |
|
||||||
|
|
||||||
|
### 6.3 `work/tasks`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I3-TSK-01 | Harden concurrent status, reassignment, deletion, and daily-minute operations. | Optimistic conflicts and serialized daily total. | `TODO` | — | — |
|
||||||
|
| I3-TSK-02 | Complete due-date impact behavior for later-created global days off. | Existing due date retained and disclosed; new/changed due date rejected. | `TODO` | — | — |
|
||||||
|
| I3-TSK-03 | Complete former-assignee work-log correction and historical-deletion boundaries. | Author/member/Project lifecycle matrix. | `TODO` | — | — |
|
||||||
|
| I3-TSK-04 | Complete Task authorization/ID-guessing matrix and progress query verification. | Admin/Mentor/Leader/member/creator/current-assignee distinctions, creator-right loss after reassignment, generic same-Project actors, and real query indexes. | `TODO` | — | — |
|
||||||
|
|
||||||
|
### 6.4 `work/attendance`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I3-ATT-01 | Finalize attendance-rate and compliance formulas plus `N/A` denominators. | Hand-derived expected values across absence, leave, days off, and violations. | `TODO` | — | — |
|
||||||
|
| I3-ATT-02 | Prove historical stability after workday, schedule, check-in/checkout grace, quota, penalty, and calendar changes. | Before/after report equality plus unchanged cutoff for an older attendance row. | `TODO` | — | — |
|
||||||
|
| I3-ATT-03 | Harden concurrent leave quota/overlap and correction-decision races. | PostgreSQL exclusion plus transactional locking/optimistic conflicts. | `TODO` | — | — |
|
||||||
|
| I3-ATT-04 | Prove request-time and scheduler equivalence at checkout/correction boundaries and both expiry windows. | Pre-cutoff correction rejection; inclusive submission deadline; delayed/repeated scheduler produces one final transition. | `TODO` | — | — |
|
||||||
|
| I3-ATT-05 | Complete terminal-Intern and date-classification edge cases. | Terminal date with/without attendance, approved leave, and global day off. | `TODO` | — | — |
|
||||||
|
|
||||||
|
### 6.5 `work/reports-ui`
|
||||||
|
|
||||||
|
| ID | Deliverable | Test/evidence emphasis | Status | Owner/date | Result/commit |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| I3-UI-01 | Export attendance/compliance and Project/Task datasets to XLSX. | Parse workbook and compare filters, rows, totals, and `N/A` with HTML. | `TODO` | — | — |
|
||||||
|
| I3-UI-02 | Export the same datasets to PDF through a print-safe template and embedded Unicode font. | Vietnamese sample text and HTML/XLSX/PDF total parity. | `TODO` | — | — |
|
||||||
|
| I3-UI-03 | Complete desktop light/dark themes and all required desktop screens/states. | Theme before paint, system override, contrast, error/empty/stale/unavailable states. | `TODO` | — | — |
|
||||||
|
| I3-UI-04 | Complete WCAG-focused keyboard, focus, labels, icon names, and chart alternatives. | Web/accessibility evidence for representative critical pages. | `TODO` | — | — |
|
||||||
|
| I3-UI-05 | Add critical end-to-end flows across the integrated application. | Bootstrap/onboarding, Project/Task, attendance/correction/leave, and reports. | `TODO` | — | — |
|
||||||
|
| I3-UI-06 | Perform best-effort narrow-screen smoke checks only. | Prevent catastrophic corruption where practical; no mobile parity or mobile mockup gate. | `TODO` | — | — |
|
||||||
|
|
||||||
|
### 6.6 Iteration 3 integration gate
|
||||||
|
|
||||||
|
Integration order:
|
||||||
|
|
||||||
|
1. `work/platform`
|
||||||
|
2. `work/projects`, `work/tasks`, and `work/attendance` after their platform dependencies are available
|
||||||
|
3. `work/reports-ui`
|
||||||
|
|
||||||
|
Exit demonstration:
|
||||||
|
|
||||||
|
- Historical results and checkout cutoffs remain stable after later policy/calendar changes.
|
||||||
|
- Concurrent bootstrap, invitation/direct-add, membership exit/transfer, leadership, Task, leave, and correction operations fail safely.
|
||||||
|
- HTML, XLSX, and PDF expose identical authorized totals.
|
||||||
|
- Production refuses unsafe origin, proxy, datasource, or master-key configuration.
|
||||||
|
- The same non-root image works with bundled and external PostgreSQL.
|
||||||
|
- Work branches verify without publishing; only `main` publishes immutable SHA and convenience tags.
|
||||||
|
- SSH deployment remains dormant without secrets or an explicit enable variable.
|
||||||
|
- Desktop light/dark and accessibility acceptance passes; mobile/tablet remains best-effort only.
|
||||||
|
- Full integrated tests pass at the final integration commit.
|
||||||
|
|
||||||
|
## 7. Mandatory TDD workflow
|
||||||
|
|
||||||
|
Every feature follows this sequence:
|
||||||
|
|
||||||
|
1. Select a requirement and acceptance scenario.
|
||||||
|
2. Write the smallest behavioral test.
|
||||||
|
3. Run it and confirm the intended RED failure.
|
||||||
|
4. Record the RED command/result in the appropriate evidence file.
|
||||||
|
5. Write the minimum production code required for GREEN and add its meaningful Javadoc in the same implementation milestone.
|
||||||
|
6. Run the focused test.
|
||||||
|
7. Run the affected module/integration/web suite.
|
||||||
|
8. Refactor without weakening assertions.
|
||||||
|
9. Run the affected suite again.
|
||||||
|
10. Record final commands/results and commit SHA.
|
||||||
|
|
||||||
|
Recommended history:
|
||||||
|
|
||||||
|
```text
|
||||||
|
test(attendance): prove 09:00 check-in grace boundary [RED]
|
||||||
|
feat(attendance): enforce inclusive check-in grace boundary [GREEN]
|
||||||
|
```
|
||||||
|
|
||||||
|
The RED and GREEN commits may be pushed together after the branch head is green. The failing historical commit proves test-first order without leaving the remote branch intentionally broken.
|
||||||
|
|
||||||
|
Required evidence locations:
|
||||||
|
|
||||||
|
| Test level | Evidence directory |
|
||||||
|
|---|---|
|
||||||
|
| Unit/state/calculation | `docs/tests/unit/` |
|
||||||
|
| PostgreSQL/module integration | `docs/tests/integration/` |
|
||||||
|
| MockMvc/Thymeleaf/security web behavior | `docs/tests/web/` |
|
||||||
|
| Cross-module/browser journey | `docs/tests/e2e/` |
|
||||||
|
|
||||||
|
Every evidence Markdown record must include:
|
||||||
|
|
||||||
|
- requirement and scenario IDs;
|
||||||
|
- protected behavior and why it matters;
|
||||||
|
- test method and hand-derived expected result;
|
||||||
|
- exact RED command and relevant failure;
|
||||||
|
- exact GREEN and affected-suite commands/results;
|
||||||
|
- external dependency or environment boundaries;
|
||||||
|
- final commit SHA when available.
|
||||||
|
|
||||||
|
Javadoc is part of production implementation, not a later documentation phase. Every new or materially changed production type and every declared public/protected method shall document its business contract, including non-obvious authorization, transaction/locking, lifecycle/history, unit, timezone, or deadline semantics. Do not add prose that merely repeats names. Iteration 1 alone may retrofit Javadocs after feature implementation is complete; those branch-owned retrofit commits still require affected verification and independent scoped re-review. Every later iteration and turn shall add/update Javadocs during the implementation milestone.
|
||||||
|
|
||||||
|
## 8. Branch-level test emphasis
|
||||||
|
|
||||||
|
| Branch | Non-negotiable evidence |
|
||||||
|
|---|---|
|
||||||
|
| `work/platform` | Bootstrap concurrency, token lifecycle, account/security authorization, encryption, SMTP failure, session invalidation, production-profile failure. |
|
||||||
|
| `work/projects` | Lifecycle graphs, ownership, membership/invitation/exit/leadership intervals, transfer transactions, optimistic locking, guessed-ID denial. |
|
||||||
|
| `work/tasks` | Parameterized status graph, Leader/member creator/assignee distinction, self-Task and same-Project generic actors, daily-minute concurrency, progress totals. |
|
||||||
|
| `work/attendance` | Injected-Clock boundaries, PostgreSQL overlap/uniqueness, policy history, quota concurrency, schedulers and request-time guards. |
|
||||||
|
| `work/reports-ui` | MockMvc forms/authorization, accessible rendering, report query totals, XLSX/PDF parsing/parity, critical browser journeys. |
|
||||||
|
|
||||||
|
## 9. Iteration handoff protocol
|
||||||
|
|
||||||
|
At each iteration boundary:
|
||||||
|
|
||||||
|
1. Each branch owner updates every claimed item to `DONE`, `BLOCKED`, or returns it to `TODO`.
|
||||||
|
2. The owner provides commit SHA, evidence paths, exact verification commands, and remaining risk.
|
||||||
|
3. Integrate branches in the iteration's declared order.
|
||||||
|
4. Resolve cross-module conflicts through the owning branch rather than duplicating code in the integrator.
|
||||||
|
5. Run the full affected integrated suite.
|
||||||
|
6. Record the integration commit in Section 3.
|
||||||
|
7. Bring the integrated `main` into all five persistent work branches before the next iteration begins.
|
||||||
|
|
||||||
|
Handoff template:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### <tracker ID> — <short title>
|
||||||
|
|
||||||
|
- Status: DONE | BLOCKED | TODO
|
||||||
|
- Owner:
|
||||||
|
- Requirements/scenarios:
|
||||||
|
- RED evidence:
|
||||||
|
- GREEN evidence:
|
||||||
|
- Focused verification:
|
||||||
|
- Affected-suite verification:
|
||||||
|
- Commit SHA:
|
||||||
|
- Remaining risk/blocker:
|
||||||
|
- Required next owner/action:
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. Global definition of done
|
||||||
|
|
||||||
|
A tracker item is complete only when:
|
||||||
|
|
||||||
|
- the numbered requirement and acceptance behavior are satisfied;
|
||||||
|
- the test existed and failed for the intended reason before production code;
|
||||||
|
- focused and affected suites pass;
|
||||||
|
- authorization and negative cases are covered where applicable;
|
||||||
|
- PostgreSQL-specific rules are tested against PostgreSQL, not H2;
|
||||||
|
- concurrency/deadline/history behavior has proportionate evidence;
|
||||||
|
- UI behavior uses server-side authorization and shared fragments;
|
||||||
|
- documentation/evidence paths are recorded in this tracker;
|
||||||
|
- new or changed production types and public/protected methods have accurate Javadoc created during implementation (Iteration 1 retrofit exception only);
|
||||||
|
- no unrelated files or another branch's ownership area were changed without coordination;
|
||||||
|
- the final branch head is green;
|
||||||
|
- integration does not alter totals, state graphs, or historical meaning.
|
||||||
|
|
||||||
|
## 11. Progress log
|
||||||
|
|
||||||
|
Append material coordination events only. Do not duplicate every commit.
|
||||||
|
|
||||||
|
| Date/time | Agent/person | Event | Tracker IDs | Evidence/commit | Next action |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| — | — | Plan initialized; no implementation item claimed. | — | — | Obtain requirements approval and begin Iteration 1. |
|
||||||
|
| 2026-08-15 | Taskmaster + five feature owners | Retrofitted targeted Lombok boilerplate after every owner fast-forwarded to the latest `main`; all five exact heads passed independent review and the combined merge passed 201 PostgreSQL 18.4 tests, compile, Javadoc/doclint, deterministic assets, and final integration review. | Iteration 1 maintenance | Platform `c9499732`; Project `58712710`; Task `469d7e27`; Attendance `5ddbd75f`; Reporting `8500cb6e`; integrated `b764707716f54ad164547b087f658c277cf46e0f`; `docs/tests/unit/lombok-*-boilerplate.md` | Push reviewed `main`, then fast-forward all five persistent work branches before Iteration 2 work. |
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
---
|
||||||
|
name: orchestrate-labtimesheet-iteration
|
||||||
|
description: Use when initiating, resuming, integrating, or completing a multi-branch Lab Timesheet iteration defined by .agents/PROJECT_PLAN.md.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Orchestrate a Lab Timesheet Iteration
|
||||||
|
|
||||||
|
Run the iteration continuously from repository audit through an evidence-backed local exit demonstration. Preserve the five long-lived work branches and keep business features isolated behind public services and DTOs.
|
||||||
|
|
||||||
|
## Load authority and preserve state
|
||||||
|
|
||||||
|
1. Read the repository `AGENTS.md`, `.agents/PROJECT_PLAN.md`, `PRODUCT.md`, `DEVELOPMENT.md`, `TESTING.md`, and the applicable numbered requirements in `labtimesheet-docs-hub/requirements-specification.md`.
|
||||||
|
2. Apply authority in this order: current user decisions, numbered requirements, approved plan, tests/code, then inference.
|
||||||
|
3. Inspect every worktree and the root `git status` before mutation. Record and preserve unrelated dirty or untracked files.
|
||||||
|
4. Confirm the actual Maven, Spring Boot, Java, Node, PostgreSQL, Flyway, and Testcontainers versions. Use the documented project baseline even if a newer JDK is installed.
|
||||||
|
5. Create or update the active goal and plan only when the user requests goal-mode execution.
|
||||||
|
|
||||||
|
## Enforce the project structure
|
||||||
|
|
||||||
|
Keep `LabtimesheetApplication` in `com.lab.labtimesheet`, shared wiring in `config`, and business code under:
|
||||||
|
|
||||||
|
```text
|
||||||
|
feature.account
|
||||||
|
feature.integration
|
||||||
|
feature.project
|
||||||
|
feature.task
|
||||||
|
feature.attendance
|
||||||
|
feature.notification
|
||||||
|
feature.reporting
|
||||||
|
```
|
||||||
|
|
||||||
|
Repeat only the layers a feature needs: `controller`, `model`, `model.dto`, `model.entity`, `repository`, `service`, and `exception`. Mirror these packages in tests.
|
||||||
|
|
||||||
|
- Call another feature only through its concrete public service methods and DTOs.
|
||||||
|
- Never import another feature's repository or JPA entity.
|
||||||
|
- Use Spring Data JPA for business persistence.
|
||||||
|
- Limit direct SQL to Flyway and schema/catalog verification.
|
||||||
|
- Do not add empty `common`, `core`, `utils`, or boundary-placeholder packages.
|
||||||
|
- Do not add one-implementation interfaces, shadow mappings of foreign tables, or speculative abstractions.
|
||||||
|
|
||||||
|
Use the installed Lombok processor as the default for safe Java boilerplate:
|
||||||
|
|
||||||
|
- `@RequiredArgsConstructor` for injection-only constructors with required final dependencies;
|
||||||
|
- targeted `@Getter`, `@Setter`, and protected `@NoArgsConstructor` instead of blanket `@Data`;
|
||||||
|
- no generated JPA entity `equals`, `hashCode`, or `toString` over mutable state, associations, or secrets;
|
||||||
|
- records remain records for immutable DTOs and commands;
|
||||||
|
- explicit constructors and methods remain when they validate, normalize, enforce invariants, preserve history, define identity, or select qualified dependencies.
|
||||||
|
|
||||||
|
Require each owner to audit its production package, record a source-contract RED for eligible handwritten boilerplate, make the smallest behavior-preserving Lombok conversion, and prove compile/Javadoc, Spring injection, JPA mapping, template property access, and affected behavior remain green. Do not add annotations that generate unused API.
|
||||||
|
|
||||||
|
## Establish the shared baseline first
|
||||||
|
|
||||||
|
Before dispatching feature work:
|
||||||
|
|
||||||
|
1. Verify the Maven wrapper, application profile, Flyway baseline, PostgreSQL connectivity, test-only clock/encryption, and frontend asset toolchain.
|
||||||
|
2. Run a clean baseline test and capture any pre-existing failure separately.
|
||||||
|
3. Run the application locally against a disposable or dedicated PostgreSQL service. A PostgreSQL or Mailpit test container is acceptable; do not containerize the application when the exit gate says local process.
|
||||||
|
4. Commit the shared platform foundation before dependent branches use it.
|
||||||
|
5. Hand off only full immutable commit SHAs from clean worktrees.
|
||||||
|
|
||||||
|
## Own five branches and worktrees
|
||||||
|
|
||||||
|
Use exactly these persistent branches unless the user changes the plan:
|
||||||
|
|
||||||
|
| Branch | Ownership |
|
||||||
|
|---|---|
|
||||||
|
| `work/platform` | Baseline, migration, accounts, security, bootstrap, integrations, notification plumbing |
|
||||||
|
| `work/projects` | Projects, membership, leadership, lifecycle, project authorization |
|
||||||
|
| `work/tasks` | Tasks, comments, work logs, status, task progress |
|
||||||
|
| `work/attendance` | Policy, calendar, attendance, corrections, leave, metrics |
|
||||||
|
| `work/reports-ui` | Shared Thymeleaf UI, dashboards, reports, exports |
|
||||||
|
|
||||||
|
For a targeted repair outside the next iteration, create a clean isolated
|
||||||
|
`work/fix/<feature>/<what-fix>` worktree from the taskmaster-verified current
|
||||||
|
`main`. Do not use `work/<feature>/fix/<what-fix>`: the persistent
|
||||||
|
`work/<feature>` ref already occupies that Git ref prefix.
|
||||||
|
|
||||||
|
Create one isolated worktree per branch. Give each implementation agent explicit ownership, tell it other agents share the repository, forbid reverting others' work, require medium-milestone local commits, and forbid push unless separately authorized.
|
||||||
|
|
||||||
|
Before any owner edits its module, require it to:
|
||||||
|
|
||||||
|
1. verify its worktree has no uncommitted changes;
|
||||||
|
2. confirm the taskmaster's exact latest `main` SHA;
|
||||||
|
3. fast-forward its persistent work branch to that SHA with `git merge --ff-only main`;
|
||||||
|
4. prove `git rev-parse HEAD` equals the supplied main SHA;
|
||||||
|
5. stop rather than resolving unexpected divergence or overwriting user work.
|
||||||
|
|
||||||
|
Store durable coordination under `.superpowers/sdd/PROJECT_PLAN/`:
|
||||||
|
|
||||||
|
- `progress.md` with exact SHAs, dependency pins, tests, blockers, and review rounds;
|
||||||
|
- one task report per branch;
|
||||||
|
- review packages and reviewer reports.
|
||||||
|
|
||||||
|
Trust this ledger and Git history after context compaction. Never redispatch a completed milestone.
|
||||||
|
|
||||||
|
## Require RED, GREEN, Javadoc, and companion evidence
|
||||||
|
|
||||||
|
For every behavior:
|
||||||
|
|
||||||
|
1. Read its requirement and acceptance scenario IDs.
|
||||||
|
2. Write the narrowest production-shaped test first.
|
||||||
|
3. Run it and record RED for the expected missing behavior, not an environment or test defect.
|
||||||
|
4. Implement the minimum coherent change and its meaningful Javadoc in the same milestone. Document every new or materially changed production type and declared public/protected method, emphasizing business contracts, authorization, transactions/locking, state/history semantics, units, and time boundaries rather than restating names.
|
||||||
|
5. Run focused GREEN, affected-suite verification, then the branch-wide suite.
|
||||||
|
6. Refactor only while tests remain green.
|
||||||
|
7. Commit when a medium milestone is complete.
|
||||||
|
|
||||||
|
Use PostgreSQL 18.4 Testcontainers for persistence semantics; do not substitute H2. Keep time and randomness controllable. Test public outcomes, persisted state, authorization denial, and boundary cases.
|
||||||
|
|
||||||
|
Create a Markdown evidence record under the matching directory:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/tests/unit/
|
||||||
|
docs/tests/integration/
|
||||||
|
docs/tests/web/
|
||||||
|
docs/tests/e2e/
|
||||||
|
```
|
||||||
|
|
||||||
|
Start from that directory's `_TEMPLATE.md`. Every record must include requirement/scenario IDs, protected behavior, test method, hand-derived expected result, exact RED command/result, exact GREEN and affected-suite commands/results, implementation milestone, and external-test boundaries. Never replace executable evidence with a verbal claim.
|
||||||
|
|
||||||
|
Iteration 1 is the only retrofit exception: after all feature tasks finish, return each branch to its original owner to add missing Javadocs before integration, rerun affected verification, commit, and undergo scoped re-review. In every later iteration or turn, reject delayed Javadoc cleanup; it belongs in the implementation milestone.
|
||||||
|
|
||||||
|
## Merge dependencies by immutable pin
|
||||||
|
|
||||||
|
Derive the dependency graph from the current iteration plan. For Iteration 1, use:
|
||||||
|
|
||||||
|
```text
|
||||||
|
platform foundation
|
||||||
|
-> projects base
|
||||||
|
-> attendance
|
||||||
|
-> tasks (projects + attendance public APIs)
|
||||||
|
-> projects activation guard (final Task query API)
|
||||||
|
-> reports-ui (final public dashboard APIs)
|
||||||
|
```
|
||||||
|
|
||||||
|
- A producer reports a clean full SHA and public API before a consumer merges it.
|
||||||
|
- Never merge a moving branch or a short ambiguous SHA.
|
||||||
|
- Preserve dirty consumer work during the merge and immediately rerun its structure test.
|
||||||
|
- If a consumer needs data, add the smallest producer-owned public query DTO/service method; never map or query the producer's tables locally.
|
||||||
|
- Keep final integration order from `.agents/PROJECT_PLAN.md` even when development dependencies require a temporary producer/base round trip.
|
||||||
|
- Reuse the last reviewed consumer branch as the integrated candidate when it already contains every approved producer pin in order. An extra integration branch adds no safety by itself.
|
||||||
|
- After a Platform merge, run the architecture test and every `@WebMvcTest` controller slice. Global advice and shared beans can invalidate otherwise unrelated slice fixtures; fix only the test fixture, never production security to accommodate a slice.
|
||||||
|
|
||||||
|
## Coordinate agents without losing control
|
||||||
|
|
||||||
|
Let the five owners work in parallel where dependencies permit. Resolve normal in-scope questions without pausing the run. Stop only for a genuine blocker, consequential ambiguity, destructive action, or sensitive external effect requiring user approval.
|
||||||
|
|
||||||
|
For a sensitive action, state the exact effect and obtain approval in the root task. If a child cannot inherit that approval, the root may apply only the isolated approved patch; return the worktree to its owner for tests, evidence, self-review, and commit.
|
||||||
|
|
||||||
|
Do not accept an agent's completion claim alone. Require:
|
||||||
|
|
||||||
|
- exact branch and full SHA;
|
||||||
|
- clean worktree;
|
||||||
|
- focused, affected, and full test counts;
|
||||||
|
- confirmation that new/changed production APIs carry accurate Javadoc;
|
||||||
|
- a list of Lombok conversions and explicit boilerplate deliberately retained with its business reason;
|
||||||
|
- evidence/report path;
|
||||||
|
- public API handoff;
|
||||||
|
- blockers and unverified boundaries;
|
||||||
|
- confirmation of no push.
|
||||||
|
|
||||||
|
## Review only after all five owners finish
|
||||||
|
|
||||||
|
After every implementation owner reports `DONE`, dispatch independent code-review assignments, one branch per assignment. Give each reviewer the plan/requirements paths, branch report, ledger, merge base, full diff package, and exact structure/TDD constraints.
|
||||||
|
|
||||||
|
Each review must return both spec-compliance and code-quality verdicts with file/line evidence. For Critical or Important findings:
|
||||||
|
|
||||||
|
1. Send the complete finding list back to that branch's original owner.
|
||||||
|
2. Require a focused regression test, RED when applicable, GREEN, affected suite, evidence update, and a fix commit.
|
||||||
|
3. Dispatch an independent scoped re-review of only the fix range.
|
||||||
|
4. Repeat up to five rounds; use a fresh stronger fixer for rounds four and five.
|
||||||
|
5. At the cap, record a reasoned ruling for non-load-bearing findings or stop on a load-bearing blocker.
|
||||||
|
|
||||||
|
Do not merge a branch with unresolved load-bearing findings.
|
||||||
|
|
||||||
|
## Prove the iteration exit gate
|
||||||
|
|
||||||
|
Integrate only reviewed immutable branch SHAs in the plan's order. Then verify from the integrated tree:
|
||||||
|
|
||||||
|
1. `git diff --check` and package/JPA architecture tests.
|
||||||
|
2. Flyway replay against fresh PostgreSQL and the required table/foreign-key catalogue checks.
|
||||||
|
3. Full Maven tests on the documented Java version and PostgreSQL engine.
|
||||||
|
4. Frontend asset build on the pinned Node version.
|
||||||
|
5. A local application process connected to the configured PostgreSQL service.
|
||||||
|
6. Health/readiness and the iteration's real role-correct web workflow.
|
||||||
|
7. No application containerization when explicitly deferred.
|
||||||
|
8. Root dirty-state preservation, clean reviewed worktrees, and no unauthorized push.
|
||||||
|
|
||||||
|
Keep environment authority explicit:
|
||||||
|
|
||||||
|
- real `.env` files are local and ignored;
|
||||||
|
- `.env.example` contains placeholders only;
|
||||||
|
- the development Spring profile maps every required runtime value from the environment;
|
||||||
|
- Admin-console SMTP and HolidayAPI secrets never move into `.env`;
|
||||||
|
- configuration-only work uses a shell/configuration RED and real application smoke test instead of an artificial Java unit test.
|
||||||
|
|
||||||
|
If SMTP participates in health, exercise both states deliberately: application startup with SMTP deferred, and aggregate health with a real temporary Mailpit connection. Report the distinction instead of calling one state universally healthy.
|
||||||
|
|
||||||
|
Store cross-module browser journeys in `docs/tests/e2e/`. Before completion, update every Iteration row and the durable progress ledger; stale `IN_PROGRESS` rows are an incomplete gate even when tests pass.
|
||||||
|
|
||||||
|
Review source Javadocs against behavior as part of the branch and integration diff; stale or content-free Javadoc does not satisfy the gate.
|
||||||
|
|
||||||
|
## Merge reviewed Iteration work to main
|
||||||
|
|
||||||
|
Only enter this phase with explicit user authority.
|
||||||
|
|
||||||
|
1. Add any final environment example, profile configuration, implementation-status README, development guide, and testing/TDD guide to the integrated candidate under RED/GREEN evidence.
|
||||||
|
2. Run the complete PostgreSQL suite and a real local-process smoke test on that exact candidate.
|
||||||
|
3. Fetch the remote base. Stop on unexpected divergence; never force-push to hide it.
|
||||||
|
4. Preserve unrelated dirty root files. Commit only separately authorized tracked root guidance before merging if the incoming branch also changes that file.
|
||||||
|
5. Merge the exact candidate into `main` without squashing or rewriting the reviewed branch history.
|
||||||
|
6. Rerun the full suite on merged `main`, inspect the diff, and verify `.env` is ignored and absent from the index.
|
||||||
|
7. Push `main` normally, verify the remote ref equals local `HEAD`, and keep host-managed worktrees unless cleanup was explicitly requested.
|
||||||
|
|
||||||
|
Record the integration SHA, commands, outputs, limitations, and deferred next-iteration scope. Mark the goal complete only after every stated exit condition has fresh evidence.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "Orchestrate Lab Timesheet Iteration"
|
||||||
|
short_description: "Run five-branch TDD and Javadoc delivery"
|
||||||
|
default_prompt: "Use $orchestrate-labtimesheet-iteration to execute the current iteration across the five work branches with TDD, Javadoc, and review gates."
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
.git
|
||||||
|
.gitea
|
||||||
|
.idea
|
||||||
|
.agents
|
||||||
|
.superpowers
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.compose.example
|
||||||
|
labtimesheet-docs-hub
|
||||||
|
node_modules
|
||||||
|
target
|
||||||
|
docs
|
||||||
|
src/test
|
||||||
|
*.log
|
||||||
|
*.7z
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Production Compose example only. Store the real file outside the repository with mode 0600.
|
||||||
|
# Use an immutable sha-<full-commit> tag. The moving main tag is for convenience, not rollback.
|
||||||
|
LAB_IMAGE=git.sechmachine.io.vn/sechmachine/labtimesheet:sha-replace-with-full-commit
|
||||||
|
|
||||||
|
# The app is intended to sit behind an HTTPS reverse proxy on the same host.
|
||||||
|
LAB_HTTP_BIND=127.0.0.1
|
||||||
|
LAB_HTTP_PORT=8080
|
||||||
|
LAB_PUBLIC_ORIGIN=https://timesheet.example.edu
|
||||||
|
LAB_FORWARD_HEADERS_STRATEGY=framework
|
||||||
|
LAB_SECURITY_MASTER_KEY=replace-with-base64-encoded-32-byte-key
|
||||||
|
|
||||||
|
# Bundled mode uses the Compose service name. For external mode, replace this URL and credentials.
|
||||||
|
LAB_DB_URL=jdbc:postgresql://postgres:5432/labtimesheet
|
||||||
|
LAB_DB_USERNAME=labtimesheet
|
||||||
|
LAB_DB_PASSWORD=replace-with-database-password
|
||||||
|
|
||||||
|
# Used only when the bundled-db profile is enabled.
|
||||||
|
POSTGRES_DB=labtimesheet
|
||||||
|
POSTGRES_USER=labtimesheet
|
||||||
|
POSTGRES_PASSWORD=replace-with-the-same-database-password
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Copy to .env and replace every placeholder. The dev profile imports it from the repository root.
|
||||||
|
SPRING_PROFILES_ACTIVE=dev
|
||||||
|
LAB_SERVER_PORT=8080
|
||||||
|
LAB_FORWARD_HEADERS_STRATEGY=NONE
|
||||||
|
|
||||||
|
LAB_DB_URL=jdbc:postgresql://localhost:55432/labtimesheet
|
||||||
|
LAB_DB_USERNAME=labtimesheet
|
||||||
|
LAB_DB_PASSWORD=replace-with-local-database-password
|
||||||
|
|
||||||
|
LAB_SMTP_HOST=localhost
|
||||||
|
LAB_SMTP_PORT=1025
|
||||||
|
|
||||||
|
LAB_PUBLIC_ORIGIN=http://localhost:8080
|
||||||
|
LAB_SECURITY_MASTER_KEY=replace-with-base64-encoded-32-byte-key
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
name: Container
|
||||||
|
|
||||||
|
'on':
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: container-${{ gitea.workflow }}-${{ gitea.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: git.sechmachine.io.vn
|
||||||
|
IMAGE_NAME: sechmachine/labtimesheet
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
env:
|
||||||
|
TESTCONTAINERS_HOST_OVERRIDE: host.docker.internal
|
||||||
|
steps:
|
||||||
|
- name: Check out source
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up Java 25
|
||||||
|
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: '25'
|
||||||
|
cache: maven
|
||||||
|
|
||||||
|
- name: Set up Node 24
|
||||||
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
cache: npm
|
||||||
|
|
||||||
|
- name: Verify Docker for PostgreSQL tests
|
||||||
|
run: docker info
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run frontend tests
|
||||||
|
run: npm run test:ui
|
||||||
|
|
||||||
|
- name: Build frontend assets
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Verify generated assets are committed
|
||||||
|
run: git diff --exit-code -- src/main/resources/static/assets/app.css src/main/resources/static/assets/icons.svg
|
||||||
|
|
||||||
|
- name: Run Maven tests
|
||||||
|
run: ./mvnw -B test
|
||||||
|
|
||||||
|
- name: Verify Javadoc
|
||||||
|
run: ./mvnw -B -DskipTests -Ddoclint=all javadoc:javadoc
|
||||||
|
|
||||||
|
- name: Verify whitespace
|
||||||
|
run: git diff --check
|
||||||
|
|
||||||
|
amd64:
|
||||||
|
needs: verify
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Check out source
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up Buildx
|
||||||
|
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||||
|
|
||||||
|
- name: Select image tags
|
||||||
|
id: image
|
||||||
|
env:
|
||||||
|
EVENT_NAME: ${{ gitea.event_name }}
|
||||||
|
GITEA_REF: ${{ gitea.ref }}
|
||||||
|
GITEA_SHA: ${{ gitea.sha }}
|
||||||
|
run: |
|
||||||
|
publish=false
|
||||||
|
image="$REGISTRY/$IMAGE_NAME"
|
||||||
|
if [ "$EVENT_NAME" = "push" ] && [ "$GITEA_REF" = "refs/heads/main" ]; then
|
||||||
|
publish=true
|
||||||
|
fi
|
||||||
|
{
|
||||||
|
echo "publish=$publish"
|
||||||
|
echo "image=$image"
|
||||||
|
echo "tags<<EOF"
|
||||||
|
echo "$image:sha-${GITEA_SHA}-amd64"
|
||||||
|
if [ "$publish" = "true" ]; then
|
||||||
|
echo "$image:sha-${GITEA_SHA}"
|
||||||
|
echo "$image:main"
|
||||||
|
fi
|
||||||
|
echo "EOF"
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Log in to registry
|
||||||
|
if: steps.image.outputs.publish == 'true'
|
||||||
|
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ gitea.actor }}
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build AMD64 image and publish main
|
||||||
|
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: ${{ steps.image.outputs.publish }}
|
||||||
|
tags: ${{ steps.image.outputs.tags }}
|
||||||
|
build-args: |
|
||||||
|
VCS_REF=${{ gitea.sha }}
|
||||||
|
|
||||||
|
arm64:
|
||||||
|
# Gitea cannot schedule a probe on a missing label. Enable this repository variable only
|
||||||
|
# while a trusted ubuntu-latest-arm runner is registered and online.
|
||||||
|
if: vars.ARM64_RUNNER_AVAILABLE == 'true'
|
||||||
|
needs: verify
|
||||||
|
runs-on: ubuntu-latest-arm
|
||||||
|
timeout-minutes: 30
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Check out source
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up Buildx
|
||||||
|
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||||
|
|
||||||
|
- name: Select image tag
|
||||||
|
id: image
|
||||||
|
env:
|
||||||
|
EVENT_NAME: ${{ gitea.event_name }}
|
||||||
|
GITEA_REF: ${{ gitea.ref }}
|
||||||
|
GITEA_SHA: ${{ gitea.sha }}
|
||||||
|
run: |
|
||||||
|
publish=false
|
||||||
|
image="$REGISTRY/$IMAGE_NAME"
|
||||||
|
if [ "$EVENT_NAME" = "push" ] && [ "$GITEA_REF" = "refs/heads/main" ]; then
|
||||||
|
publish=true
|
||||||
|
fi
|
||||||
|
echo "publish=$publish" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "tag=$image:sha-${GITEA_SHA}-arm64" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Log in to registry
|
||||||
|
if: steps.image.outputs.publish == 'true'
|
||||||
|
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ gitea.actor }}
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build native ARM64 image
|
||||||
|
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/arm64
|
||||||
|
push: ${{ steps.image.outputs.publish }}
|
||||||
|
tags: ${{ steps.image.outputs.tag }}
|
||||||
|
build-args: |
|
||||||
|
VCS_REF=${{ gitea.sha }}
|
||||||
|
|
||||||
|
manifest:
|
||||||
|
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main' && vars.ARM64_RUNNER_AVAILABLE == 'true'
|
||||||
|
needs: [amd64, arm64]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Set up Buildx
|
||||||
|
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||||
|
|
||||||
|
- name: Log in to registry
|
||||||
|
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ gitea.actor }}
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Publish combined manifest
|
||||||
|
env:
|
||||||
|
IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
GITEA_SHA: ${{ gitea.sha }}
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools create \
|
||||||
|
--tag "$IMAGE:sha-${GITEA_SHA}" \
|
||||||
|
--tag "$IMAGE:main" \
|
||||||
|
"$IMAGE:sha-${GITEA_SHA}-amd64" \
|
||||||
|
"$IMAGE:sha-${GITEA_SHA}-arm64"
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
name: Verify
|
||||||
|
|
||||||
|
'on':
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: verify-${{ gitea.workflow }}-${{ gitea.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
env:
|
||||||
|
TESTCONTAINERS_HOST_OVERRIDE: host.docker.internal
|
||||||
|
steps:
|
||||||
|
- name: Check out source
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up Java 25
|
||||||
|
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: '25'
|
||||||
|
cache: maven
|
||||||
|
|
||||||
|
- name: Set up Node 24
|
||||||
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
cache: npm
|
||||||
|
|
||||||
|
- name: Verify Docker for PostgreSQL tests
|
||||||
|
run: docker info
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run frontend tests
|
||||||
|
run: npm run test:ui
|
||||||
|
|
||||||
|
- name: Build frontend assets
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Verify generated assets are committed
|
||||||
|
run: git diff --exit-code -- src/main/resources/static/assets/app.css src/main/resources/static/assets/icons.svg
|
||||||
|
|
||||||
|
- name: Run Maven tests
|
||||||
|
run: ./mvnw -B test
|
||||||
|
|
||||||
|
- name: Verify Javadoc
|
||||||
|
run: ./mvnw -B -DskipTests -Ddoclint=all javadoc:javadoc
|
||||||
|
|
||||||
|
- name: Verify whitespace
|
||||||
|
run: git diff --check
|
||||||
+4
-9
@@ -3,6 +3,7 @@ target/
|
|||||||
.mvn/wrapper/maven-wrapper.jar
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
!**/src/main/**/target/
|
!**/src/main/**/target/
|
||||||
!**/src/test/**/target/
|
!**/src/test/**/target/
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
### STS ###
|
### STS ###
|
||||||
.apt_generated
|
.apt_generated
|
||||||
@@ -28,17 +29,11 @@ target/
|
|||||||
build/
|
build/
|
||||||
!**/src/main/**/build/
|
!**/src/main/**/build/
|
||||||
!**/src/test/**/build/
|
!**/src/test/**/build/
|
||||||
|
node_modules/
|
||||||
|
/.env
|
||||||
|
|
||||||
### VS Code ###
|
### VS Code ###
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
### Agentic ###
|
|
||||||
.agents/
|
|
||||||
.agent/
|
|
||||||
AGENTS.md
|
|
||||||
|
|
||||||
# Local requirements review artifacts
|
# Local requirements review artifacts
|
||||||
/labtimesheet-docs-hub/
|
/labtimesheet-docs-hub/
|
||||||
|
|
||||||
# Local Impeccable product context
|
|
||||||
/PRODUCT.md
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"northStar": "The Calm Operations Ledger",
|
||||||
|
"rules": [
|
||||||
|
"Neutral structure carries the interface; semantic color is rare.",
|
||||||
|
"Use border and tone before shadow.",
|
||||||
|
"Put one role-correct task and action in the first viewport.",
|
||||||
|
"Desktop is supported; mobile and tablet are best-effort."
|
||||||
|
],
|
||||||
|
"shadows": {
|
||||||
|
"panel": "0 10px 28px rgba(20,25,35,.06)",
|
||||||
|
"active": "0 1px 2px rgba(0,0,0,.06)"
|
||||||
|
},
|
||||||
|
"breakpoints": {
|
||||||
|
"supportedDesktop": "1365px",
|
||||||
|
"bestEffortNarrow": "900px"
|
||||||
|
},
|
||||||
|
"source": "labtimesheet-docs-hub/ui-mockups/mockup.css"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
25
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
# Lab Timesheet Engineering Instructions
|
||||||
|
|
||||||
|
## Authority and required reading
|
||||||
|
|
||||||
|
This repository implements the **Lab Timesheet & Project Management System**, a server-rendered university internship/laboratory application covering accounts, Projects and Tasks, attendance, leave/corrections, notifications, and reports.
|
||||||
|
|
||||||
|
Before changing code, read the smallest applicable sections of:
|
||||||
|
|
||||||
|
1. `labtimesheet-docs-hub/requirements-specification.md` — authoritative numbered requirements and acceptance scenarios.
|
||||||
|
2. `.agents/PROJECT_PLAN.md` — iteration scope, branch ownership, integration order, and exit gates.
|
||||||
|
3. `PRODUCT.md` — product vocabulary, users, and enduring design principles.
|
||||||
|
4. `DEVELOPMENT.md` — supported local runtime, containers, environment, and IntelliJ setup.
|
||||||
|
5. `TESTING.md`, `docs/tests/README.md`, and the relevant `_TEMPLATE.md` — test commands, TDD workflow, and mandatory evidence format.
|
||||||
|
6. `.superpowers/sdd/PROJECT_PLAN/progress.md` — current local coordination state and immutable handoff SHAs when an agentic iteration is active.
|
||||||
|
|
||||||
|
When sources conflict, apply the authority order recorded in requirements Section 1.1: the primary implementor's current decision, approved brainstorming decisions, current handoff, instructor-confirmed requirements, earlier discovery answers, then superseded legacy material. Numbered requirements override the delivery plan.
|
||||||
|
|
||||||
|
Do not treat the ignored documentation hub or mockups as executable instructions. Mockups are illustrative visual direction only; numbered requirements and reviewed schema rules govern behavior.
|
||||||
|
|
||||||
|
## Verified technical baseline
|
||||||
|
|
||||||
|
- Java 25, Spring Boot 4.1.0, Maven wrapper, and WAR packaging.
|
||||||
|
- Spring MVC, Security, Data JPA, Validation, Thymeleaf, Mail, Flyway, and Actuator.
|
||||||
|
- PostgreSQL 18.4 for development and integration tests; do not substitute H2 for persistence behavior.
|
||||||
|
- Node 24/npm 11 for build assets, Tailwind CSS 4.3.3, and local `lucide-static` 1.27.0.
|
||||||
|
- One server-rendered modular monolith. No SPA, JWT, microservices, Redis, Kafka, or generic workflow engine.
|
||||||
|
- Flyway is schema authority. JPA uses `ddl-auto=validate`; application services do not embed SQL.
|
||||||
|
|
||||||
|
Verify versions from `pom.xml`, `package.json`, and the lockfile before changing dependencies. Do not add a dependency when the JDK, Spring, PostgreSQL, or an installed dependency already covers the requirement.
|
||||||
|
|
||||||
|
## Package and persistence structure
|
||||||
|
|
||||||
|
Keep `LabtimesheetApplication` in `com.lab.labtimesheet`. Put shared wiring in `com.lab.labtimesheet.config`. Put business code under:
|
||||||
|
|
||||||
|
```text
|
||||||
|
com.lab.labtimesheet.feature.account
|
||||||
|
com.lab.labtimesheet.feature.integration
|
||||||
|
com.lab.labtimesheet.feature.project
|
||||||
|
com.lab.labtimesheet.feature.task
|
||||||
|
com.lab.labtimesheet.feature.attendance
|
||||||
|
com.lab.labtimesheet.feature.notification
|
||||||
|
com.lab.labtimesheet.feature.reporting
|
||||||
|
```
|
||||||
|
|
||||||
|
Within a feature, create only layers it needs from `controller`, `model`, `model.dto`, `model.entity`, `repository`, `service`, and `exception`. Mirror this shape in tests.
|
||||||
|
|
||||||
|
- Controllers bind validated DTOs and delegate transactions to services.
|
||||||
|
- Services use their feature's Spring Data JPA repositories and models.
|
||||||
|
- Cross-feature calls use concrete public services and DTOs only.
|
||||||
|
- Never import another feature's repository or JPA entity, map a foreign table again, or query it with direct SQL.
|
||||||
|
- Direct SQL is limited to Flyway and schema/catalog verification.
|
||||||
|
- Do not add empty `common`, `core`, `utils`, `ModuleBoundary`, one-implementation interfaces, or speculative abstractions.
|
||||||
|
- Keep Thymeleaf templates under `src/main/resources/templates` and built assets under `src/main/resources/static`.
|
||||||
|
|
||||||
|
Preserve the domain boundaries: attendance time never derives Task work time; historical policies, memberships, leadership, creator/assignee attribution, and decisions do not silently move when current configuration changes.
|
||||||
|
|
||||||
|
Membership-exit implementation must preserve two distinct paths. A pending exit keeps membership and existing Task rights active but excludes the target from new/self-assignment; the current/new Leader performs repeatable atomic multi-Task/one-recipient transfer batches before Mentor approval, and cancellation/rejection does not undo completed batches. Direct Mentor removal remains the atomic shortcut that transfers all unfinished Tasks to the current or replacement Leader. Completed Tasks keep the removed member's historical assignee name.
|
||||||
|
|
||||||
|
History UI must read the retained feature-owned rows already present. Project History covers memberships, leadership, invitations, exit decisions, completed/soft-deleted Tasks, comments, work logs, and stored attribution under AUTH-006 visibility. Admin-only Policy, Calendar, SMTP, and HolidayAPI History exposes non-secret domain metadata only. Do not add a generic audit/event-sourcing layer, Task-assignment-history table, or fabricated previous-assignee/status/edit timeline.
|
||||||
|
|
||||||
|
## Lombok is the default for Java boilerplate
|
||||||
|
|
||||||
|
Lombok is already installed and configured as an annotation processor. Use it by default when it removes mechanical Java without hiding a business rule.
|
||||||
|
|
||||||
|
- Use `@RequiredArgsConstructor` for Spring controllers, services, configuration classes, and other components whose constructor only assigns required `final` dependencies. Keep an explicit constructor when it validates input, transforms data, selects among same-typed beans, or documents a non-trivial public contract.
|
||||||
|
- Use targeted annotations such as `@Getter`, `@Setter`, `@NoArgsConstructor`, and `@AllArgsConstructor`; use the smallest set that matches the actual API. Do not use `@Data` as a blanket shortcut.
|
||||||
|
- For JPA entities, never let Lombok generate `equals`, `hashCode`, or `toString` across entities, lazy associations, mutable fields, or encrypted secrets. Prefer `@Getter` and `@NoArgsConstructor(access = AccessLevel.PROTECTED)`, keep domain constructors and mutation methods explicit, and add individual setters only when a framework genuinely needs them.
|
||||||
|
- Keep Java records for immutable DTOs and commands. Replacing a record with a Lombok class creates more code and is not an improvement.
|
||||||
|
- Use `@Slf4j` only when the class actually logs. Do not add builders, withers, or generated setters speculatively.
|
||||||
|
- Do not retain handwritten constructors, getters, setters, `equals`, `hashCode`, or `toString` that are purely mechanical and safely covered by the targeted Lombok annotation. Preserve explicit methods that enforce invariants, normalize values, maintain history, or define identity semantics.
|
||||||
|
- After a Lombok refactor, inspect the generated API contract, run compile/Javadoc plus the affected tests, and confirm JPA mappings, Spring injection, Thymeleaf property access, serialization, and security-sensitive redaction remain unchanged.
|
||||||
|
|
||||||
|
This rule applies during implementation, not as deferred cleanup. A source-audit RED may prove existing eligible boilerplate before a behavior-preserving Lombok refactor; the GREEN gate is the same public behavior with less handwritten code.
|
||||||
|
|
||||||
|
## Javadoc is part of implementation
|
||||||
|
|
||||||
|
Add meaningful Javadoc while implementing production Java code, in the same milestone and before its final GREEN/commit.
|
||||||
|
|
||||||
|
- Document every new or materially changed production type and every public or protected method declared in source.
|
||||||
|
- Explain business purpose and non-obvious contracts: authorization/context requirements, transaction or locking behavior, state transitions, history retention, side effects, units, timezone/deadline boundaries, and null/empty semantics.
|
||||||
|
- Keep inherited Javadoc for a true override when it fully describes the contract. Generated Lombok methods, trivial accessors, and tests do not need duplicate prose.
|
||||||
|
- Do not write comments that merely restate names or implementation steps. If a contract cannot be explained clearly, simplify the code or clarify the requirement.
|
||||||
|
- Update Javadoc whenever behavior changes; stale Javadoc is a defect.
|
||||||
|
|
||||||
|
Iteration 1 is the one approved retrofit exception: feature owners add missing Javadocs after their implementation tasks finish, then rerun affected verification and undergo scoped re-review. Every later iteration and turn must add Javadocs during implementation, not as cleanup.
|
||||||
|
|
||||||
|
## Mandatory TDD and evidence
|
||||||
|
|
||||||
|
Use strict RED → GREEN → affected-suite verification → refactor:
|
||||||
|
|
||||||
|
1. Select requirement and acceptance-scenario IDs.
|
||||||
|
2. Write the smallest production-shaped failing test.
|
||||||
|
3. Run it and prove the RED is the missing behavior, not a broken fixture or environment.
|
||||||
|
4. Record the exact RED command/result in the matching evidence file.
|
||||||
|
5. Implement the minimum behavior and its Javadoc.
|
||||||
|
6. Run focused GREEN, then the affected suite; refactor only while green.
|
||||||
|
7. Update evidence with exact commands/results and external boundaries.
|
||||||
|
8. Commit a medium-sized green milestone locally.
|
||||||
|
|
||||||
|
Evidence belongs under:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/tests/unit/
|
||||||
|
docs/tests/integration/
|
||||||
|
docs/tests/web/
|
||||||
|
docs/tests/e2e/
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy the directory's `_TEMPLATE.md`; do not invent a second format. PostgreSQL-specific behavior uses PostgreSQL 18.4 Testcontainers. Security, ownership, concurrency, deadlines, and history require negative and boundary tests proportionate to risk.
|
||||||
|
|
||||||
|
## Five-branch ownership and subagent workflow
|
||||||
|
|
||||||
|
The persistent implementation branches are:
|
||||||
|
|
||||||
|
| Branch | Primary ownership |
|
||||||
|
|---|---|
|
||||||
|
| `work/platform` | Maven/app baseline, Flyway, accounts/security/bootstrap, integrations/notifications, container and CI assets |
|
||||||
|
| `work/projects` | Projects, membership/leadership intervals, invitations/exits, lifecycle, Project authorization |
|
||||||
|
| `work/tasks` | Tasks, actor/assignee rules, comments, work logs, status, progress |
|
||||||
|
| `work/attendance` | Policy/calendar, attendance, corrections, leave, schedulers, metrics |
|
||||||
|
| `work/reports-ui` | Shared Thymeleaf UI, dashboards, reports/exports, UI/accessibility consistency |
|
||||||
|
|
||||||
|
For a multi-branch iteration:
|
||||||
|
|
||||||
|
- Use one worktree and one named owner/subagent per branch. Tell every owner that other agents share the repository and it must not revert others' work.
|
||||||
|
- Before starting assigned module work, every owner verifies its worktree is clean, fetches or uses the taskmaster-verified latest `main`, and fast-forwards its persistent branch to that exact main SHA. Do not build new work on a stale pre-integration branch, and do not use a merge that would rewrite or discard branch history.
|
||||||
|
- A targeted repair uses a clean, isolated `work/fix/<feature>/<what-fix>` branch and worktree from the taskmaster-verified current `main`. Do not use `work/<feature>/fix/<what-fix>`: the persistent `work/<feature>` ref already occupies that Git ref prefix.
|
||||||
|
- Every targeted repair starts from the taskmaster-verified latest `main`, uses TDD RED → GREEN, adds Javadoc during implementation, records companion evidence, undergoes independent review, and uses a normal, non-force merge only when separately authorized.
|
||||||
|
- Establish and commit the platform foundation before dependent persistence work.
|
||||||
|
- Exchange only full immutable SHAs from clean worktrees; never merge a moving branch or ambiguous short SHA.
|
||||||
|
- Preserve branch ownership. Request a producer-owned service/DTO boundary instead of reading its tables from a consumer.
|
||||||
|
- Commit each medium green milestone locally. Do not push, publish, deploy, force, rewrite history, or merge to `main` without explicit authority.
|
||||||
|
- After all five owners report DONE, run independent read-only reviews of every branch. Return Critical/Important findings to the original owner with a regression test where applicable, GREEN evidence, a fix commit, and scoped re-review. Do not integrate unresolved load-bearing findings.
|
||||||
|
- Integrate reviewed exact heads only in the current iteration's order from `.agents/PROJECT_PLAN.md`, then run the full integrated exit gate.
|
||||||
|
|
||||||
|
Keep durable coordination under `.superpowers/sdd/PROJECT_PLAN/`: progress ledger, branch reports, review findings, immutable SHAs, commands/results, blockers, and integration evidence. Do not redispatch completed milestones after context compaction.
|
||||||
|
|
||||||
|
## Local commands and runtime
|
||||||
|
|
||||||
|
Default development expects PostgreSQL on port `55432`; override with `LAB_DB_URL`, `LAB_DB_USERNAME`, and `LAB_DB_PASSWORD`. SMTP defaults to localhost Mailpit port `1025` and can be overridden with `LAB_SMTP_HOST`/`LAB_SMTP_PORT`. Never commit real secrets.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw test
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
./mvnw spring-boot:run
|
||||||
|
```
|
||||||
|
|
||||||
|
When using local OrbStack Testcontainers, set the actual Docker socket for that machine. Tests must not depend on the developer database or a real SMTP server.
|
||||||
|
|
||||||
|
Before completion, run focused tests, the affected suite, the full suite appropriate to the branch, frontend build when assets changed, `git diff --check`, and an adversarial diff review. The integrated iteration additionally requires Flyway/PostgreSQL validation and a real local Java process connected to PostgreSQL. Production container checks supplement those gates; they do not substitute for them.
|
||||||
|
|
||||||
|
The root `Dockerfile`, `compose.yaml`, and `.env.compose.example` are production-only. Development runs Java from the IDE or Maven as documented in `DEVELOPMENT.md`. Only `main` may publish container images. Native ARM64 publication is gated by the repository variable `ARM64_RUNNER_AVAILABLE`; leave it absent or false unless a trusted `ubuntu-latest-arm` runner is online.
|
||||||
|
|
||||||
|
## Post-iteration integration and push
|
||||||
|
|
||||||
|
- Use the final reviewed consumer branch as the integration candidate when it already contains every approved producer SHA in plan order; do not create an extra integration branch without a concrete need.
|
||||||
|
- Keep real `.env` files untracked. Commit only `.env.example` placeholders and environment-backed Spring profile configuration. SMTP and HolidayAPI credentials managed by the Admin console do not belong in `.env`.
|
||||||
|
- Keep `README.md`, `DEVELOPMENT.md`, and `TESTING.md` aligned with the merged application. Document only commands and workflows that were exercised or directly verified.
|
||||||
|
- Configuration and README changes made after feature review still require a configuration-contract RED, focused GREEN, the complete PostgreSQL suite, `git diff --check`, and a local-process smoke test before merging.
|
||||||
|
- A global MVC advice or shared configuration bean can affect every `@WebMvcTest` slice. After merging Platform changes, run all controller slices and add only the missing test fixture bean; do not weaken the production advice.
|
||||||
|
- Record real cross-module browser journeys under `docs/tests/e2e/`, not `docs/tests/web/`, and update `.agents/PROJECT_PLAN.md` plus the progress ledger before declaring the iteration complete.
|
||||||
|
- Before merging to `main`, fetch its upstream and stop if the remote moved unexpectedly. Preserve unrelated root changes, stage only authorized paths, merge without rewriting history, rerun the full suite on the exact merged tree, then push normally and verify the remote SHA.
|
||||||
|
- Worktrees under `/private/tmp` are host-managed. Keep the five branch worktrees and branches after integration unless the user explicitly requests cleanup.
|
||||||
|
|
||||||
|
## Safety and scope
|
||||||
|
|
||||||
|
- Inspect `git status` before editing and preserve unrelated dirty/untracked files.
|
||||||
|
- Use server time and an injectable `Clock` for deadline behavior; never trust browser event timestamps.
|
||||||
|
- Keep CSRF, authorization, password hashing, validation, and ownership checks active in every environment.
|
||||||
|
- Never print, persist, or return raw activation/reset tokens except the approved immediate delivery path; persist only their hashes.
|
||||||
|
- Desktop is the supported UI target. Mobile responsiveness is best-effort and has no mockup/parity gate.
|
||||||
|
- Iteration scope is exact. Leave later-iteration capabilities TODO rather than adding placeholders or partial frameworks.
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Production Container Deployment
|
||||||
|
|
||||||
|
These files deploy Lab Timesheet in production. They are not the development workflow; continue using [DEVELOPMENT.md](DEVELOPMENT.md) for IDE work.
|
||||||
|
|
||||||
|
## 1. Prepare the host
|
||||||
|
|
||||||
|
Install Docker Engine and Docker Compose v2.20 or newer. Put an HTTPS reverse proxy in front of the application. By default, Compose binds the application only to `127.0.0.1:8080`.
|
||||||
|
|
||||||
|
Copy [`.env.compose.example`](.env.compose.example) to a protected path outside the repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo install -d -m 0700 /etc/labtimesheet
|
||||||
|
sudo install -m 0600 .env.compose.example /etc/labtimesheet/compose.env
|
||||||
|
sudo editor /etc/labtimesheet/compose.env
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate `LAB_SECURITY_MASTER_KEY` with `openssl rand -base64 32`. Use an immutable `sha-<full-commit>` application image tag. Never place Admin-managed SMTP or HolidayAPI credentials in this file.
|
||||||
|
|
||||||
|
## 2. Choose the database topology
|
||||||
|
|
||||||
|
### Bundled PostgreSQL 18.4
|
||||||
|
|
||||||
|
Keep the example JDBC host `postgres`, then run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --env-file /etc/labtimesheet/compose.env --profile bundled-db up -d
|
||||||
|
docker compose --env-file /etc/labtimesheet/compose.env ps
|
||||||
|
```
|
||||||
|
|
||||||
|
The application waits for PostgreSQL health and stores database files in the `postgres_data` named volume.
|
||||||
|
|
||||||
|
### External PostgreSQL
|
||||||
|
|
||||||
|
Set `LAB_DB_URL`, `LAB_DB_USERNAME`, and `LAB_DB_PASSWORD` for the external database. Do not enable the `bundled-db` profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --env-file /etc/labtimesheet/compose.env up -d app
|
||||||
|
docker compose --env-file /etc/labtimesheet/compose.env ps
|
||||||
|
```
|
||||||
|
|
||||||
|
The same application image is used in both modes.
|
||||||
|
|
||||||
|
## 3. Health and operation
|
||||||
|
|
||||||
|
- Liveness: `GET /actuator/health/liveness`
|
||||||
|
- Readiness: `GET /actuator/health/readiness` (includes PostgreSQL)
|
||||||
|
- Logs: `docker compose --env-file /etc/labtimesheet/compose.env logs -f app`
|
||||||
|
|
||||||
|
The container runs as UID/GID `10001`, with a read-only root filesystem, no Linux capabilities, and only `/tmp` writable. TLS termination is intentionally outside this Compose example.
|
||||||
|
|
||||||
|
Back up PostgreSQL with database-aware tooling such as `pg_dump`. The named volume survives container replacement, but it is not a backup. Test restore procedures before upgrades.
|
||||||
|
|
||||||
|
To update or roll back, change `LAB_IMAGE` to the required immutable SHA tag and run `docker compose ... up -d` again. Keep the previous SHA recorded until the new image is healthy.
|
||||||
|
|
||||||
|
## 4. Gitea Actions setup
|
||||||
|
|
||||||
|
Configure these repository settings:
|
||||||
|
|
||||||
|
| Kind | Name | Value |
|
||||||
|
|---|---|---|
|
||||||
|
| Variable | `ARM64_RUNNER_AVAILABLE` | `true` only while a trusted `ubuntu-latest-arm` runner is registered and online; otherwise omit it or set `false` |
|
||||||
|
| Secret | `REGISTRY_TOKEN` | Token for the triggering Gitea account with package read/write access |
|
||||||
|
|
||||||
|
The workflow publishes `git.sechmachine.io.vn/sechmachine/labtimesheet` and authenticates as the triggering Gitea account. `verify.yml` runs for every pull request and push. `container.yml` runs only when manually dispatched or when `main` is pushed, and it repeats verification before either architecture build. Manual runs build without publishing. A push to `main` publishes immutable `sha-<commit>` and convenience `main` tags.
|
||||||
|
|
||||||
|
When ARM64 is disabled, those canonical tags remain valid AMD64 images and the workflow succeeds. When it is enabled, the native ARM runner publishes an architecture tag and the final job replaces the canonical tags with a combined AMD64/ARM64 manifest. Gitea cannot discover an unavailable runner from inside an unscheduled job, so the repository variable is the deliberate availability gate.
|
||||||
|
|
||||||
|
The workflows stop at verification and image publication. They do not contain SSH deployment or receive host deployment secrets.
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
---
|
||||||
|
name: Lab Timesheet
|
||||||
|
description: A quiet, high-density operations system for internship attendance and Project work.
|
||||||
|
colors:
|
||||||
|
ink: "#15171a"
|
||||||
|
canvas: "#f6f7f8"
|
||||||
|
sidebar: "#f0f1f2"
|
||||||
|
panel: "#ffffff"
|
||||||
|
panel-muted: "#f7f8f9"
|
||||||
|
border: "#dfe1e5"
|
||||||
|
border-strong: "#c9cdd3"
|
||||||
|
text-muted: "#626a75"
|
||||||
|
text-subtle: "#818894"
|
||||||
|
accent: "#3157e7"
|
||||||
|
success: "#087a48"
|
||||||
|
warning: "#996000"
|
||||||
|
danger: "#b42318"
|
||||||
|
typography:
|
||||||
|
headline:
|
||||||
|
fontFamily: "ui-sans-serif, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
|
||||||
|
fontSize: "25px"
|
||||||
|
fontWeight: 700
|
||||||
|
lineHeight: 1.2
|
||||||
|
letterSpacing: "-0.025em"
|
||||||
|
body:
|
||||||
|
fontFamily: "ui-sans-serif, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
|
||||||
|
fontSize: "14px"
|
||||||
|
fontWeight: 400
|
||||||
|
lineHeight: 1.45
|
||||||
|
label:
|
||||||
|
fontFamily: "ui-sans-serif, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
|
||||||
|
fontSize: "11px"
|
||||||
|
fontWeight: 650
|
||||||
|
lineHeight: 1.45
|
||||||
|
rounded:
|
||||||
|
control: "8px"
|
||||||
|
tab: "9px"
|
||||||
|
surface: "12px"
|
||||||
|
dialog: "14px"
|
||||||
|
spacing:
|
||||||
|
xs: "4px"
|
||||||
|
sm: "8px"
|
||||||
|
md: "16px"
|
||||||
|
lg: "24px"
|
||||||
|
components:
|
||||||
|
button-primary:
|
||||||
|
backgroundColor: "{colors.ink}"
|
||||||
|
textColor: "{colors.panel}"
|
||||||
|
rounded: "{rounded.control}"
|
||||||
|
padding: "8px 13px"
|
||||||
|
height: "37px"
|
||||||
|
input:
|
||||||
|
backgroundColor: "{colors.panel}"
|
||||||
|
textColor: "{colors.ink}"
|
||||||
|
rounded: "{rounded.control}"
|
||||||
|
padding: "9px 10px"
|
||||||
|
height: "39px"
|
||||||
|
panel:
|
||||||
|
backgroundColor: "{colors.panel}"
|
||||||
|
textColor: "{colors.ink}"
|
||||||
|
rounded: "{rounded.surface}"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Design System: Lab Timesheet
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
**Creative North Star: "The Calm Operations Ledger"**
|
||||||
|
|
||||||
|
Lab Timesheet is a permission-aware operations workspace. It favors legible state, compact controls, clear ownership, and reviewable records over decorative dashboard theater. The Vercel/shadcn-style reference is translated into server-rendered Thymeleaf surfaces with a stable shell and one role-correct task per page.
|
||||||
|
|
||||||
|
The visual system is quiet but not empty: thin structure, disciplined spacing, tabular data, and rare semantic color make consequential actions and deadlines easy to find. Example content must always be identified as illustrative.
|
||||||
|
|
||||||
|
**Key Characteristics:**
|
||||||
|
|
||||||
|
- Cool near-white surfaces with charcoal text.
|
||||||
|
- Fixed desktop sidebar and compact page header.
|
||||||
|
- Dense tables and direct forms as the main working surfaces.
|
||||||
|
- Black primary actions; color reserved for focus, status, warning, and error.
|
||||||
|
- Rounded corners and restrained ambient shadow, never floating card mosaics.
|
||||||
|
|
||||||
|
## Colors
|
||||||
|
|
||||||
|
The light palette uses cool neutral layers; dark mode must be designed from the supplied near-black references rather than mechanically inverted from these values.
|
||||||
|
|
||||||
|
### Primary
|
||||||
|
|
||||||
|
- **Operational Ink** (`#15171a`): primary text, brand mark, and primary actions.
|
||||||
|
- **Controlled Accent** (`#3157e7`): focus, selected data, and rare contextual emphasis.
|
||||||
|
|
||||||
|
### Neutral
|
||||||
|
|
||||||
|
- **Work Canvas** (`#f6f7f8`): page background.
|
||||||
|
- **Navigation Shell** (`#f0f1f2`): desktop sidebar.
|
||||||
|
- **Record Surface** (`#ffffff`): forms, tables, panels, and active navigation.
|
||||||
|
- **Muted Surface** (`#f7f8f9`): headers, tabs, and supporting rows.
|
||||||
|
- **Ledger Border** (`#dfe1e5`): default one-pixel structure.
|
||||||
|
- **Control Border** (`#c9cdd3`): inputs and action outlines.
|
||||||
|
- **Muted Text** (`#626a75`) and **Subtle Text** (`#818894`): secondary and tertiary copy.
|
||||||
|
|
||||||
|
### Semantic
|
||||||
|
|
||||||
|
- **Success** (`#087a48`), **Warning** (`#996000`), and **Danger** (`#b42318`) communicate state with text and shape, never color alone.
|
||||||
|
|
||||||
|
**The Rare Color Rule.** Neutral structure carries the interface. Semantic and accent colors appear only when they clarify state, focus, validation, or a consequential decision.
|
||||||
|
|
||||||
|
## Typography
|
||||||
|
|
||||||
|
**Display and Body Font:** the local system sans stack (`ui-sans-serif`, platform UI fonts, `Segoe UI`, sans-serif). No remote font is required.
|
||||||
|
|
||||||
|
**Character:** compact, familiar, and operational. Weight and spacing establish hierarchy without oversized marketing display type.
|
||||||
|
|
||||||
|
### Hierarchy
|
||||||
|
|
||||||
|
- **Page headline** (700, `25px`, 1.2): one per screen.
|
||||||
|
- **Panel title** (600–700, `14px`): names the current dataset or decision surface.
|
||||||
|
- **Body** (400, `14px`, 1.45): instructions and explanatory copy, normally no wider than 72ch.
|
||||||
|
- **Data** (500–650, `12px`): dense tables and facts; numeric summaries use tabular numerals.
|
||||||
|
- **Label** (650, `11px`): fields and supporting metadata.
|
||||||
|
- **Navigation group label** (750, `10px`, uppercase, `0.08em`): rare structural labels only.
|
||||||
|
|
||||||
|
**The One Headline Rule.** Each screen gets one page headline; hierarchy below it is compact and task-oriented.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
Desktop is the supported product target. The shell uses a 236px sidebar and a minimum-width content column, with a 60px header and 24–26px content inset. The implementation target may round the sidebar to approximately 16rem and its collapsed rail to approximately 4rem.
|
||||||
|
|
||||||
|
Content uses a four-cell metric strip, full-width table/form panels, and an occasional two-column decision or form/detail layout. The primary record or decision remains in the first viewport at 1365×900. Tables may scroll horizontally inside their own region but must not create page-level overflow.
|
||||||
|
|
||||||
|
Mobile and tablet responsiveness is best-effort only. It may reflow or scroll to avoid preventable breakage, but it is not required to provide complete workflow parity and has no dedicated mockup set.
|
||||||
|
|
||||||
|
## Elevation & Depth
|
||||||
|
|
||||||
|
Structure comes primarily from surface contrast and one-pixel borders. The only recurring ambient shadow is a soft panel lift (`0 10px 28px rgba(20,25,35,.06)`); active navigation and tabs use a smaller `0 1px 2px` shadow. Deep stacks and card-within-card effects are not part of this world.
|
||||||
|
|
||||||
|
**The Flat-First Rule.** Use border and tone before shadow. Shadow confirms grouping; it does not turn every region into a floating card.
|
||||||
|
|
||||||
|
## Shapes
|
||||||
|
|
||||||
|
Controls use an 8px radius, segmented containers 9px, primary panels 12px, and centered dialogs 14px. Status badges may be fully rounded because their small silhouette communicates state. Larger containers are not pill-shaped. Borders are neutral and one pixel.
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### Buttons
|
||||||
|
|
||||||
|
- **Primary:** Operational Ink background, white text, 8px radius, 37px minimum height.
|
||||||
|
- **Secondary:** white background, stronger neutral border, same geometry.
|
||||||
|
- **Danger:** pale danger surface with explicit consequence copy; destructive actions require confirmation.
|
||||||
|
- **Focus:** visible high-contrast focus treatment is mandatory in implementation.
|
||||||
|
|
||||||
|
### Tables and panels
|
||||||
|
|
||||||
|
- Panels use white surface, one-pixel border, 12px radius, and optional ambient shadow.
|
||||||
|
- Table headers use muted surface, compact uppercase labels, and stable desktop columns.
|
||||||
|
- Status is shown with a text badge plus a non-color cue.
|
||||||
|
- Empty, unavailable, stale, and access-denied states replace the table body with direct operational copy.
|
||||||
|
|
||||||
|
### Inputs and forms
|
||||||
|
|
||||||
|
- Fields use white surface, stronger neutral border, 8px radius, and 39px minimum height.
|
||||||
|
- Labels stay visible; placeholder text never replaces a label.
|
||||||
|
- Validation preserves safe input, associates field errors, and adds a form-level error summary.
|
||||||
|
- Form actions appear once, at the end of the form; list/detail actions live in the page header.
|
||||||
|
|
||||||
|
### Navigation
|
||||||
|
|
||||||
|
- The desktop sidebar shows only authorized destinations.
|
||||||
|
- Active navigation uses a white surface and ink text without a colored stripe.
|
||||||
|
- The lower account area exposes profile, theme, and logout.
|
||||||
|
- Icon-only collapsed navigation requires accessible names and tooltips.
|
||||||
|
|
||||||
|
### Metric strips and tabs
|
||||||
|
|
||||||
|
- Metric strips are one bounded row divided by one-pixel rules, not separate floating cards.
|
||||||
|
- Tabs are compact segmented controls; a tab labels a true view switch, not a decorative category badge.
|
||||||
|
|
||||||
|
## Do's and Don'ts
|
||||||
|
|
||||||
|
### Do:
|
||||||
|
|
||||||
|
- **Do** put the user’s current task, deadline, record state, and authorized action in the first viewport.
|
||||||
|
- **Do** use server-authoritative dates/times and honest illustrative-data labels.
|
||||||
|
- **Do** keep role, ownership, membership, leadership, and assignee distinctions visible in copy and action placement.
|
||||||
|
- **Do** supply accessible labels, keyboard focus, error summaries, and chart text/table alternatives.
|
||||||
|
|
||||||
|
### Don't:
|
||||||
|
|
||||||
|
- **Don't** use gradients, glass effects, remote fonts, decorative charts, or oversized marketing headings.
|
||||||
|
- **Don't** use cards as the default container for every piece of content.
|
||||||
|
- **Don't** expose an action merely because the current global role sounds powerful enough; contextual authorization wins.
|
||||||
|
- **Don't** treat the desktop mockups as mobile requirements or imply mobile workflow parity.
|
||||||
|
- **Don't** invent production endorsements, adoption metrics, or unlabeled example records.
|
||||||
+258
@@ -0,0 +1,258 @@
|
|||||||
|
# Development Guide
|
||||||
|
|
||||||
|
This guide explains how to prepare and run Lab Timesheet on a developer
|
||||||
|
computer. The application runs from Java. PostgreSQL and Mailpit run in Docker
|
||||||
|
containers.
|
||||||
|
|
||||||
|
The root Dockerfile and Compose file are production-only. They are not part of
|
||||||
|
the development loop. Development still runs Java from the IDE or Maven while
|
||||||
|
PostgreSQL and Mailpit run as separate local containers.
|
||||||
|
|
||||||
|
## 1. Install the required tools
|
||||||
|
|
||||||
|
Install:
|
||||||
|
|
||||||
|
- Java 25
|
||||||
|
- Docker Desktop or OrbStack
|
||||||
|
- Node.js 24 and npm 11
|
||||||
|
- Git
|
||||||
|
- IntelliJ IDEA, if you want to run the application from the IDE
|
||||||
|
|
||||||
|
Confirm the tools are available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -version
|
||||||
|
docker version
|
||||||
|
node --version
|
||||||
|
npm --version
|
||||||
|
```
|
||||||
|
|
||||||
|
On macOS, select an installed Java 25 JDK with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export JAVA_HOME=$(/usr/libexec/java_home -v 25)
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
```
|
||||||
|
|
||||||
|
Java 25 is the supported project baseline. A newer local JDK can compile the
|
||||||
|
project but is not the shared team baseline.
|
||||||
|
|
||||||
|
## 2. Prepare the project
|
||||||
|
|
||||||
|
Clone the repository, open a terminal in its root directory, then create your
|
||||||
|
local environment file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit `.env` and replace the database password placeholder. Generate the
|
||||||
|
encryption master key with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy that output into `LAB_SECURITY_MASTER_KEY`. Never commit `.env` or share a
|
||||||
|
real key in chat, screenshots, test evidence, or documentation.
|
||||||
|
|
||||||
|
Install and build the local frontend assets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use an isolated repair branch
|
||||||
|
|
||||||
|
For a targeted repair, start a clean worktree from the taskmaster-verified
|
||||||
|
current `main` on `work/fix/<feature>/<what-fix>`. Keep it separate from the
|
||||||
|
five persistent `work/<feature>` branches. Do not use
|
||||||
|
`work/<feature>/fix/<what-fix>` because the persistent `work/<feature>` ref
|
||||||
|
already occupies that Git ref prefix.
|
||||||
|
|
||||||
|
Every targeted repair starts from the taskmaster-verified latest `main`, uses TDD RED → GREEN, adds Javadoc during implementation, records companion evidence, undergoes independent review, and uses a normal, non-force merge only when separately authorized.
|
||||||
|
|
||||||
|
## 3. Start the development containers
|
||||||
|
|
||||||
|
### PostgreSQL 18.4
|
||||||
|
|
||||||
|
Create a named volume once. The volume keeps your development data when the
|
||||||
|
container is stopped or replaced.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker volume create labtimesheet-postgres-data
|
||||||
|
```
|
||||||
|
|
||||||
|
Start PostgreSQL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name labtimesheet-postgres \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-e POSTGRES_DB=labtimesheet \
|
||||||
|
-e POSTGRES_USER=labtimesheet \
|
||||||
|
-e POSTGRES_PASSWORD=replace-with-same-password-as-env \
|
||||||
|
-p 127.0.0.1:55432:5432 \
|
||||||
|
-v labtimesheet-postgres-data:/var/lib/postgresql \
|
||||||
|
postgres:18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the same password for `POSTGRES_PASSWORD` and `LAB_DB_PASSWORD` in `.env`.
|
||||||
|
PostgreSQL stores the original password in the volume. Changing only `.env`
|
||||||
|
later will not change the database password.
|
||||||
|
|
||||||
|
### Mailpit
|
||||||
|
|
||||||
|
Mailpit receives development email without sending it to real people.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name labtimesheet-mailpit \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 127.0.0.1:1025:1025 \
|
||||||
|
-p 127.0.0.1:8025:8025 \
|
||||||
|
axllent/mailpit:v1.27.4
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm both containers are running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful container commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs labtimesheet-postgres
|
||||||
|
docker logs labtimesheet-mailpit
|
||||||
|
docker stop labtimesheet-postgres labtimesheet-mailpit
|
||||||
|
docker start labtimesheet-postgres labtimesheet-mailpit
|
||||||
|
```
|
||||||
|
|
||||||
|
Stopping the containers keeps the database volume. Do not remove the volume
|
||||||
|
unless you intentionally want to discard your local development data.
|
||||||
|
|
||||||
|
## 4. Run from a terminal
|
||||||
|
|
||||||
|
The `dev` profile imports the ignored root `.env` file automatically. From the
|
||||||
|
repository root, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw spring-boot:run
|
||||||
|
```
|
||||||
|
|
||||||
|
Shell environment variables still override values from `.env`, which is useful
|
||||||
|
for a one-off local override. If you run from another working directory, set
|
||||||
|
`LAB_DEV_ENV_FILE` to the absolute path of your `.env` file.
|
||||||
|
|
||||||
|
Open:
|
||||||
|
|
||||||
|
- First-Admin setup: open `http://localhost:8080` and follow the automatic
|
||||||
|
redirect to `/bootstrap`.
|
||||||
|
- Login: `http://localhost:8080/login`
|
||||||
|
- Mailpit inbox: `http://localhost:8025`
|
||||||
|
|
||||||
|
At first setup, configure SMTP through the Admin console with:
|
||||||
|
|
||||||
|
| Setting | Development value |
|
||||||
|
|---|---|
|
||||||
|
| Host | `localhost` |
|
||||||
|
| Port | `1025` |
|
||||||
|
| Security | `NONE` |
|
||||||
|
| Username | leave empty |
|
||||||
|
| Password | leave empty |
|
||||||
|
| From address | a local address such as `labtimesheet@example.test` |
|
||||||
|
| From name | `Lab Timesheet` |
|
||||||
|
|
||||||
|
Test the draft before activating it. Mailpit's web inbox shows activation and
|
||||||
|
other development messages.
|
||||||
|
|
||||||
|
Stop the application with `Control+C`.
|
||||||
|
|
||||||
|
## 5. Run with IntelliJ IDEA
|
||||||
|
|
||||||
|
### Open the project
|
||||||
|
|
||||||
|
1. Open IntelliJ IDEA.
|
||||||
|
2. Choose **Open** and select the repository root.
|
||||||
|
3. Allow IntelliJ to import the Maven project.
|
||||||
|
4. Open **File > Project Structure > Project**.
|
||||||
|
5. Select a Java 25 SDK. Add the JDK installation if it is not listed.
|
||||||
|
|
||||||
|
### Create the run configuration
|
||||||
|
|
||||||
|
1. Open **Run > Edit Configurations**.
|
||||||
|
2. Select **+**, then **Spring Boot**.
|
||||||
|
3. Use the name `Lab Timesheet (dev)`.
|
||||||
|
4. Set **Main class** to
|
||||||
|
`com.lab.labtimesheet.LabtimesheetApplication`.
|
||||||
|
5. Set **Use classpath of module** to the main `labtimesheet` module.
|
||||||
|
6. Set **JRE** to Java 25.
|
||||||
|
7. Set **Active profiles** to `dev`.
|
||||||
|
8. Set **Working directory** to the repository root.
|
||||||
|
9. Leave **Environment variables** empty. With the repository root as the
|
||||||
|
working directory, `application-dev.yaml` imports the ignored `.env` file.
|
||||||
|
10. Apply the configuration and run it.
|
||||||
|
|
||||||
|
If company policy requires IntelliJ to inject the values instead, select the
|
||||||
|
local `.env` in the **Environment variables** field. Environment variables take
|
||||||
|
precedence over the imported file. Do not store real secrets in a shared or
|
||||||
|
committed run configuration.
|
||||||
|
|
||||||
|
Run `npm ci` and `npm run build` in IntelliJ's terminal before the first launch
|
||||||
|
and after changing Tailwind or icon sources.
|
||||||
|
|
||||||
|
## 6. Common problems
|
||||||
|
|
||||||
|
### The application cannot connect to PostgreSQL
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps
|
||||||
|
docker logs labtimesheet-postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
Check that `.env` uses port `55432`, database `labtimesheet`, user
|
||||||
|
`labtimesheet`, and the password used when the PostgreSQL volume was first
|
||||||
|
created.
|
||||||
|
|
||||||
|
### Port 8080, 55432, 1025, or 8025 is already in use
|
||||||
|
|
||||||
|
Stop the other program or container using that port. Keep `.env` and the Docker
|
||||||
|
port mapping consistent if you intentionally select another development port.
|
||||||
|
|
||||||
|
### Mail does not appear in Mailpit
|
||||||
|
|
||||||
|
Check that Mailpit is running and that the active Admin SMTP configuration uses
|
||||||
|
host `localhost`, port `1025`, and security `NONE`. A container health warning
|
||||||
|
does not by itself prove that SMTP is unavailable; use the Admin SMTP test.
|
||||||
|
|
||||||
|
### IntelliJ uses the wrong Java version
|
||||||
|
|
||||||
|
Check both **Project SDK** and the run configuration's **JRE**. They should both
|
||||||
|
be Java 25.
|
||||||
|
|
||||||
|
### Spring reports an unresolved `LAB_*` placeholder
|
||||||
|
|
||||||
|
Confirm the run configuration uses the repository root as its working
|
||||||
|
directory and that `.env` exists there. If the working directory must differ,
|
||||||
|
set `LAB_DEV_ENV_FILE` to the absolute `.env` path in the run configuration's
|
||||||
|
environment variables.
|
||||||
|
|
||||||
|
### Styles or icons are missing
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
For test setup, commands, TDD, and test evidence rules, read
|
||||||
|
[TESTING.md](TESTING.md).
|
||||||
|
|
||||||
|
For production image and Compose operation, read [DEPLOYMENT.md](DEPLOYMENT.md).
|
||||||
|
Do not use the production Compose file as a replacement for this development
|
||||||
|
setup.
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
# Multi-stage production build. Base images are pinned multi-architecture indexes.
|
||||||
|
FROM node:24-alpine@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43 AS frontend
|
||||||
|
WORKDIR /workspace
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY src/main ./src/main
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM eclipse-temurin:25-jdk-alpine@sha256:5ecfde8e5ecde5954ea3721155b345ef56c1d579b940c761318ad4c05959a151 AS builder
|
||||||
|
WORKDIR /workspace
|
||||||
|
RUN apk add --no-cache curl
|
||||||
|
COPY .mvn .mvn
|
||||||
|
COPY mvnw pom.xml ./
|
||||||
|
RUN ./mvnw -B -Dmaven.test.skip=true dependency:go-offline
|
||||||
|
COPY src/main ./src/main
|
||||||
|
COPY --from=frontend /workspace/src/main/resources/static/assets/app.css ./src/main/resources/static/assets/app.css
|
||||||
|
COPY --from=frontend /workspace/src/main/resources/static/assets/icons.svg ./src/main/resources/static/assets/icons.svg
|
||||||
|
RUN ./mvnw -B -Dmaven.test.skip=true package
|
||||||
|
|
||||||
|
FROM eclipse-temurin:25-jre-alpine@sha256:28db6fdf60e38945e43d840c0333aeaec66c15943070104f7586fd3c9d1665b0
|
||||||
|
ARG VCS_REF=unknown
|
||||||
|
ARG SOURCE_URL=https://git.sechmachine.io.vn/sechmachine/labtimesheet
|
||||||
|
LABEL org.opencontainers.image.title="Lab Timesheet" \
|
||||||
|
org.opencontainers.image.source="${SOURCE_URL}" \
|
||||||
|
org.opencontainers.image.revision="${VCS_REF}"
|
||||||
|
|
||||||
|
RUN addgroup -S -g 10001 app && adduser -S -D -H -u 10001 -G app app
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder --chown=10001:10001 /workspace/target/*.war /app/app.war
|
||||||
|
|
||||||
|
ENV SPRING_PROFILES_ACTIVE=prod \
|
||||||
|
JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=75.0"
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 10001:10001
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=45s --retries=3 CMD wget -q -O /dev/null http://127.0.0.1:8080/actuator/health/readiness || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT ["java", "-jar", "/app/app.war"]
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
# Product
|
||||||
|
|
||||||
|
<!-- impeccable:product-schema 1 -->
|
||||||
|
|
||||||
|
## Platform
|
||||||
|
|
||||||
|
web
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- Java 25 and Spring Boot 4.1.0.
|
||||||
|
- Maven-built, server-rendered Spring MVC modular monolith organized by business feature.
|
||||||
|
- Spring Security, Spring Data JPA, Bean Validation, Thymeleaf, Spring Mail, and Flyway.
|
||||||
|
- Tailwind CSS 4 with Node 24 LTS used only for frontend build assets.
|
||||||
|
- PostgreSQL 18.4 across development, integration testing, and production.
|
||||||
|
- No SPA framework, JWT authentication, microservices, Redis, Kafka, or generic workflow engine in v1.
|
||||||
|
|
||||||
|
## Users
|
||||||
|
|
||||||
|
- **Admins** operate accounts, internship lifecycles, attendance policy, the global calendar, SMTP, HolidayAPI, and system configuration. They inspect all Project and attendance progress/history read-only but do not perform Mentor or Project Leader work.
|
||||||
|
- **Mentors** own Projects, directly manage membership and leadership, decide membership exits, monitor Project and Intern progress/history, comment on Tasks, inspect attendance, and decide leave and missed-checkout corrections.
|
||||||
|
- **Interns** check in and out, request leave and missed-checkout corrections, respond to their own Project invitations, request/cancel their own Project exit, participate in multiple Projects, create self-assigned Tasks when eligible, perform assigned Tasks, comment, update their own assigned Task status, record Task work, and inspect authorized Project history.
|
||||||
|
- A **Project Leader** is an Intern with a current leadership term for one Project. It is contextual authority, never a global account role. The Leader may invite eligible Interns, request a member's removal, manage Task definitions/assignment, and redistribute unfinished Tasks away from a pending exit target in confirmed batches.
|
||||||
|
- The product is reviewed and maintained by a university project team and its instructor or appointed maintainer.
|
||||||
|
|
||||||
|
## Product Purpose
|
||||||
|
|
||||||
|
Lab Timesheet supports a university laboratory or internship program by bringing account administration, attendance, leave, missed-checkout correction, Project work, Task progress, notifications, and authorized reporting into one working system.
|
||||||
|
|
||||||
|
Success means each role can complete its permitted work without spreadsheets or informal message trails, while deadlines, decisions, historical attribution, and report totals remain explainable and auditable.
|
||||||
|
|
||||||
|
## Positioning
|
||||||
|
|
||||||
|
The product joins attendance oversight and Project delivery without pretending they are the same measurement. Check-in and checkout establish attendance; dated Task work logs establish Project effort. Effective-dated policy and frozen historical allocations prevent later configuration changes from rewriting past results.
|
||||||
|
|
||||||
|
## Operating Context
|
||||||
|
|
||||||
|
- The business timezone is `Asia/Ho_Chi_Minh`; business dates use the applicable attendance-policy timezone and persisted event instants are treated as UTC.
|
||||||
|
- The system is operated as one server-rendered web application with PostgreSQL. Desktop browsers are the supported interface target; mobile and tablet behavior is best-effort and is not guaranteed to expose every workflow optimally.
|
||||||
|
- Initial installation uses a one-time first-Admin bootstrap. Later account creation and password recovery depend on a tested SMTP configuration.
|
||||||
|
- Admins may preview and import Vietnamese holiday candidates from HolidayAPI, while the stored Admin decision remains authoritative. Manual calendar management remains available.
|
||||||
|
- Mentors review global leave and correction queues and separately oversee only the Projects they own.
|
||||||
|
- Reports cover attendance/compliance and Project/Task progress in HTML, Excel, and PDF from one shared dataset definition.
|
||||||
|
- The authoritative requirements are currently a review draft. Product-context initialization does not authorize application implementation or promote the review DDL into Flyway.
|
||||||
|
|
||||||
|
## Capabilities and Constraints
|
||||||
|
|
||||||
|
- Global account roles are exactly `ADMIN`, `MENTOR`, and `INTERN`, and are immutable after account creation.
|
||||||
|
- Project membership is many-to-many and interval-based. The owning Mentor may add/remove directly and makes every exit decision; the current Leader may invite; only the intended authenticated Intern may accept/decline; members may request but cannot unilaterally leave. Pending exit keeps existing rights but blocks new/self-assignment to the target; the Leader redistributes unfinished Tasks before approval, while direct Mentor removal retains its atomic automatic-transfer shortcut.
|
||||||
|
- Every `PLANNED` or `ACTIVE` Project has exactly one current Intern Leader. Any active member may create a Task assigned only to themselves; only the current Leader may create for another member or reassign broader Task work.
|
||||||
|
- Each Task has one current assignee. Only that assignee changes its status and records work.
|
||||||
|
- Attendance uses server-time check-in and checkout. Effective-dated policy stores separate check-in and checkout grace periods, both defaulting to 30 minutes; with the default 15:30 end, normal checkout closes immediately after the inclusive 16:00:00 cutoff. Task work is a separate dated-minute record and never proves attendance.
|
||||||
|
- Leave is full-day. Only frozen eligible workdays consume quota, and pending or approved requests reserve it.
|
||||||
|
- Corrections apply only to missing checkout after the attendance row's historical checkout cutoff. Submission remains open through scheduled end plus 24 hours, and the Mentor then receives a separate 24-hour decision window.
|
||||||
|
- Global attendance policy is effective-dated; historical attendance and leave allocations must not drift after later policy or calendar changes. Admin-only setting History tabs and authorized Project History read the retained domain rows already present; they never expose secrets or invent previous-assignee/status/edit timelines that are not stored.
|
||||||
|
- SMTP and HolidayAPI secrets are Admin-managed and encrypted with a deployment-provided master key. Email-dependent account actions fail closed when SMTP is unavailable; other domain actions retain in-app delivery.
|
||||||
|
- HTML, Excel, and PDF reports must agree on the same hand-checkable totals and render undefined denominators as `N/A`.
|
||||||
|
- The product language is English in v1. Displayed business dates use `dd/MM/yyyy` and times use 24-hour local time.
|
||||||
|
- Features absent from the reviewed requirements are not silently in scope.
|
||||||
|
|
||||||
|
## Brand Commitments
|
||||||
|
|
||||||
|
- The working product name is **Lab Timesheet & Project Management System**, shortened to **Lab Timesheet** where space is constrained.
|
||||||
|
- The supplied Vercel/shadcn-style operations-shell images are illustrative references for a compact permission-aware application shell with supported light and dark modes. They do not define fields, workflows, authorization, or persistence and never override numbered requirements or reviewed DDL.
|
||||||
|
- Interface copy must be direct, operational, and honest about permissions, deadlines, destructive consequences, unavailable integrations, and illustrative data.
|
||||||
|
|
||||||
|
## Evidence on Hand
|
||||||
|
|
||||||
|
- `labtimesheet-docs-hub/requirements-specification.md` is the authoritative requirements review draft.
|
||||||
|
- `labtimesheet-docs-hub/database-schema.sql` is the companion PostgreSQL design baseline, not yet a production migration.
|
||||||
|
- `labtimesheet-docs-hub/assets/ui-reference-light.png`, `ui-reference-dark-shell.png`, and `ui-reference-dark-dashboard.png` are the supplied visual references.
|
||||||
|
- The repository contains an early Spring Boot scaffold matching the recorded Java/Spring/Maven direction but no implemented product interface yet.
|
||||||
|
- No production data, customer testimonials, adoption metrics, institutional endorsements, or performance claims are available. Future design work must not fabricate them.
|
||||||
|
|
||||||
|
## Product Principles
|
||||||
|
|
||||||
|
1. **Authorization follows stored context.** Global role alone is insufficient; ownership, membership, leadership, assignment, lifecycle, and record scope determine access.
|
||||||
|
2. **History does not move or pretend.** Later policy, calendar, membership, invitation, exit decision, leadership, assignment, or assignee changes must not silently rewrite past results, completed-Task assignee names, creator attribution, or provenance. History views expose only retained domain facts and non-secret metadata; they do not fabricate event timelines the schema never stored.
|
||||||
|
3. **Attendance and Project work stay distinct.** The product may report them together, but one never derives or proves the other.
|
||||||
|
4. **Deadlines are enforced at every path.** Scheduled workers improve timeliness, while request-time guards preserve correctness when scheduling is late.
|
||||||
|
5. **Prefer explicit, reviewable operations.** Feature-owned controller/service/repository flows, constrained state transitions, focused integrations, and shared report datasets serve clarity over speculative machinery.
|
||||||
|
6. **Fixes preserve branch ownership.** A targeted repair uses a clean `work/fix/<feature>/<what-fix>` branch from verified `main`, not `work/<feature>/fix/<what-fix>`; persistent `work/<feature>` refs already occupy that Git ref prefix.
|
||||||
|
|
||||||
|
## Accessibility & Inclusion
|
||||||
|
|
||||||
|
- The web interface must meet WCAG 2.2 AA contrast and interaction requirements in both light and dark themes.
|
||||||
|
- Controls require associated labels or accessible names, visible keyboard focus, keyboard operation, and adequate target sizes.
|
||||||
|
- Status and validation cannot depend on color alone. Forms retain safe input, identify field errors, and provide an error summary.
|
||||||
|
- Charts are supplemental: every canvas requires an accessible label and an adjacent textual or tabular alternative.
|
||||||
|
- Desktop navigation and data tables must remain fully operable without page-level horizontal overflow. Mobile and tablet layouts should avoid preventable breakage on a best-effort basis but are not a fully supported v1 target.
|
||||||
@@ -1,2 +1,154 @@
|
|||||||
# labtimesheet
|
# Lab Timesheet
|
||||||
|
|
||||||
|
Server-rendered Spring Boot application for managing laboratory internships,
|
||||||
|
Projects, Tasks, and attendance. Iteration 1 is complete and was verified on
|
||||||
|
15 August 2026.
|
||||||
|
|
||||||
|
## Iteration 1: working now
|
||||||
|
|
||||||
|
### Accounts and onboarding
|
||||||
|
|
||||||
|
- Atomic first-Admin bootstrap that remains closed after initialization and restart.
|
||||||
|
- Optional SMTP onboarding with five distinct deferral warnings and a persistent restricted-state notice.
|
||||||
|
- Admin SMTP draft, connection test, and activation against Mailpit or another configured server.
|
||||||
|
- Admin creation of Admin, Mentor, and Intern accounts through single-use email activation.
|
||||||
|
- Password setup, form login, logout, global roles, and role-protected Admin routes.
|
||||||
|
|
||||||
|
### Projects
|
||||||
|
|
||||||
|
- Owning Mentors create `PLANNED` Projects with an eligible initial Leader.
|
||||||
|
- Mentor-controlled direct membership with historical membership and leadership intervals.
|
||||||
|
- Leader reassignment and guarded `PLANNED` to `ACTIVE` activation.
|
||||||
|
- Role-correct Project lists, details, member views, and guessed-ID concealment.
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
- One current assignee per Task.
|
||||||
|
- Active members create self-assigned Tasks; the current Leader may assign another active member.
|
||||||
|
- Due dates are checked against Project dates and current global days off.
|
||||||
|
- The fixed `TODO`, `IN_PROGRESS`, `BLOCKED`, and `DONE` transition graph is enforced.
|
||||||
|
- Authorized comments, Task lists/details, assignee display, status counts, and completion progress.
|
||||||
|
|
||||||
|
### Attendance and calendar
|
||||||
|
|
||||||
|
- Effective attendance-policy resolution with Vietnam business time, configured workdays, and separate 30-minute check-in and checkout grace defaults.
|
||||||
|
- Admin-managed manual global calendar days off.
|
||||||
|
- Server-time check-in and checkout with duplicate, off-day, leave-day, lifecycle, and cutoff rejection.
|
||||||
|
- `MISSING_CHECKOUT` classification without a second early-departure violation.
|
||||||
|
- Intern history plus authorized Mentor/Admin attendance inspection using the historical applied policy.
|
||||||
|
|
||||||
|
### Desktop UI
|
||||||
|
|
||||||
|
- Shared Thymeleaf/Tailwind shell with role-aware navigation and dashboards.
|
||||||
|
- Light, dark, and system themes applied before paint.
|
||||||
|
- Collapsible desktop sidebar, accessible forms/errors, tables, badges, empty states, and local Lucide icons.
|
||||||
|
- Bootstrap, authentication, SMTP, Project, Task, calendar, and attendance pages integrated into the same shell.
|
||||||
|
|
||||||
|
## Deliberately not implemented yet
|
||||||
|
|
||||||
|
The baseline schema includes later-workflow tables; table presence does not mean
|
||||||
|
the corresponding feature is complete.
|
||||||
|
|
||||||
|
- Iteration 2: Project invitations; assisted membership exits with persistent readiness warnings and Leader-managed transfer batches before approval; the existing direct-removal automatic-transfer shortcut; Task edit/delete/reassignment and work logs; authorized Project History; Admin-only non-secret Policy/Calendar/SMTP/HolidayAPI History; leave, missed-checkout corrections, notifications, schedulers, and complete metrics.
|
||||||
|
- Iteration 3: HTML/XLSX/PDF report parity, Chart.js trends, remaining production security hardening, and operational backup/restore qualification.
|
||||||
|
- Mobile layouts are best-effort. Desktop is the supported interface target.
|
||||||
|
|
||||||
|
## Architecture and versions
|
||||||
|
|
||||||
|
- Java 25, Spring Boot 4.1.0, Maven, Spring MVC/Security/Data JPA/Validation, Thymeleaf, Flyway, and PostgreSQL 18.4.
|
||||||
|
- Node 24/npm 11, Tailwind CSS 4.3.3, and `lucide-static` 1.27.0 for local assets.
|
||||||
|
- Package-by-feature modular monolith under `com.lab.labtimesheet.feature`.
|
||||||
|
- Cross-feature access through public services and DTOs; no cross-feature repositories, shadow entities, or business SQL.
|
||||||
|
- Flyway owns the schema; Hibernate validates it with `ddl-auto=validate`.
|
||||||
|
|
||||||
|
## Local development
|
||||||
|
|
||||||
|
Follow [DEVELOPMENT.md](DEVELOPMENT.md) for the complete beginner-friendly
|
||||||
|
setup, PostgreSQL and Mailpit container commands, terminal launch steps, and an
|
||||||
|
IntelliJ IDEA run-configuration walkthrough.
|
||||||
|
|
||||||
|
The committed [`.env.example`](.env.example) contains placeholders only. Real
|
||||||
|
database passwords and the AES-256 master key belong in an untracked `.env`.
|
||||||
|
Product SMTP and HolidayAPI credentials are configured through the Admin
|
||||||
|
console, not environment variables.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# Edit .env. Generate LAB_SECURITY_MASTER_KEY with: openssl rand -base64 32
|
||||||
|
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="/opt/homebrew/opt/node@24/bin:$JAVA_HOME/bin:$PATH"
|
||||||
|
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
./mvnw spring-boot:run
|
||||||
|
```
|
||||||
|
|
||||||
|
Development defaults to the application on port `8080`, PostgreSQL on `55432`,
|
||||||
|
and Mailpit SMTP on `1025`. The exact Spring settings are in
|
||||||
|
[`application-dev.yaml`](src/main/resources/application-dev.yaml), which imports
|
||||||
|
the ignored root `.env` file when the `dev` profile is active.
|
||||||
|
|
||||||
|
On first launch, open `http://localhost:8080`; the application redirects to
|
||||||
|
`/bootstrap`, where you create the first Admin. Then configure and test SMTP or
|
||||||
|
complete all five explicit deferral warnings.
|
||||||
|
|
||||||
|
## Verification status
|
||||||
|
|
||||||
|
The final Iteration 1 integration gate recorded:
|
||||||
|
|
||||||
|
- 197 Maven tests passed with PostgreSQL 18.4 Testcontainers.
|
||||||
|
- Flyway replay produced exactly 23 application tables and 56 foreign keys.
|
||||||
|
- Java compilation and full Javadoc/doclint passed.
|
||||||
|
- Two consecutive Node/Tailwind/Lucide builds produced identical assets.
|
||||||
|
- A real Java process completed bootstrap, login, SMTP deferral, persistent warning recovery, and a separate Mailpit draft/test/activate flow with health `UP`.
|
||||||
|
- Independent reviews of all five work branches closed with no remaining Critical, Important, or Minor findings.
|
||||||
|
|
||||||
|
Tests require Docker for PostgreSQL Testcontainers:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export DOCKER_HOST=unix:///Users/your-name/.orbstack/run/docker.sock # only when using OrbStack
|
||||||
|
./mvnw test
|
||||||
|
```
|
||||||
|
|
||||||
|
See [TESTING.md](TESTING.md) for setup, test commands, the required TDD cycle,
|
||||||
|
evidence records, best practices, and common fixes. Every behavior test has a
|
||||||
|
companion record under [`docs/tests`](docs/tests/README.md).
|
||||||
|
|
||||||
|
## Continuous integration and production containers
|
||||||
|
|
||||||
|
Gitea Actions verifies every pull request and push. The separate container
|
||||||
|
workflow runs only for a manual dispatch or a push to `main`, and its verification
|
||||||
|
job must pass before either image build starts. Manual runs build without publishing;
|
||||||
|
`main` pushes publish Linux AMD64 and add native Linux ARM64 only when the repository
|
||||||
|
explicitly declares that its ARM runner is online. Every published revision has an
|
||||||
|
immutable `sha-<full-commit>` tag, with `main` as a convenience alias.
|
||||||
|
|
||||||
|
The production image is a non-root Java 25 image. The root [compose.yaml](compose.yaml)
|
||||||
|
supports either a persistent PostgreSQL 18.4 sidecar or an external PostgreSQL
|
||||||
|
database. It is not used for development. Follow [DEPLOYMENT.md](DEPLOYMENT.md)
|
||||||
|
and start from [`.env.compose.example`](.env.compose.example); keep the real
|
||||||
|
production environment file outside the repository.
|
||||||
|
|
||||||
|
## Branch ownership
|
||||||
|
|
||||||
|
| Branch | Primary area |
|
||||||
|
|---|---|
|
||||||
|
| `work/platform` | Application baseline, schema, accounts, security, integrations |
|
||||||
|
| `work/projects` | Projects, membership, leadership, lifecycle |
|
||||||
|
| `work/tasks` | Tasks, comments, status, progress |
|
||||||
|
| `work/attendance` | Policy, calendar, attendance workflows |
|
||||||
|
| `work/reports-ui` | Shared UI, dashboards, reporting presentation |
|
||||||
|
|
||||||
|
For a targeted repair, create a clean isolated branch and worktree from the
|
||||||
|
taskmaster-verified current `main` named
|
||||||
|
`work/fix/<feature>/<what-fix>`. Do not nest it as
|
||||||
|
`work/<feature>/fix/<what-fix>`: the persistent `work/<feature>` ref already
|
||||||
|
uses that Git ref prefix.
|
||||||
|
|
||||||
|
Every targeted repair starts from the taskmaster-verified latest `main`, uses TDD RED → GREEN, adds Javadoc during implementation, records companion evidence, undergoes independent review, and uses a normal, non-force merge only when separately authorized.
|
||||||
|
|
||||||
|
Iteration 2 work must start from the merged Iteration 1 `main`, continue with
|
||||||
|
strict RED-to-GREEN TDD, add Javadoc during implementation, and update the
|
||||||
|
matching Markdown evidence record before each milestone commit.
|
||||||
|
|||||||
+178
@@ -0,0 +1,178 @@
|
|||||||
|
# Lab Timesheet Technology Stack
|
||||||
|
|
||||||
|
This document is the team reference for the technologies and development tools
|
||||||
|
used by Lab Timesheet. The five-person development team uses Windows 11 and
|
||||||
|
IntelliJ IDEA. Versions controlled by the repository must not be changed in one
|
||||||
|
developer's environment without a reviewed project-wide update.
|
||||||
|
|
||||||
|
## 1. Architecture
|
||||||
|
|
||||||
|
| Choice | Use | Rationale |
|
||||||
|
|---|---|---|
|
||||||
|
| Server-rendered modular monolith | One Spring Boot application organized by account, integration, Project, Task, attendance, notification, and reporting features | A single deployable application keeps transactions, authorization, testing, and deployment manageable for a small team while feature packages preserve clear ownership. |
|
||||||
|
| Spring MVC with Thymeleaf | Controllers return HTML pages rendered on the server | The product is a form- and workflow-heavy desktop web application. Server rendering avoids the extra API, SPA state, and authentication complexity of a separate frontend application. |
|
||||||
|
| Feature-first Java packages | Each feature owns its controllers, DTOs, entities, repositories, services, and exceptions | Related code stays together, while cross-feature access is limited to public services and DTOs. This supports the five-branch team workflow without duplicating database models. |
|
||||||
|
| Executable WAR | Maven packages the application as a WAR that can still run with `java -jar` | It works with the current Spring Boot deployment while keeping conventional servlet-container compatibility. |
|
||||||
|
| HTML sessions and CSRF protection | Spring Security manages authenticated browser sessions | The application is server-rendered. Session cookies and CSRF protection are simpler and safer here than introducing JWTs. |
|
||||||
|
|
||||||
|
The project deliberately does not use a SPA framework, JWT authentication,
|
||||||
|
microservices, Redis, Kafka, or a generic workflow engine. Those technologies
|
||||||
|
would add operational and development cost without solving a current need.
|
||||||
|
|
||||||
|
## 2. Team workstations
|
||||||
|
|
||||||
|
| Tool | Team baseline | Use | Rationale |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Windows | Windows 11 | Team development operating system | One shared OS baseline makes IDE, Docker, path, and command guidance reproducible for the student team. |
|
||||||
|
| IntelliJ IDEA | Current supported release | Main IDE for Java, Maven, Spring Boot, Thymeleaf, debugging, and test execution | IntelliJ has strong Spring and Java support and provides one consistent run/debug workflow for the team. |
|
||||||
|
| Eclipse Temurin JDK | Java 25 | Compile, test, and run the application | Java 25 is the project language baseline, and Temurin matches the JDK distribution used by CI and container builds. |
|
||||||
|
| IntelliJ annotation processing | Enabled for the project | Makes Lombok-generated constructors and accessors visible to the IDE | Maven already runs Lombok as an annotation processor. Enabling the same behavior in IntelliJ prevents false editor errors. |
|
||||||
|
| Git | Current supported release | Version control and the branch/worktree workflow | Git supports the five persistent feature branches, isolated fix branches, review, and traceable milestone commits. |
|
||||||
|
| PowerShell | Included with Windows 11 | Run Windows commands and `mvnw.cmd` | It is available on every team workstation and avoids requiring a separate shell for normal development. |
|
||||||
|
| OpenSSL | Current supported release | Generates the Base64 256-bit application master key | A standard cryptographic tool avoids inventing or manually typing security keys. Git for Windows or another trusted Windows package may provide it. |
|
||||||
|
| Microsoft Edge or Google Chrome | Current stable release | Manual desktop-browser checks and debugging | The product targets desktop browsers, and both provide standards-based developer tools for HTML, CSS, accessibility, storage, and network inspection. |
|
||||||
|
|
||||||
|
## 3. Java and build platform
|
||||||
|
|
||||||
|
| Technology | Version | Use | Rationale |
|
||||||
|
|---|---:|---|---|
|
||||||
|
| Java | 25 | Application language and runtime | Provides a modern supported Java baseline while keeping one version across local development, CI, and production. |
|
||||||
|
| Spring Boot | 4.1.0 | Application framework and dependency management | Supplies compatible Spring modules, production conventions, testing support, and a managed dependency set. |
|
||||||
|
| Maven Wrapper | Wrapper 3.3.4; Maven 3.9.16 | Java dependency resolution, compilation, tests, Javadoc, and packaging | The checked-in wrapper gives every Windows workstation and CI runner the same Maven version without a separate Maven installation. Use `mvnw.cmd` on Windows. |
|
||||||
|
| Javadoc with doclint | Java 25 toolchain | Validates generated API documentation | Documentation errors are caught during development and CI with the same JDK that compiles the application. |
|
||||||
|
| Lombok | Spring Boot-managed version | Removes mechanical constructors and accessors | Targeted Lombok annotations reduce boilerplate while explicit domain constructors, validation, state changes, and entity identity methods remain visible. |
|
||||||
|
| Spring Boot configuration processor | Spring Boot-managed version | Generates typed Spring configuration metadata during compilation | It improves configuration accuracy and IntelliJ assistance without adding runtime code. |
|
||||||
|
| Embedded/provided Tomcat | Spring Boot-managed version | Servlet runtime for the executable WAR | It is the standard Spring MVC runtime and requires no separate application server for development or the production image. |
|
||||||
|
|
||||||
|
Unless a row gives an explicit version, Java dependency versions are managed by
|
||||||
|
the Spring Boot 4.1.0 dependency set. This prevents individual libraries from
|
||||||
|
being upgraded into incompatible combinations.
|
||||||
|
|
||||||
|
## 4. Spring and Java dependencies
|
||||||
|
|
||||||
|
| Dependency | Use | Rationale |
|
||||||
|
|---|---|---|
|
||||||
|
| Spring Web MVC | Controllers, request binding, validation errors, and server-rendered routes | It matches the Thymeleaf form workflow and keeps browser navigation on the server. |
|
||||||
|
| Spring Security | Login, password hashing, sessions, CSRF, role checks, and security headers | Security rules stay in the established Spring filter and authorization model rather than custom code. |
|
||||||
|
| Spring Data JPA and Hibernate | Entity mapping, repositories, transactions, optimistic locking, and pessimistic locks | JPA removes routine persistence code while PostgreSQL and Flyway remain the schema authority. |
|
||||||
|
| Spring Validation / Jakarta Validation | Request DTO and configuration validation | Validation annotations provide consistent trust-boundary checks and actionable form errors. |
|
||||||
|
| Thymeleaf | HTML page and email-template rendering | Templates integrate directly with Spring MVC and work without a client-side framework. |
|
||||||
|
| Thymeleaf Spring Security extras | Role- and authentication-aware template rendering | Navigation and controls can reflect server authorization without duplicating role parsing. |
|
||||||
|
| Spring Mail | SMTP connectivity and email delivery | Uses the standard Jakarta Mail integration while SMTP settings remain Admin-managed application data. |
|
||||||
|
| Spring Boot Actuator | Liveness, readiness, and application health | Standard health endpoints support Docker health checks and production operations. |
|
||||||
|
| Spring Boot Flyway integration | Runs reviewed database migrations at startup | Database changes are ordered, repeatable, and validated before Hibernate mappings are used. |
|
||||||
|
| Flyway PostgreSQL support | PostgreSQL-specific migration support | The schema uses PostgreSQL features such as `btree_gist`, checks, partial indexes, and exclusion constraints. |
|
||||||
|
| PostgreSQL JDBC driver | Runtime database connection | It is the official Java driver for the selected database. |
|
||||||
|
| Spring Boot DevTools | Development-only restart support | Shortens the local feedback loop without becoming a production dependency. |
|
||||||
|
| Spring Boot Docker Compose support | Optional runtime integration | It is available for Spring tooling, although the documented development loop currently starts PostgreSQL and Mailpit explicitly. |
|
||||||
|
|
||||||
|
## 5. Database and persistence
|
||||||
|
|
||||||
|
| Technology | Version | Use | Rationale |
|
||||||
|
|---|---:|---|---|
|
||||||
|
| PostgreSQL | 18.4 | Development, test, and production database | The domain requires reliable transactions, constraints, date/time types, partial indexes, exclusion constraints, and strong concurrency behavior. Using the same engine everywhere avoids H2-specific surprises. |
|
||||||
|
| Flyway | Spring Boot-managed version | Versioned schema migrations | Flyway makes the reviewed SQL schema reproducible on an empty database and safe to validate in CI. |
|
||||||
|
| Hibernate schema validation | `ddl-auto=validate` | Confirms entity mappings match the migrated schema | Hibernate must not silently create or alter production tables; Flyway remains authoritative. |
|
||||||
|
| PostgreSQL `btree_gist` | Database extension | Supports exclusion constraints such as overlapping leave prevention | The database can reject invalid concurrent data even when two application requests race. |
|
||||||
|
|
||||||
|
## 6. Frontend stack
|
||||||
|
|
||||||
|
| Technology | Version | Use | Rationale |
|
||||||
|
|---|---:|---|---|
|
||||||
|
| Thymeleaf | Spring Boot-managed version | Page layouts, fragments, forms, validation messages, and role-aware navigation | It keeps rendering and authorization close to the Spring MVC application. |
|
||||||
|
| HTML5 | Browser standard | Semantic forms, tables, native dialogs, and accessible page structure | Native elements reduce custom JavaScript and provide built-in keyboard and form behavior. |
|
||||||
|
| Tailwind CSS | 4.3.3 | Compiled design tokens and utility-based styling | It supports the shared light/dark desktop design without shipping a runtime CSS framework. |
|
||||||
|
| Tailwind CLI | 4.3.3 | Builds the committed production CSS asset | The small CLI is sufficient; no frontend bundler or SPA toolchain is needed. |
|
||||||
|
| Lucide Static | 1.27.0 | Local SVG icon sprite | Icons are available offline, inherit theme color, and do not require React or an icon CDN. |
|
||||||
|
| Native JavaScript modules | Browser standard | Small interactions such as theme selection, sidebar state, dialogs, and local search | The current interactions do not justify a client-side application framework. |
|
||||||
|
| Node.js | 24.x | Frontend build scripts and JavaScript tests | Node 24 is the pinned LTS toolchain used consistently by developers, CI, and the Docker build. |
|
||||||
|
| npm | 11.x | Reproducible frontend dependency installation | `npm ci` and the committed lockfile install exactly the reviewed dependency graph. |
|
||||||
|
|
||||||
|
## 7. External services and integrations
|
||||||
|
|
||||||
|
| Service or standard | Use | Rationale |
|
||||||
|
|---|---|---|
|
||||||
|
| SMTP | Activation, password recovery, and ordinary workflow email | SMTP is widely supported and allows the application to work with university or other approved mail providers. Configuration is tested and activated through the Admin console. |
|
||||||
|
| Mailpit | Development SMTP server and web inbox | Mailpit captures messages locally so developers never send test activation or recovery email to real users. The development image is pinned to `axllent/mailpit:v1.27.4`. |
|
||||||
|
| HolidayAPI | Optional Vietnam holiday preview/import | It reduces manual holiday entry while imported dates remain a preview and the Admin's local day-off decision remains authoritative. |
|
||||||
|
| AES-256-GCM from the JDK | Encrypts stored SMTP and HolidayAPI secrets | Authenticated encryption protects confidentiality and detects modification without adding another cryptography dependency. |
|
||||||
|
| HTTPS reverse proxy | Production TLS termination and forwarding | The application image stays focused on Java while an operator-managed proxy handles certificates and the public HTTPS endpoint. |
|
||||||
|
|
||||||
|
## 8. Testing tools
|
||||||
|
|
||||||
|
| Tool | Use | Rationale |
|
||||||
|
|---|---|---|
|
||||||
|
| JUnit Jupiter | Unit and integration test framework | It is the standard JUnit 5 programming model supplied by Spring Boot and works with Maven Surefire and IntelliJ. |
|
||||||
|
| Maven Surefire | Maven/Spring Boot-managed version | Discovers and runs the Java test suite | The same Maven command behaves consistently in IntelliJ terminals, PowerShell, and CI. |
|
||||||
|
| Spring Boot Test | Application-context and integration testing | It verifies real Spring configuration, dependency injection, transactions, and profile behavior. |
|
||||||
|
| Focused Spring Boot test starters | Data JPA, Flyway, Mail, Security, Thymeleaf, Validation, and Web MVC test support | Each test slice receives the framework support it actually exercises instead of one unrelated test environment. |
|
||||||
|
| Spring MVC Test / MockMvc | Controller, security, validation, and Thymeleaf route tests | HTTP behavior can be tested quickly without launching a separate browser process. |
|
||||||
|
| Spring Security Test | Authenticated role and CSRF test support | Tests can prove allowed and denied behavior using the same security filter chain. |
|
||||||
|
| Mockito | Test doubles for external or out-of-scope collaborators | It isolates a focused unit or MVC slice without replacing the database behavior being tested. |
|
||||||
|
| Testcontainers | Spring Boot-managed version | Starts disposable infrastructure for integration tests | Tests use real PostgreSQL 18.4 without depending on a developer's database or leaving shared state behind. |
|
||||||
|
| Testcontainers PostgreSQL | PostgreSQL 18.4 test container integration | It validates Flyway SQL, JPA mappings, constraints, locking, and concurrency against the production database engine. |
|
||||||
|
| Node built-in test runner | Frontend asset and workflow contract tests | The required JavaScript checks run without adding another test framework. |
|
||||||
|
| Playwright | Automated desktop-browser end-to-end journeys | Playwright provides repeatable Chromium-based tests for bootstrap, login, role navigation, Projects, Tasks, attendance, and accessibility-sensitive workflows required by the instructor. |
|
||||||
|
| Manual Edge/Chrome journeys | Exploratory and final visual checks | Manual checks still catch layout, focus, contrast, and real-browser integration issues that focused automated tests may not explain clearly. |
|
||||||
|
| Markdown evidence records | RED/GREEN and affected-suite evidence under `docs/tests/` | Each feature change remains traceable to requirements, commands, expected results, and test boundaries. |
|
||||||
|
|
||||||
|
## 9. Reporting technologies
|
||||||
|
|
||||||
|
These tools are approved for the reporting iteration. They must be added with
|
||||||
|
reviewed, pinned versions when their corresponding feature is implemented.
|
||||||
|
|
||||||
|
| Technology | Approved baseline | Use | Rationale |
|
||||||
|
|---|---:|---|---|
|
||||||
|
| Chart.js | 4.5.1 | Meaningful attendance and Project trend charts | It provides accessible, lightweight charts without changing the server-rendered architecture; every chart also requires a text or table alternative. |
|
||||||
|
| Apache POI XSSF | Compatible 5.5.x | Excel `.xlsx` exports | POI is the established Java library for native Excel workbooks and supports typed cells and formatting. |
|
||||||
|
| OpenPDF `openpdf-html` | Compatible 3.0.x | PDF generation from a dedicated print-safe template | It keeps PDF generation inside Java and supports an embedded Unicode font for Vietnamese content. |
|
||||||
|
|
||||||
|
## 10. Containers and production delivery
|
||||||
|
|
||||||
|
| Technology | Version or baseline | Use | Rationale |
|
||||||
|
|---|---:|---|---|
|
||||||
|
| Docker Desktop | Current supported Windows release using Linux containers | Development infrastructure, Testcontainers, and local production-image checks | It provides the Docker Engine expected by PostgreSQL, Mailpit, Testcontainers, and multi-stage builds on Windows 11. |
|
||||||
|
| Docker Compose | v2.20 or newer | Production example with bundled or external PostgreSQL | One documented file supports both deployment topologies while retaining persistent database storage. |
|
||||||
|
| Docker BuildKit / Buildx | Current workflow-pinned release | Multi-stage and multi-architecture image builds | Buildx produces native Linux AMD64 and optional ARM64 images with reproducible build stages. |
|
||||||
|
| Node Alpine image | Node 24, digest-pinned | Builds Tailwind and Lucide assets | Frontend tools do not remain in the final Java runtime image. |
|
||||||
|
| Eclipse Temurin images | Java 25 JDK and JRE, digest-pinned | Builds the WAR and runs the production application | Separate build and runtime images reduce the final image size and match the Java baseline. |
|
||||||
|
| PostgreSQL image | 18.4, digest-pinned | Optional bundled production database | Digest pinning prevents an image tag from silently changing during deployment. |
|
||||||
|
| OCI image registry | Gitea package registry | Stores immutable application images | Commit-SHA tags make a deployed version identifiable and allow a controlled rollback. |
|
||||||
|
|
||||||
|
The final application container runs as non-root UID/GID `10001`, uses a
|
||||||
|
read-only root filesystem in Compose, drops Linux capabilities, and exposes
|
||||||
|
Actuator readiness and liveness checks.
|
||||||
|
|
||||||
|
## 11. Source control and CI/CD
|
||||||
|
|
||||||
|
| Tool | Use | Rationale |
|
||||||
|
|---|---|---|
|
||||||
|
| Gitea | Git hosting, review, Actions, and OCI package registry | One project-owned platform stores source, reviews changes, runs checks, and publishes production images. |
|
||||||
|
| Gitea Actions | Verification on every pull request and push | CI repeats frontend, Java, PostgreSQL, Javadoc, generated-asset, and whitespace checks outside a developer workstation. |
|
||||||
|
| Container workflow | Manual dispatch or `main` push only | Image builds are expensive and potentially publish artifacts, so they run only after an internal verification job and never for ordinary feature branches or pull requests. |
|
||||||
|
| `actions/checkout` | 7.0.1, immutable SHA pin | Checks out source without retaining push credentials | An immutable pin prevents a moving action tag from changing CI behavior unexpectedly. |
|
||||||
|
| `actions/setup-java` | 5.7.0, immutable SHA pin | Installs Temurin Java 25 and manages the Maven cache | CI uses the same Java baseline as the team and production build. |
|
||||||
|
| `actions/setup-node` | 7.0.0, immutable SHA pin | Installs Node 24 and manages the npm cache | CI uses the same frontend toolchain as the lockfile and Docker build. |
|
||||||
|
| Docker Buildx action | 4.2.0, immutable SHA pin | Prepares multi-architecture image building | It supports native AMD64 and optional native ARM64 production builds. |
|
||||||
|
| Docker Login action | 4.6.0, immutable SHA pin | Authenticates only publication jobs to the registry | Registry credentials stay out of scripts and are used only when publishing is authorized. |
|
||||||
|
| Docker Build Push action | 7.3.0, immutable SHA pin | Builds and publishes OCI images | It provides one reviewed image-build path for both supported Linux architectures. |
|
||||||
|
|
||||||
|
## 12. Configuration and source-of-truth files
|
||||||
|
|
||||||
|
| File | Controls |
|
||||||
|
|---|---|
|
||||||
|
| `pom.xml` | Java version, Spring Boot version, Java dependencies, packaging, and annotation processors |
|
||||||
|
| `.mvn/wrapper/maven-wrapper.properties` | Maven Wrapper and Maven distribution |
|
||||||
|
| `package.json` and `package-lock.json` | Node/npm baseline and exact frontend dependencies |
|
||||||
|
| `src/main/resources/application*.yaml` | Shared, development, and production Spring configuration |
|
||||||
|
| `src/main/resources/db/migration/` | Flyway database schema history |
|
||||||
|
| `Dockerfile` | Production multi-stage application image |
|
||||||
|
| `compose.yaml` | Production application and optional PostgreSQL deployment example |
|
||||||
|
| `.gitea/workflows/` | Verification and container publication workflows |
|
||||||
|
| `DEVELOPMENT.md` | Windows/IDE-oriented local setup and run instructions |
|
||||||
|
| `TESTING.md` | Test commands, TDD rules, and evidence format |
|
||||||
|
| `DEPLOYMENT.md` | Production container configuration and operation |
|
||||||
|
|
||||||
|
When documentation and a build file disagree about an installed version, the
|
||||||
|
build file and lockfile are authoritative. Update this document in the same
|
||||||
|
reviewed change whenever the selected stack changes.
|
||||||
+267
@@ -0,0 +1,267 @@
|
|||||||
|
# Testing Guide
|
||||||
|
|
||||||
|
This guide explains how to prepare the test environment, run each type of test,
|
||||||
|
and follow the project's required test-driven development workflow.
|
||||||
|
|
||||||
|
## 1. What you need
|
||||||
|
|
||||||
|
Install these tools before running tests:
|
||||||
|
|
||||||
|
- Java 25
|
||||||
|
- Docker Desktop or OrbStack
|
||||||
|
- Node.js 24 and npm 11
|
||||||
|
- Git
|
||||||
|
|
||||||
|
Confirm the tools are available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -version
|
||||||
|
docker version
|
||||||
|
node --version
|
||||||
|
npm --version
|
||||||
|
```
|
||||||
|
|
||||||
|
On macOS with Homebrew, the project normally uses:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="/opt/homebrew/opt/node@24/bin:$JAVA_HOME/bin:$PATH"
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use OrbStack and Testcontainers cannot find Docker, set:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export DOCKER_HOST=unix:///Users/your-name/.orbstack/run/docker.sock
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `your-name` with your macOS account name. Docker Desktop users normally
|
||||||
|
do not need this setting.
|
||||||
|
|
||||||
|
Tests use temporary PostgreSQL 18.4 containers. They do not use the development
|
||||||
|
database, Mailpit, or the local `.env` file.
|
||||||
|
|
||||||
|
## 2. First test run
|
||||||
|
|
||||||
|
From the repository root, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw test
|
||||||
|
```
|
||||||
|
|
||||||
|
The first run may take longer because Docker downloads PostgreSQL and
|
||||||
|
Testcontainers support images. A successful run ends with `BUILD SUCCESS`.
|
||||||
|
|
||||||
|
Frontend assets have a separate check:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Test types used by this project
|
||||||
|
|
||||||
|
### Unit tests
|
||||||
|
|
||||||
|
Unit tests check a small rule or calculation without starting the full
|
||||||
|
application. Examples include Task status transitions and progress calculations.
|
||||||
|
|
||||||
|
Run one class:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw -Dtest=TaskDomainRulesTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration tests
|
||||||
|
|
||||||
|
Integration tests check real Spring services, Flyway migrations, JPA mappings,
|
||||||
|
transactions, and PostgreSQL constraints. Docker must be running.
|
||||||
|
|
||||||
|
Run one integration class:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw -Dtest=AttendancePersistenceIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Web tests
|
||||||
|
|
||||||
|
Web tests send requests through Spring MVC and check security, validation,
|
||||||
|
Thymeleaf pages, redirects, and error messages without opening a browser.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw -Dtest=TaskControllerTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
### End-to-end checks
|
||||||
|
|
||||||
|
End-to-end checks use the running application in a real desktop browser. They
|
||||||
|
cover complete journeys such as bootstrap, login, SMTP setup, Projects, Tasks,
|
||||||
|
and attendance.
|
||||||
|
|
||||||
|
Current end-to-end checks are guided manual checks:
|
||||||
|
|
||||||
|
1. Prepare `.env` by following the main README.
|
||||||
|
2. Start PostgreSQL 18.4 and Mailpit.
|
||||||
|
3. Run `./mvnw spring-boot:run`.
|
||||||
|
4. Follow the scenario written in `docs/tests/e2e/`.
|
||||||
|
5. Record the browser, viewport, result, and any boundary that was not tested.
|
||||||
|
|
||||||
|
Do not record a real browser journey as a web test. Use `docs/tests/e2e/`.
|
||||||
|
|
||||||
|
### Structure and configuration checks
|
||||||
|
|
||||||
|
Structure tests protect package boundaries and prevent one feature from reading
|
||||||
|
another feature's repositories or database entities.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw -Dtest=LayerStructureTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
Simple configuration or documentation changes use the smallest useful shell
|
||||||
|
check, followed by the affected Maven suite. Do not create an artificial Java
|
||||||
|
test only to check that a text file exists.
|
||||||
|
|
||||||
|
Run that check from the clean targeted-fix branch named
|
||||||
|
`work/fix/<feature>/<what-fix>` when repairing one feature. Do not use
|
||||||
|
`work/<feature>/fix/<what-fix>`: a persistent `work/<feature>` ref already
|
||||||
|
occupies that Git ref prefix. Record the expected RED and the matching GREEN
|
||||||
|
shell output in the evidence record.
|
||||||
|
|
||||||
|
Every targeted repair starts from the taskmaster-verified latest `main`, uses TDD RED → GREEN, adds Javadoc during implementation, records companion evidence, undergoes independent review, and uses a normal, non-force merge only when separately authorized.
|
||||||
|
|
||||||
|
## 4. Useful commands
|
||||||
|
|
||||||
|
Run one test method:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw '-Dtest=TaskControllerTest#validCreateFormUsesAuthenticatedIdentityAndRedirectsToCreatedTask' test
|
||||||
|
```
|
||||||
|
|
||||||
|
Run tests for one feature by name:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw -Dtest='*Attendance*Test' test
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the complete backend suite:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw test
|
||||||
|
```
|
||||||
|
|
||||||
|
Check compilation and Javadoc:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw -DskipTests compile
|
||||||
|
./mvnw -DskipTests -Ddoclint=all javadoc:javadoc
|
||||||
|
```
|
||||||
|
|
||||||
|
Check whitespace and patch formatting:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git diff --check
|
||||||
|
```
|
||||||
|
|
||||||
|
Maven test reports are written to `target/surefire-reports/`.
|
||||||
|
|
||||||
|
## 5. Required TDD workflow
|
||||||
|
|
||||||
|
TDD means writing the test before writing the production behavior.
|
||||||
|
|
||||||
|
1. Choose the requirement and acceptance-scenario IDs.
|
||||||
|
2. Copy the matching template from `docs/tests/unit`, `integration`, `web`, or `e2e`.
|
||||||
|
3. Write the smallest test that proves the missing behavior.
|
||||||
|
4. Run that test and confirm it fails for the expected reason. This is **RED**.
|
||||||
|
5. Record the exact command and useful failure output in the evidence file.
|
||||||
|
6. Write the minimum production code and its Javadoc. Do not add unrelated work.
|
||||||
|
7. Run the same test again. It must pass. This is **GREEN**.
|
||||||
|
8. Run the affected feature tests, then the full suite when the milestone is complete.
|
||||||
|
9. Refactor only while the tests stay green.
|
||||||
|
10. Update the evidence file and commit the complete milestone.
|
||||||
|
|
||||||
|
If the first test fails because Docker is stopped, a class name is wrong, or the
|
||||||
|
test setup is broken, that is not a valid RED. Fix the environment or test first.
|
||||||
|
|
||||||
|
## 6. Evidence records
|
||||||
|
|
||||||
|
Every behavior test needs one Markdown record in the matching directory:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/tests/unit/
|
||||||
|
docs/tests/integration/
|
||||||
|
docs/tests/web/
|
||||||
|
docs/tests/e2e/
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep every heading from `_TEMPLATE.md`. Record:
|
||||||
|
|
||||||
|
- requirement and scenario IDs;
|
||||||
|
- the behavior being protected;
|
||||||
|
- how the expected result was calculated;
|
||||||
|
- exact RED and GREEN commands and results;
|
||||||
|
- the affected-suite result;
|
||||||
|
- anything the test did not prove.
|
||||||
|
|
||||||
|
One record may cover a closely related parameterized scenario set. A written
|
||||||
|
claim never replaces a test command and result.
|
||||||
|
|
||||||
|
## 7. Testing best practices
|
||||||
|
|
||||||
|
- Test user-visible behavior and stored results, not private method details.
|
||||||
|
- Use PostgreSQL 18.4 for persistence tests. Do not replace it with H2.
|
||||||
|
- Test allowed actions and denied actions, including guessed IDs and wrong roles.
|
||||||
|
- Include boundary values for dates, times, grace periods, passwords, and status transitions.
|
||||||
|
- Use the project's injectable `Clock`; do not make tests depend on the real current time.
|
||||||
|
- Keep each test independent. Do not rely on another test running first.
|
||||||
|
- Use real Spring and database components at the boundary being tested. Mock only external services such as SMTP or HolidayAPI when appropriate.
|
||||||
|
- Never put real passwords, API keys, activation links, or reset links in test code or evidence.
|
||||||
|
- Do not remove assertions, catch errors, or disable security simply to make a test pass.
|
||||||
|
- Run the focused test first so feedback is fast, then run the broader suite before committing.
|
||||||
|
- Give tests names that describe the rule and expected result.
|
||||||
|
- Clean up temporary browser data, application processes, and manually started containers after end-to-end work.
|
||||||
|
|
||||||
|
### What CI runs
|
||||||
|
|
||||||
|
Gitea runs the frontend tests/build, complete Maven/PostgreSQL suite, Javadoc,
|
||||||
|
generated-asset check, and whitespace check for every pull request and push.
|
||||||
|
The separate container workflow runs only when manually dispatched or when
|
||||||
|
`main` is pushed. It repeats the verification job before building either image.
|
||||||
|
Manual runs do not publish; only a push to `main` publishes.
|
||||||
|
|
||||||
|
Run focused and affected tests locally before pushing. CI is the shared
|
||||||
|
confirmation, not a substitute for local RED and GREEN evidence.
|
||||||
|
|
||||||
|
## 8. Common problems
|
||||||
|
|
||||||
|
### Testcontainers cannot find Docker
|
||||||
|
|
||||||
|
Start Docker Desktop or OrbStack. Run `docker version`. OrbStack users should
|
||||||
|
also check the `DOCKER_HOST` command shown in Section 1.
|
||||||
|
|
||||||
|
The Gitea Docker runner exposes the daemon through Docker Desktop, so its jobs
|
||||||
|
set `TESTCONTAINERS_HOST_OVERRIDE=host.docker.internal`. Keep that override if
|
||||||
|
the runner stays containerized; otherwise Ryuk may try an unreachable bridge IP.
|
||||||
|
|
||||||
|
### The wrong Java version is used
|
||||||
|
|
||||||
|
Run `java -version` and `./mvnw -version`. Both should report Java 25. Set
|
||||||
|
`JAVA_HOME` again if Maven uses another JDK.
|
||||||
|
|
||||||
|
### The application cannot start for a manual browser check
|
||||||
|
|
||||||
|
Confirm the process working directory is the repository root so
|
||||||
|
`application-dev.yaml` can import `.env`, PostgreSQL is reachable, and
|
||||||
|
`LAB_SECURITY_MASTER_KEY` decodes from Base64 to 32 bytes. Automated tests do
|
||||||
|
not need this local file.
|
||||||
|
|
||||||
|
### A test passes alone but fails in the full suite
|
||||||
|
|
||||||
|
Check for shared state, fixed ports, assumptions about test order, or data that
|
||||||
|
was not created by the test itself. Do not hide the failure with retries.
|
||||||
|
|
||||||
|
### Build output looks stale
|
||||||
|
|
||||||
|
Use this only after confirming the ordinary command is using stale compiled output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./mvnw clean test
|
||||||
|
```
|
||||||
+60
-6
@@ -1,9 +1,63 @@
|
|||||||
|
# Production deployment example. Development continues to run Java from the IDE.
|
||||||
|
name: labtimesheet-prod
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
app:
|
||||||
image: 'postgres:latest'
|
image: "${LAB_IMAGE:?Set LAB_IMAGE to an immutable sha-* image tag}"
|
||||||
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- 'POSTGRES_DB=mydatabase'
|
SPRING_PROFILES_ACTIVE: prod
|
||||||
- 'POSTGRES_PASSWORD=secret'
|
LAB_DB_URL: "${LAB_DB_URL:?Set the JDBC PostgreSQL URL}"
|
||||||
- 'POSTGRES_USER=myuser'
|
LAB_DB_USERNAME: "${LAB_DB_USERNAME:?Set the database username}"
|
||||||
|
LAB_DB_PASSWORD: "${LAB_DB_PASSWORD:?Set the database password}"
|
||||||
|
LAB_PUBLIC_ORIGIN: "${LAB_PUBLIC_ORIGIN:?Set the public HTTPS origin}"
|
||||||
|
LAB_SECURITY_MASTER_KEY: "${LAB_SECURITY_MASTER_KEY:?Set a Base64 256-bit key}"
|
||||||
|
LAB_FORWARD_HEADERS_STRATEGY: "${LAB_FORWARD_HEADERS_STRATEGY:?Set the explicit proxy strategy}"
|
||||||
ports:
|
ports:
|
||||||
- '5432'
|
# Bind locally by default; terminate HTTPS in a reverse proxy on the same host.
|
||||||
|
- "${LAB_HTTP_BIND:-127.0.0.1}:${LAB_HTTP_PORT:-8080}:8080"
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
# External-database mode leaves the bundled-db profile disabled.
|
||||||
|
required: false
|
||||||
|
read_only: true
|
||||||
|
tmpfs:
|
||||||
|
- /tmp:size=64m,mode=1777
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
stop_grace_period: 40s
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:8080/actuator/health/readiness"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
start_period: 45s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
# Pinned PostgreSQL 18.4 multi-architecture image.
|
||||||
|
image: postgres:18.4@sha256:a02db8cac496f15b094798a38254f14d6e00741f709360e5e00bb6668ea31636
|
||||||
|
profiles: ["bundled-db"]
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: "${POSTGRES_DB:-labtimesheet}"
|
||||||
|
POSTGRES_USER: "${POSTGRES_USER:-labtimesheet}"
|
||||||
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?Set the bundled PostgreSQL password}"
|
||||||
|
volumes:
|
||||||
|
# PostgreSQL 18 stores versioned data beneath this parent directory.
|
||||||
|
- postgres_data:/var/lib/postgresql
|
||||||
|
shm_size: 256mb
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U \"$$POSTGRES_USER\" -d \"$$POSTGRES_DB\""]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Container replacement keeps this volume. It is not a substitute for backups.
|
||||||
|
postgres_data:
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Access, Navigation, Icon, and Intern Picker Fix Plan
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Record the durable workflow for the targeted fixes in this plan. This plan does
|
||||||
|
not change product behavior, dependencies, schemas, or the five persistent
|
||||||
|
feature-branch ownership areas.
|
||||||
|
|
||||||
|
## Implementation steps
|
||||||
|
|
||||||
|
1. Prove RED: the contributor guides lack the realizable repair-branch name.
|
||||||
|
2. Add one branch rule to contributor guides, design records, plans, and tracked
|
||||||
|
coordination authority: `work/fix/<feature>/<what-fix>` from verified
|
||||||
|
`main`.
|
||||||
|
3. State why `work/<feature>/fix/<what-fix>` is invalid while its persistent
|
||||||
|
`work/<feature>` ref exists.
|
||||||
|
|
||||||
|
Every targeted repair starts from the taskmaster-verified latest `main`, uses TDD RED → GREEN, adds Javadoc during implementation, records companion evidence, undergoes independent review, and uses a normal, non-force merge only when separately authorized.
|
||||||
|
|
||||||
|
4. Regenerate the local SRS after amending the existing operational requirement;
|
||||||
|
do not add a requirement ID or a use case.
|
||||||
|
5. Prove GREEN with the executable six-guide regression that independently
|
||||||
|
rejects a positive nested-form recommendation in every guide,
|
||||||
|
coordination-authority consistency, requirement/use-case counts, local-link
|
||||||
|
resolution, and an immutable base-to-candidate whitespace check. Commit the
|
||||||
|
tracked guidance locally; do not push or merge.
|
||||||
|
|
||||||
|
## Exit criteria
|
||||||
|
|
||||||
|
- The tracked guides, design record, implementation plan, root coordination
|
||||||
|
authority, and evidence record agree on the same repair-branch spelling.
|
||||||
|
- The local authoritative, explained, simple, and generated SRS catalogues keep
|
||||||
|
exactly 260 unique requirement IDs and the SRS keeps 14 use cases.
|
||||||
|
- The forbidden nested form is documented only as forbidden, not as a usable
|
||||||
|
branch name.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Assisted Exit Transfer and History Documentation Plan
|
||||||
|
|
||||||
|
**Status:** Documentation update only. No application or schema implementation is authorized by this record.
|
||||||
|
**Date:** 20 August 2026
|
||||||
|
|
||||||
|
## Deliverables
|
||||||
|
|
||||||
|
1. Amend the existing `AUTH`, `PRJ`, `TSK`, `ATT`, `CAL`, `INT`, `UI`, and `DB` rows without adding requirement IDs.
|
||||||
|
2. Synchronize the explained and very-simple teaching copies.
|
||||||
|
3. Update `UC-04`, `UC-05`, `UC-06`, and `UC-14`, required workflow pages, and regenerate the SRS from its generator.
|
||||||
|
4. Update `PRODUCT.md`, `.agents/PROJECT_PLAN.md`, `AGENTS.md`, and `README.md` with the locked boundary and Iteration 2 ownership.
|
||||||
|
5. Add the append-only requirements update manifest.
|
||||||
|
6. Preserve both DDL files, both Mermaid structures, all mockup/reference assets, and `.DS_Store` byte-for-byte.
|
||||||
|
|
||||||
|
## Validation gates
|
||||||
|
|
||||||
|
- Exactly 260 unique requirement rows in authoritative, explained, simple, and generated SRS catalogues.
|
||||||
|
- Exactly 14 SRS use cases.
|
||||||
|
- Exactly 23 documented tables and 56 named foreign-key relationships; no DDL byte change.
|
||||||
|
- Generated SRS agrees with the canonical requirement rows and updated use cases/workflow pages.
|
||||||
|
- All local Markdown links resolve.
|
||||||
|
- Protected DDL/mockup/reference hashes and `.DS_Store` hash match the pre-update snapshot.
|
||||||
|
- `git diff --check` passes.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
No Java, Flyway, SQL, Mermaid structure, mockup, frontend asset, branch, commit, push, or deployment change is part of this task.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Design Record: Durable Fix-Branch Workflow
|
||||||
|
|
||||||
|
- **Date:** 2026-08-15
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Related plan:** [Access, Navigation, Icon, and Intern Picker Fix Plan](../plans/2026-08-15-access-navigation-icon-intern-picker.md)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The repository keeps five persistent feature refs: `work/platform`,
|
||||||
|
`work/projects`, `work/tasks`, `work/attendance`, and `work/reports-ui`.
|
||||||
|
A proposed nested repair name such as `work/platform/fix/example` cannot coexist
|
||||||
|
with the existing `work/platform` ref because Git cannot use one ref as both a
|
||||||
|
leaf and a prefix.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Use `work/fix/<feature>/<what-fix>` for each targeted repair. Create its clean,
|
||||||
|
isolated worktree from the taskmaster-verified current `main`. The `<feature>`
|
||||||
|
segment identifies the owning persistent area; it does not nest below that
|
||||||
|
persistent branch.
|
||||||
|
|
||||||
|
The forbidden form is `work/<feature>/fix/<what-fix>`. A repair owner preserves
|
||||||
|
other worktrees, records RED and GREEN evidence, commits locally, and does not
|
||||||
|
push or merge without separate authority.
|
||||||
|
|
||||||
|
Every targeted repair starts from the taskmaster-verified latest `main`, uses TDD RED → GREEN, adds Javadoc during implementation, records companion evidence, undergoes independent review, and uses a normal, non-force merge only when separately authorized.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Persistent feature branches remain available for their iteration ownership.
|
||||||
|
- A repair can be reviewed and handed off as one immutable branch head.
|
||||||
|
- Contributor documentation, local coordination authority, and generated SRS
|
||||||
|
traceability use the same spelling.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
The executable documentation validator checks the exact approved statement in
|
||||||
|
each of the six tracked guides and independently rejects an injected positive
|
||||||
|
nested-branch recommendation in every guide. The copied root coordination
|
||||||
|
authority uses the same rule and is checked separately for consistency. The
|
||||||
|
evidence record also verifies requirement counts, generated SRS use-case count,
|
||||||
|
and local Markdown links.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Assisted Exit Transfer and History Design
|
||||||
|
|
||||||
|
**Status:** Approved documentation baseline; application implementation remains Iteration 2 work.
|
||||||
|
**Date:** 20 August 2026
|
||||||
|
**Authority:** Current primary-implementor decision, applied through existing requirement IDs.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Define a safe, understandable way to prepare a member's requested Project exit and expose useful retained history without changing the approved database structure.
|
||||||
|
|
||||||
|
## Assisted pending exit
|
||||||
|
|
||||||
|
A pending request keeps membership, existing assignments, and existing Task rights active. The target cannot receive a newly created/reassigned Task or create a self-Task. Every authorized Project viewer sees one readiness state: replacement Leader required, unfinished Task count, or ready for Mentor decision.
|
||||||
|
|
||||||
|
When the target is Leader, the owning Mentor appoints the replacement first. The current/new Leader then uses a right-side drawer to select multiple unfinished `TODO`, `IN_PROGRESS`, or `BLOCKED` Tasks and one eligible active current member. One confirmation commits one immediate all-or-none batch. The Leader repeats until no unfinished Tasks remain.
|
||||||
|
|
||||||
|
Cancellation or rejection restores the target's new-assignment eligibility but does not undo completed transfer batches. Approval is allowed only when the target is no longer Leader and owns zero unfinished Tasks; request approval and membership closure then commit atomically.
|
||||||
|
|
||||||
|
## Direct Mentor removal
|
||||||
|
|
||||||
|
Direct removal remains the existing atomic shortcut. An ordinary member's unfinished Tasks transfer to the current Leader. Removing the current Leader requires a replacement and transfers unfinished Tasks to that replacement. Completed Tasks never move and continue to display the removed Intern's retained name.
|
||||||
|
|
||||||
|
## History views
|
||||||
|
|
||||||
|
One Project History tab reads retained feature-owned records for memberships, leadership, invitations, exit decisions, completed and soft-deleted Tasks, comments, work logs, and stored attribution. Admin sees all Projects read-only; the owning Mentor and current members see authorized open Projects; removed members regain read-only Project history only after completion.
|
||||||
|
|
||||||
|
Task history shows creator, current/final assignee, status/dates, comment/work-log authors, and deletion attribution. It does not claim previous-assignee or edit/status-event timelines that the schema does not store.
|
||||||
|
|
||||||
|
Admin-only read-only History tabs cover Attendance Policy, Calendar, SMTP, and HolidayAPI. They show user-meaningful non-secret metadata and never expose tokens, ciphertext/nonces, passwords, API keys, master-key material, bootstrap state, or internal retry records.
|
||||||
|
|
||||||
|
## Persistence boundary
|
||||||
|
|
||||||
|
No table, foreign key, DDL statement, or Mermaid entity changes. The existing 23 tables and 56 foreign keys already retain the required domain records. `GOV-009` remains authoritative: no generic audit/event-sourcing or Task-assignment-history table.
|
||||||
|
|
||||||
|
## Iteration ownership
|
||||||
|
|
||||||
|
- `work/tasks`: pending-target assignment exclusion, atomic batch transfer, direct-removal transfer helper, unfinished count, and retained Task projection.
|
||||||
|
- `work/projects`: exit readiness, Leader replacement order, approval guard/closure, direct-removal orchestration, and Project-history authorization.
|
||||||
|
- `work/attendance`: policy/calendar retained-history queries.
|
||||||
|
- `work/platform`: non-secret SMTP/HolidayAPI revision-history queries.
|
||||||
|
- `work/reports-ui`: persistent warning, transfer drawer, Project History, and Admin-setting History tabs.
|
||||||
|
|
||||||
|
## Acceptance focus
|
||||||
|
|
||||||
|
Tests must cover replacement-before-transfer, a newly joined eligible recipient, pending-target exclusion, repeatable batches, cancel/reject without rollback, zero-unfinished approval guard, direct-removal atomic transfer, completed-name retention, every history role boundary, and secret redaction.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Test Evidence
|
||||||
|
|
||||||
|
TDD is mandatory. Every feature test must have one companion Markdown record
|
||||||
|
in the directory matching its test type:
|
||||||
|
|
||||||
|
- `unit/` — isolated state, calculation, and domain behavior.
|
||||||
|
- `integration/` — PostgreSQL, Flyway, repository, transaction, and module integration.
|
||||||
|
- `web/` — MockMvc, Thymeleaf, validation, and security behavior.
|
||||||
|
- `e2e/` — cross-module, browser, or full user journeys.
|
||||||
|
|
||||||
|
Copy that directory's `_TEMPLATE.md` and keep every heading. One evidence file
|
||||||
|
may cover a cohesive parameterized scenario set, but it must name every
|
||||||
|
requirement and scenario ID it protects.
|
||||||
|
|
||||||
|
An evidence record is complete only when it contains the observed RED failure,
|
||||||
|
the observed GREEN result, the affected-suite result, and honest external-test
|
||||||
|
boundaries. Commands and relevant output are copied exactly; prose such as
|
||||||
|
"passed locally" is not evidence.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Test Evidence: <short behavior name>
|
||||||
|
|
||||||
|
- **Test type:** E2E
|
||||||
|
- **Requirement IDs:** `<ID>`
|
||||||
|
- **Scenario IDs:** `<ID>`
|
||||||
|
- **Test class/method:** `<fully qualified class and method>`
|
||||||
|
- **Implementation commit:** `<short SHA or pending>`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
<Externally observable rule and failure mode protected by this test.>
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
<Setup, action, and assertions. Explain why this is the narrowest production-shaped test.>
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
<Expected values or state derived independently of the implementation.>
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact command>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<relevant failing output and why it failed for the expected missing behavior>
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact command>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<relevant passing output>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact broader command and result>
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
<What this test deliberately does not prove, including infrastructure or browser boundaries.>
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# Test Evidence: Edge SMTP onboarding and persistent restriction journey
|
||||||
|
|
||||||
|
- **Test type:** E2E
|
||||||
|
- **Requirement IDs:** `ACC-005`, `ACC-006`, `ACC-007`, `UI-002`, `UI-004`, `UI-007`, `UI-010`, `I1-UI-01`, `I1-UI-02`, `I1-UI-04`
|
||||||
|
- **Scenario IDs:** `AC-ACC-003`, `AC-UI-001`, `AC-UI-002`, `AC-UI-003`
|
||||||
|
- **Test class/method:** `Manual Edge journey: bootstrap -> login -> SMTP -> five deferrals -> dashboard warning -> configure SMTP`
|
||||||
|
- **Implementation commit:** `ddf688a5336421762ff970499bafb09505474fca`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
A first Admin can bootstrap and authenticate, then defer SMTP only after five sequential warnings. The fifth Finish returns to the Admin dashboard without hiding the restricted-installation state, and the persistent warning provides a working path back to SMTP configuration. SMTP and deferral pages use the same authenticated desktop shell, pre-paint theme, keyboard focus, collapsed-sidebar tooltip, local assets, and overflow containment as other Admin pages.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
A disposable `postgres:18.4` container exposed an empty `labtimesheet_round2` database on local port `55433`. The real Java 25 Spring process connected to that database, applied Flyway V1, and listened on local port `8080`. Microsoft Edge with the Chromium extension used an explicit 1365x900 viewport. The browser created a non-production test Admin through `/bootstrap`, signed in, opened SMTP onboarding, exercised keyboard/theme/sidebar behavior, traversed all five server-owned deferral POSTs, finished to `/dashboard`, and followed the persistent warning action back to `/admin/smtp`. Browser DOM, computed styles, URLs, scroll widths, and console logs were inspected directly. The browser viewport override was reset, its test tab finalized, the Java process gracefully stopped, and the disposable PostgreSQL container removed.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The warning sequence is account onboarding, activation resend, password recovery, reduced workflow-email immediacy, and restricted-installation acknowledgement. Every step has Back and Configure SMTP; steps one through four have no Finish, and step five has exactly one Finish. The resulting dashboard warning links to `/admin/smtp`. At 1365x900, `documentElement.scrollWidth` and `body.scrollWidth` equal `innerWidth`; keyboard focus has a 3px solid indicator; collapsed navigation exposes tooltip text and `aria-expanded=false`, then returns to `true`; the saved dark theme is present after reload with `theme.js` before `app.css`.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```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=DashboardControllerWebTest,SmtpOnboardingWebIntegrationTest,BootstrapOnboardingWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 13, Failures: 3, Errors: 0, Skipped: 0
|
||||||
|
The rendered Admin dashboard omitted the persistent warning/action.
|
||||||
|
The rendered SMTP form and deferral pages omitted /assets/theme.js because they were standalone pages.
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
No pre-change Edge journey was executed; the production-shaped MockMvc RED above was the intentional failing gate before implementation. The pre-change templates were also manually inspected and contained standalone `<head>`/`<body>` documents rather than the shared shell. This record does not relabel those observations as a browser run.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
docker run -d --rm --name labtimesheet-ui-round2-pg -e POSTGRES_DB=labtimesheet_round2 -e POSTGRES_USER=lab_ui_round2 -e POSTGRES_PASSWORD=<local-test-placeholder> -p 55433:5432 postgres:18.4
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="/opt/homebrew/opt/node@24/bin:$JAVA_HOME/bin:$PATH"
|
||||||
|
export LAB_DB_URL=jdbc:postgresql://localhost:55433/labtimesheet_round2
|
||||||
|
export LAB_DB_USERNAME=lab_ui_round2
|
||||||
|
export LAB_DB_PASSWORD=<local-test-placeholder>
|
||||||
|
./mvnw spring-boot:run
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Edge/Chromium explicit viewport: 1365x900
|
||||||
|
Java: 25.0.4
|
||||||
|
PostgreSQL: 18.4; Flyway V1 applied to an empty disposable database
|
||||||
|
|
||||||
|
Bootstrap created the first Admin and redirected to /login.
|
||||||
|
Login redirected to /admin/smtp?onboarding&continue.
|
||||||
|
SMTP onboarding rendered the shared Admin shell and persistent warning.
|
||||||
|
Steps 1-5 displayed all five required warnings in order; every step exposed Back and two visible Configure SMTP links (page action plus persistent warning); Finish counts were 0,0,0,0,1.
|
||||||
|
Finish navigated to /dashboard. The warning remained visible and its href was /admin/smtp.
|
||||||
|
Following the warning navigated to /admin/smtp with the warning still visible.
|
||||||
|
|
||||||
|
Every sampled SMTP, deferral, and dashboard page reported innerWidth=1365 and documentElement.scrollWidth=body.scrollWidth=1365.
|
||||||
|
Keyboard focus rendered outline 3px solid rgb(49, 87, 231).
|
||||||
|
Collapsed sidebar reported aria-expanded=false and exposed tooltip content "Overview" on keyboard focus; expanding restored aria-expanded=true.
|
||||||
|
Dark theme persisted across reload with data-theme=dark and body background rgb(11, 12, 14); theme.js head index 4 preceded app.css index 5. No flash was practically observed during the reload.
|
||||||
|
Edge console error/warning log: []
|
||||||
|
|
||||||
|
The Spring process ended through graceful shutdown with BUILD SUCCESS. The disposable PostgreSQL container stopped and was removed; ports 8080 and 55433 were no longer listening.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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=SecurityResponseIntegrationTest,BootstrapOnboardingWebIntegrationTest,AccountWebIntegrationTest,SmtpOnboardingWebIntegrationTest,RoleDashboardWebIntegrationTest,UiContractWebTest,AccountTemplateIntegrationTest,AttendanceTemplateIntegrationTest,DashboardControllerWebTest,DashboardTemplateWebTest,ProjectTaskFormAccessibilityWebTest,SharedErrorTemplateWebTest,ProjectControllerTest,TaskControllerTest,AttendanceControllerTest test
|
||||||
|
|
||||||
|
PostgreSQL 18.4 via Testcontainers
|
||||||
|
Tests run: 81, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 50.418 s
|
||||||
|
```
|
||||||
|
|
||||||
|
The final integrated PostgreSQL 18.4 suite also passed 197/197 tests with no failures, errors, or skips in 01:24. Java compile and full Javadoc/doclint each completed with `BUILD SUCCESS`.
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The real browser run proves the specified local Edge/Chromium desktop journey and observable shell behavior against Java and PostgreSQL. The practical theme-flash observation is not a frame-by-frame measurement. It does not test mobile/tablet layouts, real SMTP transport, production TLS configuration, or non-Edge engines. Automated integration tests separately prove active-SMTP suppression and non-Admin warning suppression without creating additional browser fixture accounts.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Test Evidence: <short behavior name>
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `<ID>`
|
||||||
|
- **Scenario IDs:** `<ID>`
|
||||||
|
- **Test class/method:** `<fully qualified class and method>`
|
||||||
|
- **Implementation commit:** `<short SHA or pending>`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
<Externally observable rule and failure mode protected by this test.>
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
<Setup, action, and assertions. Explain why this is the narrowest production-shaped test.>
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
<Expected values or state derived independently of the implementation.>
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact command>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<relevant failing output and why it failed for the expected missing behavior>
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact command>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<relevant passing output>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact broader command and result>
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
<What this test deliberately does not prove, including infrastructure or browser boundaries.>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Test Evidence: SMTP-gated account creation and activation
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `ACC-008`–`ACC-012`, `ACC-014`, `ACC-019`, `ACC-020`, `NOT-008`, `SEC-002`–`SEC-004`
|
||||||
|
- **Scenario IDs:** `AC-ACC-004` (Mentor path), `AC-ACC-005` (Mentor/Intern paths), `AC-ACC-006` (initial delivery failure only)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.service.AccountActivationIntegrationTest#smtpGatedCreationHashesSingleUseActivationAndRetainsFailedDeliveryHistory`
|
||||||
|
- **Implementation commit:** `98a52a1ac23591fa1cd30b7b175da81ec607e521`; start-date guard added in `6181984cf85f184be39513d6313f9cbe8267add5`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
An active Admin can create pending Mentor/Intern accounts only while a tested SMTP revision is active. The raw activation secret exists only in the immediate email, PostgreSQL stores only its SHA-256 hash, activation is single-use, and a failed initial delivery keeps history while invalidating that token. Activating an Intern's lifecycle separately makes the account eligible only inside its inclusive internship dates. Reporting reads account counts through the Account service boundary.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The PostgreSQL 18.4 integration test bootstraps the first Admin, proves creation is blocked before SMTP activation, activates a recorded SMTP boundary, and exercises production account creation/activation. It independently hashes the captured raw link token, inspects persisted state through platform-owned repositories, simulates delivery failure, activates an Intern lifecycle, checks date boundaries, and checks the service-level summary used by reporting.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The first non-bootstrap creation attempt adds zero rows. A delivered Mentor is pending with no password until one successful activation; replay fails. A failed Intern delivery leaves one pending account and one invalidated token. After the successful Intern is activated at both account and internship levels, the final state has three active accounts (Admin, Mentor, Intern), one pending account, and one active internship.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
JAVA_HOME=/opt/homebrew/opt/openjdk@25 DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw -Dtest=AccountActivationIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
BUILD FAILURE. Test compilation reported five missing account-activation API/model symbols, including CreateAccountCommand, TokenPurpose, and UserActionTokenRepository. No test ran.
|
||||||
|
```
|
||||||
|
|
||||||
|
After the first GREEN implementation, the exact-expiry assertion was added and observed RED before exposing the persisted expiry:
|
||||||
|
|
||||||
|
```text
|
||||||
|
BUILD FAILURE. AccountActivationIntegrationTest could not compile because UserActionToken#getExpiresAt() did not exist.
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
JAVA_HOME=/opt/homebrew/opt/openjdk@25 DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw -Dtest=AccountActivationIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
JAVA_HOME=/opt/homebrew/opt/openjdk@25 DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw test
|
||||||
|
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The recording SMTP boundary proves the exact in-memory handoff but not Mailpit/network delivery. MVC creation, activation, login, role denial, logout, and the additional-Admin path are covered separately by `AccountWebIntegrationTest`. This test covers only the Mentor path of SMTP gating and the Mentor/Intern paths of hash-only creation; it does not claim all-role coverage for AC-ACC-004/005. It covers the initial failure/invalidation part of AC-ACC-006, not resend. Resend, password reset, session invalidation after credential/state changes, lock/deactivation, and production origin/readiness hardening remain separate slices.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# Test Evidence: Cross-feature account boundary
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `ACC-002, ACC-014, ACC-020–ACC-021, PRJ-017, ATT-007`
|
||||||
|
- **Scenario IDs:** No direct acceptance-scenario mapping (cross-feature API regression)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.service.BootstrapIntegrationTest.exposesIdentityAndDateAwareInternEligibilityWithoutPersistenceTypes`
|
||||||
|
- **Implementation commit:** `1235204bf1298599264a07943ca1167432556bd2`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Other features can resolve an account by normalized email or ID through an immutable identity DTO and can ask whether an Intern is active and within an inclusive internship interval for a supplied work date. They do not need access to account repositories or JPA entities.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The PostgreSQL 18.4 integration test creates the initial Admin through the production bootstrap transaction, resolves the resulting identity through `AccountService`, and verifies ID/email equivalence, normalized lookup, role, status, and rejection by both current and date-aware Intern eligibility gates. Starting the context also parses the Spring Data derived interval query against the mapped `intern_profiles` entity.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
` ADMIN@EXAMPLE.COM ` resolves to the persisted `admin@example.com` identity. An active Admin is not an eligible Intern on `2026-08-14`. The date-aware gate requires an active Intern account, an `ACTIVE` internship, and `start_date <= workDate <= end_date`.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
BootstrapIntegrationTest.java: method isEligibleIntern in class AccountService
|
||||||
|
cannot be applied to given types; required: long; found: long, java.time.LocalDate
|
||||||
|
Tests did not run; test compilation failed
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw test
|
||||||
|
|
||||||
|
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The test proves identity lookup and rejection of a non-Intern plus successful repository-query initialization. The positive active-Intern and interval-edge cases remain part of I1-PLAT-06 activation/account lifecycle work; dependent features must still enforce their own authorization and transaction invariants.
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# Test Evidence: Frozen leave dates and concurrent punch outcomes
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `ATT-005`, `ATT-006`, `ATT-007`, `ATT-008`, `ATT-010`, `LEV-003`, `LEV-011`
|
||||||
|
- **Scenario IDs:** `AC-ATT-003`, `AC-ATT-004`, `AC-LEV-001`, `AC-LEV-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.service.AttendancePersistenceIntegrationTest#approvedLeaveBlocksOnlyItsFrozenAllocatedDates`, `com.lab.labtimesheet.feature.attendance.service.AttendanceConcurrencyIntegrationTest#concurrentDuplicatePunchesReturnStableDomainOutcomes`
|
||||||
|
- **Implementation commit:** `4c39df70e1f901e232669e9090ff5d21393519f0`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Approved leave blocks check-in only on exact immutable `leave_request_days`, not
|
||||||
|
every calendar date inside the request range. Concurrent duplicate punches return
|
||||||
|
stable attendance rejection codes while preserving a single raw check-in and checkout.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Spring Boot migrates PostgreSQL 18.4, creates an active Intern only through public
|
||||||
|
Account and SMTP services, and persists an Attendance-owned approved leave request
|
||||||
|
plus one frozen allocation through JPA. A separate non-transactional test releases
|
||||||
|
two threads simultaneously against each transactional punch endpoint.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
For an approved 14–17 August range with only 17 August allocated, check-in on
|
||||||
|
14 August succeeds and 17 August returns `APPROVED_LEAVE`. Two simultaneous
|
||||||
|
check-ins produce one success and one `ALREADY_CHECKED_IN`; two simultaneous
|
||||||
|
checkouts produce one success and one `ALREADY_CHECKED_OUT`.
|
||||||
|
|
||||||
|
## 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#approvedLeaveBlocksOnlyItsFrozenAllocatedDates test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
AttendanceException: APPROVED_LEAVE at AttendanceApplicationService.checkIn for
|
||||||
|
the unallocated 2026-08-14 range date.
|
||||||
|
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
|
||||||
|
BUILD FAILURE
|
||||||
|
Process exited 1 because the query used the whole leave request range.
|
||||||
|
```
|
||||||
|
|
||||||
|
The repository-exception unit regressions separately failed because raw
|
||||||
|
`DataIntegrityViolationException` and `ObjectOptimisticLockingFailureException`
|
||||||
|
escaped the application boundary.
|
||||||
|
|
||||||
|
## 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=AttendanceConcurrencyIntegrationTest test
|
||||||
|
./mvnw -Dtest=AttendancePersistenceIntegrationTest#approvedLeaveBlocksOnlyItsFrozenAllocatedDates test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
AttendanceConcurrencyIntegrationTest: Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
PostgreSQL reported SQLSTATE 23505 on uq_attendance_records_intern_date; the caller
|
||||||
|
received ALREADY_CHECKED_IN. The checkout race returned ALREADY_CHECKED_OUT.
|
||||||
|
AttendancePersistenceIntegrationTest focused allocation test: Tests run: 1,
|
||||||
|
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: 32, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Process exited 0.
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The tests do not implement the later leave workflow or account terminal-state
|
||||||
|
transitions. They prove the current read/query boundary, exact PostgreSQL 18.4
|
||||||
|
allocation semantics, and duplicate-punch conflict translation.
|
||||||
@@ -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:** `8b48e281f7e860af435ae35b16c4edeb139286dc`
|
||||||
|
|
||||||
|
## 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: 32, 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,83 @@
|
|||||||
|
# Test Evidence: development-profile attendance policy time hydration
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `ATT-002`, `ATT-003`, `I1-ATT-01`
|
||||||
|
- **Scenario IDs:** `AC-ATT-001`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.controller.CalendarDevelopmentProfileWebIntegrationTest#v1SeededPolicyLetsFormAuthenticatedAdminOpenCalendarInAsiaHoChiMinhDevelopmentProfile`
|
||||||
|
- **Implementation commit:** pending
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The unmodified V1 attendance policy must hydrate its `time` schedule as the configured local wall-clock values when the development profile runs in `Asia/Ho_Chi_Minh`. A form-authenticated Admin can therefore open the calendar without weakening the policy rule that requires the checkout cutoff to be before local midnight.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The test starts the application with the real `dev` profile plus isolated test configuration, forces the JVM default zone to `Asia/Ho_Chi_Minh` before JPA starts, and uses PostgreSQL 18.4 Testcontainers with Flyway V1. It bootstraps an Admin through the form, logs in through the form, and requests `/attendance/calendar`, which resolves the current policy through `AttendanceApplicationService.currentBusinessDate`.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
V1 explicitly stores `scheduled_start = 08:30`, `scheduled_end = 15:30`, and `checkout_grace_minutes = 30`. The checkout cutoff is therefore `16:00`, which is strictly before local midnight, so the calendar request returns HTTP 200.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=CalendarDevelopmentProfileWebIntegrationTest' test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
PostgreSQL 18.4 Testcontainers applied Flyway V1, then the form-authenticated GET /attendance/calendar failed.
|
||||||
|
BUILD FAILURE: CalendarDevelopmentProfileWebIntegrationTest ... ServletException caused by
|
||||||
|
IllegalArgumentException: checkout cutoff must be before local midnight
|
||||||
|
at AttendancePolicy.java:58 via AttendancePolicyEntity.toDomain and AttendanceApplicationService.currentBusinessDate.
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=CalendarDevelopmentProfileWebIntegrationTest' test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
PostgreSQL 18.4 Testcontainers applied Flyway V1.
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=CalendarAuthorizationWebIntegrationTest,CalendarDevelopmentProfileWebIntegrationTest,RoleDashboardWebIntegrationTest,AttendancePersistenceIntegrationTest,AttendancePolicyTest' test
|
||||||
|
|
||||||
|
Tests run: 13, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=*Attendance*Test,*Calendar*Test,Dashboard*Test,RoleDashboardWebIntegrationTest,AdminDashboardWebTest' test
|
||||||
|
|
||||||
|
Tests run: 60, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Java 26 smoke
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env JAVA_HOME=/Users/sechmachine/Library/Java/JavaVirtualMachines/corretto-26.0.2/Contents/Home PATH=/Users/sechmachine/Library/Java/JavaVirtualMachines/corretto-26.0.2/Contents/Home/bin:/opt/homebrew/bin:/usr/bin:/bin ./mvnw clean compile -DskipTests
|
||||||
|
|
||||||
|
Amazon Corretto 26.0.2 compiled 129 source files with release 25.
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This integration test proves the fresh Flyway/JPA/real-login calendar path under the development profile and Vietnam JVM zone. It does not operate the already-running browser-gate application or exercise the Intern dashboard UI itself; both paths resolve the same policy timeline.
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# Test Evidence: Gitea Testcontainers and container workflow gates
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `OPS-011`, `OPS-012`, `TST-001`, `TST-005`, `TST-009`
|
||||||
|
- **Scenario IDs:** `AC-OPS-002`, `AC-OPS-004`
|
||||||
|
- **Test class/method:** `src/test/js/delivery-contract.test.mjs`
|
||||||
|
- **Implementation commit:** `d13443e338770dec0ca9822600f9a9d8405dfdbb`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Gitea verification must reach Docker Desktop-published Testcontainers ports from
|
||||||
|
inside its job container. Container builds may start only after an equivalent
|
||||||
|
verification job succeeds, and the container workflow may run only by manual
|
||||||
|
dispatch or by a push to `main`. Every third-party workflow action is pinned to
|
||||||
|
the reviewed latest release commit rather than a moving tag.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The dependency-free delivery contract reads both workflow files and checks the
|
||||||
|
Testcontainers host override, event filters, verify-to-build dependencies, and
|
||||||
|
the complete allowlist of immutable action SHAs. The remote failure log supplies
|
||||||
|
the production-shaped network reproduction because it ran inside the real Gitea
|
||||||
|
Docker runner.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The runner already resolves `host.docker.internal` to its Docker host. Therefore
|
||||||
|
Testcontainers must use that host instead of the job-network gateway
|
||||||
|
`172.17.0.1`. Pull requests and non-main branch pushes must never schedule the
|
||||||
|
container workflow. Manual dispatches build but do not publish, while main pushes
|
||||||
|
publish only after verification succeeds.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env PATH=/opt/homebrew/opt/node@24/bin:/usr/bin:/bin \
|
||||||
|
node --test src/test/js/delivery-contract.test.mjs
|
||||||
|
|
||||||
|
tea actions runs logs 174 --repo sechmachine/labtimesheet \
|
||||||
|
--login sechmachine-git
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Delivery contract: 4 tests, 1 passed, 3 failed. The workflows lacked the
|
||||||
|
Testcontainers host override, container event/dependency gates, and current
|
||||||
|
action pins.
|
||||||
|
|
||||||
|
Gitea run 174 found Docker at unix:///var/run/docker.sock but selected host
|
||||||
|
172.17.0.1. Ryuk started, then repeated connections to 172.17.0.1:57499 were
|
||||||
|
refused. Maven ended with 217 tests, 64 errors.
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env PATH=/opt/homebrew/opt/node@24/bin:/usr/bin:/bin \
|
||||||
|
node --test src/test/js/delivery-contract.test.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Delivery contract: 4 tests, 4 passed.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env PATH=/opt/homebrew/opt/node@24/bin:/usr/bin:/bin npm run test:ui
|
||||||
|
Result: 5 tests passed.
|
||||||
|
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 \
|
||||||
|
PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin \
|
||||||
|
DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock \
|
||||||
|
./mvnw -B test
|
||||||
|
Result: 205 tests passed across 44 suites; 0 failures, errors, or skips.
|
||||||
|
|
||||||
|
npm ci && npm run build
|
||||||
|
Result: Tailwind and Lucide assets built successfully; tracked assets remained unchanged.
|
||||||
|
|
||||||
|
./mvnw -B -DskipTests -Ddoclint=all javadoc:javadoc
|
||||||
|
Result: BUILD SUCCESS with 83 existing missing-comment warnings and no production Java change.
|
||||||
|
|
||||||
|
Ruby YAML parsing and git diff --check
|
||||||
|
Result: both workflow files parsed and the diff check passed.
|
||||||
|
|
||||||
|
Gitea Actions run 177 on `work/fix/platform/ci-testcontainers-actions`
|
||||||
|
Result: Verify completed successfully in 8 minutes on the real Docker-mode runner.
|
||||||
|
The non-main branch push scheduled `verify.yml` only; `container.yml` did not run.
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The local contract cannot prove action-runner compatibility, registry credentials,
|
||||||
|
or availability of the optional ARM runner. Those are checked by the actual Gitea
|
||||||
|
branch verification and main container runs. Release freshness was checked against
|
||||||
|
the official upstream release APIs on 2026-08-15; the immutable pins remain stable,
|
||||||
|
but a later release requires an intentional reviewed update.
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Test Evidence: Gitea container registry authentication
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `OPS-012`, `OPS-016`, `OPS-017`
|
||||||
|
- **Scenario IDs:** `AC-OPS-004`
|
||||||
|
- **Test class/method:** `src/test/js/delivery-contract.test.mjs` — `container workflow runs only manually or on main and verifies before either image build`
|
||||||
|
- **Implementation commit:** `4dd9f96a231316ce2c14755157a380a2123c2f0b`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
A push to `main` publishes `git.sechmachine.io.vn/sechmachine/labtimesheet` by authenticating the triggering Gitea account with the repository `REGISTRY_TOKEN`. Publication does not depend on separately configured image-name or username settings.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The dependency-free Node contract reads the committed workflow and checks its fixed registry/image coordinates, actor-based username, token secret, and absence of the obsolete `CONTAINER_IMAGE` and `REGISTRY_USERNAME` settings. Ruby's YAML parser separately checks workflow syntax.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The repository and package location are stable project facts. Therefore the workflow needs one credential only: a token belonging to the triggering actor with package read/write permission. Manual dispatch still builds without publishing; only a `main` push logs in and publishes.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env PATH=/opt/homebrew/opt/node@24/bin:/usr/bin:/bin node --test src/test/js/delivery-contract.test.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
4 tests ran: 3 passed, 1 failed. The container contract could not find the fixed registry/image or actor-based login. Real Gitea Container run 179 independently failed before registry login with "Repository variable CONTAINER_IMAGE is required", so REGISTRY_TOKEN was never used.
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env PATH=/opt/homebrew/opt/node@24/bin:/usr/bin:/bin node --test src/test/js/delivery-contract.test.mjs
|
||||||
|
ruby -e 'require "yaml"; YAML.safe_load(File.read(".gitea/workflows/container.yml"), aliases: true); puts "container workflow YAML: OK"'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Delivery contract: 4 tests, 4 passed. Container workflow YAML: OK.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
git diff --check
|
||||||
|
! rg -n 'CONTAINER_IMAGE|REGISTRY_USERNAME' .gitea/workflows/container.yml DEPLOYMENT.md
|
||||||
|
|
||||||
|
Both checks passed. Application tests were deliberately not repeated because the change is limited to workflow metadata, its contract test, and deployment guidance; the container workflow retains its mandatory verify job before building.
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
Local checks do not authenticate to the private registry. The first `main` push containing this change is the production-shaped check of `REGISTRY_TOKEN`, package permissions, and registry publication.
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Test Evidence: Development environment configuration
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `OPS-001`, `OPS-004`, `SEC-013`
|
||||||
|
- **Scenario IDs:** `AC-OPS-001`, `AC-SEC-005`
|
||||||
|
- **Test class/method:** Shell configuration contract, an unsourced dev-profile startup probe, and the full Spring Boot Maven suite
|
||||||
|
- **Implementation commit:** `531c6078521341b156d69cb1013e54f65c092311`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Development starts from datasource, encryption, public-origin, server, proxy,
|
||||||
|
and local Mailpit values in the ignored root `.env` file without requiring an
|
||||||
|
IDE-specific environment-variable copy, committing secrets, or weakening
|
||||||
|
application security controls.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
A shell contract verifies that the committed placeholder and development YAML
|
||||||
|
exist, the superseded properties file is absent, the YAML explicitly imports
|
||||||
|
the ignored root `.env`, and every required placeholder remains represented. A
|
||||||
|
real dev-profile process is launched without sourcing `.env` to prove Spring
|
||||||
|
loads it. The full Maven suite then exercises Spring configuration binding,
|
||||||
|
Flyway, JPA validation, security, and PostgreSQL behavior.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The committed tree contains `.env.example` and `application-dev.yaml`, never
|
||||||
|
tracks `.env`, and exposes exactly the inputs needed by the current
|
||||||
|
application. Starting the `dev` profile from the repository root, without
|
||||||
|
exporting the file, gives Spring the PostgreSQL connection, 32-byte Base64
|
||||||
|
encryption key, public origin, local Mailpit endpoint, server port, and explicit
|
||||||
|
forwarded-header policy.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
/bin/zsh -lc 'config_check_failed=0; if test -e src/main/resources/application-dev.properties; then echo "STALE application-dev.properties"; config_check_failed=1; fi; if ! test -f src/main/resources/application-dev.yaml; then echo "MISSING application-dev.yaml"; config_check_failed=1; fi; if test -f src/main/resources/application-dev.yaml && ! grep -Fq "optional:file:\${LAB_DEV_ENV_FILE:.env}[.properties]" src/main/resources/application-dev.yaml; then echo "MISSING .env import"; config_check_failed=1; fi; exit "$config_check_failed"'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
STALE application-dev.properties
|
||||||
|
MISSING application-dev.yaml
|
||||||
|
exit 1
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
/bin/zsh -lc 'dev_contract_failed=0; dev_required_files=(.env.example src/main/resources/application-dev.yaml); dev_required_env=(SPRING_PROFILES_ACTIVE LAB_SERVER_PORT LAB_FORWARD_HEADERS_STRATEGY LAB_DB_URL LAB_DB_USERNAME LAB_DB_PASSWORD LAB_SMTP_HOST LAB_SMTP_PORT LAB_PUBLIC_ORIGIN LAB_SECURITY_MASTER_KEY); dev_required_placeholders=(LAB_SERVER_PORT LAB_FORWARD_HEADERS_STRATEGY LAB_DB_URL LAB_DB_USERNAME LAB_DB_PASSWORD LAB_SMTP_HOST LAB_SMTP_PORT LAB_PUBLIC_ORIGIN LAB_SECURITY_MASTER_KEY); for dev_file in $dev_required_files; do if ! test -f "$dev_file"; then echo "MISSING $dev_file"; dev_contract_failed=1; fi; done; if test -e src/main/resources/application-dev.properties; then echo "STALE application-dev.properties"; dev_contract_failed=1; fi; if ! grep -Fq "optional:file:\${LAB_DEV_ENV_FILE:.env}[.properties]" src/main/resources/application-dev.yaml; then echo "MISSING .env import"; dev_contract_failed=1; fi; if ! grep -qx "/.env" .gitignore; then echo "MISSING /.env ignore rule"; dev_contract_failed=1; fi; for dev_key in $dev_required_env; do if ! grep -q "^${dev_key}=" .env.example; then echo "MISSING example $dev_key"; dev_contract_failed=1; fi; if ! grep -q "^${dev_key}=" .env; then echo "MISSING local $dev_key"; dev_contract_failed=1; fi; done; for dev_key in $dev_required_placeholders; do dev_placeholder="\${${dev_key}}"; if ! grep -Fq "$dev_placeholder" src/main/resources/application-dev.yaml; then echo "MISSING YAML placeholder $dev_key"; dev_contract_failed=1; fi; done; set -a; source .env; set +a; dev_decoded_key_bytes=$(printf "%s" "$LAB_SECURITY_MASTER_KEY" | base64 -d | wc -c | tr -d " "); if test "$dev_decoded_key_bytes" != 32; then echo "INVALID master key bytes=$dev_decoded_key_bytes"; dev_contract_failed=1; fi; if ! git check-ignore -q .env; then echo "LOCAL .env is not ignored"; dev_contract_failed=1; fi; if git ls-files --error-unmatch .env >/dev/null 2>&1; then echo "LOCAL .env is tracked"; dev_contract_failed=1; fi; if test "$dev_contract_failed" -eq 0; then echo "development configuration contract: PASS"; fi; exit "$dev_contract_failed"'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
development configuration contract: PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
An additional Java 25 process was started with all `LAB_*` and
|
||||||
|
`SPRING_PROFILES_ACTIVE` environment variables removed. It loaded the root
|
||||||
|
`.env` through `application-dev.yaml`, connected to PostgreSQL 18.4, validated
|
||||||
|
Flyway/JPA, and started successfully. The process was then stopped cleanly.
|
||||||
|
|
||||||
|
```text
|
||||||
|
env -u SPRING_PROFILES_ACTIVE -u LAB_SERVER_PORT -u LAB_FORWARD_HEADERS_STRATEGY -u LAB_DB_URL -u LAB_DB_USERNAME -u LAB_DB_PASSWORD -u LAB_SMTP_HOST -u LAB_SMTP_PORT -u LAB_PUBLIC_ORIGIN -u LAB_SECURITY_MASTER_KEY -u LAB_DEV_ENV_FILE /bin/zsh -lc 'export JAVA_HOME=/opt/homebrew/opt/openjdk@25; export PATH="$JAVA_HOME/bin:$PATH"; ./mvnw -DskipTests spring-boot:run'
|
||||||
|
|
||||||
|
No active profile set, falling back to 1 default profile: "dev"
|
||||||
|
Database: jdbc:postgresql://localhost:55432/labtimesheet (PostgreSQL 18.4)
|
||||||
|
Started LabtimesheetApplication in 4.068 seconds
|
||||||
|
Graceful shutdown complete
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env -u SPRING_PROFILES_ACTIVE -u LAB_SERVER_PORT -u LAB_FORWARD_HEADERS_STRATEGY -u LAB_DB_URL -u LAB_DB_USERNAME -u LAB_DB_PASSWORD -u LAB_SMTP_HOST -u LAB_SMTP_PORT -u LAB_PUBLIC_ORIGIN -u LAB_SECURITY_MASTER_KEY -u LAB_DEV_ENV_FILE /bin/zsh -lc '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 test'
|
||||||
|
|
||||||
|
Tests run: 201, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS in 01:29 using PostgreSQL 18.4 Testcontainers.
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The committed example cannot prove another developer's local credentials or
|
||||||
|
an IDE working directory. Product SMTP and HolidayAPI revisions remain
|
||||||
|
Admin-console configuration and are intentionally absent from `.env`.
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
# Test Evidence: Eligible Intern picker query
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** ACC-014, ACC-019–ACC-021, AUTH-001, PRJ-017, TST-001–TST-010
|
||||||
|
- **Scenario IDs:** AC-ACC-009, AC-ACC-010, AC-PRJ-010 (selection-eligibility support)
|
||||||
|
- **Test class/method:** com.lab.labtimesheet.feature.account.service.EligibleInternOptionIntegrationTest#listsOnlyActiveInternsWithActiveInclusiveInternshipsInPickerOrder; #rejectsMissingBusinessDate
|
||||||
|
- **Implementation commit:** e70159a81b6445825f6d5f912ecf3c4aa3c1aa85
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Pending, locked, deactivated, non-Intern, not-started, completed, and date-expired records must not appear in the
|
||||||
|
Account-owned Intern picker. An option is selectable only when both account and internship are ACTIVE and the
|
||||||
|
explicit business date lies within the inclusive internship range. The returned numeric user ID is the internal
|
||||||
|
submission identity, and options sort by display name then student code.
|
||||||
|
The public query rejects a missing business date with the documented actionable message instead of issuing an
|
||||||
|
ambiguous null-bound database query.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The PostgreSQL 18.4 integration test persists valid account/profile combinations through the account feature's JPA
|
||||||
|
entities and repositories. It uses SQL only as a test fixture for future lock, deactivation, and completion states
|
||||||
|
whose production transitions are outside this change. It calls the public Account service query and compares the
|
||||||
|
complete immutable DTO sequence, including both inclusive date boundaries and unique user IDs.
|
||||||
|
Its separate null-date regression calls the same public service method and asserts the exact
|
||||||
|
<code>IllegalArgumentException</code> message documented by that method.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
For business date 2026-08-14, profiles starting on that date and ending on that date remain eligible. The only
|
||||||
|
expected options are Alpha / STU-100, Alpha / STU-200, and Zeta / STU-300, in that order. Every other seeded
|
||||||
|
row fails at least one account role/state, internship state, or inclusive date condition.
|
||||||
|
For a missing business date, the service must immediately throw
|
||||||
|
<code>IllegalArgumentException("Business date is required")</code>.
|
||||||
|
|
||||||
|
## 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=EligibleInternOptionIntegrationTest test
|
||||||
|
~~~
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
~~~text
|
||||||
|
[ERROR] EligibleInternOptionIntegrationTest.java:[11,54] cannot find symbol
|
||||||
|
symbol: class EligibleInternOption
|
||||||
|
location: package com.lab.labtimesheet.feature.account.model.dto
|
||||||
|
BUILD FAILURE
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## 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=EligibleInternOptionIntegrationTest test
|
||||||
|
~~~
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
~~~text
|
||||||
|
PostgreSQL 18.4 Testcontainers started and Flyway applied V1 baseline.
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## Review follow-up: missing business date
|
||||||
|
|
||||||
|
The public guard was temporarily removed solely to prove the new regression fails for the intended reason, then
|
||||||
|
restored exactly before the GREEN checks. The follow-up commit contains only the regression test and evidence.
|
||||||
|
|
||||||
|
### 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=EligibleInternOptionIntegrationTest#rejectsMissingBusinessDate' test
|
||||||
|
~~~
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
~~~text
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
java.lang.AssertionError: Expecting code to raise a throwable.
|
||||||
|
BUILD FAILURE
|
||||||
|
~~~
|
||||||
|
|
||||||
|
### 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=EligibleInternOptionIntegrationTest#rejectsMissingBusinessDate' test
|
||||||
|
~~~
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
~~~text
|
||||||
|
PostgreSQL 18.4 Testcontainers started and Flyway applied V1 baseline.
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## 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=EligibleInternOptionIntegrationTest,AccountActivationIntegrationTest,BootstrapIntegrationTest,AccountWebIntegrationTest,AuthenticationWebIntegrationTest,BootstrapOnboardingWebIntegrationTest test
|
||||||
|
|
||||||
|
Selected account reports: 13 tests, 0 failures, 0 errors, 0 skipped.
|
||||||
|
|
||||||
|
./mvnw -Dtest=AccountActivationIntegrationTest test
|
||||||
|
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
|
||||||
|
./mvnw -Dtest=LayerStructureTest test
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
|
||||||
|
./mvnw test
|
||||||
|
Tests run: 105, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
~~~
|
||||||
|
|
||||||
|
### Review follow-up affected account-service checks
|
||||||
|
|
||||||
|
~~~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=EligibleInternOptionIntegrationTest,AccountActivationIntegrationTest,BootstrapIntegrationTest' test
|
||||||
|
|
||||||
|
EligibleInternOptionIntegrationTest: 2 tests, 0 failures, 0 errors, 0 skipped
|
||||||
|
BootstrapIntegrationTest: 4 tests, 0 failures, 0 errors, 0 skipped
|
||||||
|
AccountActivationIntegrationTest: 2 tests, 0 failures, 0 errors, 0 skipped
|
||||||
|
Selected account-service reports: 8 tests, 0 failures, 0 errors, 0 skipped.
|
||||||
|
BUILD SUCCESS
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This query does not authorize Project membership itself; the consuming Project transaction must still recheck
|
||||||
|
membership and ownership invariants. It does not test the later lifecycle mutation workflows that produce locked,
|
||||||
|
deactivated, or completed rows.
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Test Evidence: Atomic first administrator bootstrap
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `ACC-001–ACC-003, ACC-009, SEC-001`
|
||||||
|
- **Scenario IDs:** `AC-ACC-001, AC-ACC-002`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.service.BootstrapIntegrationTest`
|
||||||
|
- **Implementation commit:** `bc70db1d0d8eaa68bb8e22db44e38af27b0fa945`; restart characterization added in `8ff6ee3d873db909b1ce9df690f7a3abb2c3c79d`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Before initialization only bootstrap, bootstrap assets, health, and error rendering are reachable. Concurrent valid submissions create exactly one active Admin, atomically persist initialization, and permanently close bootstrap. A separately started Spring application context connected to the same PostgreSQL database observes the initialized state and cannot create another Admin.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
A PostgreSQL 18.4 integration test releases two Java 25 tasks onto the same service concurrently and asserts the row-locked outcomes and database state through Spring Data JPA. MockMvc checks pre/post-bootstrap route exposure. A characterization method then starts and closes an independent servlet application context against the same container datasource and verifies the durable state through the public bootstrap service.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Two simultaneous submissions produce one `CREATED`, one `ALREADY_INITIALIZED`, one Admin row, and one initialized singleton. Later bootstrap requests cannot create another Admin.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
BootstrapIntegrationTest.java: cannot find symbol class BootstrapService
|
||||||
|
17 compilation errors
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The public bootstrap behavior did not exist.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
The independent-context restart assertion was added as characterization coverage for an evidence gap. No
|
||||||
|
retrospective RED is claimed because the persisted implementation already satisfied it when the test was added.
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw test
|
||||||
|
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
The command used the Java 25 and OrbStack environment exports shown above.
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This test does not prove deployment-network privacy for the temporary bootstrap route. Operations must still bootstrap on a private interface before public exposure.
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Test Evidence: Internship cannot activate before its business start date
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `ACC-019`, `ACC-020`
|
||||||
|
- **Scenario IDs:** `AC-ACC-010` (start-date transition only)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.service.AccountActivationIntegrationTest#internshipCannotActivateBeforeItsBusinessStartDate`
|
||||||
|
- **Implementation commit:** `6181984cf85f184be39513d6313f9cbe8267add5`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
An active Intern account cannot move its separately stored internship from `NOT_STARTED` to `ACTIVE` before the
|
||||||
|
configured inclusive start date in the application's injected business timezone.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The PostgreSQL 18.4 test creates and activates an Intern account through the production SMTP/account services. Its
|
||||||
|
internship starts one business day after the fixed test clock. The Admin attempts the lifecycle transition and the
|
||||||
|
test reloads the profile through the owning feature repository.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The service throws an actionable start-date error and the persisted internship remains `NOT_STARTED`.
|
||||||
|
|
||||||
|
## 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=AccountActivationIntegrationTest#internshipCannotActivateBeforeItsBusinessStartDate test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
Expected code to raise a throwable, but the internship activated before its start date.
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=AccountActivationIntegrationTest#internshipCannotActivateBeforeItsBusinessStartDate test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest,SmtpOnboardingWebIntegrationTest,AccountActivationIntegrationTest,AccountWebIntegrationTest,BootstrapOnboardingWebIntegrationTest,JavaMailSmtpProbeTest,SecurityResponseIntegrationTest test
|
||||||
|
Tests run: 20, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This covers only the early-activation guard. It does not claim the later scheduler, completion, withdrawal, transfer,
|
||||||
|
or session-lifecycle portions of AC-ACC-010.
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Test Evidence: Platform foundation
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `ARC-001–ARC-008, DB-003–DB-012, OPS-003, TST-001–TST-010`
|
||||||
|
- **Scenario IDs:** `AC-DB-001, AC-OPS-002, AC-TST-001`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.config.PlatformFoundationTest.flywayCreatesApprovedPostgresCatalog`, `com.lab.labtimesheet.config.PlatformFoundationTest.testClockIsDeterministic`
|
||||||
|
- **Implementation commit:** `4b37f8fd05804d2d76e11cec1afce52919f2eb59`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Flyway creates the approved 23-table/56-foreign-key PostgreSQL catalog and seed, and tests receive deterministic time without a developer database. Package structure is protected separately by `LayerStructureTest`.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
A full Spring context starts against a PostgreSQL 18.4 Testcontainer. JDBC is used only in this schema/catalog verification test to independently count application tables and foreign keys and inspect the seed. The injected test `Clock` is asserted exactly.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The approved DDL catalog contains 23 application tables and 56 foreign keys. The seed has checkout grace 30 and five Monday–Friday rows. Test time is `2026-08-14T00:00:00Z` in `Asia/Ho_Chi_Minh`.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=PlatformFoundationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 3, Failures: 1, Errors: 1, Skipped: 0
|
||||||
|
PlatformFoundationTest.flywayCreatesApprovedPostgresCatalog: expected: 23 but was: 0
|
||||||
|
PlatformFoundationTest.applicationExposesRequiredModulePackages: ClassNotFound com.lab.labtimesheet.accounts.package-info
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
Flyway reported zero migrations and the first required boundary class was absent, so the failure was caused by the missing foundation.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=PlatformFoundationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Successfully applied 1 migration to schema "public", now at version v1
|
||||||
|
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw test
|
||||||
|
|
||||||
|
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This proves migration replay and catalog shape on an ephemeral local PostgreSQL 18.4 container. It does not prove application container, Compose, CI, external SMTP, browser, publication, or deployment behavior; those boundaries are deferred or owned elsewhere.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# Test Evidence: Production delivery baseline
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `OPS-005`–`OPS-013`, `OPS-017`, `TST-001`, `TST-005`, `TST-009`
|
||||||
|
- **Scenario IDs:** `AC-OPS-002`, `AC-OPS-003`, `AC-OPS-004`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.service.BootstrapIntegrationTest.rootGuidesFreshInstallToBootstrapWhileOtherRoutesRemainHidden`, `src/test/js/delivery-contract.test.mjs`
|
||||||
|
- **Implementation commit:** `cea4378f5699de4919c283b12371941d6742ca4b`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The production image runs as a non-root Java 25 process, exposes health probes, and accepts the same environment-backed datasource configuration with either the optional PostgreSQL 18.4 Compose sidecar or an external database. Gitea verifies every pull request and push, publishes immutable SHA plus `main` image tags only from `main`, and skips native ARM64 work unless the matching runner is explicitly available.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The existing PostgreSQL-backed bootstrap integration test requests the liveness and readiness endpoints before initialization. A dependency-free Node contract checks the deployment files for the required runtime, Compose, trigger, permission, publication, and optional-runner boundaries. Docker and Compose validation then exercise the real build and both database topologies.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
An absent ARM runner must skip the ARM job without blocking AMD64 publication. Enabling the runner creates an ARM64 architecture tag and a combined manifest, while the canonical SHA and `main` tags remain valid AMD64 images when ARM is disabled. Compose must preserve PostgreSQL data in a named volume and must not require the bundled database when an external JDBC URL is supplied.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env PATH=/opt/homebrew/opt/node@24/bin:/usr/bin:/bin node --test src/test/js/delivery-contract.test.mjs
|
||||||
|
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 \
|
||||||
|
PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin \
|
||||||
|
DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock \
|
||||||
|
./mvnw '-Dtest=BootstrapIntegrationTest#rootGuidesFreshInstallToBootstrapWhileOtherRoutesRemainHidden' test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
The delivery contract ran 3 tests and failed all 3 because Dockerfile,
|
||||||
|
.gitea/workflows/verify.yml, and .gitea/workflows/container.yml did not exist.
|
||||||
|
|
||||||
|
The PostgreSQL-backed bootstrap test ran 1 test and failed because
|
||||||
|
/actuator/health/liveness returned 404 instead of 200.
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env PATH=/opt/homebrew/opt/node@24/bin:/usr/bin:/bin node --test src/test/js/delivery-contract.test.mjs
|
||||||
|
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 \
|
||||||
|
PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin \
|
||||||
|
DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock \
|
||||||
|
./mvnw '-Dtest=BootstrapIntegrationTest#rootGuidesFreshInstallToBootstrapWhileOtherRoutesRemainHidden' test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Delivery contract: 3 tests, 3 passed.
|
||||||
|
Bootstrap health regression: 1 test, 1 passed against PostgreSQL 18.4.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
npm ci
|
||||||
|
npm run test:ui
|
||||||
|
npm run build
|
||||||
|
git diff --exit-code -- src/main/resources/static/assets/app.css src/main/resources/static/assets/icons.svg
|
||||||
|
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 \
|
||||||
|
PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin \
|
||||||
|
DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock \
|
||||||
|
./mvnw test
|
||||||
|
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 \
|
||||||
|
PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin \
|
||||||
|
./mvnw -DskipTests -Ddoclint=all javadoc:javadoc
|
||||||
|
|
||||||
|
docker build --check .
|
||||||
|
docker build --platform linux/amd64 --build-arg VCS_REF=validation -t labtimesheet:ci-amd64 .
|
||||||
|
docker build --platform linux/arm64 --build-arg VCS_REF=validation -t labtimesheet:ci-arm64 .
|
||||||
|
docker compose --env-file .env.compose.example config
|
||||||
|
docker compose --env-file .env.compose.example --profile bundled-db config
|
||||||
|
|
||||||
|
Frontend tests: 4 passed; generated assets remained byte-clean.
|
||||||
|
Maven: 217 tests across 49 suites, 0 failures, 0 errors, 0 skipped.
|
||||||
|
Javadoc: BUILD SUCCESS; 83 pre-existing repository-wide warnings.
|
||||||
|
Dockerfile check: passed without warnings. Both Linux architecture images built
|
||||||
|
and reported the requested platform, UID/GID 10001, and readiness HEALTHCHECK.
|
||||||
|
Both Compose configurations parsed successfully.
|
||||||
|
|
||||||
|
Real smoke tests used the AMD64 image with disposable resources. Bundled mode
|
||||||
|
started PostgreSQL 18.4 with the named volume and returned UP from liveness and
|
||||||
|
readiness on 127.0.0.1:28080. External mode used a separately started
|
||||||
|
PostgreSQL 18.4 service and returned UP from readiness on 127.0.0.1:28081.
|
||||||
|
All disposable containers, networks, and the bundled test volume were removed.
|
||||||
|
|
||||||
|
git diff --check: passed.
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
Local validation cannot prove that the private Gitea registry credentials are configured or that an `ubuntu-latest-arm` runner is online. Repository variable `ARM64_RUNNER_AVAILABLE` is the scheduler-safe availability signal because an unavailable runner label cannot be discovered from inside a job that has not yet been scheduled.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Integration Test Evidence
|
||||||
|
|
||||||
|
## Requirement and scenario IDs
|
||||||
|
|
||||||
|
- AUTH-001, AUTH-002, AUTH-011; PRJ-003, PRJ-004, PRJ-017; ERR-001, ERR-003; TST-001 through TST-010.
|
||||||
|
- AC-AUTH-001, AC-AUTH-010, AC-PRJ-001, AC-TST-001.
|
||||||
|
|
||||||
|
## Behavior under test
|
||||||
|
|
||||||
|
The owning Mentor adds several eligible nonmembers under one Project lock and transaction. Null, empty, duplicate, current-member, invalid, or stale/noneligible selections reject the whole batch; no valid prefix becomes a membership.
|
||||||
|
|
||||||
|
## Expected result derivation
|
||||||
|
|
||||||
|
The fixture begins with one Leader. A successful two-Intern batch must yield three current memberships. Every rejected batch leaves the eligible and stale candidate membership count at zero.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
`env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ./mvnw '-Dtest=ProjectControllerTest,ProjectServiceIntegrationTest' test` failed during test compilation with eight `cannot find symbol` errors for the requested `ProjectService.addMembers(long,long,List<Long>)` API. Production compiled first; the failure was the missing behavior boundary rather than the environment or fixture.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
The focused PostgreSQL command was:
|
||||||
|
|
||||||
|
`env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=ProjectServiceIntegrationTest#ownerAddsSeveralEligibleMembersInOneLockedTransaction+memberBatchRejectsMissingDuplicateCurrentAndStaleSelectionsWithoutPartialMutation' test`
|
||||||
|
|
||||||
|
Result: 2 tests, 0 failures, 0 errors, 0 skipped against PostgreSQL 18.4. The
|
||||||
|
successful case added two memberships; the rejection case covered null, empty, duplicate,
|
||||||
|
invalid, current-member, and one-valid-plus-one-stale selections without partial persistence.
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
`env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=ProjectServiceIntegrationTest' test`
|
||||||
|
passed 9/9 tests with no failures, errors, or skips.
|
||||||
|
|
||||||
|
The complete Project plus layer-architecture command was:
|
||||||
|
|
||||||
|
`env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=ProjectControllerTest,ProjectEntityTest,ProjectPersistenceStructureTest,ProjectServiceIntegrationTest,ProjectTaskMutationContextTest,LayerStructureTest' test`
|
||||||
|
|
||||||
|
Result: 38 tests, 0 failures, 0 errors, 0 skipped.
|
||||||
|
|
||||||
|
## External boundaries
|
||||||
|
|
||||||
|
PostgreSQL 18.4 Testcontainers provides the real schema, constraints, JPA transaction, and Project pessimistic lock path. The test does not exercise concurrent requests; existing Project locking coverage remains unchanged.
|
||||||
|
|
||||||
|
After merging exact reviewed `main` `32c8a2d315d2175760c5d4792988cd0aa5ab6dd0`, the affected command was rerun with `UiContractWebTest` included. It passed 45/45 tests with no failures, errors, or skips; the Project service portion remained 9/9 against PostgreSQL 18.4.
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Test Evidence: Completed Project member and Task history
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `AUTH-006`, `PRJ-014`
|
||||||
|
- **Scenario IDs:** `AC-AUTH-007`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.project.service.ProjectServiceIntegrationTest#completedProjectQueriesReturnHistoricalMembersWithoutRequiringACurrentLeader`
|
||||||
|
- **Implementation commit:** `3d954dd`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
After Project completion closes every membership and leadership interval, a historical member can still retrieve read-only Task context and member history. The Task context reports no current Leader and no active members, and every historical member row reports `currentLeader=false`.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The Spring Boot integration test creates a Project and second member through public Project services, then uses direct SQL only as a fixture to reproduce the Iteration 2 completion result: all leadership and membership intervals are closed and the Project is marked `COMPLETED`. After clearing the persistence context, it calls the public Project query APIs as the former member and checks the DTOs.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
A completed Project cannot have a current Leader or active member. Therefore `ProjectTaskContext.currentLeaderMembershipId` is `null`, `activeMembers` is empty, both membership-history rows remain visible, both have leave timestamps, and neither is marked as current Leader.
|
||||||
|
|
||||||
|
## 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=ProjectServiceIntegrationTest#completedProjectQueriesReturnHistoricalMembersWithoutRequiringACurrentLeader test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] ProjectRuleViolationException: Project has no current Leader
|
||||||
|
at com.lab.labtimesheet.feature.project.model.entity.ProjectEntity.currentLeader(ProjectEntity.java:232)
|
||||||
|
at com.lab.labtimesheet.feature.project.service.ProjectQueryService.taskContext(ProjectQueryService.java:113)
|
||||||
|
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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=ProjectServiceIntegrationTest#completedProjectQueriesReturnHistoricalMembersWithoutRequiringACurrentLeader test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Running com.lab.labtimesheet.feature.project.service.ProjectServiceIntegrationTest
|
||||||
|
[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="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest='Project*Test' test
|
||||||
|
|
||||||
|
[INFO] Tests run: 21, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This regression proves completed-state DTO behavior against PostgreSQL 18.4. It does not implement or test the future Project-completion mutation itself, browser rendering, or Task-owned authorization and presentation; direct SQL is confined to constructing the completed aggregate fixture.
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# Test Evidence: Atomic Project workflows
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `PRJ-001`–`PRJ-007`, `PRJ-012`, `PRJ-017`, `AUTH-001`–`AUTH-004`, `AUTH-011`, `DB-003`, `DB-007`
|
||||||
|
- **Scenario IDs:** `AC-AUTH-001`, `AC-AUTH-007`, `AC-AUTH-010`, `AC-PRJ-001`, `AC-PRJ-003`, `AC-PRJ-006`, `AC-PRJ-009`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.project.service.ProjectServiceIntegrationTest`
|
||||||
|
- **Implementation commits:** `25a855e`, `dbf1202`, `af0eb3c`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
PostgreSQL transactions persist a planned Project with its initial membership and leadership term, reject unauthorized or duplicate direct additions, change exactly one Leader without moving Task assignments, enforce role/membership visibility without ID disclosure, and activate only when current eligible membership/leadership and live-Task assignee guards pass.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
A Spring Boot integration test uses the platform-owned PostgreSQL 18.4 Testcontainer and Flyway V1 schema. It calls the public Project service and verifies committed-shape rows and negative-case non-mutation with independent SQL.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Creation yields one Project, one active membership, and one current leadership term. Direct addition yields one membership per Project/Intern pair while allowing the same Intern in a second Project. Leader change yields one closed and one current term while the Task assignee ID remains unchanged. Activation persists `ACTIVE` and `activated_at` when every live Task is assigned to a current eligible membership; a live Task assigned to a closed membership leaves the Project `PLANNED` and the Task intact. Admin and owner visibility is allowed. Intern visibility requires a current membership while the Project is `PLANNED` or `ACTIVE`; a closed membership becomes visible again only after the Project is `COMPLETED`. Unrelated and former-member open-Project IDs are denied uniformly. Completed detail has no current Leader and no mutation capability for Admin, owner, or former members.
|
||||||
|
|
||||||
|
## 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=ProjectServiceIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] cannot find symbol: class CreateProjectCommand
|
||||||
|
[ERROR] cannot find symbol: class ProjectService
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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=ProjectServiceIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Running com.lab.labtimesheet.feature.project.service.ProjectServiceIntegrationTest
|
||||||
|
[INFO] Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Activation transaction regression
|
||||||
|
|
||||||
|
**RED:** the focused PostgreSQL activation tests failed at test compilation because `ProjectService.activate(long, long)` did not exist.
|
||||||
|
|
||||||
|
**GREEN:** after wiring the locked Project aggregate to Account eligibility and `TaskQueryService.countCurrentTasksAssignedOutside`, both focused activation tests passed. The valid Project became `ACTIVE`; the former-member assignee case threw `ProjectRuleViolationException`, retained `PLANNED`, and preserved its live Task.
|
||||||
|
|
||||||
|
## Review round 1 visibility and completed-detail regression
|
||||||
|
|
||||||
|
**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=ProjectServiceIntegrationTest#listAndDetailQueriesEnforceRoleOwnershipAndMembershipWithoutIdDisclosure+completedProjectQueriesReturnHistoricalMembersWithoutRequiringACurrentLeader test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed RED:** `Tests run: 2, Failures: 1, Errors: 1`. The former member still received the active Project in `listVisible`, and completed `detail` threw `ProjectRuleViolationException: Project has no current Leader`.
|
||||||
|
|
||||||
|
**Observed GREEN:** the same command completed with `Tests run: 2, Failures: 0, Errors: 0, Skipped: 0` and `BUILD SUCCESS` against PostgreSQL 18.4. The test closes a real membership and, for completed history, closes all membership and leadership intervals before querying Admin, owner, and former-member detail DTOs.
|
||||||
|
|
||||||
|
## 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='Project*Test' test
|
||||||
|
|
||||||
|
[INFO] Tests run: 31, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This test does not prove MockMvc authorization, Thymeleaf rendering, browser accessibility, or a two-transaction leadership race. In particular it does not claim `AC-PRJ-002`; that concurrency proof remains Iteration 3 scope. Iteration 2 invitations/removals/completion services are also out of scope; SQL is used only to shape the already specified completed-history fixture. Task query semantics have their own Task-owned unit evidence; this integration proves Project consumes that public service boundary atomically without importing Task persistence.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Test Evidence: SMTP draft, test, and activation
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `INT-001–INT-008, ACC-011, SEC-001`
|
||||||
|
- **Scenario IDs:** `AC-INT-001, AC-INT-002`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.integration.service.SmtpIntegrationTest.failedSmtpTestNeverActivatesDraftAndSecretsRemainEncrypted`
|
||||||
|
- **Implementation commit:** `bc70db1d0d8eaa68bb8e22db44e38af27b0fa945`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
SMTP credentials are AES-256-GCM encrypted, only a successfully tested draft can activate, and a failed test cannot alter the draft into an active configuration.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The test persists a draft through Spring Data JPA against PostgreSQL 18.4 using a deterministic test-only master key and a recording SMTP boundary. It forces send failure, inspects database state, rejects activation, then allows the probe and activates the tested draft.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Ciphertext must not contain the submitted password. Failure leaves `status=DRAFT` and `tested_at=null`; activation fails. A successful test sets test provenance and permits exactly that draft to become `ACTIVE`.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=SmtpIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
The pre-refactor RED test source, then named SmtpAccountIntegrationTest.java, reported missing
|
||||||
|
SmtpConfigurationService and SmtpProbe symbols.
|
||||||
|
17 compilation errors
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The SMTP revision and controllable delivery boundaries were absent.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=SmtpIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw test
|
||||||
|
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
The command used the Java 25 and OrbStack environment exports shown above.
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The test intentionally does not contact Mailpit or an external SMTP server. The production adapter is compiled, while delivery semantics are exercised through the recording boundary without network or secret egress.
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# Test Evidence: Iteration 1 Task persistence and authorization
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `AUTH-001`, `AUTH-002`, `AUTH-005`–`AUTH-009`, `AUTH-011`, `PRJ-013`, `PRJ-015`, `PRJ-016`, `TSK-001`–`TSK-005`, `TSK-007`, `TSK-008`, `TSK-011`, `TSK-012`, `TSK-018`
|
||||||
|
- **Scenario IDs:** `I1-TSK-01`–`I1-TSK-05`, `AC-AUTH-001`, `AC-AUTH-003`–`AC-AUTH-007`, `AC-AUTH-010`, `AC-PRJ-008`, `AC-TSK-002`, `AC-TSK-003`, `AC-TSK-006`, `AC-TSK-010`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.task.service.TaskCreationIntegrationTest`
|
||||||
|
- **Implementation commit:** `511ee81a91a79a61cc6afb00097e1b38577c1968`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
PostgreSQL-backed Task operations preserve generic same-Project membership actors, limit ordinary members to self-Task creation, allow current Leaders to assign active same-Project members, validate due dates, restrict status changes to the active current assignee, append authorized comments, exclude deleted Tasks from current reads/progress, render assignee names and empty progress, deny guessed/cross-Project identifiers without writes, give former members read-only access only after completion, and execute the Project-activation and dashboard Task queries.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Thirteen transactional Spring integration tests create real users, Intern profiles, Projects, memberships, leadership terms, calendar events, Tasks, and comments against the approved PostgreSQL 18.4 V1 schema. Assertions inspect returned behavior and persisted rows; there are no mocked domain or database operations.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
A self-Task stores one membership in creator, assigner, and assignee fields. A Leader-created Task retains the Leader membership as creator/assigner and the selected member as assignee. Project start/end due dates are valid; dates before, after, or on a current global day off are invalid. Only an active Project's current assignee can traverse an allowed status edge. Authorized member/Mentor comments append two rows. Four current Tasks with one in each status produce 25% and four unit counts; a deleted fifth Task is absent; zero Tasks has no percentage.
|
||||||
|
|
||||||
|
A former member cannot read Task data while the Project remains planned or active, but can read the completed Project history. List/detail views resolve the assignee display name from the Project service boundary, and their capability flags match current membership, assignment, role, and Project lifecycle.
|
||||||
|
|
||||||
|
## 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=TaskCreationIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] TaskCreationIntegrationTest.java:[6,34] cannot find symbol
|
||||||
|
symbol: class CreateTaskCommand
|
||||||
|
[ERROR] TaskCreationIntegrationTest.java:[8,34] cannot find symbol
|
||||||
|
symbol: class TaskService
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
After creation reached GREEN, the next cohesive workflow increment was separately observed RED:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] TaskCreationIntegrationTest.java:[7,34] cannot find symbol
|
||||||
|
symbol: class TaskCommentView
|
||||||
|
[ERROR] TaskCreationIntegrationTest.java:[8,34] cannot find symbol
|
||||||
|
symbol: class TaskDetails
|
||||||
|
[ERROR] TaskCreationIntegrationTest.java:[9,34] cannot find symbol
|
||||||
|
symbol: class TaskListView
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The review-hardening increment was also observed RED before its implementation. The former-member PostgreSQL regression reached the old list behavior instead of throwing, and the view-contract tests could not compile because `assigneeName`, `canCreate`, `canChangeStatus`, and `canComment` did not exist.
|
||||||
|
|
||||||
|
The second review then made the completed-history fixture production-shaped by closing the current leadership term and all memberships. That focused test was observed RED because the Project read boundary still required `currentLeader()` for a completed Project:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] TaskCreationIntegrationTest.formerMemberReadsOnlyCompletedProjectTaskHistory
|
||||||
|
» TaskNotFound Task or Project was not found
|
||||||
|
[INFO] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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=TaskCreationIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Tests run: 13, 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="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw test
|
||||||
|
|
||||||
|
[INFO] Tests run: 107, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This evidence does not prove browser behavior, shared-shell integration, notification delivery, Iteration 2 work logs/reassignment/edit/deletion, or Iteration 3 concurrency/index plans. The status edge matrix is separately protected by unit evidence. HTTP form, CSRF, template, and direct-route behavior require the companion web evidence.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# Test Evidence: Windows legacy Vietnam timezone startup
|
||||||
|
|
||||||
|
- **Test type:** Integration
|
||||||
|
- **Requirement IDs:** `ARC-001`, `ARC-003`, `GOV-011`, `ATT-002`, `TST-001`, `TST-005`
|
||||||
|
- **Scenario IDs:** `N/A — user-reported cross-platform startup defect`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.ApplicationTimeZoneIntegrationTest.mainCanonicalizesLegacyAliasBeforeStartingSpring`, `com.lab.labtimesheet.ApplicationTimeZoneIntegrationTest.canonicalizesLegacyVietnamAliasBeforePostgresConnects`, `com.lab.labtimesheet.ApplicationTimeZoneIntegrationTest.leavesSupportedSystemTimeZoneUnchanged`
|
||||||
|
- **Implementation commit:** `a9fb9487692e84f5a7e7923570cbded58c362a2f`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The executable entry point replaces the legacy Windows JVM timezone ID `Asia/Saigon` with the canonical business timezone ID `Asia/Ho_Chi_Minh` before pgJDBC opens a PostgreSQL connection. Other supported operating-system timezone IDs remain unchanged.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The entry-point test replaces Spring startup with Mockito's existing static test seam, invokes the real `main` method with a legacy JVM default, and checks that normalization happens before Spring starts. The PostgreSQL test starts a real PostgreSQL 18.4 Testcontainer, proves pgJDBC 42.7.11 is rejected while the JVM default is `Asia/Saigon`, invokes the same startup normalization, and then opens a valid JDBC connection. A negative test verifies that an unrelated supported timezone is not overwritten.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
PostgreSQL does not accept `Asia/Saigon` as a startup `TimeZone`, while the approved business timezone is `Asia/Ho_Chi_Minh`. Therefore only the legacy alias is replaced, the following connection succeeds, and a supported non-Vietnam timezone remains unchanged.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw -Dtest=ApplicationTimeZoneIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
BUILD FAILURE during test compilation.
|
||||||
|
ApplicationTimeZoneIntegrationTest.java: cannot find symbol normalizeDefaultTimeZone()
|
||||||
|
```
|
||||||
|
|
||||||
|
The failing test established that the application had no pre-Spring normalization boundary.
|
||||||
|
|
||||||
|
A second mutation check temporarily removed the new call from `main` and ran:
|
||||||
|
|
||||||
|
```text
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin ./mvnw '-Dtest=ApplicationTimeZoneIntegrationTest#mainCanonicalizesLegacyAliasBeforeStartingSpring' test
|
||||||
|
```
|
||||||
|
|
||||||
|
It failed `1/1` with `expected: "Asia/Ho_Chi_Minh" but was: "Asia/Saigon"`, proving the test protects the entry-point ordering rather than only the helper.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw -Dtest=ApplicationTimeZoneIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=ApplicationTimeZoneIntegrationTest,LabtimesheetApplicationTests,PlatformFoundationTest,TimeConfigurationTest,CalendarDevelopmentProfileWebIntegrationTest' test
|
||||||
|
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw test
|
||||||
|
Tests run: 217, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin ./mvnw -DskipTests -Ddoclint=all javadoc:javadoc
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
A local Java 25 process was also started with `-Duser.timezone=Asia/Saigon` against a disposable PostgreSQL 18.4 database on port `55439`. Hikari connected, Flyway migrated the fresh database, Tomcat started on port `18080`, and Spring reported `Started LabtimesheetApplication`. The process shut down cleanly and the disposable database container was removed.
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The regression executes the installed pgJDBC version against PostgreSQL 18.4 and reproduces the exact rejected timezone value from the Windows report. It does not run the Windows JVM itself; the supplied Windows log is the evidence that its OS/JDK mapping produced `Asia/Saigon`.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Test Evidence: <short behavior name>
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `<ID>`
|
||||||
|
- **Scenario IDs:** `<ID>`
|
||||||
|
- **Test class/method:** `<fully qualified class and method>`
|
||||||
|
- **Implementation commit:** `<short SHA or pending>`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
<Externally observable rule and failure mode protected by this test.>
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
<Setup, action, and assertions. Explain why this is the narrowest production-shaped test.>
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
<Expected values or state derived independently of the implementation.>
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact command>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<relevant failing output and why it failed for the expected missing behavior>
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact command>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<relevant passing output>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact broader command and result>
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
<What this test deliberately does not prove, including infrastructure or browser boundaries.>
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# Test Evidence: Checkout eligibility and stable conflict outcomes
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `ATT-007`, `ATT-008`, `ATT-010`, `ATT-012`
|
||||||
|
- **Scenario IDs:** `AC-ATT-003`, `AC-ATT-004`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.service.AttendanceApplicationServiceTest#rejectsCheckoutWhenInternIsNoLongerEligibleForPersistedWorkDate`, `#translatesConcurrentCheckInUniqueConflictToStableDuplicateRejection`, `#translatesConcurrentCheckoutVersionConflictToStableDuplicateRejection`
|
||||||
|
- **Implementation commit:** `4c39df70e1f901e232669e9090ff5d21393519f0`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Checkout revalidates active internship eligibility for the attendance row's
|
||||||
|
persisted work date. A terminal Intern cannot checkout after checking in.
|
||||||
|
Database uniqueness and optimistic-lock races are translated to stable duplicate
|
||||||
|
punch rejection codes instead of leaking persistence exceptions.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Plain JUnit and Mockito drive the production transactional application service
|
||||||
|
with a fixed Clock, attached policy, persisted row, AccountService eligibility,
|
||||||
|
and repository exceptions. Account state remains behind its public service API.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
False date-aware eligibility returns `INACTIVE_INTERN` before raw checkout is
|
||||||
|
saved. A check-in uniqueness race returns `ALREADY_CHECKED_IN`; a checkout
|
||||||
|
version race returns `ALREADY_CHECKED_OUT`.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=AttendanceApplicationServiceTest#rejectsCheckoutWhenInternIsNoLongerEligibleForPersistedWorkDate test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Expected AttendanceException(INACTIVE_INTERN) but was NullPointerException after
|
||||||
|
the service continued to save checkout without calling AccountService eligibility.
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
BUILD FAILURE
|
||||||
|
Process exited 1.
|
||||||
|
```
|
||||||
|
|
||||||
|
The conflict regressions were also observed RED in the combined focused run:
|
||||||
|
|
||||||
|
```text
|
||||||
|
DataIntegrityViolationException: concurrent unique conflict
|
||||||
|
ObjectOptimisticLockingFailureException: optimistic locking failed
|
||||||
|
Both escaped AttendanceApplicationService instead of stable AttendanceException values.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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: 5, 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: 32, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Process exited 0.
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The account platform has no Iteration 1 terminal-state mutation API, so the
|
||||||
|
completed/withdrawn state is represented through its public date-aware eligibility
|
||||||
|
result. The companion PostgreSQL concurrency test proves the real unique conflict.
|
||||||
@@ -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:** `8b48e281f7e860af435ae35b16c4edeb139286dc`
|
||||||
|
|
||||||
|
## 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: 32, 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:** `8b48e281f7e860af435ae35b16c4edeb139286dc`
|
||||||
|
|
||||||
|
## 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: 32, 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:** `71901d1670f633a1b594bdce3348efebe73fc175`
|
||||||
|
|
||||||
|
## 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: 32, 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:** `71901d1670f633a1b594bdce3348efebe73fc175`
|
||||||
|
|
||||||
|
## 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: 32, 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,210 @@
|
|||||||
|
# Test Evidence: durable fix-branch documentation workflow
|
||||||
|
|
||||||
|
- **Test type:** Unit (documentation contract)
|
||||||
|
- **Requirement IDs:** `OPS-019`, `TST-009`, `TST-010`
|
||||||
|
- **Scenario IDs:** `AC-TST-001`
|
||||||
|
- **Test class/method:** `scripts/verify-fix-branch-workflow.cjs --self-test`
|
||||||
|
- **Implementation commit:** `f013ad7707b36959ddca891fe0d52f81bba3ee80`
|
||||||
|
- **Round-1 review-fix commits:** `d617769499362e92d058684501af3c1ae6b145b0`, `719e02ea902bfb2dbeddc04f12be3617be3427b5`
|
||||||
|
- **Round-2 all-guide regression commit:** `97e991317d55db4f7414678a89a45921802a14b8`
|
||||||
|
- **Round-2 coordination-authority commit:** `9802d5d17f5c07511e1f9cf59ace4b7e48fcdc0e`
|
||||||
|
- **Round-2 plan-contract commit:** `f98e7f39ef38c7882106ffb250155d2a72dcf0dd`
|
||||||
|
- **Round-3 complete-workflow commit:** `445e4fedeb0e06724b876c5731437d2c355cacb2`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Targeted repairs use the realizable `work/fix/<feature>/<what-fix>` branch and
|
||||||
|
clean worktree from taskmaster-verified `main`. Contributor guidance must reject
|
||||||
|
the impossible `work/<feature>/fix/<what-fix>` form while persistent
|
||||||
|
`work/<feature>` refs exist. The tracked copies of root coordination authority
|
||||||
|
must use the same rule. Every targeted-fix guide must also require the complete
|
||||||
|
lifecycle: latest `main`, TDD RED → GREEN, Javadoc during implementation,
|
||||||
|
companion evidence, independent review, and an authorized normal, non-force
|
||||||
|
merge.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Use the tracked Node validator rather than an artificial Java test. It requires
|
||||||
|
the exact approved branch statement and complete targeted-repair lifecycle in
|
||||||
|
each of the six guides. Its self-test independently removes each of the six
|
||||||
|
lifecycle elements from every guide and asserts the file-specific rejection. It
|
||||||
|
also retains a fresh positive nested-branch mutation for every guide. The copied
|
||||||
|
root coordination files are compared byte-for-byte with their main-root sources
|
||||||
|
and checked for their exact approved rules. The original RED proves the
|
||||||
|
required branch name was absent from the four contributor guides; the first
|
||||||
|
review-fix RED proves the executable regression was absent.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The required fix-branch spelling appears exactly once in each of the six tracked
|
||||||
|
documentation artifacts, and the only nested-form reference is inside that
|
||||||
|
artifact's exact approved statement. Each independent simulated positive
|
||||||
|
nested-branch recommendation must fail. The three tracked coordination files
|
||||||
|
must exactly match the authorized main-root versions. Loss of any lifecycle
|
||||||
|
element from any guide must fail. The existing SRS generator must still report
|
||||||
|
260 requirements and 14 use cases.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
rg -n -F 'work/fix/<feature>/<what-fix>' AGENTS.md README.md DEVELOPMENT.md TESTING.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
exit 1; no matching lines
|
||||||
|
```
|
||||||
|
|
||||||
|
The failure was expected: the required realizable repair-branch rule was absent
|
||||||
|
before this documentation change.
|
||||||
|
|
||||||
|
### Review-fix RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
node scripts/verify-fix-branch-workflow.cjs --self-test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
exit 1
|
||||||
|
Error: Cannot find module '.../scripts/verify-fix-branch-workflow.cjs'
|
||||||
|
```
|
||||||
|
|
||||||
|
The executable regression required to reject a positive nested-branch
|
||||||
|
recommendation did not exist.
|
||||||
|
|
||||||
|
### Round-2 RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
node scripts/verify-fix-branch-workflow.cjs --self-test | rg -x 'Positive nested branch recommendations: 6/6 rejected'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
exit 1; no matching line
|
||||||
|
```
|
||||||
|
|
||||||
|
The prior self-test reported only a singular rejection and mutated only
|
||||||
|
`AGENTS.md`; it did not prove an independent rejection for each of the six
|
||||||
|
guides.
|
||||||
|
|
||||||
|
### Round-3 RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
node --check scripts/verify-fix-branch-workflow.cjs
|
||||||
|
node scripts/verify-fix-branch-workflow.cjs --self-test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
exit 1
|
||||||
|
Error: AGENTS.md must contain the complete required targeted-repair workflow exactly once
|
||||||
|
README.md must contain the complete required targeted-repair workflow exactly once
|
||||||
|
DEVELOPMENT.md must contain the complete required targeted-repair workflow exactly once
|
||||||
|
TESTING.md must contain the complete required targeted-repair workflow exactly once
|
||||||
|
docs/superpowers/specs/2026-08-15-access-navigation-icon-intern-picker-design.md must contain the complete required targeted-repair workflow exactly once
|
||||||
|
docs/superpowers/plans/2026-08-15-access-navigation-icon-intern-picker.md must contain the complete required targeted-repair workflow exactly once
|
||||||
|
```
|
||||||
|
|
||||||
|
The six guides had branch naming but not the complete lifecycle contract.
|
||||||
|
|
||||||
|
## Initial GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
node scripts/verify-fix-branch-workflow.cjs --self-test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Fix-branch workflow documentation: 6 approved statements validated
|
||||||
|
Positive nested branch recommendation: rejected
|
||||||
|
```
|
||||||
|
|
||||||
|
### Round-2 GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
node --check scripts/verify-fix-branch-workflow.cjs
|
||||||
|
node scripts/verify-fix-branch-workflow.cjs --self-test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Fix-branch workflow documentation: 6 approved statements validated
|
||||||
|
Positive nested branch recommendations: 6/6 rejected
|
||||||
|
```
|
||||||
|
|
||||||
|
### Round-3 GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
node --check scripts/verify-fix-branch-workflow.cjs
|
||||||
|
node scripts/verify-fix-branch-workflow.cjs --self-test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Fix-branch workflow documentation: 6 approved statements validated
|
||||||
|
Targeted-repair workflow element removals: 36/36 rejected
|
||||||
|
Positive nested branch recommendations: 6/6 rejected
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
node labtimesheet-docs-hub/ui-mockups/build-srs.cjs
|
||||||
|
node -e 'const fs=require("node:fs"); const checks=[["authoritative","labtimesheet-docs-hub/requirements-specification.md",/^\| ([A-Z]{2,4}-\d{3}) \|/gm],["explained","labtimesheet-docs-hub/explained/requirements-specification.md",/^\| ([A-Z]{2,4}-\d{3}) \|/gm],["simple","labtimesheet-docs-hub/explained/requirements-specification-simple.md",/^- \*\*([A-Z]{2,4}-\d{3}):\*\*/gm],["generated SRS","labtimesheet-docs-hub/software-requirements-specification.md",/^\| ([A-Z]{2,4}-\d{3}) \|/gm]]; for (const [name,file,pattern] of checks) { const ids=[...fs.readFileSync(file,"utf8").matchAll(pattern)].map(match=>match[1]); if (ids.length !== 260 || new Set(ids).size !== 260) throw new Error(`${name}: ${ids.length} rows, ${new Set(ids).size} unique`); console.log(`${name}: ${ids.length} rows, ${new Set(ids).size} unique IDs`); } const srs=fs.readFileSync("labtimesheet-docs-hub/software-requirements-specification.md","utf8"); const useCases=(srs.match(/^### 5\.\d+ UC-\d{2} —/gm)||[]).length; if (useCases !== 14) throw new Error(`SRS use cases: ${useCases}`); console.log(`generated SRS: ${useCases} use cases`);'
|
||||||
|
node -e 'const fs=require("node:fs"); const path=require("node:path"); let checked=0; const broken=[]; for (const file of process.argv.slice(1)) { const text=fs.readFileSync(file,"utf8"); for (const match of text.matchAll(/!?\[[^\]]*\]\(([^)]+)\)/g)) { const target=match[1].trim().replace(/^<|>$/g,"").split("#")[0].split("?")[0]; if (!target || /^[a-z][a-z0-9+.-]*:/i.test(target) || target.startsWith("//")) continue; checked += 1; if (!fs.existsSync(path.resolve(path.dirname(file), decodeURIComponent(target)))) broken.push(`${file}: ${target}`); } } if (broken.length) throw new Error(`Broken local Markdown links:\n${broken.join("\n")}`); console.log(`Local Markdown links: ${checked} resolved`);' AGENTS.md README.md DEVELOPMENT.md TESTING.md docs/superpowers/specs/2026-08-15-access-navigation-icon-intern-picker-design.md docs/superpowers/plans/2026-08-15-access-navigation-icon-intern-picker.md docs/tests/unit/fix-branch-workflow-documentation.md
|
||||||
|
cmp -s .agents/PROJECT_PLAN.md /Users/sechmachine/Documents/WebProjects/labtimesheet/.agents/PROJECT_PLAN.md && cmp -s .agents/skills/orchestrate-labtimesheet-iteration/SKILL.md /Users/sechmachine/Documents/WebProjects/labtimesheet/.agents/skills/orchestrate-labtimesheet-iteration/SKILL.md && cmp -s PRODUCT.md /Users/sechmachine/Documents/WebProjects/labtimesheet/PRODUCT.md && node -e 'const fs=require("node:fs"); const files=[".agents/PROJECT_PLAN.md",".agents/skills/orchestrate-labtimesheet-iteration/SKILL.md","PRODUCT.md"]; const forms=["work/fix/<feature>/<what-fix>","work/<feature>/fix/<what-fix>"]; for (const file of files) { const text=fs.readFileSync(file,"utf8"); for (const form of forms) { if (text.split(form).length !== 2) throw new Error(file+": expected one "+form); } } console.log("Root coordination authority: "+files.length+" approved branch rules match exactly");'
|
||||||
|
git diff --check 8be1b754e188367b260981718a5d33fc2d4d8a3b 445e4fedeb0e06724b876c5731437d2c355cacb2
|
||||||
|
```
|
||||||
|
|
||||||
|
The SRS regeneration and count assertion ran from the main root because the
|
||||||
|
ignored requirements hub is local authority there. The root-authority
|
||||||
|
comparisons, link assertion, and exact base-to-candidate `git diff --check`
|
||||||
|
ran from this fix worktree; the SRS generator also rejects a broken local SRS
|
||||||
|
target before it writes the generated file.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Wrote labtimesheet-docs-hub/software-requirements-specification.md
|
||||||
|
Requirements: 260; use cases: 14; screens: 48; mockup embeds: 48
|
||||||
|
authoritative: 260 rows, 260 unique IDs
|
||||||
|
explained: 260 rows, 260 unique IDs
|
||||||
|
simple: 260 rows, 260 unique IDs
|
||||||
|
generated SRS: 260 rows, 260 unique IDs
|
||||||
|
generated SRS: 14 use cases
|
||||||
|
Fix-branch workflow documentation: 6 approved statements validated
|
||||||
|
Targeted-repair workflow element removals: 36/36 rejected
|
||||||
|
Positive nested branch recommendations: 6/6 rejected
|
||||||
|
Root coordination authority: 3 approved branch rules match exactly
|
||||||
|
Local Markdown links: 7 resolved
|
||||||
|
git diff --check 8be1b754e188367b260981718a5d33fc2d4d8a3b 445e4fedeb0e06724b876c5731437d2c355cacb2: exit 0
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This documentation contract does not create or manipulate Git branches, start
|
||||||
|
the application, or replace branch-owner review. It validates the durable rule
|
||||||
|
and SRS traceability only; a taskmaster still authorizes branch creation,
|
||||||
|
integration, and any push.
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# Test Evidence: Attendance targeted Lombok boilerplate retrofit
|
||||||
|
|
||||||
|
- **Test type:** Unit compiled-contract audit
|
||||||
|
- **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`
|
||||||
|
- **Test class/method:**
|
||||||
|
`com.lab.labtimesheet.feature.attendance.AttendanceLombokBoilerplateTest#generatedConstructorsPreserveParameterListsAndVisibility`,
|
||||||
|
`com.lab.labtimesheet.feature.attendance.AttendanceLombokBoilerplateTest#immutableModelsRemainRecordsWithTheirComponentContracts`,
|
||||||
|
`com.lab.labtimesheet.feature.attendance.AttendanceLombokBoilerplateTest#entitiesExposeOnlyIntentionalPublicAndProtectedDeclaredMethods`,
|
||||||
|
`com.lab.labtimesheet.feature.attendance.AttendanceLombokBoilerplateTest#componentsExposeOnlyIntentionalPublicAndProtectedDeclaredMethods`
|
||||||
|
- **Implementation commit:** `82ad8202fd31f77db8c3932a902dba07cee70894`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Attendance uses the installed Lombok processor only for mechanical constructors while preserving the compiled API:
|
||||||
|
package-level Spring injection, protected JPA construction, immutable record components, domain constructors and
|
||||||
|
mutations, raw punch and attached-policy history rules, composite-key identity, and existing public method names.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Reflection inspects compiled `feature.attendance` classes rather than source spelling. It verifies every constructor's
|
||||||
|
parameter order and modifier, every immutable model's record components, and the exact public/protected declared method
|
||||||
|
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. Exact controller and service surfaces likewise prevent Lombok from exposing
|
||||||
|
collaborator getters/setters or generated `equals`/`hashCode`/`toString` methods.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Five injection-only components expose only their package-scoped dependency constructors. Six JPA/embeddable types
|
||||||
|
retain protected no-argument construction alongside their intentional domain constructors, and the stateless domain
|
||||||
|
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`. Both controllers and all four Attendance
|
||||||
|
services expose only their existing route or application/domain operations, never their injected collaborators.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=AttendanceLombokBoilerplateTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 3, Failures: 2, Errors: 0, Skipped: 0
|
||||||
|
The initial source audit first failed on AttendanceController because its mechanical dependency constructor remained,
|
||||||
|
and on AttendancePolicyEntity because its mechanical protected JPA constructor remained. The immutable-record and
|
||||||
|
business-method retention guard passed. After this RED established the retrofit gap, the permanent regression was
|
||||||
|
replaced with compiled reflection/API checks so formatting or annotation spelling cannot affect the result.
|
||||||
|
BUILD FAILURE
|
||||||
|
Process exited 1.
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=AttendanceLombokBoilerplateTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 4, 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: 39, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
PostgreSQL 18.4 started and Flyway applied V1 for the persistence and concurrency contexts.
|
||||||
|
BUILD SUCCESS
|
||||||
|
Process exited 0.
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The reflection audit does not replace Spring/JPA bootstrapping, MVC property access, PostgreSQL persistence, or
|
||||||
|
Javadoc/doclint. Those checks remain affected verification. No application behavior or public API is intentionally
|
||||||
|
changed by this retrofit.
|
||||||
|
|
||||||
|
Additional verification on the same source tree:
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -DskipTests compile
|
||||||
|
BUILD SUCCESS
|
||||||
|
|
||||||
|
./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
|
||||||
|
expand Lombok constructors; repository policy exempts generated trivial constructors from duplicate Javadoc.
|
||||||
|
```
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# Test Evidence: Platform Lombok boilerplate retrofit
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `Engineering policy — targeted Lombok retrofit`
|
||||||
|
- **Scenario IDs:** `Source-audit RED/GREEN`
|
||||||
|
- **Test class/method:** `N/A — reproducible source audit; behavior is covered by the affected suites below`
|
||||||
|
- **Implementation commit:** `41448903aa924dc5852db8d7bb4d9319cb9f91a7`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Platform-owned Spring collaborators, request forms, and JPA entities must not retain eligible handwritten
|
||||||
|
dependency-assignment constructors, trivial accessors, or empty persistence constructors. The retrofit must preserve
|
||||||
|
constructor visibility, form normalization, entity encapsulation, defensive copies of credential/token bytes, and all
|
||||||
|
account, authentication, SMTP, and cross-feature behavior.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The source audit searches only the 79 members classified as mechanical after reading every root/config,
|
||||||
|
`feature.account`, and `feature.integration` production type. It deliberately excludes normalized email/display-name
|
||||||
|
setters, defensive byte-array getters, domain constructors and factories, state transitions, the normalized
|
||||||
|
`AccountService` public-origin constructor, `SecretCipher` key construction, and the two-constructor JavaMail test
|
||||||
|
seam. No permanent annotation-presence test was added because annotations are an implementation detail; compilation
|
||||||
|
and production-shaped tests protect the real contracts.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Before the retrofit the audit must find 79 eligible handwritten members and exit 1. After targeted Lombok generation,
|
||||||
|
the same audit must find none and exit 0, while the retained non-mechanical members remain explicit.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
matches=$( { rg -n '^ (public )?(AccountController|BootstrapAccessFilter|BootstrapController|BootstrapService|DatabaseUserDetailsService|SmtpController|SmtpWarningAdvice|MailDeliveryService|SmtpConfigurationService)\(' src/main/java/com/lab/labtimesheet/feature/account src/main/java/com/lab/labtimesheet/feature/integration; rg -n '^ public (String getMasterKey|void setMasterKey)\(' src/main/java/com/lab/labtimesheet/config/SecurityProperties.java; rg -n '^ public (String get(Token|Password|ConfirmPassword)|void set(Token|Password|ConfirmPassword))\(' src/main/java/com/lab/labtimesheet/feature/account/model/dto/ActivationForm.java; rg -n '^ public (String get(Email|DisplayName|Password)|void setPassword)\(' src/main/java/com/lab/labtimesheet/feature/account/model/dto/BootstrapForm.java; rg -n '^ public .+ (get(Email|DisplayName|Role|StudentCode|InternshipStart|InternshipEnd)|set(Role|StudentCode|InternshipStart|InternshipEnd))\(' src/main/java/com/lab/labtimesheet/feature/account/model/dto/CreateAccountForm.java; rg -n '^ public (Long getDraftId|void setDraftId)\(' src/main/java/com/lab/labtimesheet/feature/integration/model/dto/SmtpActionForm.java; rg -n '^ public .+ (get(Host|Port|SecurityMode|Username|Password|FromAddress|FromName)|set(Host|Port|SecurityMode|Username|Password|FromAddress|FromName))\(' src/main/java/com/lab/labtimesheet/feature/integration/model/dto/SmtpForm.java; rg -n '^ protected (AppUser|InternProfile|SystemState|UserActionToken|SmtpConfiguration)\(\)' src/main/java/com/lab/labtimesheet/feature/account/model/entity src/main/java/com/lab/labtimesheet/feature/integration/model/entity; rg -n '^ public .+ (get(Id|Email|DisplayName|PasswordHash|GlobalRole|AccountStatus|ActivatedAt|InternshipStatus|InternshipStartDate|InternshipEndDate|UserId|Purpose|ExpiresAt|UsedAt|InvalidatedAt|Status|Host|Port|SecurityMode|Username|SecretKeyVersion|FromAddress|FromName|TestedAt)|isInitialized)\(' src/main/java/com/lab/labtimesheet/feature/account/model/entity src/main/java/com/lab/labtimesheet/feature/integration/model/entity; } ); if [ -n "$matches" ]; then printf '%s\n' "$matches"; printf 'RED: eligible handwritten Lombok boilerplate remains (%s matches)\n' "$(printf '%s\n' "$matches" | wc -l | tr -d ' ')"; exit 1; fi; printf 'GREEN: no eligible handwritten Lombok boilerplate remains\n'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
RED: eligible handwritten Lombok boilerplate remains (79 matches)
|
||||||
|
Process exited with code 1 because the confirmed mechanical members were still handwritten.
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
The exact RED source-audit command above was repeated without alteration.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
GREEN: no eligible handwritten Lombok boilerplate remains
|
||||||
|
Process exited with code 0.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -DskipTests compile
|
||||||
|
BUILD SUCCESS — 127 production source files compiled on Java 25.
|
||||||
|
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=LabtimesheetApplicationTests,LayerStructureTest,PlatformFoundationTest,TimeConfigurationTest,SecurityResponseIntegrationTest,AccountWebIntegrationTest,AuthenticationWebIntegrationTest,BootstrapOnboardingWebIntegrationTest,AccountActivationIntegrationTest,BootstrapIntegrationTest,SmtpOnboardingWebIntegrationTest,JavaMailSmtpProbeTest,SmtpIntegrationTest test
|
||||||
|
BUILD SUCCESS — Tests run: 29, Failures: 0, Errors: 0, Skipped: 0; PostgreSQL 18.4.
|
||||||
|
|
||||||
|
./mvnw test
|
||||||
|
BUILD SUCCESS — Tests run: 197, Failures: 0, Errors: 0, Skipped: 0; PostgreSQL 18.4.
|
||||||
|
|
||||||
|
./mvnw -DskipTests -Ddoclint=all javadoc:javadoc
|
||||||
|
BUILD SUCCESS — doclint reported no errors; Maven emitted 66 non-fatal missing-comment warnings across the integrated
|
||||||
|
tree, including generated default constructors/accessors.
|
||||||
|
|
||||||
|
git diff --check
|
||||||
|
No output; exit 0.
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This source audit does not prove Lombok internals or enforce a preferred annotation spelling. The compile and
|
||||||
|
PostgreSQL-backed affected/full suites prove generated constructor/accessor compatibility with Spring binding,
|
||||||
|
Security, JPA/Hibernate, Thymeleaf, and existing cross-feature consumers. No dependency, schema, migration, token,
|
||||||
|
credential, template, container, CI, or runtime configuration was changed.
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
# Test Evidence: Targeted Project Lombok boilerplate
|
||||||
|
|
||||||
|
- **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:** N/A; the temporary source audit was removed after its RED/GREEN cycle
|
||||||
|
- **Implementation commit:** `e5639c1`
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## Temporary RED/GREEN method
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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=ProjectEntityTest,ProjectPersistenceStructureTest,ProjectTaskMutationContextTest test
|
||||||
|
[INFO] Tests run: 8, 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: 44, 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 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.
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Test Evidence: Reporting Lombok boilerplate boundary
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `ARC-005`, `OPS-019`, `OPS-021`
|
||||||
|
- **Scenario IDs:** `N/A — user-directed behavior-preserving refactor with no product acceptance scenario`
|
||||||
|
- **Test class/method:** Temporary executable source audit plus stable Reporting compile/behavior/architecture suites
|
||||||
|
- **Implementation commit:** `e2b206c27e494fbcd0cc3ed99ff8c2c470285f9e`
|
||||||
|
|
||||||
|
## 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 -Dtest=LayerStructureTest,ReportingArchitectureTest test
|
||||||
|
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 2.198 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.
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# Test Evidence: Task Lombok boilerplate retrofit
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `AUTH-011`, `TSK-001`–`TSK-012`, `DB-004`
|
||||||
|
- **Scenario IDs:** `AC-TSK-001`–`AC-TSK-006`, `AC-TSK-010`
|
||||||
|
- **Test class/method:** Source audit plus existing Task unit, web, and PostgreSQL integration suites
|
||||||
|
- **Implementation commit:** `7c1a26d`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The Task feature uses the configured Lombok processor for mechanical dependency-injection constructors, JPA no-argument constructors, and existing entity getters. Explicit Task constructors and mutation methods remain responsible for initial state, attribution, timestamps, and workflow invariants. The retrofit must not add entity equality, hash, string, or setter behavior and must not alter the existing public getter contract.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The source audit counts the eligible handwritten constructors and getter methods before and after the retrofit. Existing Task tests then exercise Spring injection, MVC binding, JPA materialization, entity getters, authorization, status transitions, comments, and Project/attendance boundaries through the same public behavior used before the source-only change.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Four Spring components have injection-only constructors, so all four may use `@RequiredArgsConstructor`. `Task` and `TaskComment` need protected JPA no-argument constructors and may use targeted Lombok generation. The 17 existing entity getters may be generated, but `Task.deletedByMembershipId`, `Task.updatedAt`, and `Task.version` must remain without newly exposed getters. Domain constructors and `Task.changeStatus` must remain explicit.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
task_component_boilerplate=$(rg -n 'public (TaskController|TaskService|TaskQueryService|TaskDashboardService)\(' src/main/java/com/lab/labtimesheet/feature/task | wc -l | tr -d ' ')
|
||||||
|
task_entity_boilerplate=$(rg -n 'protected (Task|TaskComment)\(\)|public (Long|long|String|Instant|LocalDate|TaskStatus) get[A-Z][A-Za-z0-9]*\(\)' src/main/java/com/lab/labtimesheet/feature/task/model/entity | wc -l | tr -d ' ')
|
||||||
|
printf 'component_boilerplate=%s entity_boilerplate=%s\n' "$task_component_boilerplate" "$task_entity_boilerplate"
|
||||||
|
test "$task_component_boilerplate" -eq 0 -a "$task_entity_boilerplate" -eq 0
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
component_boilerplate=4 entity_boilerplate=19
|
||||||
|
Exit status 1. The Task package still contained all eligible handwritten boilerplate.
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
task_component_boilerplate=$(rg -n 'public (TaskController|TaskService|TaskQueryService|TaskDashboardService)\(' src/main/java/com/lab/labtimesheet/feature/task | wc -l | tr -d ' ')
|
||||||
|
task_entity_boilerplate=$(rg -n 'protected (Task|TaskComment)\(\)|public (Long|long|String|Instant|LocalDate|TaskStatus) get[A-Z][A-Za-z0-9]*\(\)' src/main/java/com/lab/labtimesheet/feature/task/model/entity | wc -l | tr -d ' ')
|
||||||
|
printf 'component_boilerplate=%s entity_boilerplate=%s\n' "$task_component_boilerplate" "$task_entity_boilerplate"
|
||||||
|
test "$task_component_boilerplate" -eq 0 -a "$task_entity_boilerplate" -eq 0
|
||||||
|
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -DskipTests compile
|
||||||
|
|
||||||
|
javap -classpath target/classes -p \
|
||||||
|
com.lab.labtimesheet.feature.task.model.entity.Task \
|
||||||
|
com.lab.labtimesheet.feature.task.model.entity.TaskComment \
|
||||||
|
com.lab.labtimesheet.feature.task.service.TaskService \
|
||||||
|
com.lab.labtimesheet.feature.task.service.TaskQueryService \
|
||||||
|
com.lab.labtimesheet.feature.task.service.TaskDashboardService \
|
||||||
|
com.lab.labtimesheet.feature.task.controller.TaskController
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
component_boilerplate=0 entity_boilerplate=0
|
||||||
|
Maven compile: BUILD SUCCESS; 127 production source files compiled with Java 25.
|
||||||
|
Bytecode inspection retained the four public component constructors, both protected JPA constructors, and all 17 existing entity getters. No getter exists for deletedByMembershipId, updatedAt, or version; domain constructors and Task.changeStatus remain explicit.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest='TaskDomainRulesTest,TaskPersistenceStructureTest,TaskQueryServiceTest,TaskDashboardServiceTest' test
|
||||||
|
# BUILD SUCCESS: 26 tests, 0 failures, 0 errors, 0 skipped.
|
||||||
|
|
||||||
|
./mvnw -Dtest='TaskControllerTest,ProjectTaskShellContractTest,ProjectTaskFormAccessibilityWebTest' test
|
||||||
|
# BUILD SUCCESS: 28 tests, 0 failures, 0 errors, 0 skipped.
|
||||||
|
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest='TaskCreationIntegrationTest,TaskMutationBoundaryTest' test
|
||||||
|
# BUILD SUCCESS against PostgreSQL 18.4: 16 tests, 0 failures, 0 errors, 0 skipped.
|
||||||
|
|
||||||
|
./mvnw -DskipTests -Ddoclint=all javadoc:javadoc
|
||||||
|
# BUILD SUCCESS. Existing warnings were outside feature.task; the Task package emitted no warning.
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This source audit does not prove runtime behavior by itself. The affected Task tests and compilation/Javadoc gates cover the behavior-preserving contract; no browser walkthrough or production database is required because templates, mappings, schema, and business logic are unchanged. Unprivileged sandbox attempts could not attach Mockito's Java agent or reach the host Docker socket; the same commands passed outside that sandbox, with the verified OrbStack socket supplied for Testcontainers.
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Test Evidence: Package-by-feature structure
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `ARC-001–ARC-008`
|
||||||
|
- **Scenario IDs:** No direct acceptance-scenario mapping (architecture regression)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.config.LayerStructureTest.applicationUsesOnlyApprovedPackageByFeatureStructure`
|
||||||
|
- **Implementation commit:** `1235204bf1298599264a07943ca1167432556bd2`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The Spring Boot application class remains in the root package, shared wiring remains in `config`, and business code uses only the approved feature and feature-layer packages. Legacy feature-first placeholders, global business layers, and cross-feature repository/entity imports are rejected.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
A no-dependency JUnit test inspects the production source tree. It checks the root directories, permits the complete seven-feature vocabulary for branch integration, limits nested packages to the approved feature layers, and scans Java imports for persistence leakage across features.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The platform branch has only `config` and `feature` below `com.lab.labtimesheet`; its present features are a nonempty subset of account, integration, project, task, attendance, notification, and reporting. A feature may call another feature's public service/DTO API but must not import another feature's repository or entity.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
./mvnw -Dtest=LayerStructureTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
actual directories included exception, controller, projects, configuration,
|
||||||
|
repository, service, model, accounts, config, attendance, dto, reporting,
|
||||||
|
and notifications; expected feature and config
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The failure exposed both the superseded global-layer worktree and the committed legacy `ModuleBoundary` package placeholders before the corrective move.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
./mvnw -Dtest=LayerStructureTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw test
|
||||||
|
|
||||||
|
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This source-tree regression protects package naming and import direction. It does not prove runtime authorization, database transaction behavior, browser flows, containerization, CI, or deployment.
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# Test Evidence: Platform production API Javadocs
|
||||||
|
|
||||||
|
- **Test type:** Unit (documentation/static verification)
|
||||||
|
- **Requirement IDs:** Repository Javadoc implementation standard; Iteration 1 retrofit exception
|
||||||
|
- **Scenario IDs:** No runtime acceptance-scenario mapping
|
||||||
|
- **Test class/method:** Maven Javadoc Plugin 3.12.0 over Platform production sources
|
||||||
|
- **Implementation commit:** `8ff6ee3d873db909b1ce9df690f7a3abb2c3c79d`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Platform-owned production types and declared public/protected non-trivial APIs under the root application package,
|
||||||
|
`config`, `feature.account`, and `feature.integration` describe their business purpose and important authorization,
|
||||||
|
transaction, state-transition, time, persistence, encryption, and raw-token boundaries. Trivial form/entity accessors
|
||||||
|
remain intentionally undocumented as permitted by the repository standard.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The Maven Javadoc Plugin generates protected/public API documentation using Java 25 with doclint enabled. The
|
||||||
|
`missing` category is disabled because the repository explicitly exempts trivial accessors and generated methods;
|
||||||
|
all structural HTML/reference/syntax categories remain enabled. Compilation and the full runtime suite separately
|
||||||
|
verify the documented sources.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Documentation generation completes without doclint errors or warnings for the selected categories, and Java
|
||||||
|
compilation plus all Platform tests remain green.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Not applicable: this is the approved Iteration 1 documentation retrofit. No runtime RED was invented.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Before the retrofit, manual source audit found missing type and non-trivial API Javadocs throughout Platform-owned
|
||||||
|
config, account, and integration code. This is review evidence, not a claimed executable RED.
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -DskipTests -Dshow=protected -Ddoclint=all,-missing javadoc:javadoc
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Maven Javadoc Plugin 3.12.0
|
||||||
|
BUILD SUCCESS
|
||||||
|
No Javadoc warnings were emitted.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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 test
|
||||||
|
Tests run: 26, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
Generated Javadocs validate documentation syntax and references, not whether every statement is behaviorally true.
|
||||||
|
The focused and full production-shaped tests provide that separate runtime evidence. Private fields/helpers and
|
||||||
|
trivial accessors are outside the retrofit contract.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Test Evidence: Locked Project context for Task mutations
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `AUTH-001`, `AUTH-011`, `PRJ-012`
|
||||||
|
- **Scenario IDs:** `AC-AUTH-001`, `AC-AUTH-010`, `AC-PRJ-006`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.project.service.ProjectTaskMutationContextTest#loadsTheProjectForUpdateBeforeBuildingTheTaskMutationContext`
|
||||||
|
- **Implementation commit:** `19a3518`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Task mutations obtain their Project authorization and current lifecycle, Leader, owning-Mentor, and active-member facts from a DTO-only Project service boundary after the Project row has been locked for update. Missing and unauthorized Projects retain the same non-disclosing denial behavior.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The isolated service test invokes `ProjectService.taskMutationContext(actorUserId, projectId)`, verifies that `ProjectRepository.findLockedById` is used and the ordinary `findById` path is not used, and verifies that only the locked entity is passed to the existing Project-owned authorization and DTO mapper. The PostgreSQL integration test additionally exercises the public API with authorized, unauthorized, current-member, and former-member data.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Exactly one pessimistic Project lookup occurs before context evaluation. The returned `ProjectTaskContext` exposes scalar/DTO facts only; no Project repository or entity crosses the feature boundary. When called from Task's active transaction, Spring's default `REQUIRED` propagation keeps the row lock in that transaction through its commit or rollback.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=ProjectTaskMutationContextTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] constructor ProjectService ... cannot be applied to given types
|
||||||
|
[ERROR] incompatible types: ProjectEntity cannot be converted to long
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The test failed to compile because Project had no mutation-context API and its context mapper accepted only an unlocked Project ID lookup.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=ProjectTaskMutationContextTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Running com.lab.labtimesheet.feature.project.service.ProjectTaskMutationContextTest
|
||||||
|
[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="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest='Project*Test' test
|
||||||
|
|
||||||
|
[INFO] Tests run: 20, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The unit test proves the locked repository path and DTO-only handoff, while the integration coverage proves current Project authorization/member mapping against PostgreSQL 18.4. It does not orchestrate two concurrent database transactions; the lock-retention guarantee relies on the public method's `@Transactional` default `REQUIRED` propagation and the Task caller retaining its outer transaction.
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# Test Evidence: Project lifecycle domain rules
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `PRJ-001`–`PRJ-007`, `PRJ-012`, `PRJ-017`, `AUTH-001`–`AUTH-004`
|
||||||
|
- **Scenario IDs:** `AC-PRJ-001`, `AC-PRJ-003`, `AC-PRJ-006`, `AC-PRJ-009`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.project.model.entity.ProjectEntityTest`
|
||||||
|
- **Implementation commits:** `25a855e`, `dbf1202`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Project creation cannot produce an empty or leaderless aggregate; direct membership rejects ineligible or duplicate current members; leadership changes leave one current term; activation is owning-Mentor-only and requires an eligible active member, an eligible active current Leader, and valid current Task assignees.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Plain JUnit drives the aggregate through its public factory and mutation methods. It asserts externally observable state and denials without Spring or database infrastructure.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
A planned Project starts with one current membership and one current leadership term. Adding a different eligible Intern yields two current memberships. Changing Leader closes one term and opens one term while retaining both memberships. Activation changes only `PLANNED` to `ACTIVE` when the owning Mentor acts, the supplied active-Intern set contains a current member and the current Leader, and every Task assignee guard passes.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=ProjectTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] ProjectTest.java:[124,20] cannot find symbol: class Project
|
||||||
|
[ERROR] ProjectTest.java:[135,20] cannot find symbol: class EligibleIntern
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=ProjectEntityTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Running com.lab.labtimesheet.feature.project.model.entity.ProjectEntityTest
|
||||||
|
[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Activation guard regression
|
||||||
|
|
||||||
|
**RED:** after strengthening the aggregate test with the active-Intern set, compilation failed because `ProjectEntity.activate` still accepted only `(long, boolean, Instant)` and could not prove that the current Leader remained eligible and active.
|
||||||
|
|
||||||
|
**GREEN:** after adding the active-Intern input and aggregate checks, `./mvnw -Dtest=ProjectEntityTest test` passed 6 tests with zero failures, errors, or skips.
|
||||||
|
|
||||||
|
## 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='Project*Test' test
|
||||||
|
|
||||||
|
[INFO] Tests run: 25, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This unit test does not prove JPA/Flyway mappings, PostgreSQL constraints or transaction concurrency, Spring Security routing, the Task query implementation, or browser rendering; those are covered at their narrower integration and web layers.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# Test Evidence: Project layer and JPA structure
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `ARC-002`, `ARC-005`–`ARC-007`, `OPS-018`–`OPS-020`, `TST-001`–`TST-010`
|
||||||
|
- **Scenario IDs:** `I1-PRJ-01`–`I1-PRJ-05`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.project.repository.ProjectPersistenceStructureTest#projectPersistenceUsesTheRequiredLayerPackagesAndSpringDataJpa`
|
||||||
|
- **Implementation commits:** `25a855e`, `af0eb3c`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Project-owned production code follows the authoritative feature-first package layout, persists aggregate entities through Spring Data JPA, keeps JDBC operations out of Project business services, and does not shadow Account or Task persistence.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Plain JUnit inspects the public Project entity, repository, and service types. It verifies their exact feature/layer packages, the entity's JPA mapping, the repository's `JpaRepository` contract, the absence of JDBC service dependencies, and the absence of foreign-table Account/Task shadow entities.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The Project aggregate is under `feature.project.model.entity`, persistence under `feature.project.repository`, business logic under `feature.project.service`, the service has zero JDBC collaborators, and Account/Task persistence remains owned by those features.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=ProjectPersistenceStructureTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] cannot find symbol: class ProjectUserRepository
|
||||||
|
[ERROR] cannot find symbol: class ProjectInternProfileRepository
|
||||||
|
[ERROR] cannot find symbol: class ProjectTaskRepository
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The RED was observed after removing Project-owned shadow mappings of Account and Task tables. It proves the service still required cross-feature dependencies and could not be made green by retaining forbidden repositories.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=ProjectPersistenceStructureTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Running com.lab.labtimesheet.feature.project.repository.ProjectPersistenceStructureTest
|
||||||
|
[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="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=LayerStructureTest,ProjectPersistenceStructureTest,ProjectEntityTest test
|
||||||
|
|
||||||
|
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Iteration 1 Javadoc retrofit verification
|
||||||
|
|
||||||
|
No behavioral RED was manufactured for documentation. The initial Project-scoped doclint run
|
||||||
|
reported 29 warnings for missing type comments, an implicit public advice constructor, and
|
||||||
|
accessor comments without main descriptions. After documenting every Project-owned production
|
||||||
|
type and declared public/protected API, the same scoped command passed:
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -DskipTests -Dmaven.javadoc.failOnWarnings=true -Ddoclint=all -Dsubpackages=com.lab.labtimesheet.feature.project javadoc:javadoc
|
||||||
|
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
[INFO] Total time: 2.579 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This check does not prove database mappings, transaction behavior, MVC routing, or runtime authorization; those remain covered by PostgreSQL and MockMvc tests. Whole-application fail-on-warning Javadoc remains an integration responsibility after every feature owner completes the approved Iteration 1 retrofit; this evidence deliberately scopes generation to the Project-owned package.
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# Test Evidence: Bounded SMTP transport and configured sender name
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `INT-005`, `INT-007`, `NOT-008`
|
||||||
|
- **Scenario IDs:** No direct acceptance-scenario mapping (transport-adapter regression)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.integration.service.JavaMailSmtpProbeTest`
|
||||||
|
- **Implementation commit:** `6181984cf85f184be39513d6313f9cbe8267add5`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Immediate SMTP calls configure finite connection, read, and write timeouts for SMTP and SMTPS, and apply both the
|
||||||
|
configured From address and human-readable From name to the MIME message.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The test injects a local JavaMail sender factory, exercises both STARTTLS and TLS connections, and inspects the
|
||||||
|
resulting JavaMail properties and MIME From header without opening a network connection or exposing a real secret.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
STARTTLS uses `mail.smtp.*` timeout properties; TLS uses `mail.smtps.*`. Each timeout is 5000 milliseconds and the
|
||||||
|
encoded From header contains the configured address and display name.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=JavaMailSmtpProbeTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
BUILD FAILURE during test compilation: JavaMailSmtpProbe had no injectable sender-factory constructor needed to
|
||||||
|
inspect production message construction without network I/O.
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=JavaMailSmtpProbeTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest,SmtpOnboardingWebIntegrationTest,AccountActivationIntegrationTest,AccountWebIntegrationTest,BootstrapOnboardingWebIntegrationTest,JavaMailSmtpProbeTest,SecurityResponseIntegrationTest test
|
||||||
|
Tests run: 20, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This is a network-free adapter construction test. It does not prove DNS, TLS negotiation, authentication, Mailpit,
|
||||||
|
or production SMTP interoperability. Test values are non-secret fixtures.
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Test Evidence: Task public API documentation retrofit
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `TST-009`
|
||||||
|
- **Scenario IDs:** `Iteration 1 Task Javadoc retrofit`
|
||||||
|
- **Test class/method:** `Maven compiler and Javadoc doclint (no synthetic test)`
|
||||||
|
- **Implementation commit:** `fb0ed7f12c9d89235c102b67f2b13f786011c9ee`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Every Task-owned production type and declared public or protected API carries meaningful Javadoc for its business contract. The documented contracts include authorization and lifecycle scope, Project-first/Task-row lock order, non-disclosing HTTP behavior, fixed status transitions, empty progress, actor/history/version invariants, repository filtering and locks, DTO identifier domains and capability flags, the cross-feature activation guard, and dashboard scope/order/limit.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
This is prose and API documentation, so `TST-009` forbids an artificial unit test. Java 25 compilation checks source validity. The Maven Javadoc plugin runs standard doclint against only `com.lab.labtimesheet.feature.task`, making missing or malformed Task API documentation directly observable without treating unrelated feature retrofit work as Task-owned.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The Task package contains 21 production Java types. Each type has a main description. Every declared public/protected constructor and method has a contract comment; record components document their identifier domains, null/empty meanings, and capability semantics. Task-scoped Javadoc generation completes with no warnings.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -DskipTests -Ddoclint=all -Dsubpackages=com.lab.labtimesheet.feature.task javadoc:javadoc
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[WARNING] Javadoc Warnings
|
||||||
|
[WARNING] Task.java: warning: no main description (12 accessors)
|
||||||
|
[WARNING] TaskComment.java: warning: no main description (5 accessors)
|
||||||
|
[WARNING] TaskStatus.java: warning: no comment (4 enum constants)
|
||||||
|
[WARNING] 21 warnings
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
This was a diagnostic documentation baseline rather than a failing behavioral test. The parent instruction explicitly required doclint/compile instead of a fake test.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -DskipTests -Ddoclint=all -Dsubpackages=com.lab.labtimesheet.feature.task javadoc:javadoc
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] --- javadoc:3.12.0:javadoc (default-cli) @ labtimesheet ---
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
No Task-scoped Javadoc warning was emitted.
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -DskipTests compile
|
||||||
|
|
||||||
|
[INFO] Compiling 112 source files with javac [debug parameters release 25] to target/classes
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw clean test
|
||||||
|
|
||||||
|
[INFO] Tests run: 113, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
Doclint validates Javadoc structure and references, not whether prose perfectly models runtime behavior. Contract accuracy was checked by a scoped adversarial diff review against the numbered Task, authorization, Project-lifecycle, UI, and database requirements. Other feature owners retain responsibility for their own Iteration 1 Javadoc retrofits.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# Test Evidence: Role-correct Task dashboard query
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `AUTH-003`–`AUTH-005`, `AUTH-009`, `TSK-001`, `TSK-002`, `TSK-004`, `PRJ-016`
|
||||||
|
- **Scenario IDs:** `I1-UI-03`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.task.service.TaskDashboardServiceTest`
|
||||||
|
- **Implementation commit:** `511ee81a91a79a61cc6afb00097e1b38577c1968`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The public Task dashboard service reports blocked Tasks only for a Mentor's active owned Projects. For an Intern, it excludes former/completed memberships, counts current assigned Tasks, and returns at most five priority Tasks ordered by due date with null dates last and Task ID as the stable tie-breaker.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Two focused Mockito tests provide Project service DTOs and verify the Task service result. The repository remains mocked so the test isolates role/project/member filtering and the Task-owned dashboard DTO boundary; PostgreSQL query ordering is verified by the affected integration suite.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
A Mentor with one active and one planned Project receives the active Project's four blocked Tasks only. An Intern with one current active membership, one former membership, and one completed Project receives six assigned Tasks and the due-first Task from the current Project.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskDashboardServiceTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] TaskDashboardServiceTest.java:[34,13] cannot find symbol
|
||||||
|
symbol: class TaskDashboardService
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskDashboardServiceTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with approved sandbox escalation for Mockito Java 25 self-attach.
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Tests run: 2, 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="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=TaskPersistenceStructureTest,TaskDomainRulesTest,TaskControllerTest,TaskQueryServiceTest,TaskDashboardServiceTest,TaskMutationBoundaryTest,TaskCreationIntegrationTest test
|
||||||
|
|
||||||
|
[INFO] Tests run: 51, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This test does not prove the shared dashboard controller/template, which belongs to `work/reports-ui`. PostgreSQL ordering, soft-delete filtering, and repository query syntax remain integration-test concerns.
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Test Evidence: Task mutation authorization and locking boundary
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `AUTH-011`, `TSK-003`, `TSK-007`, `TSK-012`, `TSK-018`
|
||||||
|
- **Scenario IDs:** `I1-TSK-01`, `I1-TSK-03`, `I1-TSK-04`, `AC-AUTH-010`, `AC-TSK-003`, `AC-TSK-006`, `AC-TSK-010`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.task.service.TaskMutationBoundaryTest`
|
||||||
|
- **Implementation commit:** `511ee81a91a79a61cc6afb00097e1b38577c1968`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Every Task create/status/comment mutation first asks the concrete Project service for a current authorization context while holding the Project row lock. Status and comment mutations then load the Task with `PESSIMISTIC_WRITE` before checking or changing Task state.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Three focused Mockito tests verify call order for create, status, and comment. They prove the Project mutation context precedes the Task write, the unlocked Project query is not used for create, and status/comment use the locked Task lookup before mutation. `TaskPersistenceStructureTest` separately inspects the real repository method's lock annotation, while the PostgreSQL workflow suite executes the query.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Create calls `ProjectService.taskMutationContext(5, 10)` before saving. Status and comment call that same Project boundary, then `TaskRepository.findLockedByIdAndProjectIdAndDeletedAtIsNull(25, 10)`, before changing status or appending the comment. The Project service joins the outer Task transaction, so both locks remain through commit or rollback.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskMutationBoundaryTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] constructor TaskService ... cannot be applied to given types
|
||||||
|
required: TaskRepository,TaskCommentRepository,ProjectQueryService,CalendarApplicationService,Clock
|
||||||
|
found: TaskRepository,TaskCommentRepository,ProjectQueryService,ProjectService,CalendarApplicationService,Clock
|
||||||
|
[ERROR] cannot find symbol
|
||||||
|
symbol: method findLockedByIdAndProjectIdAndDeletedAtIsNull(long,long)
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskMutationBoundaryTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with approved sandbox escalation for Mockito Java 25 self-attach.
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Tests run: 3, 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="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=TaskPersistenceStructureTest,TaskDomainRulesTest,TaskControllerTest,TaskQueryServiceTest,TaskDashboardServiceTest,TaskMutationBoundaryTest,TaskCreationIntegrationTest test
|
||||||
|
|
||||||
|
[INFO] Tests run: 51, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
These unit tests establish service call order and repository lock metadata; they do not simulate two concurrent database transactions. PostgreSQL execution of the locked Task lookup is covered by `TaskCreationIntegrationTest`, and the producing Project feature separately proves its locked DTO boundary. Broader concurrency stress remains the explicit Iteration 3 hardening scope.
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# Test Evidence: Task feature persistence structure
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `TSK-001`–`TSK-005`, `TSK-007`, `TSK-011`, `TSK-012`
|
||||||
|
- **Scenario IDs:** `I1-TSK-01`–`I1-TSK-04`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.task.repository.TaskPersistenceStructureTest#taskPersistenceUsesJpaEntitiesAndSpringDataRepositories`
|
||||||
|
- **Implementation commit:** `511ee81a91a79a61cc6afb00097e1b38577c1968`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Task persistence uses JPA entities in `feature.task.model.entity` and Spring Data repositories in `feature.task.repository`. Status/comment mutation lookup is protected by `PESSIMISTIC_WRITE`. This prevents a regression to business-level JDBC access, unlocked mutation reads, or a global layer package.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Four focused tests load the production `Task` and `TaskComment` classes, verify their `@Entity` annotations, verify that both production repository interfaces extend `JpaRepository`, reject direct JDBC imports in Task business code, and inspect the locked lookup's `@Lock(PESSIMISTIC_WRITE)` annotation.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Exactly two Task-owned persisted aggregates are required for Iteration 1: `Task` and append-only `TaskComment`. Each must be a JPA entity, and each repository must be a Spring Data JPA repository under the Task feature package.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskPersistenceStructureTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] TaskPersistenceStructureTest.java:[5,54] package com.lab.labtimesheet.feature.task.model.entity does not exist
|
||||||
|
[ERROR] TaskPersistenceStructureTest.java:[6,54] package com.lab.labtimesheet.feature.task.model.entity does not exist
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The final feature-first package contract did not yet exist.
|
||||||
|
|
||||||
|
After that package move reached GREEN, the business-persistence boundary was tightened with a second test and separately observed RED:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] Tests run: 3, Failures: 2, Errors: 0, Skipped: 0
|
||||||
|
Expecting [org.springframework.jdbc.core.simple.JdbcClient]
|
||||||
|
to contain [TaskRepository, TaskCommentRepository]
|
||||||
|
Expecting empty but was: [src/main/java/com/lab/labtimesheet/feature/task/service/TaskService.java]
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The second failure proves that `TaskService` still depended on direct JDBC instead of the two Task-owned Spring Data repositories.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskPersistenceStructureTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Tests run: 4, 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="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=TaskPersistenceStructureTest,TaskDomainRulesTest,TaskControllerTest,TaskQueryServiceTest,TaskDashboardServiceTest,TaskMutationBoundaryTest,TaskCreationIntegrationTest test
|
||||||
|
|
||||||
|
[INFO] Tests run: 51, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
The suite ran with approved escalation for PostgreSQL 18.4 Testcontainers and Mockito Java 25 self-attach.
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This structure test does not prove persistence mappings against PostgreSQL, transactional authorization, cross-feature service contracts, or rendered behavior. Those remain protected by the Task integration and web evidence after the dependency foundations are merged.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# Test Evidence: Project activation Task-assignment query
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `PRJ-012`
|
||||||
|
- **Scenario IDs:** `I1-PRJ-04`, `AC-PRJ-006`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.task.service.TaskQueryServiceTest`
|
||||||
|
- **Implementation commit:** `511ee81a91a79a61cc6afb00097e1b38577c1968`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The Project feature can ask the public Task service whether any current non-deleted Task is assigned outside the Project's active membership set, without accessing Task repositories or entities.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Two focused Mockito tests exercise the concrete public service. An empty active-membership set counts every current Task without issuing an invalid `NOT IN ()` query. A non-empty set delegates to the filtered Spring Data repository query.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
With no active memberships, all three current Tasks are invalid assignments. With active memberships 7 and 9, the repository-derived count of assignments outside that set is two.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskQueryServiceTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] TaskQueryServiceTest.java:[22,13] cannot find symbol
|
||||||
|
symbol: class TaskQueryService
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskQueryServiceTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with approved sandbox escalation for Mockito Java 25 self-attach.
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Tests run: 2, 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="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=TaskPersistenceStructureTest,TaskDomainRulesTest,TaskControllerTest,TaskQueryServiceTest,TaskDashboardServiceTest,TaskMutationBoundaryTest,TaskCreationIntegrationTest test
|
||||||
|
|
||||||
|
[INFO] Tests run: 51, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This unit test does not prove the JPQL query against PostgreSQL or Project activation integration. The Task PostgreSQL suite and the Project feature's own integration tests cover those boundaries after dependency merge.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Test Evidence: Fixed Task status graph and initial Project progress
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `TSK-007`, `TSK-008`, `PRJ-015`, `PRJ-016`
|
||||||
|
- **Scenario IDs:** `I1-TSK-03`, `I1-TSK-05`, `AC-TSK-003`, `AC-PRJ-008`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.task.model.TaskDomainRulesTest`
|
||||||
|
- **Implementation commit:** `17a3c5d`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The Task status graph accepts exactly the seven specified directed edges. Initial Project progress counts each current Task status and represents a Project without current Tasks as no percentage rather than zero percent.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
One parameterized test checks all 16 source/target status pairs against a hand-written allowed-edge table. Two focused tests check empty progress and a four-Task example with two `DONE` Tasks.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Allowed edges are `TODO` to `IN_PROGRESS` or `BLOCKED`; `IN_PROGRESS` to `DONE` or `BLOCKED`; `BLOCKED` to `TODO` or `IN_PROGRESS`; and `DONE` to `IN_PROGRESS`. All other pairs are forbidden. Zero Tasks has no percentage. Two `DONE` among four Tasks is 50%, with counts 1 `TODO`, 1 `IN_PROGRESS`, 0 `BLOCKED`, and 2 `DONE`.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskDomainRulesTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] COMPILATION ERROR :
|
||||||
|
TaskDomainRulesTest.java:[16,30] cannot find symbol
|
||||||
|
symbol: class TaskStatus
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The test could not compile because the required Task status and progress domain types did not exist.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskDomainRulesTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Tests run: 18, 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="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw test
|
||||||
|
|
||||||
|
[INFO] Tests run: 107, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This unit evidence does not prove current-assignee authorization, Project lifecycle enforcement, PostgreSQL persistence/query filtering, non-deleted selection, HTTP authorization, or rendered `N/A`. Those require the platform/Project foundation and PostgreSQL/web tests.
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Test Evidence: Vietnam business-date clock boundary
|
||||||
|
|
||||||
|
- **Test type:** Unit
|
||||||
|
- **Requirement IDs:** `ACC-019`, `ACC-020`
|
||||||
|
- **Scenario IDs:** `AC-ACC-010` (business-date boundary only)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.config.TimeConfigurationTest#utcInstantAtVietnamMidnightUsesTheNewLocalBusinessDate`
|
||||||
|
- **Implementation commit:** `06dba4fb13eed675cc08ff8c00fe3e3650468c3b`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The production application clock uses `Asia/Ho_Chi_Minh`, so account lifecycle decisions based on `LocalDate.now`
|
||||||
|
advance at Vietnam midnight rather than seven hours later at UTC midnight.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The test obtains the real production clock configuration, fixes its configured zone at the UTC instant
|
||||||
|
`2026-08-14T17:00:00Z`, and derives the local business date. No Spring context or database is needed because the
|
||||||
|
contract under test is the clock bean's zone.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Vietnam is UTC+07:00, so `2026-08-14T17:00:00Z` is `2026-08-15T00:00:00+07:00` and the business date is
|
||||||
|
`2026-08-15`.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TimeConfigurationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
Expected 2026-08-15 but was 2026-08-14 because the production clock used UTC.
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=TimeConfigurationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=TimeConfigurationTest,BootstrapIntegrationTest,SmtpOnboardingWebIntegrationTest,AccountActivationIntegrationTest,AccountWebIntegrationTest,BootstrapOnboardingWebIntegrationTest,JavaMailSmtpProbeTest,SecurityResponseIntegrationTest test
|
||||||
|
Tests run: 22, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This verifies the production clock zone and its midnight boundary. It does not exercise later scheduler behavior or
|
||||||
|
attendance-policy timezone versioning.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Test Evidence: <short behavior name>
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `<ID>`
|
||||||
|
- **Scenario IDs:** `<ID>`
|
||||||
|
- **Test class/method:** `<fully qualified class and method>`
|
||||||
|
- **Implementation commit:** `<short SHA or pending>`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
<Externally observable rule and failure mode protected by this test.>
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
<Setup, action, and assertions. Explain why this is the narrowest production-shaped test.>
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
<Expected values or state derived independently of the implementation.>
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact command>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<relevant failing output and why it failed for the expected missing behavior>
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact command>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<relevant passing output>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
<exact broader command and result>
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
<What this test deliberately does not prove, including infrastructure or browser boundaries.>
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# Test Evidence: Account creation, activation, authentication, and logout
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `ACC-008–ACC-011, ACC-014, ACC-019, AUTH-001–AUTH-002, SEC-002–SEC-004`
|
||||||
|
- **Scenario IDs:** `AC-ACC-005` (Mentor/Intern browser paths), `AC-ACC-007`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.controller.AccountWebIntegrationTest.adminCreatesMentorAndInternThenMentorActivatesAuthenticatesAndLogsOut`
|
||||||
|
- **Implementation commit:** `8e786ba37ba7fcff09cf88d5951acb21fbb36ea8`; validation/additional-Admin coverage added in `17fa25bb0921718f780037cd8c55a956bbdf6b19`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
An authenticated Admin can use the account form to create pending Mentor and Intern accounts, the intended recipient can follow the emailed activation link and set a first password, normalized email login succeeds, a Mentor is denied the Admin account route, and logout clears authentication. Browser-submitted blank Intern fields do not prevent Mentor creation.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
MockMvc drives the production controllers, Thymeleaf templates, CSRF protection, Spring Security login/logout handlers, JPA services, and PostgreSQL 18.4. SMTP is replaced only at the network boundary by an in-memory recording probe. The test extracts the activation token from that immediate test message without logging or persisting the raw value, then exercises the public activation form.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The Admin form returns 200. Mentor and Intern submissions redirect to `?created` and persist their immutable roles as pending accounts. Activation redirects to `/login?activated`; login with a case/whitespace variant authenticates the normalized Mentor identity. That session receives 403 at the Admin form and becomes unauthenticated after POST `/logout`.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=AccountWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /admin/accounts/new resolved to ResourceHttpRequestHandler
|
||||||
|
Status expected:<200> but was:<404>
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
After the MVC boundary first reached GREEN, the test was tightened to submit blank Intern controls exactly as the browser form does and observed a second RED:
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /admin/accounts returned accounts/new with
|
||||||
|
"Internship fields are allowed only for Intern accounts"
|
||||||
|
Range for response status value 200 expected:<REDIRECTION> but was:<SUCCESSFUL>
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=AccountWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw test
|
||||||
|
|
||||||
|
Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This test does not contact Mailpit or an external SMTP server and is not a real browser/accessibility test. Hash-only persistence and exact expiry are covered by the integration test. It does not cover activation resend, password reset, account lock/deactivation, session invalidation after credential/state changes, production origin configuration, containerization, CI, or deployment.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# Test Evidence: account shell integration
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `UI-001`, `UI-002`, `UI-004`, `UI-009`, `I1-PLAT-06`, `I1-UI-04`
|
||||||
|
- **Scenario IDs:** `AC-UI-001`, `AC-UI-002`, `AC-UI-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.reporting.controller.AccountTemplateIntegrationTest`
|
||||||
|
- **Implementation commits:** `7dd61b9`, `f48fc63`, `f9ddef6`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The authenticated account-creation page consumes the shared role-aware desktop shell and posts to the real account endpoint. The public bootstrap, activation, and login pages consume the local themed authentication shell while preserving their first-Admin, raw-token, and Spring Security form contracts. The Admin dashboard and navigation link to the implemented `/admin/accounts/new` route, and logout remains a CSRF-protected POST in the shared shell.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
A focused MockMvc slice renders the production bootstrap, account-creation, activation, and login templates through a test-only controller. It asserts the authenticated and public shell markers, local pre-paint theme and CSS assets, real form actions, accessible error status, activation token retention, and the real account-creation URL. The existing PostgreSQL Bootstrap, Account, and Authentication flows then exercise one-time initialization, account creation, activation, normalized login, failed login, authorization, and logout through the production controllers and services.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The account-creation response contains `app-shell`, posts to `/admin/accounts`, and exposes `/admin/accounts/new` as the account navigation target. The activation response contains `auth-shell`, posts to `/activate`, retains `raw-token`, and loads `/assets/theme.js` before `/assets/app.css`. Login contains `auth-shell`, posts the expected `username` and `password` fields to `/login`, and exposes a live error announcement. Existing account lifecycle and Admin dashboard requests remain successful on PostgreSQL.
|
||||||
|
|
||||||
|
## 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=AccountTemplateIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 2, Failures: 2, Errors: 0, Skipped: 0
|
||||||
|
AccountTemplateIntegrationTest.accountCreationUsesAuthenticatedShellAndRealAccountRoute expected class="app-shell"
|
||||||
|
AccountTemplateIntegrationTest.activationUsesPublicAuthShellAndLocalAssets expected class="auth-shell"
|
||||||
|
BUILD FAILURE
|
||||||
|
Total time: 4.763 s
|
||||||
|
```
|
||||||
|
|
||||||
|
The account-creation and activation templates were standalone documents and did not consume either shared layout.
|
||||||
|
|
||||||
|
After the custom login page landed, its focused pre-change contract also failed as expected:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
AccountTemplateIntegrationTest.loginUsesPublicAuthShellAndPreservesAuthenticationContract expected class="auth-shell"
|
||||||
|
BUILD FAILURE
|
||||||
|
Total time: 5.343 s
|
||||||
|
```
|
||||||
|
|
||||||
|
The first-Admin bootstrap page then established its own layout RED:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
AccountTemplateIntegrationTest.bootstrapUsesPublicAuthShellAndPreservesFirstAdminContract expected class="auth-shell"
|
||||||
|
BUILD FAILURE
|
||||||
|
Total time: 4.771 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=AccountTemplateIntegrationTest,DashboardTemplateWebTest,UiContractWebTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 3.710 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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=AccountTemplateIntegrationTest,AuthenticationWebIntegrationTest,AccountWebIntegrationTest test
|
||||||
|
|
||||||
|
PostgreSQL 18.4
|
||||||
|
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 18.361 s
|
||||||
|
```
|
||||||
|
|
||||||
|
Bootstrap-specific affected suite:
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=AccountTemplateIntegrationTest,BootstrapIntegrationTest test
|
||||||
|
PostgreSQL 18.4
|
||||||
|
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 17.350 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The checks prove server rendering, local asset wiring, security-aware account navigation, and the complete account lifecycle through MockMvc/PostgreSQL. They do not replace a real-browser visual check of theme paint timing, password-manager behavior, or desktop overflow.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Test Evidence: Constraint-specific account uniqueness feedback
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `ACC-019`, `DB-003`
|
||||||
|
- **Scenario IDs:** `AC-ACC-005` (Intern creation uniqueness boundary)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.controller.AccountWebIntegrationTest#duplicateNormalizedStudentCodeIsReportedOnStudentCodeRatherThanEmail`
|
||||||
|
- **Implementation commit:** `06dba4fb13eed675cc08ff8c00fe3e3650468c3b`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
A case- and whitespace-normalized duplicate Intern student code is reported on the student-code field. A distinct
|
||||||
|
email is not falsely labeled as duplicate, and unknown uniqueness constraints fall back to a non-specific conflict.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
MockMvc creates one Intern through the authenticated CSRF-protected production form and then submits a second Intern
|
||||||
|
with a distinct email and the same student code in different case with surrounding whitespace. PostgreSQL 18.4
|
||||||
|
enforces the real Flyway expression index; the controller maps Hibernate's known constraint name to the form field.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The second request returns HTTP 200 on `accounts/new`, retains the safe display name, shows the student-code conflict,
|
||||||
|
and does not claim that the distinct email already exists.
|
||||||
|
|
||||||
|
## 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=AccountWebIntegrationTest#duplicateNormalizedStudentCodeIsReportedOnStudentCodeRatherThanEmail test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
PostgreSQL reported uq_intern_profiles_student_code_ci, but the form displayed "this email already exists".
|
||||||
|
BUILD FAILURE
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=AccountWebIntegrationTest#duplicateNormalizedStudentCodeIsReportedOnStudentCodeRatherThanEmail test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=TimeConfigurationTest,BootstrapIntegrationTest,SmtpOnboardingWebIntegrationTest,AccountActivationIntegrationTest,AccountWebIntegrationTest,BootstrapOnboardingWebIntegrationTest,JavaMailSmtpProbeTest,SecurityResponseIntegrationTest test
|
||||||
|
Tests run: 22, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The test covers the two Platform-owned normalized identity constraints. It does not enumerate later-iteration feature
|
||||||
|
constraints or perform a real-browser accessibility pass.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# Test Evidence: form-authenticated global calendar access
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `AUTH-002`, `CAL-001`, `SEC-001`, `SEC-013`
|
||||||
|
- **Scenario IDs:** `AC-SEC-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.controller.CalendarAuthorizationWebIntegrationTest#formAuthenticatedAdminCanOpenCalendarWhileMentorAndInternAreDenied`
|
||||||
|
- **Implementation commit:** `c8d4e9eecc59c78941769487af30953fb31a83c5`
|
||||||
|
|
||||||
|
## Incident scope
|
||||||
|
|
||||||
|
This record covers only the reported HTTP 403 for a fresh Admin session on
|
||||||
|
`GET /attendance/calendar`. The separately supplied 500 about policy
|
||||||
|
materialization is not a calendar-session or identity-mapping claim. It is
|
||||||
|
cross-referenced to
|
||||||
|
`.superpowers/sdd/access-navigation-icon-intern-picker/task-4-intern-dashboard-report.md`,
|
||||||
|
which independently records valid current PostgreSQL policy/constraint state
|
||||||
|
and no reproduction of that 500.
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The persisted first Admin can open global calendar management after a real CSRF-protected form login. Persisted Mentor and Intern accounts, each authenticated by the same form-login path, receive HTTP 403 for that route.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The test posts the actual bootstrap form, logs in through Spring Security, and follows the resulting session to `/attendance/calendar`. It configures a test-only SMTP probe solely to activate Mentor and Intern accounts through the public AccountService, then logs in those accounts before asserting denial. Spring Boot applies Flyway to PostgreSQL 18.4 through the shared Testcontainers configuration.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The bootstrap entity always has immutable `ADMIN` role, so its fresh authenticated session must receive HTTP 200 from the Admin-only calendar route. Immutable `MENTOR` and `INTERN` roles are not permitted by `CAL-001`, so their matching fresh authenticated sessions must receive HTTP 403. No calendar mutation is attempted.
|
||||||
|
|
||||||
|
## 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=CalendarAuthorizationWebIntegrationTest#formAuthenticatedAdminCanOpenCalendarWhileMentorAndInternAreDenied' test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
No valid RED occurred. On exact base 8be1b754e188367b260981718a5d33fc2d4d8a3b,
|
||||||
|
the new incident reproducer passed immediately: Tests run: 1, Failures: 0,
|
||||||
|
Errors: 0, Skipped: 0; BUILD SUCCESS. The production authorization guard was
|
||||||
|
not temporarily weakened merely to manufacture a failing result.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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=CalendarAuthorizationWebIntegrationTest#formAuthenticatedAdminCanOpenCalendarWhileMentorAndInternAreDenied' test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
No production correction was warranted. The strengthened regression, including
|
||||||
|
form-login authority assertions, passed: Tests run: 1, Failures: 0, Errors: 0,
|
||||||
|
Skipped: 0; BUILD SUCCESS. It observed Admin HTTP 200 and Mentor/Intern HTTP
|
||||||
|
403 after distinct persisted-account logins.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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=CalendarAuthorizationWebIntegrationTest,AttendanceControllerTest,AttendanceTemplateIntegrationTest,AuthenticationWebIntegrationTest,SecurityResponseIntegrationTest,RoleDashboardWebIntegrationTest test
|
||||||
|
|
||||||
|
Tests run: 16, Failures: 0, Errors: 0, Skipped: 0; BUILD SUCCESS.
|
||||||
|
|
||||||
|
Full backend suite:
|
||||||
|
./mvnw -q test
|
||||||
|
|
||||||
|
Result: exit code 0 with Java 25.0.4 and PostgreSQL 18.4 Testcontainers.
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This web test uses real Spring MVC, form authentication, account identity mapping, Flyway, and PostgreSQL 18.4. It substitutes only SMTP transport with an in-memory probe, does not exercise calendar mutations or a real browser, and does not establish production deployment configuration.
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# Test Evidence: attendance shell integration
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `UI-001`, `UI-002`, `UI-003`, `UI-008`, `UI-013`, `I1-ATT-03`, `I1-UI-04`
|
||||||
|
- **Scenario IDs:** `AC-ATT-003`, `AC-ATT-004`, `AC-UI-001`, `AC-UI-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.reporting.controller.AttendanceTemplateIntegrationTest`
|
||||||
|
- **Implementation commit:** `3064485`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The Intern attendance-history and Admin global-calendar pages consume the role-aware shared shell while preserving their existing routes, CSRF-protected mutation forms, filter values, empty states, and local theme assets. Populated history presents `dd/MM/yyyy` dates and 24-hour times in the attached policy timezone and does not collapse simultaneous violations.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
A focused MockMvc slice supplies empty and populated production-shaped models to the two production Attendance templates and renders them with role-specific Spring Security principals. The populated fixture uses UTC instants, the attached `Asia/Ho_Chi_Minh` seeded policy, and late-plus-early and late-plus-missing combinations. The owning feature's `AttendanceControllerTest` remains the affected behavioral suite for authorization, punch actions, calendar mutation, and view selection.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Both responses contain `app-shell` and `/assets/theme.js`. Intern history posts to `/attendance/check-in` and `/attendance/check-out` and renders its empty period state. Admin calendar posts to `/attendance/calendar` and renders its empty upcoming-events state. The shell highlights the real attendance/calendar route for the current role.
|
||||||
|
|
||||||
|
## 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=AttendanceTemplateIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 2, Failures: 2, Errors: 0, Skipped: 0
|
||||||
|
AttendanceTemplateIntegrationTest.adminCalendarUsesSharedShellAndPreservesEventForm expected class="app-shell"
|
||||||
|
AttendanceTemplateIntegrationTest.internHistoryUsesSharedShellAndPreservesPunchActions expected class="app-shell"
|
||||||
|
BUILD FAILURE
|
||||||
|
Total time: 4.977 s
|
||||||
|
```
|
||||||
|
|
||||||
|
Both Attendance templates were standalone HTML documents.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export PATH="/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
npm run build
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="/opt/homebrew/opt/node@24/bin:$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=AttendanceTemplateIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tailwind CSS v4.3.3: Done in 72ms
|
||||||
|
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 3.832 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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 -Dtest=AttendanceTemplateIntegrationTest,AttendanceControllerTest test
|
||||||
|
|
||||||
|
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 3.974 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The checks prove server-rendered shell integration and preserve the owning controller's tested contracts. They do not exercise PostgreSQL attendance persistence, live punch timing, browser overflow, or the visual state of populated editable calendar rows; those remain covered by the Attendance feature suite and final integrated UI checks.
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# Test Evidence: Attendance and global-calendar web authorization
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `AUTH-001`, `AUTH-002`, `AUTH-003`, `ATT-007`, `ATT-010`, `ATT-016`, `CAL-001`, `CAL-007`, `RPT-004`, `UI-013`
|
||||||
|
- **Scenario IDs:** `AC-ATT-003`, `AC-ATT-004`, `AC-CAL-004`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.controller.AttendanceControllerTest`
|
||||||
|
- **Implementation commit:** `8b48e281f7e860af435ae35b16c4edeb139286dc`
|
||||||
|
|
||||||
|
## 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. History renders policy-local 24-hour times,
|
||||||
|
`dd/MM/yyyy` dates, and every simultaneous violation; `On time` appears only
|
||||||
|
when no violation applies.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
The presentation regression supplies a row that is both late and early and
|
||||||
|
asserts the attached Asia/Ho_Chi_Minh timezone conversion.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
`2026-08-14T02:00:00.001Z` renders as local `09:00`, and a 15:00 local checkout
|
||||||
|
on that late row renders both `Late` and `Early departure`, never `On time`.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
```
|
||||||
|
|
||||||
|
The review presentation regression was separately observed RED:
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=AttendanceApplicationServiceTest,AttendanceControllerTest test
|
||||||
|
AttendanceControllerTest.historyRendersPolicyLocalDisplayValuesAndEveryViolation:
|
||||||
|
Expected a string containing "14/08/2026" but rendered "2026-08-14";
|
||||||
|
the same row rendered one nested-ternary result, "Early departure", and raw UTC instants.
|
||||||
|
Tests run: 13, Failures: 3, Errors: 1, Skipped: 0
|
||||||
|
BUILD FAILURE
|
||||||
|
Process exited 1. The eligible behavioral failures were the missing local presentation
|
||||||
|
values and simultaneous violation output; the checkout fixture error was corrected
|
||||||
|
before its own focused RED and is not claimed as behavioral evidence.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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: 8, 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: 32, 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.
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Test Evidence: Authenticated dashboard landing
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `I1-UI-03, I1-UI-04`
|
||||||
|
- **Scenario IDs:** `I1-UI-04 authentication integration follow-up`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.controller.AuthenticationWebIntegrationTest.projectLoginPageSupportsFailureNormalizedSuccessAndLogout`
|
||||||
|
- **Implementation commit:** `c4656a88806a92cb59b2e588035a4124854feb92`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Successful database authentication retains the established `/` success target, and an authenticated GET `/` immediately redirects to the shared role-dashboard route `/dashboard` instead of rendering a standalone dead-end page. Login failure, normalized-email authentication, CSRF, and logout remain covered by the same production-shaped flow.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
MockMvc logs in through the production Spring Security filter chain using a case-and-whitespace variant of the bootstrapped Admin email. It reuses the resulting authenticated session for GET `/` and asserts the redirect target. The same test continues through the production logout handler. PostgreSQL 18.4 backs the account and session authentication setup.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The successful form login redirects to `/`. Following that landing URL with the authenticated session returns a 3xx response whose location is `/dashboard`; it does not resolve `home.html`. Logout still redirects to `/login?logout` and clears authentication.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=AuthenticationWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Authenticated GET / invoked HomeController#home and rendered view "home".
|
||||||
|
Response status was 200; expected a 3xx redirect to /dashboard.
|
||||||
|
AuthenticationWebIntegrationTest.java:76 expected:<REDIRECTION> but was:<SUCCESSFUL>
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=AuthenticationWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw test
|
||||||
|
|
||||||
|
Tests run: 11, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The `/dashboard` endpoint and its role-specific content remain owned and tested by Reporting. This test proves only the authenticated platform handoff to that route. It is not a real-browser/accessibility test and does not change dashboard styling, authorization, account activation, or email delivery.
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# Test Evidence: dark icon sprite presentation
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `UI-006`, `UI-009`, `UI-010`, `UI-018`
|
||||||
|
- **Scenario IDs:** `AC-UI-003`, `AC-UI-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.ui.UiContractWebTest#generatedLucideSymbolsRetainCurrentColorStrokePresentation`
|
||||||
|
- **Implementation commit:** `pending`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Every local Lucide sprite symbol retains the source presentation attributes so icons referenced with `<use>` inherit `currentColor` rather than rendering with the SVG default black fill on dark surfaces.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The focused web contract reads the generated classpath sprite, scans every emitted `<symbol>`, and checks the five presentation attributes on each symbol. It checks the deployable generated artifact rather than generator source text.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Lucide 1.27.0 line icons use `fill="none"`, `stroke="currentColor"`, `stroke-width="2"`, `stroke-linecap="round"`, and `stroke-linejoin="round"` on their SVG root. Each selected generated symbol must preserve those values.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ./mvnw '-Dtest=UiContractWebTest#generatedLucideSymbolsRetainCurrentColorStrokePresentation' test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
UiContractWebTest.generatedLucideSymbolsRetainCurrentColorStrokePresentation
|
||||||
|
Missing fill on id="bell" viewBox="0 0 24 24" ==> expected: <true> but was: <false>
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env PATH=/opt/homebrew/opt/node@24/bin:$PATH npm ci
|
||||||
|
env PATH=/opt/homebrew/opt/node@24/bin:$PATH npm run build
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ./mvnw '-Dtest=UiContractWebTest#generatedLucideSymbolsRetainCurrentColorStrokePresentation' test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Node v24.19.0 and npm 11.17.0 installed the locked dependencies.
|
||||||
|
Tailwind CSS v4.3.3 rebuilt app.css and build-icons regenerated icons.svg.
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ./mvnw '-Dtest=UiContractWebTest' test
|
||||||
|
|
||||||
|
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The deterministic asset contract proves the generated sprite carries theme-aware Lucide presentation attributes. It does not replace the taskmaster-owned integrated browser/detector pass for rendered layout and interactive states.
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Test Evidence: role dashboard template contract
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `AUTH-003`, `UI-003`, `UI-013`, `I1-UI-03`
|
||||||
|
- **Scenario IDs:** `AC-AUTH-002`, `AC-UI-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.reporting.ReportingArchitectureTest`, `com.lab.labtimesheet.feature.reporting.controller.DashboardTemplateWebTest`
|
||||||
|
- **Implementation commit:** `5638286`, `f8db8a3`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Reporting-owned Java starts under `com.lab.labtimesheet.feature.reporting` rather than global layer packages or a placeholder module marker. The Admin, Mentor, and Intern dashboard templates consume typed view DTOs and render role-correct metrics, actions, attendance state, and `dd/MM/yyyy` dates without illustrative production data.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The architecture test loads the reporting view contract and rejects the superseded global-layer and module-boundary classes. A narrow MVC test controller supplies explicit DTO fixtures to the production Thymeleaf templates so their rendering contract can be verified before cross-feature service APIs are integrated. It does not replace the later database-backed `/dashboard` test.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Admin markup contains system metrics and `Create account`; Mentor markup contains owned Project, eligible-member, and blocked-Task summaries plus `Create Project`; Intern markup contains attendance, active-Project and assigned-Task summaries, only the supplied assigned Task, `Check out`, and due date `18/08/2026`. Unsupported pending-decision and unread-notification metrics are absent. Actions belonging to other roles are absent.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="/opt/homebrew/opt/node@24/bin:$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw clean -Dtest=ReportingArchitectureTest test
|
||||||
|
./mvnw clean -Dtest=DashboardTemplateWebTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
ReportingArchitectureTest: ClassNotFoundException: com.lab.labtimesheet.feature.reporting.model.dto.DashboardView
|
||||||
|
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
|
||||||
|
|
||||||
|
DashboardTemplateWebTest: Error resolving template [dashboard/admin], [dashboard/mentor], and [dashboard/intern]
|
||||||
|
Tests run: 3, Failures: 0, Errors: 3, Skipped: 0
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The final feature package DTO and the three production dashboard templates were absent in the respective pre-implementation states.
|
||||||
|
|
||||||
|
After cross-feature I1 service boundaries were established, a second focused RED caught two metrics without I1 service authority:
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=DashboardTemplateWebTest test
|
||||||
|
Tests run: 3, Failures: 2, Errors: 0, Skipped: 0
|
||||||
|
mentorTemplateRendersOwnedScopeAndOnlyMentorAction expected not "Pending decisions"
|
||||||
|
internTemplateRendersOwnWorkAndAttendanceAction expected not "Unread notifications"
|
||||||
|
BUILD FAILURE
|
||||||
|
Total time: 5.215 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 clean -Dtest=ReportingArchitectureTest,DashboardTemplateWebTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 25.380 s
|
||||||
|
```
|
||||||
|
|
||||||
|
Unsupported-metric correction:
|
||||||
|
|
||||||
|
```text
|
||||||
|
npm run build
|
||||||
|
./mvnw -Dtest=DashboardTemplateWebTest test
|
||||||
|
Tailwind CSS v4.3.3: Done in 72ms
|
||||||
|
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 6.254 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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"
|
||||||
|
npm run build
|
||||||
|
./mvnw -Dtest=UiContractWebTest,ReportingArchitectureTest,DashboardTemplateWebTest test
|
||||||
|
|
||||||
|
v24.19.0 / npm 11.17.0
|
||||||
|
Tailwind CSS v4.3.3: Done in 56ms
|
||||||
|
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 24.823 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
These tests prove package and template contracts with controlled view DTOs. The separate role-dashboard routing evidence covers the now-complete cross-feature service composition and PostgreSQL-backed Admin route. Browser viewport, contrast, and pre-paint behavior remain integrated UI gates.
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
# Test Evidence: Fresh-install root navigation
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `ACC-001`
|
||||||
|
- **Scenario IDs:** `N/A — user-reported fresh-install navigation regression`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.service.BootstrapIntegrationTest.rootGuidesFreshInstallToBootstrapWhileOtherRoutesRemainHidden`
|
||||||
|
- **Implementation commit:** `531c6078521341b156d69cb1013e54f65c092311`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Before the first Admin exists, opening `/` redirects to the public one-time
|
||||||
|
bootstrap workflow instead of rendering a Whitelabel 404 page. Other protected
|
||||||
|
application routes remain concealed with HTTP 404 until bootstrap completes.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The production Spring Security and bootstrap filter chain runs against a fresh
|
||||||
|
PostgreSQL 18.4 Testcontainer. MockMvc requests `/bootstrap`, health, `/`, and
|
||||||
|
`/dashboard`, then verifies that only the root receives the new navigation
|
||||||
|
redirect while the protected dashboard remains hidden.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
On an uninitialized installation, GET `/` returns a 3xx response with Location
|
||||||
|
`/bootstrap`. GET `/dashboard` still returns 404. The bootstrap form and health
|
||||||
|
endpoint remain available.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/Users/sechmachine/Library/Java/JavaVirtualMachines/corretto-26.0.2/Contents/Home
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest#rootGuidesFreshInstallToBootstrapWhileOtherRoutesRemainHidden test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET / returned 404.
|
||||||
|
Expected a 3xx redirect to /bootstrap.
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest#rootGuidesFreshInstallToBootstrapWhileOtherRoutesRemainHidden test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest,AuthenticationWebIntegrationTest,BootstrapOnboardingWebIntegrationTest,SecurityResponseIntegrationTest test
|
||||||
|
|
||||||
|
Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
MockMvc verifies server routing, security, and persistence-backed initialization
|
||||||
|
state. It does not prove browser rendering or exercise the user's IntelliJ-run
|
||||||
|
process. The existing browser screenshot independently established the original
|
||||||
|
Whitelabel 404 symptom.
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Test Evidence: Validated bootstrap, SMTP, and account onboarding
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `ACC-005–ACC-012`, `INT-004`, `INT-006–INT-008`, `SEC-001`
|
||||||
|
- **Scenario IDs:** `AC-ACC-003`; `AC-INT-002` (Admin browser boundary)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.controller.BootstrapOnboardingWebIntegrationTest`, `com.lab.labtimesheet.feature.integration.controller.SmtpOnboardingWebIntegrationTest`, `com.lab.labtimesheet.feature.account.controller.AccountWebIntegrationTest#invalidAndDuplicateAccountFormsReturnActionableErrorsWithoutCreatingAnotherAccount`
|
||||||
|
- **Implementation commit:** `17fa25bb0921718f780037cd8c55a956bbdf6b19`; SMTP failure feedback added in `8ff6ee3d873db909b1ce9df690f7a3abb2c3c79d`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Bootstrap offers SMTP setup after creating the first Admin. The Admin can save a validated draft, test it, and
|
||||||
|
activate only a successful test; or traverse five distinct ordered deferral acknowledgements before finishing.
|
||||||
|
Restricted-installation warnings persist until activation. Invalid bootstrap/account/SMTP forms retain only safe
|
||||||
|
non-secret values and show actionable errors. All state-changing browser operations require CSRF.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
MockMvc drives the production controllers, Bean Validation, Thymeleaf rendering, Spring Security filter chain, JPA
|
||||||
|
services, and PostgreSQL 18.4. SMTP is replaced only at its network adapter. The tests inspect rendered status,
|
||||||
|
buttons, warnings, validation messages, password non-retention, CSRF denial, ordered deferral navigation, and the
|
||||||
|
failed-probe response while verifying that activation remains unavailable and raw adapter diagnostics are absent.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Successful bootstrap lands on `/admin/smtp?onboarding`. A saved draft shows Test but not Activate; a successful test
|
||||||
|
shows Activate; activation clears the restricted warning. Deferral exposes warnings one through five in order, Back
|
||||||
|
and Configure on every screen, and Finish only on screen five. Invalid data returns HTTP 200 with field/global errors
|
||||||
|
and no submitted password. A failed SMTP probe displays fixed operator guidance and leaves the draft untested without
|
||||||
|
rendering the adapter's diagnostic.
|
||||||
|
|
||||||
|
## 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=BootstrapOnboardingWebIntegrationTest,SmtpOnboardingWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 6, Failures: 5, Errors: 1, Skipped: 0
|
||||||
|
Bootstrap redirected to /login instead of SMTP onboarding; deferral returned 404; SMTP status and warning were
|
||||||
|
absent; invalid form input raised a validation exception.
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The later failure-feedback regression used this focused command:
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=SmtpOnboardingWebIntegrationTest#failedSmtpTestRendersActionableFeedbackWithoutActivatingTheDraft test
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
Expected the configured connection-refusal message, but smtp/form omitted it.
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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=BootstrapOnboardingWebIntegrationTest,SmtpOnboardingWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest,SmtpOnboardingWebIntegrationTest,AccountActivationIntegrationTest,AccountWebIntegrationTest,BootstrapOnboardingWebIntegrationTest,JavaMailSmtpProbeTest,SecurityResponseIntegrationTest test
|
||||||
|
Tests run: 20, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The SMTP adapter is in-memory here, so this does not prove external Mailpit/server interoperability. MockMvc is not a
|
||||||
|
real browser or accessibility run. The test uses a non-secret diagnostic fixture only to prove that raw adapter text
|
||||||
|
is absent; it never exposes a password, integration secret, or activation bearer token.
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Test Evidence: Public assets and activation-safe response headers
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `ACC-001`, `SEC-001`, `SEC-003`, `SEC-009`
|
||||||
|
- **Scenario IDs:** No direct acceptance-scenario mapping (response-security regression)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.config.SecurityResponseIntegrationTest`
|
||||||
|
- **Implementation commit:** `6181984cf85f184be39513d6313f9cbe8267add5`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Public `/assets/**` requests remain reachable before bootstrap in both the Spring Security chain and bootstrap access
|
||||||
|
filter. Responses use `Referrer-Policy: no-referrer` so an activation URL bearer token cannot be forwarded in a
|
||||||
|
same-origin Referer header when a user follows another link.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
MockMvc starts the production filter chain against PostgreSQL 18.4 before initialization. It requests a known static
|
||||||
|
test asset and the activation page, asserting successful resource delivery and the exact global response header.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The known asset returns HTTP 200 before bootstrap. The activation response contains exactly
|
||||||
|
`Referrer-Policy: no-referrer`.
|
||||||
|
|
||||||
|
## 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=SecurityResponseIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 2, Failures: 2, Errors: 0, Skipped: 0
|
||||||
|
The asset request returned 404 and the activation response Referrer-Policy header was null.
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=SecurityResponseIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=BootstrapIntegrationTest,SmtpOnboardingWebIntegrationTest,AccountActivationIntegrationTest,AccountWebIntegrationTest,BootstrapOnboardingWebIntegrationTest,JavaMailSmtpProbeTest,SecurityResponseIntegrationTest test
|
||||||
|
Tests run: 20, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This verifies server response behavior through MockMvc, not browser enforcement of Referrer-Policy or Reporting's
|
||||||
|
integrated asset graph. It does not place a real activation token in logs, evidence, or request fixtures.
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Web Test Evidence
|
||||||
|
|
||||||
|
## Requirement and scenario IDs
|
||||||
|
|
||||||
|
- AUTH-001, AUTH-002, AUTH-011; PRJ-001, PRJ-004, PRJ-005, PRJ-006, PRJ-017; UI-001, UI-005, UI-014, UI-018; TST-001 through TST-010.
|
||||||
|
- AC-AUTH-001, AC-AUTH-010, AC-PRJ-001, AC-PRJ-003, AC-PRJ-009, AC-UI-005, AC-TST-001.
|
||||||
|
|
||||||
|
## Behavior under test
|
||||||
|
|
||||||
|
Project creation, direct member addition, and leadership reassignment render only server-provided eligible Intern choices. The native dialog picker exposes name, student code, and internship dates while numeric identifiers remain form values rather than visible labels. Local search, selection summaries, focus, apply, cancel, empty results, and retained server errors remain usable without adding a client API.
|
||||||
|
|
||||||
|
## Expected result derivation
|
||||||
|
|
||||||
|
The expected options are literal fixtures from the Account public DTO. Project membership history independently determines which eligible users are valid nonmembers or current-member leadership candidates. Native dialog controls keep server forms and CSRF as the mutation boundary.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
`env PATH=/opt/homebrew/opt/node@24/bin:$PATH npm run test:ui` executed the dependency-free interaction contract first: 1 test, 1 failure. Opening the picker left `dialog.open` undefined because no picker behavior existed.
|
||||||
|
|
||||||
|
The combined Java RED command was `env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ./mvnw '-Dtest=ProjectControllerTest,ProjectServiceIntegrationTest' test`. After correcting test-only assertion imports, test compilation failed only because the requested `ProjectService.addMembers(long,long,List<Long>)` API did not exist. Controller rendering RED will be rerun after that producer API compiles.
|
||||||
|
|
||||||
|
After the producer API compiled, `env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ./mvnw '-Dtest=ProjectControllerTest' test` ran 19 tests with 4 expected assertion failures for the missing eligible-option model, filtered multi-select markup, and retained selection rendering. A separate no-roster regression ran 1 test with 1 assertion failure because the disabled picker trigger had no reachable explanatory copy.
|
||||||
|
|
||||||
|
Independent review added rendered regressions before the correction. The same focused controller command ran 22 tests with exactly 3 failures and no errors: both closed-dialog radio contracts detected browser `required`, and stale batch recovery lacked the count-only replacement message. The new missing-selection POST contracts already passed through server Bean Validation.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
`env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ./mvnw '-Dtest=ProjectControllerTest' test` passed the initial rendered picker suite at 19/19. After adding the no-roster regression, the affected Project command below passed the expanded controller suite at 20/20.
|
||||||
|
|
||||||
|
`env PATH=/opt/homebrew/opt/node@24/bin:$PATH npm run test:ui` passed 1/1 executable tests with no failures, proving local name/student-code filtering, summary updates, initial search focus, apply retention, cancel rollback, and opener focus restoration.
|
||||||
|
|
||||||
|
`env PATH=/opt/homebrew/opt/node@24/bin:$PATH npm run build` succeeded with Tailwind CSS 4.3.3 and the existing local icon builder. No dependency was added.
|
||||||
|
|
||||||
|
After the review correction, the focused controller command passed 22/22. Creation and leadership radios no longer use closed-dialog browser constraint validation; missing selections re-render their server field errors. A failed member batch retains submitted option 21 when refreshed eligibility contains only 21, omits all rendered value/ID markup for stale option 22, and reports one unavailable selection without exposing its identifier.
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
`env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=ProjectControllerTest,ProjectEntityTest,ProjectPersistenceStructureTest,ProjectServiceIntegrationTest,ProjectTaskMutationContextTest,LayerStructureTest' test` passed 38/38 tests with no failures, errors, or skips.
|
||||||
|
|
||||||
|
`env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ./mvnw -DskipTests compile` succeeded. Project-scoped `javadoc:javadoc` with `-Ddoclint=all` succeeded; it retained four non-fatal default-constructor warnings, including pre-existing advice/query types. `git diff --check` passed.
|
||||||
|
|
||||||
|
## External boundaries
|
||||||
|
|
||||||
|
No browser loop or Impeccable detector is run on this branch; the root owner performs one integrated pass. MockMvc proves rendered semantics and a dependency-free Node test executes the dialog/search/selection behavior with controlled DOM boundaries.
|
||||||
|
|
||||||
|
After merging exact reviewed `main` `32c8a2d315d2175760c5d4792988cd0aa5ab6dd0`, `npm ci`, the 1/1 UI test, frontend build, compile, Project-scoped Javadoc/doclint, and diff check all succeeded. The first affected Java command added the updated shared `UiContractWebTest` and passed 45/45 tests with no failures, errors, or skips.
|
||||||
|
|
||||||
|
The bounded post-review affected command reran `ProjectControllerTest,ProjectEntityTest,ProjectPersistenceStructureTest,ProjectServiceIntegrationTest,ProjectTaskMutationContextTest,LayerStructureTest,UiContractWebTest` and passed 47/47 with no failures, errors, or skips, including 9/9 Project service tests against PostgreSQL 18.4. The UI test remained 1/1; frontend build, compile, Project-scoped Javadoc/doclint, and `git diff --check` also succeeded.
|
||||||
|
|
||||||
|
The root-owned final full suite then exposed a branch-induced MVC-slice fixture RED: 213 tests ran with 0 failures and 3 errors, all `ProjectTaskFormAccessibilityWebTest` context errors because the slice did not provide the new ProjectController AccountService dependency. A focused reproduction ran the class at 3 tests, 0 failures, 3 errors and reported the same missing AccountService constructor dependency.
|
||||||
|
|
||||||
|
The smallest test-only correction supplies the controller's AccountService and Clock dependencies and the existing ProjectQueryService mock's authenticated Mentor response. The intermediate focused runs exposed each dependency in order; no production code changed. Final focused GREEN: `env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ./mvnw '-Dtest=ProjectTaskFormAccessibilityWebTest' test` passed 3/3 with no failures, errors, or skips. The root owner retains the broader rerun.
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Test Evidence: Project-owned login flow
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `ACC-009, SEC-001, I1-UI-04`
|
||||||
|
- **Scenario IDs:** `I1-UI-04 authentication integration follow-up`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.account.controller.AuthenticationWebIntegrationTest.projectLoginPageSupportsFailureNormalizedSuccessAndLogout`
|
||||||
|
- **Implementation commit:** `a18d8e1d3dd02c8978033f09563d2ec9341926c7`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
After bootstrap, GET `/login` renders the project's `accounts/login` Thymeleaf view rather than Spring Security's generated page. Invalid credentials remain unauthenticated with generic feedback, a case-and-whitespace variant of the account email authenticates successfully, and POST `/logout` clears the authenticated session. Existing CSRF-protected form processing and server-side authorization remain enabled.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
MockMvc drives the production Spring Security filter chain, account-backed `UserDetailsService`, Thymeleaf view resolution, CSRF handling, session authentication, logout handler, JPA persistence, and PostgreSQL 18.4. The test creates only the first Admin through the production bootstrap service; no authentication component is mocked.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
GET `/login` returns 200 with view name `accounts/login` and a POST form targeting `/login`. A wrong password redirects to `/login?error` without authentication and the rendered page shows the same generic error. Login with ` ADMIN@EXAMPLE.COM ` and the correct password redirects to `/`, stores normalized username `admin@example.com`, and logout redirects to `/login?logout`, clears authentication, and renders a signed-out message.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=AuthenticationWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /login returned Spring Security's generated HTML with no ModelAndView.
|
||||||
|
AuthenticationWebIntegrationTest.java:48 No ModelAndView found
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=AuthenticationWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:/opt/homebrew/opt/node@24/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw test
|
||||||
|
|
||||||
|
Tests run: 11, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This is a server-side MockMvc test, not a real-browser or accessibility run. It does not validate the future shared-shell styling, login throttling, production transport/cookie configuration, or external identity providers. The milestone does not change activation token creation, persistence, or email delivery.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# Test Evidence: Project and Task shared-shell integration
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `UI-003`, `UI-004`, `UI-007`, `UI-009`, `UI-013`, `I1-UI-04`
|
||||||
|
- **Scenario IDs:** `AC-UI-002`, `AC-UI-003`, `AC-UI-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.reporting.controller.ProjectTaskShellContractTest#projectAndTaskPageUsesSharedDesktopShell`, `com.lab.labtimesheet.feature.project.controller.ProjectControllerTest`, `com.lab.labtimesheet.feature.task.controller.TaskControllerTest`
|
||||||
|
- **Implementation and final-Project integration commits:** `401f676`, `4849e0b`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Every Iteration 1 Project and Task page uses the same authenticated desktop shell, local assets, role-aware Project navigation, table containment, form controls, empty states, status badges, and `dd/MM/yyyy` date presentation. Existing capability-gated actions, server routes, validation, authentication, and CSRF contracts remain unchanged.
|
||||||
|
Project and Task forms provide both an error summary and inline field errors for failed server validation. Every inline error has a stable ID and every invalid control references that ID through `aria-describedby`.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The focused parameterized contract checks all five Project and three Task production templates for shared-shell composition and the active Project navigation marker. The affected Project and Task MVC slices then render the production templates through their real controllers while mocking only their feature service boundary, exercising route selection, authorization, form binding, validation, and action visibility.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
All eight templates reference `fragments/layout :: shell`, identify `projects` as the active navigation section, and contain no duplicate page `<head>`. Mentor-only Project and Task creation controls remain capability-gated; Project members and leadership management stay hidden from non-managers; Task status/comment controls stay hidden when their capability flag is false. Empty Task progress remains `N/A`.
|
||||||
|
|
||||||
|
## 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=ProjectTaskShellContractTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 8, Failures: 8, Errors: 0, Skipped: 0
|
||||||
|
Each standalone Project and Task template was missing "fragments/layout :: shell(".
|
||||||
|
BUILD FAILURE
|
||||||
|
Total time: 3.455 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="/opt/homebrew/opt/node@24/bin:$JAVA_HOME/bin:$PATH"
|
||||||
|
npm run build
|
||||||
|
./mvnw -Dtest=ProjectTaskShellContractTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tailwind CSS v4.3.3: Done in 63ms
|
||||||
|
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
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 -Dtest=ProjectTaskShellContractTest,ProjectControllerTest,TaskControllerTest test
|
||||||
|
|
||||||
|
Tests run: 25, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 4.020 s
|
||||||
|
```
|
||||||
|
|
||||||
|
The first affected attempt additionally caught Thymeleaf trying to resolve a `null` action fragment on five pages. Replacing `null` with Thymeleaf's empty fragment token made the identical 25-test command green. After merging the final Project activation pin, the one overlapping detail template retained both the shell and the capability-gated activation form; the focused Project/shell set passed 25 tests.
|
||||||
|
|
||||||
|
Final-review form-summary regression:
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=ProjectTaskShellContractTest test
|
||||||
|
RED: Tests run: 10, Failures: 2, Errors: 0, Skipped: 0
|
||||||
|
Both forms were missing #fields.hasAnyErrors() and #fields.allErrors().
|
||||||
|
|
||||||
|
./mvnw -Dtest=ProjectTaskShellContractTest,ProjectControllerTest,TaskControllerTest test
|
||||||
|
GREEN: Tests run: 29, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 4.214 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## Final delivery gate
|
||||||
|
|
||||||
|
```text
|
||||||
|
npm ci
|
||||||
|
added 34 packages; audited 35 packages; 0 vulnerabilities
|
||||||
|
|
||||||
|
npm run build
|
||||||
|
Tailwind CSS v4.3.3: Done in 68ms
|
||||||
|
|
||||||
|
./mvnw test
|
||||||
|
PostgreSQL 18.4 via Testcontainers
|
||||||
|
Tests run: 152, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
36 Surefire reports
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The MVC slices verify server-rendered markup and security/control contracts but do not emulate a browser viewport or visually compare illustrative mockups. PostgreSQL query and mutation behavior remains covered by the feature-owned integration suites; final asset reproducibility and the full PostgreSQL suite are separate delivery gates.
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# Test Evidence: Authorized Project pages
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `AUTH-001`, `AUTH-002`, `AUTH-006`, `PRJ-001`, `PRJ-004`–`PRJ-006`, `PRJ-012`, `SEC-001`, `ERR-001`
|
||||||
|
- **Scenario IDs:** `AC-AUTH-001`, `AC-AUTH-002`, `AC-AUTH-007`, `AC-PRJ-006`, `I1-PRJ-04`, `I1-PRJ-05`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.project.controller.ProjectControllerTest`
|
||||||
|
- **Implementation commits:** `25a855e`, `a9ee99a`, `2f25731`, `dbf1202`, `af0eb3c`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Authenticated users receive only authorized Project routes; guessed IDs return the shared non-disclosing error contract; valid Mentor create requests use the authenticated identity; binding and domain validation re-render safe forms with retained input and no mutation. Completed owner/Admin/former-member views render without a current Leader or mutation forms. The planned-Project activation action is shown only to the owning Mentor; state changes require CSRF.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
MockMvc exercises the real controller, binding, Bean Validation, exception mapping, view selection, redirect, Spring Security authentication, and CSRF filter. Only application/query services are mocked.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
An authorized list request renders `projects/list`. Unauthorized and missing direct IDs produce the same `error/generic` view with `errorStatus`, `errorTitle`, and `errorMessage`; no exception detail is rendered. Member and leadership routes authorize through actor plus Project ID. A valid create redirects to the created detail ID; blank/date-invalid input and ineligible Leader/member selections retain safe input and render field errors without a successful mutation. An activation guard failure returns to detail with its safe rule message. Completed Project pages show no current Leader and no forms for owner, Admin, or former member. POST without CSRF returns 403.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=ProjectControllerTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] cannot find symbol: class ProjectController
|
||||||
|
[ERROR] cannot find symbol: class ProjectPageService
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=ProjectControllerTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Running com.lab.labtimesheet.feature.project.controller.ProjectControllerTest
|
||||||
|
[INFO] Tests run: 10, 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="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest='Project*Test' test
|
||||||
|
|
||||||
|
[INFO] Tests run: 31, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mentor-only control regression
|
||||||
|
|
||||||
|
**RED:** the focused MockMvc run reported two expected failures: `GET /projects/new` returned `200` for an Intern instead of non-disclosing `404`, and the member page rendered the `Add member` form for a non-owner.
|
||||||
|
|
||||||
|
**GREEN:** rerunning `./mvnw -Dtest=ProjectControllerTest test` after the controller/DTO/template correction passed 7 tests with zero failures, errors, or skips.
|
||||||
|
|
||||||
|
## Role-aware Project-list action regression
|
||||||
|
|
||||||
|
**RED:** the focused MockMvc run reported two expected failures after adding the list-action regression: the controller still resolved only a user ID, so the Mentor fixture was queried as user `0`, and an Intern-facing Project list rendered the `Create Project` link.
|
||||||
|
|
||||||
|
**GREEN:** rerunning `./mvnw -Dtest=ProjectControllerTest test` after resolving the public actor view and conditionally rendering the link passed 8 tests with zero failures, errors, or skips.
|
||||||
|
|
||||||
|
## Activation-route regression
|
||||||
|
|
||||||
|
**RED:** the focused MockMvc run reported two expected failures: `POST /projects/30/activate` returned `404`, and the owning Mentor's planned-Project detail did not render the `Activate` action.
|
||||||
|
|
||||||
|
**GREEN:** after adding the CSRF-protected POST route and owner/status-conditional Thymeleaf form, the two focused tests passed; the full `ProjectControllerTest` class passed 10 tests with zero failures, errors, or skips.
|
||||||
|
|
||||||
|
## Review round 1 safe-validation, completed-page, and error-contract regression
|
||||||
|
|
||||||
|
**RED command:**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=ProjectControllerTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed RED:** `Tests run: 14, Failures: 5, Errors: 0`. Domain validation and activation returned blank 409 responses instead of their safe originating views; authorization/conflict responses had no `ModelAndView`; and completed detail rendered an empty Leader value instead of an explicit no-current-Leader state.
|
||||||
|
|
||||||
|
**Observed GREEN:** the same command passed the expanded owner/Admin/former-member matrix with `Tests run: 16, Failures: 0, Errors: 0, Skipped: 0` and `BUILD SUCCESS`. The Project test resource supplies only a contract fixture for `error/generic`; Reporting/UI owns the production shared template.
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This slice does not prove PostgreSQL query correctness, a real login flow, shared-shell navigation, or live-browser accessibility. Reporting/UI owns the final production `error/generic` template and will consume the documented three-key model contract after merging this pin; Project deliberately does not edit that shared asset. Iteration 2 invitation/exit/completion pages remain out of scope. Server-side activation authorization and Task-assignee atomicity are covered by Project domain and PostgreSQL integration tests.
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# Test Evidence: round-one shared UI corrections
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `AUTH-002`, `UI-003`, `UI-004`, `UI-010`, `UI-013`, `UI-014`, `ERR-001`, `I1-UI-01`, `I1-UI-02`, `I1-UI-04`
|
||||||
|
- **Scenario IDs:** `AC-AUTH-001`, `AC-UI-002`, `AC-UI-003`, `AC-UI-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.ui.UiContractWebTest`, `com.lab.labtimesheet.feature.reporting.controller.AttendanceTemplateIntegrationTest#populatedHistoryUsesPolicyLocalPresentationAndListsEveryViolation`, `com.lab.labtimesheet.feature.reporting.controller.SharedErrorTemplateWebTest`, `com.lab.labtimesheet.feature.reporting.controller.ProjectTaskFormAccessibilityWebTest`, `com.lab.labtimesheet.feature.reporting.controller.RoleDashboardWebIntegrationTest#mentorAndInternDashboardsRenderRealScopedProjectTaskAndAttendanceData`
|
||||||
|
- **Implementation commit:** `c81c0df` with deterministic asset follow-up `cb76bea`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The authenticated shell exposes only reachable role-authorized links. Intern attendance uses `/attendance`; Mentor attendance, profile, and notification links remain hidden until their authorized destination flows exist. Every rendered role-navigation link resolves through an actual authenticated GET. Attendance history uses the row's attached policy timezone for 24-hour times, formats business dates as `dd/MM/yyyy`, and renders every simultaneous violation. Project and Task field errors have stable IDs associated to invalid controls. Generic 404 and 409 pages use the shared shell and safe caller-supplied copy without rendering exception details. The collapsed desktop sidebar exposes its current state, keeps every control within its rail, and gives icon-only navigation a visible keyboard-focus tooltip. Both shared shells explicitly reference a local favicon so browser console checks do not depend on an unmapped `/favicon.ico` request.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
MockMvc renders the production shell and templates with real Spring Security principals and production-shaped Attendance DTOs. Project and Task invalid POSTs pass through their real controllers and validation, with only feature services replaced at the slice boundary. The full Spring/PostgreSQL role journey creates accounts, internship, Project, and Task through public services, renders each role's real dashboard, extracts every visible shell link, and performs an authenticated GET against each extracted path. A desktop browser then exercises the real local Java process at 1365x900 for all three roles, inspecting focus, tooltip pseudo-content, runtime `aria-expanded`, theme persistence/head ordering, console output, and document overflow.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Mentor navigation contains only overview and owned Projects; Intern navigation contains overview, `/attendance`, and Projects; Admin navigation contains overview, account creation, and global calendar. No role receives `/attendance/me`, `/profile`, `/notifications`, or a selector-less Mentor attendance destination. `2026-08-14T02:05:00Z` under `Asia/Ho_Chi_Minh` renders as `14/08/2026 09:05`; `09:00:00Z` renders as `16:00`. Late plus early-departure and late plus missing-checkout labels are both retained. Every rendered validation message has a stable referenced ID. Error pages expose only status and generic copy. At 1365x900, root and body scroll widths remain 1365, the collapsed toggle reports `aria-expanded=false`, expanding reports `true`, and keyboard focus exposes the corresponding control name without horizontal overflow.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```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=UiContractWebTest,AttendanceTemplateIntegrationTest,SharedErrorTemplateWebTest,ProjectTaskFormAccessibilityWebTest test
|
||||||
|
./mvnw -Dtest=RoleDashboardWebIntegrationTest test
|
||||||
|
./mvnw -Dtest=ProjectControllerTest,AttendanceControllerTest,TaskControllerTest test
|
||||||
|
./mvnw -Dtest=UiContractWebTest#collapsedSidebarExposesStateAndKeyboardVisibleControlNames test
|
||||||
|
./mvnw -Dtest=UiContractWebTest#mentorShellRendersOnlyReachableAuthorizedNavigation test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Focused templates: Tests run: 13, Failures: 6, Errors: 2, Skipped: 0
|
||||||
|
Navigation exposed /attendance/me, selector-less Mentor attendance, /profile, and /notifications.
|
||||||
|
Attendance rendered ISO dates/raw UTC instants and only one violation.
|
||||||
|
error/generic did not exist.
|
||||||
|
Invalid controls had no aria-describedby and inline errors had no stable IDs.
|
||||||
|
|
||||||
|
PostgreSQL 18.4 role journey: Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
Following the Admin shell's visible /profile link returned 404 instead of 200.
|
||||||
|
|
||||||
|
After the four reviewed producer pins were merged, the three producer WebMvc slices ran 39 tests with 39 context errors. The merged Platform SmtpWarningAdvice required SmtpConfigurationService, which was absent only from those narrow slice fixtures; no behavior assertion ran.
|
||||||
|
|
||||||
|
The collapsed-sidebar regression failed 1/1 at the missing aria-expanded assertion. The favicon regression failed 1/1 because the rendered shared shell had no explicit local icon link; the real browser independently logged /favicon.ico as 404.
|
||||||
|
|
||||||
|
The first post-Maven deterministic asset check changed app.css because Tailwind automatic source discovery included generated target output; a generated ring token changed the production bundle without any source-template change.
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The failures occurred after real template rendering and controller validation, or at an exact missing merged slice dependency; they identify the missing reviewed behavior or fixture boundary rather than an unrelated environment failure.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```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 clean -Dtest=AccountTemplateIntegrationTest,AttendanceTemplateIntegrationTest,DashboardControllerWebTest,DashboardTemplateWebTest,ProjectTaskFormAccessibilityWebTest,SharedErrorTemplateWebTest,UiContractWebTest test
|
||||||
|
./mvnw -Dtest=RoleDashboardWebIntegrationTest test
|
||||||
|
./mvnw -Dtest=ProjectControllerTest,AttendanceControllerTest,TaskControllerTest test
|
||||||
|
./mvnw -Dtest=UiContractWebTest#collapsedSidebarExposesStateAndKeyboardVisibleControlNames test
|
||||||
|
./mvnw -Dtest=UiContractWebTest#mentorShellRendersOnlyReachableAuthorizedNavigation test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Post-merge clean Reporting/UI slices: Tests run: 26, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
PostgreSQL 18.4 role journey: Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
Producer WebMvc slices: Tests run: 39, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
Collapsed sidebar: Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
Local favicon contract: Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
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"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
npm run build
|
||||||
|
./mvnw -Dtest=SecurityResponseIntegrationTest,BootstrapOnboardingWebIntegrationTest,AccountWebIntegrationTest,SmtpOnboardingWebIntegrationTest,RoleDashboardWebIntegrationTest,UiContractWebTest,AccountTemplateIntegrationTest,AttendanceTemplateIntegrationTest,DashboardControllerWebTest,DashboardTemplateWebTest,ProjectTaskFormAccessibilityWebTest,SharedErrorTemplateWebTest,ProjectControllerTest,TaskControllerTest,AttendanceControllerTest test
|
||||||
|
./mvnw -DskipTests compile
|
||||||
|
./mvnw -DskipTests -Ddoclint=all javadoc:javadoc
|
||||||
|
|
||||||
|
Node v24.19.0; npm 11.17.0
|
||||||
|
Tailwind CSS v4.3.3: Done
|
||||||
|
Two consecutive builds produced app.css SHA-256 7bd351d0f2cae97af70532e8ee0e0248265782b508d378fd7c277cbb4f373946 and icons.svg SHA-256 001f72c93967f816fdd56f3f9b34cb5e5831b8b8c572d051669c6a3aae2c3cda.
|
||||||
|
Merged affected web suite on PostgreSQL 18.4: Tests run: 79, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
Full PostgreSQL 18.4 suite: Tests run: 195, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
Compile: success
|
||||||
|
Full Javadoc/doclint: success (producer-owned missing-comment warnings remain non-fatal)
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The automated checks prove rendering, controller validation, role-scoped navigation targets, attached-policy formatting, generic error copy, public local assets, safe Referrer-Policy, and retained safe form fields. Edge/Chromium desktop checks against the real local Java/PostgreSQL process covered Admin dashboard, Mentor dashboard/Projects, and Intern dashboard/attendance: every representative page had `documentElement.scrollWidth == body.scrollWidth == innerWidth == 1365`; keyboard focus showed a solid focus ring and tooltip; collapse/expand synchronized `aria-expanded`; the theme bootstrap preceded CSS and survived reload; console checks were empty after the explicit local favicon link. A human-observed no-flash check is inherently practical rather than deterministic, and mobile remains outside Iteration 1 scope.
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# Test Evidence: persistent SMTP warning and accessible onboarding shell
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `ACC-005`, `ACC-006`, `ACC-007`, `INT-007`, `UI-004`, `UI-007`, `UI-010`, `I1-UI-01`, `I1-UI-02`, `I1-UI-04`
|
||||||
|
- **Scenario IDs:** `AC-ACC-003`, `AC-UI-001`, `AC-UI-002`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.reporting.controller.DashboardControllerWebTest`, `com.lab.labtimesheet.feature.integration.controller.SmtpOnboardingWebIntegrationTest`, `com.lab.labtimesheet.feature.account.controller.BootstrapOnboardingWebIntegrationTest#fiveDistinctDeferralConfirmationsAreSequentialAndOnlyTheLastCanFinish`
|
||||||
|
- **Implementation commit:** `ddf688a`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
An Admin without active SMTP sees a persistent, actionable restricted-installation warning on every shared-shell page, including the dashboard reached after the fifth deferral confirmation. The warning is absent for non-Admins and after SMTP activation. SMTP configuration and deferral reuse the authenticated desktop shell and its pre-paint theme, focus, local assets, navigation, and logout behavior. Invalid SMTP fields expose a single accessible error summary plus stable field-error IDs referenced by the corresponding controls, while safe fields are retained and the submitted password is never rendered.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The reporting MVC slice renders the production Admin and Mentor dashboard templates with real Spring Security principals and only the dashboard and SMTP services mocked at their public boundaries. PostgreSQL 18.4 integration tests bootstrap a real Admin, traverse all five server-owned deferral steps, finish onto the real dashboard, and render a representative account page. The SMTP integration test submits every supported invalid field combination through the real controller, Jakarta Validation, Thymeleaf binding, and production template. A separate invalid request proves safe-value retention and request-local password clearing.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
With no active SMTP, an Admin dashboard and account page contain the exact warning and an `/admin/smtp` action. A Mentor dashboard never contains that warning, and an Admin page after activation does not contain it. The first through fourth deferral steps do not expose Finish; the fifth does; Finish redirects to `/dashboard`, where the warning persists. Host, port, security mode, authentication completeness, From address, and From name each render a unique error ID and the associated invalid control references that ID through `aria-describedby`. The global summary is labeled, safe username/sender values remain, and the submitted password is absent.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```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=DashboardControllerWebTest,SmtpOnboardingWebIntegrationTest,BootstrapOnboardingWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 13, Failures: 3, Errors: 0, Skipped: 0
|
||||||
|
DashboardControllerWebTest: Admin dashboard did not contain the persistent restricted-installation warning or SMTP action.
|
||||||
|
SmtpOnboardingWebIntegrationTest: SMTP form did not load /assets/theme.js because it was still standalone.
|
||||||
|
BootstrapOnboardingWebIntegrationTest: SMTP deferral did not load /assets/theme.js because it was still standalone.
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The initial XPath assertion attempt was discarded before implementation because the HTML5 doctype is not XML-parseable by MockMvc's XML XPath matcher. The corrected string-based run above is the recorded behavior RED.
|
||||||
|
|
||||||
|
A follow-up focused RED for the authentication-pair error ran one PostgreSQL-backed method and failed 1/1 because the password referenced `smtp-authentication-error` but the paired username did not. Associating both controls made the identical command pass 1/1.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```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=DashboardControllerWebTest,SmtpOnboardingWebIntegrationTest,BootstrapOnboardingWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
PostgreSQL 18.4 via Testcontainers
|
||||||
|
Tests run: 14, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 30.179 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
||||||
|
npm run build
|
||||||
|
./mvnw -Dtest=SecurityResponseIntegrationTest,BootstrapOnboardingWebIntegrationTest,AccountWebIntegrationTest,SmtpOnboardingWebIntegrationTest,RoleDashboardWebIntegrationTest,UiContractWebTest,AccountTemplateIntegrationTest,AttendanceTemplateIntegrationTest,DashboardControllerWebTest,DashboardTemplateWebTest,ProjectTaskFormAccessibilityWebTest,SharedErrorTemplateWebTest,ProjectControllerTest,TaskControllerTest,AttendanceControllerTest test
|
||||||
|
|
||||||
|
Node v24.19.0; npm 11.17.0; Tailwind CSS v4.3.3
|
||||||
|
Two consecutive builds produced app.css SHA-256 f0a4abbffaf66581ee7e17952743e591b8957e0cbcd19099e234d13827700e4c and icons.svg SHA-256 001f72c93967f816fdd56f3f9b34cb5e5831b8b8c572d051669c6a3aae2c3cda.
|
||||||
|
PostgreSQL 18.4 via Testcontainers
|
||||||
|
Tests run: 81, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 50.418 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## Full verification
|
||||||
|
|
||||||
|
```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 test
|
||||||
|
|
||||||
|
PostgreSQL 18.4 via Testcontainers
|
||||||
|
Tests run: 197, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 01:24 min
|
||||||
|
|
||||||
|
./mvnw -DskipTests compile
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 0.764 s
|
||||||
|
|
||||||
|
./mvnw -DskipTests -Ddoclint=all javadoc:javadoc
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 0.924 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
MockMvc verifies rendered security visibility, form binding, CSRF-generated forms, exact deferral ordering, safe retained values, and accessibility associations. It does not prove viewport overflow, keyboard focus rendering, collapse behavior, or visually observable theme flash; the separate real-browser evidence covers those boundaries. SMTP transport remains represented by the existing test probe and no real mail server is required.
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# Test Evidence: role dashboard routing and service composition
|
||||||
|
|
||||||
|
- **Test type:** Web and unit
|
||||||
|
- **Requirement IDs:** `AUTH-003`, `UI-003`, `UI-013`, `I1-UI-03`
|
||||||
|
- **Scenario IDs:** `AC-AUTH-002`, `AC-UI-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.reporting.service.DashboardServiceTest`, `com.lab.labtimesheet.feature.reporting.controller.DashboardControllerWebTest`, `com.lab.labtimesheet.feature.reporting.controller.AdminDashboardWebTest`, `com.lab.labtimesheet.feature.reporting.controller.RoleDashboardWebIntegrationTest`, `com.lab.labtimesheet.feature.reporting.ReportingArchitectureTest`
|
||||||
|
- **Implementation and integration-test commits:** `b1c6b17`, `cbdbd8e`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
`/dashboard` selects exactly one role template from the authenticated authority, while all displayed data is authorized again from the persisted account identity. Reporting composes public Account, Project, Task, and Attendance service DTOs; it owns no shadow account entity, repository, direct SQL, or business date calculation.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The unit test supplies mocked concrete public feature services to the reporting coordinator and independently checks the exact Admin, Mentor, and Intern view DTOs, including Task-status and attendance-state translation. Negative cases prove that a forged authority, locked account, missing account, or inactive internship cannot produce a dashboard. The MVC slice proves role-to-template routing and authentication. PostgreSQL web tests bootstrap a real Admin and create/activate Mentor and Intern identities, SMTP configuration, a Project, and a Task only through public application services; they then exercise all three authenticated dashboard roles without repository, entity, JDBC, or SQL fixtures.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
An active Admin sees account totals plus active Project count. An active Mentor sees their display name, visible active Project count, distinct active eligible member count, and blocked Task count. An eligible Intern sees the server-authoritative attendance state, active Project count, assigned Task count, and the Task service's ordered priority list. Unsupported roles and identities that do not satisfy the persisted role/lifecycle checks receive HTTP 403.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```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=DashboardServiceTest,DashboardControllerWebTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
DashboardService constructor required DashboardRepository and did not accept TaskDashboardService or AttendanceApplicationService.
|
||||||
|
DashboardService.intern required a caller-supplied LocalDate instead of using AttendanceApplicationService.currentState.
|
||||||
|
Tests failed during compilation with 5 errors.
|
||||||
|
BUILD FAILURE
|
||||||
|
Total time: 6.645 s
|
||||||
|
```
|
||||||
|
|
||||||
|
The focused contract could not compile against the temporary reporting-owned persistence implementation, which is the intended missing behavior.
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```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=DashboardServiceTest,DashboardControllerWebTest,ReportingArchitectureTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 12, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 6.145 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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=DashboardServiceTest,DashboardControllerWebTest,AdminDashboardWebTest,ReportingArchitectureTest,DashboardTemplateWebTest test
|
||||||
|
|
||||||
|
PostgreSQL 18.4 via Testcontainers
|
||||||
|
Tests run: 18, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 11.409 s
|
||||||
|
|
||||||
|
Production-shaped Mentor/Intern query journey:
|
||||||
|
|
||||||
|
./mvnw -Dtest=RoleDashboardWebIntegrationTest test
|
||||||
|
PostgreSQL 18.4 via Testcontainers
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 11.119 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The focused tests prove reporting composition, route selection, denial behavior, and production-shaped Admin, Mentor, and Intern journeys. Feature-owned suites separately prove additional Project, Task, Attendance, and Account query semantics. Browser viewport behavior remains an external UI boundary.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Test Evidence: Sanitized SMTP failure feedback
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `INT-005`, `INT-008`
|
||||||
|
- **Scenario IDs:** `AC-INT-002` (failed-draft browser boundary)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.integration.controller.SmtpOnboardingWebIntegrationTest#failedSmtpTestRendersActionableFeedbackWithoutActivatingTheDraft`
|
||||||
|
- **Implementation commit:** `06dba4fb13eed675cc08ff8c00fe3e3650468c3b`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
An SMTP test failure renders fixed actionable guidance but never renders the external adapter's arbitrary diagnostic.
|
||||||
|
The failed draft remains untested and cannot be activated.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
MockMvc saves a valid SMTP draft, configures the in-memory network adapter to throw a distinctive non-secret raw
|
||||||
|
diagnostic, and submits the authenticated CSRF-protected test action. It checks the production controller and
|
||||||
|
Thymeleaf response for the fixed message, absence of the raw diagnostic, and absence of the activation action.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The response is HTTP 200 on `smtp/form`, contains the fixed operator message, omits the adapter diagnostic, and does
|
||||||
|
not offer Activate SMTP.
|
||||||
|
|
||||||
|
## 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=SmtpOnboardingWebIntegrationTest#failedSmtpTestRendersActionableFeedbackWithoutActivatingTheDraft test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
The fixed guidance was absent and the rendered smtpActionError contained the adapter's distinctive diagnostic.
|
||||||
|
BUILD FAILURE
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=SmtpOnboardingWebIntegrationTest#failedSmtpTestRendersActionableFeedbackWithoutActivatingTheDraft test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=TimeConfigurationTest,BootstrapIntegrationTest,SmtpOnboardingWebIntegrationTest,AccountActivationIntegrationTest,AccountWebIntegrationTest,BootstrapOnboardingWebIntegrationTest,JavaMailSmtpProbeTest,SecurityResponseIntegrationTest test
|
||||||
|
Tests run: 22, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The SMTP adapter is in-memory, so this does not prove live server interoperability. The diagnostic is a deterministic
|
||||||
|
non-secret fixture; no password, credential, or activation token is logged or recorded.
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Test Evidence: Sanitized production mail exception feedback
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `INT-005`, `INT-008`
|
||||||
|
- **Scenario IDs:** `AC-INT-002` (production mail-failure boundary)
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.integration.controller.SmtpOnboardingWebIntegrationTest#failedSmtpTestRendersActionableFeedbackWithoutActivatingTheDraft`
|
||||||
|
- **Implementation commit:** `bf6f9af78b42151f2c26ef206978e3a55f75594a`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Spring Mail delivery failures from the production SMTP adapter return the fixed Admin guidance instead of escaping
|
||||||
|
the MVC request or exposing provider diagnostics. A failed probe does not mark the draft tested or enable activation.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
MockMvc saves a valid draft, then the test SMTP boundary throws Spring's production-shaped `MailSendException` with a
|
||||||
|
distinctive deterministic diagnostic. The authenticated CSRF-protected request crosses the real controller and SMTP
|
||||||
|
configuration service, and the rendered Thymeleaf response is inspected for the fixed message, raw-text absence, and
|
||||||
|
absence of the activation action.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
The response is HTTP 200 on `smtp/form`, contains the fixed operator guidance, omits the exception diagnostic, and
|
||||||
|
does not offer Activate SMTP because `markTested` was never reached.
|
||||||
|
|
||||||
|
## 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=SmtpOnboardingWebIntegrationTest#failedSmtpTestRendersActionableFeedbackWithoutActivatingTheDraft test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
|
||||||
|
MailSendException escaped as ServletException with the distinctive diagnostic instead of rendering smtp/form.
|
||||||
|
BUILD FAILURE
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=SmtpOnboardingWebIntegrationTest#failedSmtpTestRendersActionableFeedbackWithoutActivatingTheDraft test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
./mvnw -Dtest=TimeConfigurationTest,BootstrapIntegrationTest,SmtpOnboardingWebIntegrationTest,AccountActivationIntegrationTest,AccountWebIntegrationTest,BootstrapOnboardingWebIntegrationTest,JavaMailSmtpProbeTest,SecurityResponseIntegrationTest test
|
||||||
|
Tests run: 22, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
PostgreSQL: 18.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The test exercises the production Spring Mail exception type without contacting an external SMTP server. It does not
|
||||||
|
prove live Mailpit/provider interoperability and contains no real credential or activation token.
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# Test Evidence: persistent Admin SMTP settings navigation
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `ACC-007`, `INT-001`, `AUTH-002`, `UI-003`, `UI-008`, `UI-009`, `UI-010`
|
||||||
|
- **Scenario IDs:** `AC-ACC-003`, `AC-UI-002`, `AC-UI-003`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.reporting.controller.DashboardControllerWebTest`, `com.lab.labtimesheet.feature.reporting.controller.RoleDashboardWebIntegrationTest#mentorAndInternDashboardsRenderRealScopedProjectTaskAndAttendanceData`
|
||||||
|
- **Implementation commit:** pending
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
An Admin always receives an SMTP settings destination in the shared sidebar, whether SMTP is restricted or active. The restricted-installation warning remains conditional. Mentor and Intern sidebars never expose the Admin-only destination, and the Admin link uses the local settings sprite plus the established collapsed-sidebar tooltip.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The MVC slice renders the real dashboard controller, Spring Security Thymeleaf dialect, and shared layout with only the SMTP state and dashboard query services mocked at their public boundaries. It checks both Admin SMTP states and the active-SMTP Mentor/Intern views. The PostgreSQL 18.4 integration test activates SMTP through the real service, extracts rendered navigation links, requires the Admin SMTP route only for Admin, and follows every discovered link through the real controller/security stack.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
With SMTP restricted, an Admin dashboard contains the existing warning and a sidebar link to `/admin/smtp` identified by `data-tooltip="SMTP settings"`. After SMTP activation, the warning is absent but that same sidebar link remains. Mentor and Intern dashboards omit the SMTP-settings tooltip and route. The activated Admin link resolves successfully when followed.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/opt/node@24/bin:$PATH ./mvnw -DargLine=-javaagent:/Users/sechmachine/.m2/repository/net/bytebuddy/byte-buddy-agent/1.18.10/byte-buddy-agent-1.18.10.jar -Dtest=DashboardControllerWebTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 6, Failures: 2, Errors: 0, Skipped: 0
|
||||||
|
DashboardControllerWebTest.adminRendersAdminDashboardForAuthenticatedIdentity: expected data-tooltip="SMTP settings" but it was absent.
|
||||||
|
DashboardControllerWebTest.activeSmtpKeepsAdminDashboardFreeOfTheRestrictedInstallationWarning: expected href="/admin/smtp" data-tooltip="SMTP settings" but it was absent.
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The Mentor and Intern active-SMTP assertions passed in this RED run, so the failures establish the missing Admin navigation rather than an incorrect role fixture.
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/opt/node@24/bin:$PATH DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw -DargLine=-javaagent:/Users/sechmachine/.m2/repository/net/bytebuddy/byte-buddy-agent/1.18.10/byte-buddy-agent-1.18.10.jar -Dtest=RoleDashboardWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
PostgreSQL: 18.4 Testcontainer
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
Expected Admin visible navigation paths to contain /admin/smtp, but rendered paths were /dashboard, /admin/accounts/new, /attendance/calendar.
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/opt/node@24/bin:$PATH ./mvnw -DargLine=-javaagent:/Users/sechmachine/.m2/repository/net/bytebuddy/byte-buddy-agent/1.18.10/byte-buddy-agent-1.18.10.jar -Dtest=DashboardControllerWebTest test
|
||||||
|
|
||||||
|
JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/opt/node@24/bin:$PATH DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw -DargLine=-javaagent:/Users/sechmachine/.m2/repository/net/bytebuddy/byte-buddy-agent/1.18.10/byte-buddy-agent-1.18.10.jar -Dtest=RoleDashboardWebIntegrationTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
DashboardControllerWebTest: Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
RoleDashboardWebIntegrationTest: PostgreSQL 18.4 Testcontainer; Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Affected suite
|
||||||
|
|
||||||
|
**Command and result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
PATH=/opt/homebrew/opt/node@24/bin:$PATH node --version && PATH=/opt/homebrew/opt/node@24/bin:$PATH npm --version && PATH=/opt/homebrew/opt/node@24/bin:$PATH npm ci && PATH=/opt/homebrew/opt/node@24/bin:$PATH npm run build
|
||||||
|
Node v24.19.0; npm 11.17.0; Tailwind CSS v4.3.3
|
||||||
|
BUILD SUCCESS
|
||||||
|
|
||||||
|
JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/opt/node@24/bin:$PATH DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw -DargLine=-javaagent:/Users/sechmachine/.m2/repository/net/bytebuddy/byte-buddy-agent/1.18.10/byte-buddy-agent-1.18.10.jar -Dtest=SecurityResponseIntegrationTest,BootstrapOnboardingWebIntegrationTest,AccountWebIntegrationTest,SmtpOnboardingWebIntegrationTest,RoleDashboardWebIntegrationTest,UiContractWebTest,AccountTemplateIntegrationTest,AttendanceTemplateIntegrationTest,DashboardControllerWebTest,DashboardTemplateWebTest,ProjectTaskFormAccessibilityWebTest,SharedErrorTemplateWebTest,ProjectControllerTest,TaskControllerTest,AttendanceControllerTest test
|
||||||
|
PostgreSQL 18.4 Testcontainers; Tests run: 81, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
MockMvc proves rendered role/state visibility, while the PostgreSQL integration test proves the real Admin route follow. They do not render the collapsed rail or inspect pixels, browser focus placement, or tooltip positioning; the existing CSS and local settings sprite are reused unchanged. Server-side direct-URL authorization remains the existing `/admin/**` Admin-only security rule and is not broadened by this layout-only change.
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# Test Evidence: Task pages and server-side request boundaries
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `AUTH-001`, `AUTH-002`, `AUTH-005`, `AUTH-009`, `AUTH-011`, `PRJ-015`, `TSK-003`, `TSK-005`, `TSK-007`–`TSK-008`, `TSK-011`, `TSK-012`, `UI-014`
|
||||||
|
- **Scenario IDs:** `I1-TSK-01`–`I1-TSK-05`, `AC-AUTH-001`, `AC-AUTH-006`, `AC-AUTH-010`, `AC-PRJ-008`, `AC-TSK-002`, `AC-TSK-003`, `AC-TSK-006`, `AC-TSK-010`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.feature.task.controller.TaskControllerTest`
|
||||||
|
- **Implementation commit:** `fb0ed7f12c9d89235c102b67f2b13f786011c9ee`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Task list/detail/create/status/comment routes require authentication, obtain actor identity from Spring Security rather than request IDs, retain CSRF protection, convert guessed-record denial to HTTP 404, validate create input, render the actual Thymeleaf pages, show `N/A` for an empty Project, display assignees, and expose create/status/comment controls only when the service-provided capability permits them. The status form exposes only direct edges from the current fixed status graph. An authorized create request with an invalid due date returns the form with the due-date field error, retained safe input, and refreshed authorized assignees; an access failure still returns non-disclosing HTTP 404.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
Fifteen `@WebMvcTest` MockMvc invocations render the real Task templates and exercise the real controller, Spring Security filter chain, CSRF filter, Bean Validation binding, redirect contracts, exception-to-status mapping, assignee output, and capability-controlled actions. A four-case parameterized test independently specifies every permitted status choice set. Dedicated create tests distinguish a due-date business validation response from a guessed-Project access response. Only the PostgreSQL-backed Task service is replaced at the controller boundary.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Unauthenticated list access returns 401 under the current platform security baseline. An authorized empty list returns 200 and contains `N/A`. A denied guessed Task or Project returns 404. A valid create request passes Project 10, assignee membership 7, the supplied fields, and the authenticated email to the service, then redirects to Task 25. Blank title stays on the form with a field error and no write. An invalid due date returns 200 with the message attached to `dueDate`, keeps title, description, assignee, and date, and reloads the permitted choices. Valid status/comment posts redirect to Task 25.
|
||||||
|
|
||||||
|
When `canCreate`, `canChangeStatus`, or `canComment` is false, the corresponding control is absent. When true, it is rendered. Both list and detail output the assignee display name. The hand-derived status choices are TODO to IN_PROGRESS/BLOCKED; IN_PROGRESS to BLOCKED/DONE; BLOCKED to TODO/IN_PROGRESS; and DONE to IN_PROGRESS.
|
||||||
|
|
||||||
|
## RED
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskControllerTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] TaskControllerTest.java:[28,13] cannot find symbol
|
||||||
|
symbol: class TaskController
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The first sandboxed GREEN attempt then exposed an environment boundary, not an application failure: Mockito could not use Java 25 self-attach inside the restricted sandbox. The exact same command was rerun with approved escalation; one test expectation was corrected from a login redirect to the platform baseline's observed 401 response before the final GREEN run.
|
||||||
|
|
||||||
|
The later view-capability increment was observed RED at test compilation because the Task DTOs did not yet provide the required capability and assignee fields.
|
||||||
|
|
||||||
|
The review-fix increment used the same command and observed these additional production-shaped failures before the controller/form change:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ERROR] Tests run: 14, Failures: 5, Errors: 0, Skipped: 0
|
||||||
|
[ERROR] invalidDueDateRendersFieldErrorAndRetainsSafeInput: Status expected:<200> but was:<400>
|
||||||
|
[ERROR] taskDetailsExposeOnlyAllowedStatusTransitions: expected permitted subsets but was:<{TODO, IN_PROGRESS, BLOCKED, DONE}> for all four source states
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
## GREEN
|
||||||
|
|
||||||
|
**Command**
|
||||||
|
|
||||||
|
```text
|
||||||
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
./mvnw -Dtest=TaskControllerTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with approved sandbox escalation for Mockito Java 25 self-attach.
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Tests run: 15, 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="$JAVA_HOME/bin:$PATH"
|
||||||
|
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
|
||||||
|
./mvnw -Dtest=TaskDomainRulesTest,TaskPersistenceStructureTest,TaskMutationBoundaryTest,TaskQueryServiceTest,TaskDashboardServiceTest,TaskControllerTest,TaskCreationIntegrationTest test
|
||||||
|
|
||||||
|
[INFO] Tests run: 57, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
|
||||||
|
./mvnw clean test
|
||||||
|
|
||||||
|
[INFO] Tests run: 113, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
The suite ran with approved escalation for OrbStack and Mockito self-attach.
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This slice test does not prove PostgreSQL state changes; those are covered by `TaskCreationIntegrationTest` in the affected/full commands. Shared shell styling/navigation remains owned by `work/reports-ui`. Browser journeys, notifications, Iteration 2 workflows, and narrow-screen behavior are outside this Iteration 1 Task evidence.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Test Evidence: theme token contrast
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `UI-005`, `UI-006`, `UI-010`, `UI-018`, `I1-UI-02`
|
||||||
|
- **Scenario IDs:** `AC-UI-003`, `AC-UI-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.ui.UiContractWebTest#themeTokensMeetTextFocusAndMeaningfulBoundaryContrast`
|
||||||
|
- **Implementation commit:** `3343745`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
The committed light and dark CSS tokens provide at least 4.5:1 contrast for normal text and 3:1 for focus indicators and meaningful panel/control boundaries against their adjacent surfaces.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
The web test reads the generated classpath CSS, extracts the production light and dark custom-property values, converts sRGB colors to relative luminance, and checks WCAG contrast ratios for ink, muted/subtle text, neutral boundaries, and focus tokens.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
Both themes must keep normal text at or above 4.5:1. Borders and focus tokens must be at or above 3:1 against the panel, sidebar, or canvas on which they are used.
|
||||||
|
|
||||||
|
## 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=UiContractWebTest#themeTokensMeetTextFocusAndMeaningfulBoundaryContrast test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
border / canvas contrast 1.2206621853850066 is below 3.0
|
||||||
|
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
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"
|
||||||
|
npm run build
|
||||||
|
./mvnw -Dtest=UiContractWebTest#themeTokensMeetTextFocusAndMeaningfulBoundaryContrast test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tailwind CSS v4.3.3: Done in 73ms
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 26.385 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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 -Dtest=UiContractWebTest,DashboardTemplateWebTest,ReportingArchitectureTest,LayerStructureTest test
|
||||||
|
|
||||||
|
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 27.072 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
This deterministic check proves the declared theme-token ratios used by the shared shell. It does not replace browser inspection for antialiasing, authored colors outside the token set, image contrast, zoom, high-contrast modes, or viewport-specific focus clipping.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# Test Evidence: shared UI shell and components
|
||||||
|
|
||||||
|
- **Test type:** Web
|
||||||
|
- **Requirement IDs:** `ARC-004`, `UI-001`–`UI-010`, `UI-013`–`UI-018`, `I1-UI-01`, `I1-UI-02`, `I1-UI-04`
|
||||||
|
- **Scenario IDs:** `AC-UI-001`, `AC-UI-002`, `AC-UI-003`, `AC-UI-005`
|
||||||
|
- **Test class/method:** `com.lab.labtimesheet.ui.UiContractWebTest`
|
||||||
|
- **Implementation commit:** `bac3981`
|
||||||
|
|
||||||
|
## Protected behavior
|
||||||
|
|
||||||
|
Domain-owned Thymeleaf pages can render inside one desktop shell with role-filtered navigation, accessible controls/states, pre-paint local theme loading, and committed local CSS/JavaScript/Lucide assets. The tests catch missing fragments, unauthorized or dead navigation links, inaccessible shared form/status markup, remote icon references, or a theme bootstrap loaded after CSS.
|
||||||
|
|
||||||
|
## Test method
|
||||||
|
|
||||||
|
A test-only domain page consumes the production layout fragment through MockMvc with a real Spring Security principal. A second page renders representative production fragments. The asset test reads the committed classpath artifacts produced by the pinned Node build.
|
||||||
|
|
||||||
|
## Hand-derived expected result
|
||||||
|
|
||||||
|
A Mentor sees `Owned Projects`, account identity, theme, and logout, but not Admin `Accounts`, Intern `My attendance`, or selector-less Intern attendance. An Intern's attendance link targets the real `/attendance` route. Unimplemented profile and notification destinations are not exposed. The theme script occurs before the stylesheet. Form label/control IDs match, errors use `role="alert"`, status includes a textual accessible name, confirmation copy is described, and the reduced sprite contains the selected symbols without remote resource references.
|
||||||
|
|
||||||
|
## 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=UiContractWebTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 2, Failures: 1, Errors: 1, Skipped: 0
|
||||||
|
UiContractWebTest.compiledAssetsAreLocalAndContainOnlyTheSelectedIconSprite expected: <true> but was: <false>
|
||||||
|
UiContractWebTest.sharedShellRendersAuthorizedDesktopNavigationBeforeDomainPagesIntegrate: Request processing failed: Error resolving template [fragments/layout]
|
||||||
|
BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
The asset assertion failed because the committed build artifacts did not exist, and the rendering request reached the test controller but could not resolve the missing production layout.
|
||||||
|
|
||||||
|
## 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=UiContractWebTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observed result**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 4.514 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
git diff --exit-code -- src/main/resources/static/assets/app.css src/main/resources/static/assets/icons.svg
|
||||||
|
./mvnw test
|
||||||
|
|
||||||
|
added 34 packages, audited 35 packages, found 0 vulnerabilities
|
||||||
|
Tailwind CSS v4.3.3: Done in 45ms
|
||||||
|
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
BUILD SUCCESS
|
||||||
|
Total time: 7.697 s
|
||||||
|
```
|
||||||
|
|
||||||
|
## External-test boundaries
|
||||||
|
|
||||||
|
The tests prove server-rendered authorization-aware markup and reproducible local assets. They do not replace manual browser checks for zero-flash paint timing, measured WCAG contrast, keyboard tooltip behavior, or page-level overflow at 1365×900; those remain final integrated UI gates.
|
||||||
Generated
+1248
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user