Files
api/.junie/guidelines.md
T
Jeppe Bundgaard 51014bf774 Add development guidelines, testing rules, and secure routes documentation
- 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.
2026-02-18 14:13:05 +01:00

8.6 KiB
Raw Blame History

Copenhagen Truck Wash API — Development Guidelines (Projectspecific)

Scope

This document captures projectspecific 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.yml service traefik).
    • Web server: Caddy (caddy) serving the PHP app from services/nginx/app and proxied by Traefik.
    • PHP runtime: Multiple PHPFPM containers (php1..php5), sharing the bindmounted app directory.
    • Redis: redis for caching/queues/locks.
  • App location: services/nginx/app is the effective application root (many scripts/tests derive WD to point here).

  • Composer and dependencies:

    • services/php/Dockerfile installs Composer and PHP extensions.
    • services/php/docker-entrypoint.sh performs a guarded Composer install on php1 at container start when AUTO_COMPOSER_INSTALL=true and composer.json is present.
    • App dependencies live under services/nginx/app/composer.json (note: very light, primarily runtime libs; dev tool rector/rector).
  • Configuration source of truth during containerized runs is environment variables consumed by services/nginx/app/config.php.

    • config.php requires USE_ENV=true; otherwise it throws an exception. Many CLI scripts/tests bypass config.php entirely to remain envagnostic.
  • Local run targets and routing:

    • Traefik exposes:
      • http://localhost → routes to Caddy → app (HTTP only for dev).
      • https://localhost → also mapped, using Traefiks default/selfsigned dev cert.
      • http(s)://localhost/api/* → Traefik stripprefix middleware forwards to Caddy; the app sees paths without the /api prefix.
    • For productionlike HTTPS with real certs, see README.md for LETSENCRYPT_PATH mounting strategy (only needed if you want the exact api.truckwash.dk TLS behavior locally).
  • Minimal bringup 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 (starts traefik, redis, caddy, php1..php5, and other declared services).
    • Logs:
      • docker compose logs -f caddy
      • docker compose logs -f php1
      • docker compose logs -f traefik
  • Security note: docker-compose.yml currently embeds sensitive env values (DB, API tokens). Treat the file as secret in private repos; never republish as is. Prefer .env overrides 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

    • Selfcontained procedural PHP scripts intended to be executed with php. No framework required.
    • Many tests define the WD constant to the app root and then require_once specific class/trait/interface files they exercise.
    • Integrationstyle scripts that need configuration will rely on services/nginx/app/config.php and so must run within a properly provisioned environment (Docker containers with USE_ENV=true).
    • Fast unitstyle scripts should avoid config.php and any I/O; they manually include only whats needed and/or use test doubles.
  • 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 selfcontained 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.
        
  • 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 in docker-compose.yml for the PHP services. Running inside php1 will satisfy USE_ENV=true.
  • Adding a new test

    • Place the file under services/nginx/app/tests/<domain>/YourTest.php.
    • At top of the file, define WD if not already defined:
      if (!defined('WD')) { define('WD', dirname(__DIR__, 2)); }
      
    • Prefer selfcontained 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) with USE_ENV=true.
  • Demonstration: creating and running a minimal test

    • We created a temporary, envagnostic test at services/nginx/app/tests/examples/HelloWorldTest.php with these semantics:
      • Define WD, perform trivial assertions, and print success markers.
    • 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.
        
    • The example file was removed afterwards to keep the repository unchanged. You can replicate by creating a similar file and removing it after execution.

Additional Development Information

  • Routing and HTTP surface

    • Routes live under services/nginx/app/routes. Example route exampleRoute.php exposes GET /example returning {"message":"Hello World!"}. Local access paths (with Traefik):
      • http://localhost/example (direct)
      • http://localhost/api/example (Traefik stripprefix /api → still routes to /example in the app).
    • The OpenAPI contract is at the repo root openapi.yaml (large, authoritative). Keep it synchronized with implemented routes and payloads.
  • Module layout and traits

    • Domain modules live in services/nginx/app/modules/* and are heavily traitbased. 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 test tests/subusers/SelfservePermissionInitTest.php demonstrates overriding DBbacked methods to inject permissions for fast, deterministic checks.
  • Config and globals

    • services/nginx/app/config.php populates globals like $CONFIG_DB, $REDIS_CONFIG, etc., but only when USE_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

    • .editorconfig at repo root configures formatting. Key PHP rules:
      • Encoding: UTF8 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 crosscutting 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 zerodowntime for existing scripts.
  • 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, economic, MinIO) from tests; instead, stub/override or provide fakes.

Quick Reference

  • Bring up minimal dev stack:

    • docker compose up -d traefik redis php1 caddy
  • Run a fast, selfcontained test (host PHP):

    • php services/nginx/app/tests/subusers/SelfservePermissionInitTest.php
  • Run a test inside Docker (envbacked):

    • docker compose exec -T php1 php /var/www/html/tests/subusers/SelfservePermissionInitTest.php