Files
labtimesheet/docs/tests/integration/development-environment-configuration.md

6.1 KiB

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

/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

STALE application-dev.properties
MISSING application-dev.yaml
exit 1

GREEN

Command

/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

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.

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

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.