- Add `.junie/guidelines.md` with comprehensive development instructions. - Include `.aiassistant/rules/Creating and maintaining tests.md` and `.aiassistant/rules/Creating and securing routes.md`. - Introduce `CACHE_SELFSERVE_LANE_KEY_ALLOWED_SERVICES` for lane service validation. - Update `selfserve_lane_relay_controller_t` to enforce service-specific permissions for machine relay.
8.6 KiB
Copenhagen Truck Wash API — Development Guidelines (Project‑specific)
Scope
This document captures project‑specific knowledge for building, configuring, testing, and extending the API. It assumes an advanced developer familiar with Docker, PHP 8.2, and HTTP APIs.
Build and Configuration
-
Stack overview (local):
- Reverse proxy/router: Traefik 2.x (
docker-compose.ymlservicetraefik). - Web server: Caddy (
caddy) serving the PHP app fromservices/nginx/appand proxied by Traefik. - PHP runtime: Multiple PHP‑FPM containers (
php1..php5), sharing the bind‑mounted app directory. - Redis:
redisfor caching/queues/locks.
- Reverse proxy/router: Traefik 2.x (
-
App location:
services/nginx/appis the effective application root (many scripts/tests deriveWDto point here). -
Composer and dependencies:
services/php/Dockerfileinstalls Composer and PHP extensions.services/php/docker-entrypoint.shperforms a guarded Composer install onphp1at container start whenAUTO_COMPOSER_INSTALL=trueandcomposer.jsonis present.- App dependencies live under
services/nginx/app/composer.json(note: very light, primarily runtime libs; dev toolrector/rector).
-
Configuration source of truth during containerized runs is environment variables consumed by
services/nginx/app/config.php.config.phprequiresUSE_ENV=true; otherwise it throws an exception. Many CLI scripts/tests bypassconfig.phpentirely to remain env‑agnostic.
-
Local run targets and routing:
- Traefik exposes:
http://localhost→ routes to Caddy → app (HTTP only for dev).https://localhost→ also mapped, using Traefik’s default/self‑signed dev cert.http(s)://localhost/api/*→ Traefik strip‑prefix middleware forwards to Caddy; the app sees paths without the/apiprefix.
- For production‑like HTTPS with real certs, see
README.mdforLETSENCRYPT_PATHmounting strategy (only needed if you want the exactapi.truckwash.dkTLS behavior locally).
- Traefik exposes:
-
Minimal bring‑up for local development:
- Prerequisites: Docker Desktop 4.x+.
- First run will build PHP images and start dependent services. Composer install runs automatically on
php1. - Recommended minimal set:
docker compose up -d traefik redis php1 caddy
- Full set (scale out PHP or add observability as needed):
docker compose up -d(startstraefik,redis,caddy,php1..php5, and other declared services).
- Logs:
docker compose logs -f caddydocker compose logs -f php1docker compose logs -f traefik
-
Security note:
docker-compose.ymlcurrently embeds sensitive env values (DB, API tokens). Treat the file as secret in private repos; never re‑publish as is. Prefer.envoverrides and secrets providers for wider teams.
Testing
The repository does not use PHPUnit for the app. Instead, tests are lightweight CLI scripts under services/nginx/app/tests. Conventions:
-
Test style
- Self‑contained procedural PHP scripts intended to be executed with
php. No framework required. - Many tests define the
WDconstant to the app root and thenrequire_oncespecific class/trait/interface files they exercise. - Integration‑style scripts that need configuration will rely on
services/nginx/app/config.phpand so must run within a properly provisioned environment (Docker containers withUSE_ENV=true). - Fast unit‑style scripts should avoid
config.phpand any I/O; they manually include only what’s needed and/or use test doubles.
- Self‑contained procedural PHP scripts intended to be executed with
-
Running tests from host (fastest path)
- Prereq: PHP CLI available on host. Verified with:
php -v→ observed on our env:PHP 8.2.30 (cli)
- Example: an existing, fully self‑contained test validating subuser permission wiring:
- Command:
php services/nginx/app/tests/subusers/SelfservePermissionInitTest.php
-
Verified output (captured):
✔ SELFSERVE_ADD is granted as expected ✔ SELFSERVE_LIST is not granted as expected ✔ SELFSERVE_EDIT is not granted as expected ✔ SELFSERVEDELETE is not granted as expected SelfservePermissionInitTest completed.
- Command:
- Prereq: PHP CLI available on host. Verified with:
-
Running tests inside Docker (when host PHP is unavailable or when env is required)
- Ensure containers are up:
docker compose up -d traefik redis php1 caddy - Execute a test within
php1:docker compose exec -T php1 php /var/www/html/tests/subusers/SelfservePermissionInitTest.php
- For integration tests that depend on
config.php(e.g., Redis/DB), environment variables are preconfigured indocker-compose.ymlfor the PHP services. Running insidephp1will satisfyUSE_ENV=true.
- Ensure containers are up:
-
Adding a new test
- Place the file under
services/nginx/app/tests/<domain>/YourTest.php. - At top of the file, define
WDif not already defined:if (!defined('WD')) { define('WD', dirname(__DIR__, 2)); } - Prefer self‑contained tests that avoid I/O. If you need to touch internal classes without Composer autoloading, include files directly, mirroring existing tests.
- If the test must hit Redis/DB or rely on globals from
config.php, run it inside a PHP container (php1) withUSE_ENV=true.
- Place the file under
-
Demonstration: creating and running a minimal test
- We created a temporary, env‑agnostic test at
services/nginx/app/tests/examples/HelloWorldTest.phpwith these semantics:- Define
WD, perform trivial assertions, and print success markers.
- Define
- Command executed and verified output:
- Command:
php services/nginx/app/tests/examples/HelloWorldTest.php
-
Output (captured):
✔ Basic arithmetic works (2 + 2 = 4) ✔ WD points to the application root: C:\\Users\\2jepp\\PhpstormProjects\\api\\services\\nginx\\app HelloWorldTest completed.
- Command:
- The example file was removed afterwards to keep the repository unchanged. You can replicate by creating a similar file and removing it after execution.
- We created a temporary, env‑agnostic test at
Additional Development Information
-
Routing and HTTP surface
- Routes live under
services/nginx/app/routes. Example routeexampleRoute.phpexposesGET /examplereturning{"message":"Hello World!"}. Local access paths (with Traefik):http://localhost/example(direct)http://localhost/api/example(Traefik strip‑prefix/api→ still routes to/examplein the app).
- The OpenAPI contract is at the repo root
openapi.yaml(large, authoritative). Keep it synchronized with implemented routes and payloads.
- Routes live under
-
Module layout and traits
- Domain modules live in
services/nginx/app/modules/*and are heavily trait‑based. Tests often pull in precise files from here to avoid full app bootstrap. - Example: subusers module (
modules/subusers/...) provides permission node containers and helpers. The testtests/subusers/SelfservePermissionInitTest.phpdemonstrates overriding DB‑backed methods to inject permissions for fast, deterministic checks.
- Domain modules live in
-
Config and globals
services/nginx/app/config.phppopulates globals like$CONFIG_DB,$REDIS_CONFIG, etc., but only whenUSE_ENV=true. If you see tests throwing “Environment variables are not set”, run them inside Docker or set required envs for host PHP.
-
Code style
.editorconfigat repo root configures formatting. Key PHP rules:- Encoding: UTF‑8 with CRLF line endings.
- Indent: 4 spaces; continuation indent 4.
- Class brace style: next_line; function/method blank lines: 1.
- Import sorting: alphabetic; various alignment toggles are disabled.
- Match existing patterns (procedural scripts for tests; namespacing in app code; traits for cross‑cutting concerns). Avoid adding new frameworks for tests unless explicitly requested.
-
Composer / Autoload
- There is no Composer autoload configured for the app code in
composer.json; tests include files directly. If you introduce autoloading, coordinate with Docker entrypoint behaviors and ensure zero‑downtime for existing scripts.
- There is no Composer autoload configured for the app code in
-
Performance & reliability hints for tests
- Prefer small, deterministic CLI scripts; inject dependencies and override I/O methods (as shown in
SelfservePermissionInitTest.php). - Avoid hitting external services (Stripe, e‑conomic, MinIO) from tests; instead, stub/override or provide fakes.
- Prefer small, deterministic CLI scripts; inject dependencies and override I/O methods (as shown in
Quick Reference
-
Bring up minimal dev stack:
docker compose up -d traefik redis php1 caddy
-
Run a fast, self‑contained test (host PHP):
php services/nginx/app/tests/subusers/SelfservePermissionInitTest.php
-
Run a test inside Docker (env‑backed):
docker compose exec -T php1 php /var/www/html/tests/subusers/SelfservePermissionInitTest.php