diff --git a/.java-version b/.java-version new file mode 100644 index 0000000..7273c0f --- /dev/null +++ b/.java-version @@ -0,0 +1 @@ +25 diff --git a/README.md b/README.md index f397957..1c0acd9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ -# labtimesheet +# Lab Timesheet +## Local development + +The application targets Java 25 and expects PostgreSQL on +`localhost:55432` when the default `dev` profile is active. Override any local +value with `LAB_DB_URL`, `LAB_DB_USERNAME`, `LAB_DB_PASSWORD`, +`LAB_SMTP_HOST`, or `LAB_SMTP_PORT`. + +```bash +export JAVA_HOME=/opt/homebrew/opt/openjdk@25 +export PATH="$JAVA_HOME/bin:$PATH" +./mvnw spring-boot:run +``` + +Tests use PostgreSQL 18.4 through Testcontainers and do not use the developer +database: + +```bash +./mvnw test +``` + +Every feature test must have a companion Markdown evidence record under +[`docs/tests`](docs/tests/README.md). diff --git a/docs/tests/README.md b/docs/tests/README.md new file mode 100644 index 0000000..7ab498b --- /dev/null +++ b/docs/tests/README.md @@ -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. diff --git a/docs/tests/e2e/_TEMPLATE.md b/docs/tests/e2e/_TEMPLATE.md new file mode 100644 index 0000000..9ac40ff --- /dev/null +++ b/docs/tests/e2e/_TEMPLATE.md @@ -0,0 +1,59 @@ +# Test Evidence: + +- **Test type:** E2E +- **Requirement IDs:** `` +- **Scenario IDs:** `` +- **Test class/method:** `` +- **Implementation commit:** `` + +## Protected behavior + + + +## Test method + + + +## Hand-derived expected result + + + +## RED + +**Command** + +```text + +``` + +**Observed result** + +```text + +``` + +## GREEN + +**Command** + +```text + +``` + +**Observed result** + +```text + +``` + +## Affected suite + +**Command and result** + +```text + +``` + +## External-test boundaries + + diff --git a/docs/tests/integration/_TEMPLATE.md b/docs/tests/integration/_TEMPLATE.md new file mode 100644 index 0000000..65607b3 --- /dev/null +++ b/docs/tests/integration/_TEMPLATE.md @@ -0,0 +1,59 @@ +# Test Evidence: + +- **Test type:** Integration +- **Requirement IDs:** `` +- **Scenario IDs:** `` +- **Test class/method:** `` +- **Implementation commit:** `` + +## Protected behavior + + + +## Test method + + + +## Hand-derived expected result + + + +## RED + +**Command** + +```text + +``` + +**Observed result** + +```text + +``` + +## GREEN + +**Command** + +```text + +``` + +**Observed result** + +```text + +``` + +## Affected suite + +**Command and result** + +```text + +``` + +## External-test boundaries + + diff --git a/docs/tests/unit/_TEMPLATE.md b/docs/tests/unit/_TEMPLATE.md new file mode 100644 index 0000000..35137d0 --- /dev/null +++ b/docs/tests/unit/_TEMPLATE.md @@ -0,0 +1,59 @@ +# Test Evidence: + +- **Test type:** Unit +- **Requirement IDs:** `` +- **Scenario IDs:** `` +- **Test class/method:** `` +- **Implementation commit:** `` + +## Protected behavior + + + +## Test method + + + +## Hand-derived expected result + + + +## RED + +**Command** + +```text + +``` + +**Observed result** + +```text + +``` + +## GREEN + +**Command** + +```text + +``` + +**Observed result** + +```text + +``` + +## Affected suite + +**Command and result** + +```text + +``` + +## External-test boundaries + + diff --git a/docs/tests/web/_TEMPLATE.md b/docs/tests/web/_TEMPLATE.md new file mode 100644 index 0000000..72bba5f --- /dev/null +++ b/docs/tests/web/_TEMPLATE.md @@ -0,0 +1,59 @@ +# Test Evidence: + +- **Test type:** Web +- **Requirement IDs:** `` +- **Scenario IDs:** `` +- **Test class/method:** `` +- **Implementation commit:** `` + +## Protected behavior + + + +## Test method + + + +## Hand-derived expected result + + + +## RED + +**Command** + +```text + +``` + +**Observed result** + +```text + +``` + +## GREEN + +**Command** + +```text + +``` + +**Observed result** + +```text + +``` + +## Affected suite + +**Command and result** + +```text + +``` + +## External-test boundaries + + diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml new file mode 100644 index 0000000..765f81d --- /dev/null +++ b/src/main/resources/application-dev.yaml @@ -0,0 +1,8 @@ +spring: + datasource: + url: ${LAB_DB_URL:jdbc:postgresql://localhost:55432/labtimesheet} + username: ${LAB_DB_USERNAME:labtimesheet} + password: ${LAB_DB_PASSWORD:labtimesheet_dev} + mail: + host: ${LAB_SMTP_HOST:localhost} + port: ${LAB_SMTP_PORT:1025} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 9cec0d9..70f4092 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,3 +1,10 @@ spring: application: name: labtimesheet + profiles: + default: dev + docker: + compose: + enabled: false + jpa: + open-in-view: false diff --git a/src/test/java/com/lab/labtimesheet/LabtimesheetApplicationTests.java b/src/test/java/com/lab/labtimesheet/LabtimesheetApplicationTests.java index 87aeab2..b2c6272 100644 --- a/src/test/java/com/lab/labtimesheet/LabtimesheetApplicationTests.java +++ b/src/test/java/com/lab/labtimesheet/LabtimesheetApplicationTests.java @@ -3,9 +3,11 @@ package com.lab.labtimesheet; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; @Import(TestcontainersConfiguration.class) @SpringBootTest +@ActiveProfiles("test") class LabtimesheetApplicationTests { @Test diff --git a/src/test/java/com/lab/labtimesheet/TestcontainersConfiguration.java b/src/test/java/com/lab/labtimesheet/TestcontainersConfiguration.java index 841da8d..e8643af 100644 --- a/src/test/java/com/lab/labtimesheet/TestcontainersConfiguration.java +++ b/src/test/java/com/lab/labtimesheet/TestcontainersConfiguration.java @@ -12,7 +12,7 @@ class TestcontainersConfiguration { @Bean @ServiceConnection PostgreSQLContainer postgresContainer() { - return new PostgreSQLContainer(DockerImageName.parse("postgres:latest")); + return new PostgreSQLContainer(DockerImageName.parse("postgres:18.4")); } } diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml new file mode 100644 index 0000000..b71534c --- /dev/null +++ b/src/test/resources/application-test.yaml @@ -0,0 +1,4 @@ +spring: + docker: + compose: + enabled: false