Add 27 PHP test files + 6 TS test files + Vitest setup, surfacing
and fixing 4 production bugs in the process.
Test counts (all green):
- PHP: 545 tests / 2341 assertions / 25.25s
- 12 lint rule tests (L1, L2, L2b, L3, L4, L5, L6, L7, L8, L9, L10, L11)
- LintRunner dispatch + LintReport bucket aggregation
- DuplicateChecker bulk-query + N+1 protection
- RouteForgeService full commit pipeline (happy path, lint rollback,
attach-existing branch, fare multiplier, withoutEvents suppression)
- 6 endpoint feature tests (PreviewAirports, Subfleets, AirlineStats,
CheckDuplicates, Lint, Commit)
- TS: 46 tests / 6 files / 280ms via Vitest 4.1.7 + happy-dom
- geo, timezone, flightNumber, timeStrategy, generator, lint
In-scope production fixes discovered by writing the tests:
1. DuplicateChecker eager-load was 'airline:id,code' but 'code' is an
accessor over iata/icao columns. Loaded airline came back with
NEITHER backing column populated and Flight::ident dropped the
airline prefix in the duplicates response. Changed to
'airline:id,iata,icao'.
2. LintRunner constructor takes 'array $rules' with no container-
resolvable shape. Both /lint and /commit endpoints would have
failed at runtime with 'Unresolvable dependency'. Registered as
singleton in AppServiceProvider::register() binding to
LintRunner::defaults().
3. BaseRouteForgeBatchRequest's 'subfleet_ids' rule was
['required', 'array', 'min:0']. 'required' rejects empty arrays
in Laravel, contradicting 'min:0'. Relaxed to ['present', 'array'];
L3 lint catches the empty case as a warning downstream.
4. RouteForgeService::commit() called auth()->user() directly,
violating tests/Arch/GlobalTest's http-helpers rule (auth/session/
request only allowed in App\Http / App\Filament / App\Livewire /
App\Providers\Filament). Added $causerId field to CommitInput;
controller stamps it from auth()->id(); service resolves User by id.
Infrastructure:
- tests/Support/RouteForgeTestHelpers.php — shared LintContext + row +
airport + batchPayload builders. PSR-4 autoloaded via Tests\ namespace;
static class avoids global-function name collisions across the 15+
RouteForge test files.
- vitest.config.ts at project root, scoped to
resources/js/admin/routeforge/**/*.test.ts. happy-dom provides the
minimal window + DOM globals lib/i18n.ts depends on.
- package.json gains 'test': 'vitest run' script.
- Added vitest, happy-dom, @types/node as devDependencies.
Task 4.6.3 stays N/A — FlightNumberAssigner was removed per the
Section 4 banner; no PHP equivalent to test. Section 6.4.7's planned
PHP↔TS shared fixtures (tests/fixtures/routeforge/generator/) also
N/A for the same reason; TS generator tests stand alone.
Quality gates green:
- tsc --noEmit: clean
- oxlint (40 files / 93 rules): 0 warnings, 0 errors
- oxfmt --check: all formatted
- npm run build: routeforge bundle stays at 26.69 KB gzip
(Decision 16 budget 60 KB → 33.31 KB headroom)
- vendor/bin/pint --dirty: passed
- vendor/bin/phpstan analyse: OK no errors
- vendor/bin/rector --dry-run: clean for RouteForge files
|
||
|---|---|---|
| .github | ||
| .hooks | ||
| app | ||
| bin | ||
| bootstrap | ||
| config | ||
| database | ||
| docs | ||
| modules | ||
| public | ||
| resources | ||
| routes | ||
| scripts | ||
| storage | ||
| tests | ||
| .editorconfig | ||
| .env.example | ||
| .gitignore | ||
| .htaccess | ||
| .oxfmtrc.json | ||
| .oxlintrc.json | ||
| artisan | ||
| bun.lock | ||
| CHANGELOG.md | ||
| compose.sail.yml | ||
| composer.json | ||
| composer.lock | ||
| Directory.Build.props | ||
| docker-compose.example.yml | ||
| Dockerfile | ||
| intellij_style.xml | ||
| justfile | ||
| LICENSE | ||
| Makefile | ||
| package-lock.json | ||
| package.json | ||
| phpstan.neon | ||
| phpunit.xml | ||
| pint.json | ||
| Procfile | ||
| README.md | ||
| rector.php | ||
| symfony.lock | ||
| tsconfig.json | ||
| version.json | ||
| vite.config.js | ||
| vitest.config.ts | ||
phpVMS 8
phpVMS is a PHP application to run and simulate an airline. It allowed users to register, view flight schedules that you create, and file flight reports, built on the Laravel framework. The latest documentation, with installation instructions is available on the phpVMS documentation site.
Installation
A full distribution, with all the composer dependencies, is available at this GitHub Releases link.
Requirements
- PHP 8.3+, extensions:
- cURL
- JSON
- fileinfo
- mbstring
- openssl
- pdo
- tokenizer
- intl
- zip
- Database:
- MySQL 5.7+ (or MySQL variant, including MariaDB and Percona)
View more details on requirements
Installer
- Upload to your server
- Visit the site, and follow the link to the installer
Development Environment with Docker
A full development environment can be brought up using Docker and Laravel Sail, without having to install composer/npm locally
make docker-test
# **OR** with docker directly
docker run --rm \
-u "$(id -u):$(id -g)" \
-v "$(pwd):/var/www/html" \
-w /var/www/html \
laravelsail/php84-composer:latest \
composer install --ignore-platform-reqs
# Then you can start sail
./vendor/bin/sail up
Then go to http://localhost.
Instead of repeatedly typing vendor/bin/sail to execute Sail commands, you may wish to configure a shell alias that allows you to execute Sail's commands more easily:
alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail'
Then you can execute php, artisan, composer, npm, etc. commands using the sail prefix:
# PHP commands within Laravel Sail...
sail php --version
# Artisan commands within Laravel Sail...
sail artisan about
# Composer commands within Laravel Sail...
sail composer install
# NPM commands within Laravel Sail...
sail npm run dev
To interact with databases (MariaDB, Redis...), please refer to the Laravel Sail documentation
Building JS/CSS assets
Yarn is required, run:
make build-assets
This will build all the assets according to the webpack file.
Laravel Boost
If you want to use AI agents for your development workflow, please ensure you install Laravel Boost by running the following command:
php artisan boost:install
Contributors
Thank you to everyone who've contributed to phpVMS!