Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
619 commits
Select commit Hold shift + click to select a range
1de60ef
Cover internal driver controller helpers
roncodes Jul 27, 2026
848a990
Cover operations monitor live snapshot
roncodes Jul 27, 2026
a876c39
Cover internal order statuses endpoint
roncodes Jul 27, 2026
ad5ebb5
Add apiError and job dispatch shims to test bootstrap
roncodes Jul 27, 2026
adf2b14
Cover API driver controller helpers and route simulation
roncodes Jul 27, 2026
050daf6
Cover API driver controller lookup and organization error paths
roncodes Jul 27, 2026
58f240f
Cover internal place controller endpoints and fix avatar options type…
roncodes Jul 27, 2026
9315b56
Cover API service quote query endpoint and helper implementations
roncodes Jul 27, 2026
d61951d
Cover internal vehicle controller endpoints and lookup helpers
roncodes Jul 27, 2026
dd408eb
Cover internal telematic controller endpoints
roncodes Jul 27, 2026
9aee913
Cover internal payment controller received payments and helpers
roncodes Jul 27, 2026
1de80b1
Cover API sensor controller crud endpoints
roncodes Jul 27, 2026
32083cc
Cover internal driver controller helper implementations
roncodes Jul 27, 2026
c0b2aac
Cover API vehicle controller tracking and helper implementations
roncodes Jul 27, 2026
3d51410
Cover internal service quote controller helper implementations
roncodes Jul 27, 2026
f5bf0da
Cover internal navigator controller helper implementations
roncodes Jul 27, 2026
7884f6e
Cover internal order controller helper implementations
roncodes Jul 27, 2026
f22c4e0
Cover fuel provider service matching pipeline
roncodes Jul 27, 2026
44fcd2d
Cover API order controller lookup and resolution helpers
roncodes Jul 27, 2026
aa43d9e
Cover contact model customer user linkage behavior
roncodes Jul 27, 2026
793aa71
Cover order model payload route and driver assignment behavior
roncodes Jul 27, 2026
1c109ca
Cover order ping notification channels and order config lookups
roncodes Jul 27, 2026
ea435ca
Cover orchestration controller helper implementations
roncodes Jul 27, 2026
ebf6aa9
Cover fleetops console commands
roncodes Jul 27, 2026
191f359
Cover telematic webhook ingest and signature resolution
roncodes Jul 27, 2026
09e042d
Cover maintenance reminder command behavior
roncodes Jul 27, 2026
3a97470
Cover replay positions job and label subject resolution
roncodes Jul 27, 2026
6a6a5c2
Cover API purchase rate controller lookup helpers
roncodes Jul 27, 2026
fab79a0
Cover API order controller query filter pipeline
roncodes Jul 27, 2026
1f72208
Cover internal driver controller create and update record flows
roncodes Jul 27, 2026
2918343
Cover internal order controller create record flow
roncodes Jul 27, 2026
58be32f
Cover API order controller create flow
roncodes Jul 27, 2026
c93408e
Cover device event getting started and order config internal endpoints
roncodes Jul 27, 2026
8c951a3
Cover issues insights analytics and allocation job helpers
roncodes Jul 27, 2026
30140da
Cover petroapp fuel provider http integration
roncodes Jul 27, 2026
1d200a1
Cover lalamove quotation construction and fix instance argument order
roncodes Jul 27, 2026
eea4eb5
Cover place model shared dedupe geocoding and avatar behavior
roncodes Jul 27, 2026
1a5f4ac
Cover geofence dwell job customer scoping and navigator settings
roncodes Jul 27, 2026
e1a3610
Cover order dispatch listener helpers and fuel transaction filter
roncodes Jul 27, 2026
b0ee3e8
Cover order dispatched channels and customer payload resources
roncodes Jul 27, 2026
ed831f5
Cover api driver auth flows and monitoring notifications
roncodes Jul 27, 2026
bd4ce0d
Cover lalamove service types and operations pulse analytics
roncodes Jul 27, 2026
9e837be
Cover work order resource and telematics sync command
roncodes Jul 28, 2026
7f60f9d
Cover api driver organization endpoints and session helpers
roncodes Jul 28, 2026
56653a4
Cover purchase rate order creation and fix return place bugs
roncodes Jul 28, 2026
2322ecc
Cover live controller map endpoints
roncodes Jul 28, 2026
76816e9
Cover internal global search endpoint
roncodes Jul 28, 2026
41bd574
Cover database backed service stop resolution helpers
roncodes Jul 28, 2026
d2ab59e
Cover observers dispatch failed listener roles command and ai helpers
roncodes Jul 28, 2026
7686629
Cover sensor reading thresholds alerts and position creation
roncodes Jul 28, 2026
bec8f04
Cover internal vendor controller endpoints and personnel helpers
roncodes Jul 28, 2026
9e24ee0
Cover internal contact controller endpoints and conversion helpers
roncodes Jul 28, 2026
4dcd50c
Cover ai operational query driver geofence distribution
roncodes Jul 28, 2026
66d7e5e
Cover order failed notification customer roles and vendor lifecycle
roncodes Jul 28, 2026
d6234fc
Cover maintenance and maintenance schedule resources
roncodes Jul 28, 2026
ba44638
Cover api order start and activity update flows
roncodes Jul 28, 2026
c3d0c4a
Cover api order lifecycle actions
roncodes Jul 28, 2026
7e4ae2f
Cover api order persistence helpers
roncodes Jul 28, 2026
2fe0b1c
Cover internal order activity flows
roncodes Jul 28, 2026
359a60f
Cover fuel efficiency analytics and canceled completed order handlers
roncodes Jul 28, 2026
2dc3c37
Cover service area geometry helpers
roncodes Jul 28, 2026
494fd94
Cover service quote query record and fix stops re-wrapping fatal
roncodes Jul 28, 2026
b1e52d8
Cover driver model position import and identifier helpers
roncodes Jul 28, 2026
7731177
Cover orchestration import orders pipeline
roncodes Jul 28, 2026
f5c8cea
Cover zone purchase rate customer observer and tracking status request
roncodes Jul 28, 2026
4b17955
Cover telematic service persistence pipeline
roncodes Jul 28, 2026
009dfae
Cover position replay metrics and api contact helpers
roncodes Jul 28, 2026
098f34d
Cover maintainable trait and operational alert helpers
roncodes Jul 28, 2026
72bd77e
Cover work order email order config deletion and sensor filter
roncodes Jul 28, 2026
3819080
Cover order purchase rates transactions and closest drivers
roncodes Jul 28, 2026
c63a0c9
Cover orchestration payload builder computations
roncodes Jul 28, 2026
2cd75c5
Cover api device controller crud and input helpers
roncodes Jul 28, 2026
3768ed2
Cover fleet assignments and maintenance trigger helpers
roncodes Jul 28, 2026
28ab6ba
Cover maintenance schedule controller surfaces
roncodes Jul 28, 2026
a55ce20
Cover purchase rate observer helpers and order finalize job
roncodes Jul 28, 2026
231100f
Cover fix commands and maintenance controller helpers
roncodes Jul 28, 2026
fb34070
Cover osrm routing proof lookups and issue filter
roncodes Jul 28, 2026
b7aefe5
Cover optimize order route capability apply flow
roncodes Jul 28, 2026
b58185d
Cover service rate persistence and servicability lookups
roncodes Jul 28, 2026
cc1591c
Cover utils geo matrix and vendor helpers
roncodes Jul 28, 2026
20eb42c
Cover order payload config and distance helpers
roncodes Jul 28, 2026
1163c68
Cover customer api controller helper seams
roncodes Jul 28, 2026
750bdc3
Cover place creation resolution and import rows
roncodes Jul 28, 2026
a6d19b6
Cover payload waypoints entities and fix dead destination fallback
roncodes Jul 28, 2026
d179574
Cover create order preview drafting helpers
roncodes Jul 28, 2026
1efab4f
Cover orchestration route and vehicle task builders
roncodes Jul 28, 2026
70c0aa5
Cover vroom engine transport and settings resolution
roncodes Jul 28, 2026
3664810
Cover tracking intelligence service and context builder
roncodes Jul 28, 2026
e288ef0
Cover vehicle positions avatars and import rows
roncodes Jul 28, 2026
a18d7d2
Cover place search ranking orderings and geocoding seams
roncodes Jul 28, 2026
6492051
Cover order query nearby spatial filters
roncodes Jul 28, 2026
11bfabc
Cover afaqy provider http transport and extractors
roncodes Jul 28, 2026
333ecfd
Cover lalamove quote and service order creation flows
roncodes Jul 28, 2026
8166efa
Cover service quote preliminary query paths
roncodes Jul 28, 2026
477f00a
Cover driver organization switching and geofence crossings
roncodes Jul 28, 2026
d2f0a59
Cover internal driver create existing user adoption
roncodes Jul 28, 2026
746f7ce
Cover maintenance and work order import resolution
roncodes Jul 28, 2026
7d7f11a
Cover place helper seams and api preliminary quotes
roncodes Jul 28, 2026
6a3e350
Cover simulate geofence events command
roncodes Jul 28, 2026
5358846
Cover order capture photo flow and unblock validation closures
roncodes Jul 28, 2026
0445d2d
Cover order import from files endpoint
roncodes Jul 28, 2026
f14322d
Cover waypoint service stop activity flows
roncodes Jul 28, 2026
9db63ee
Cover api order activity waypoint and completion flows
roncodes Jul 28, 2026
8d11530
Cover integrated vendor resolver and tracking number trait
roncodes Jul 28, 2026
d6d2183
Cover search controller type dispatch arms
roncodes Jul 28, 2026
a99deb5
Cover replay vehicle locations command
roncodes Jul 28, 2026
b7831c2
Cover device controller export and query filters
roncodes Jul 28, 2026
9137438
Cover service area borders and helper seams
roncodes Jul 28, 2026
302ad33
Cover service stop activity waypoint and endpoint branches
roncodes Jul 28, 2026
5e23730
Cover order update payloads completion and nearby branches
roncodes Jul 28, 2026
08c3f62
Cover internal order controller helper seams
roncodes Jul 28, 2026
3a90faf
Cover driver geofence dwell and switch-org rejections
roncodes Jul 28, 2026
9d75b08
Cover customer create verification and stub backfill
roncodes Jul 28, 2026
40187c4
Cover place import and coordinate insertion branches
roncodes Jul 28, 2026
3aa77c4
Cover order files routes and config fallbacks
roncodes Jul 28, 2026
7f9756b
Cover utils point resolution and matrix providers
roncodes Jul 28, 2026
8929d82
Cover internal driver create and update happy paths
roncodes Jul 28, 2026
4d0e066
Cover order create record validation and catch branches
roncodes Jul 28, 2026
5252fb1
Cover contact customer role and phone adoption flows
roncodes Jul 28, 2026
afa0a39
Cover api order create customer and adhoc branches
roncodes Jul 28, 2026
21c24fd
Cover zone controller borders and helper seams
roncodes Jul 28, 2026
2434646
Cover place creation owner and location fallbacks
roncodes Jul 28, 2026
b16d7b0
Cover internal customer credentials reset flows
roncodes Jul 28, 2026
c0546d2
Cover orchestration public wrappers and manifest seams
roncodes Jul 28, 2026
c4d9f64
Cover service rate cod peak fees and driver seams
roncodes Jul 28, 2026
2ab6a08
Cover dispatched and pod-required activity branches
roncodes Jul 28, 2026
1cbd84b
Cover public api capture photo validation flow
roncodes Jul 28, 2026
df098ef
Cover lalamove quote errors and order route metas
roncodes Jul 28, 2026
0f61db0
Cover payload entity photos and search waypoint metadata
roncodes Jul 28, 2026
200cac0
Cover geofence intersection real query helpers
roncodes Jul 28, 2026
32163ed
Cover equipment scheduling and warranty imports
roncodes Jul 28, 2026
def5e12
Cover api contact photos deletion and place seams
roncodes Jul 28, 2026
317186b
Cover telematic credential validation and encryption seams
roncodes Jul 28, 2026
ee68728
Cover issue export and import endpoints
roncodes Jul 28, 2026
ec1cb28
Fix trashed contact lookup and cover portal seams
roncodes Jul 28, 2026
7520003
Cover tracking number generation and lookups
roncodes Jul 28, 2026
f08d813
Cover maintenance schedule import lookups
roncodes Jul 28, 2026
8d57e4a
Fix entity destination confirm dead code
roncodes Jul 28, 2026
b2991d0
Cover issue and device filter relation branches
roncodes Jul 28, 2026
f7205aa
Cover tracking context guards and fallback origins
roncodes Jul 28, 2026
c6ec21f
Cover order and driver resource seams
roncodes Jul 28, 2026
f798b26
Cover ai capability and query registration wiring
roncodes Jul 28, 2026
6c050c3
Cover order config namespace hooks and waypoint contexts
roncodes Jul 28, 2026
9b0c13e
Cover service area polygon conversions
roncodes Jul 28, 2026
48d1e2e
Cover safee transport guards and failure paths
roncodes Jul 28, 2026
691b1fc
Fix verify code creation delegation and cover guards
roncodes Jul 28, 2026
0946d5c
Cover service quote vendor bridge failures
roncodes Jul 28, 2026
3f9b3e3
Cover internal quote bridge provider failures
roncodes Jul 28, 2026
ba8abf2
Cover edit order route waypoint and endpoint edits
roncodes Jul 28, 2026
9406561
Cover utils edge seams and empty waypoint clears
roncodes Jul 28, 2026
565ce7a
Cover driver verify code token issuance
roncodes Jul 28, 2026
2743e09
Cover driver user provisioning and conflict guards
roncodes Jul 28, 2026
685f1e8
Cover customer creation code failures and file photos
roncodes Jul 28, 2026
ab553d9
Cover driver geocode refresh and geofence failure silence
roncodes Jul 28, 2026
3b257e7
Cover dispatch success and waypoint gating branches
roncodes Jul 28, 2026
44c5b32
Cover multi zone quote fall-through
roncodes Jul 28, 2026
a49b9dd
Cover customer config payload and verification seams
roncodes Jul 28, 2026
ff832de
Cover status options and proof subject defaults
roncodes Jul 28, 2026
2cf144d
Fix route instance set route branch and cover callbacks
roncodes Jul 28, 2026
687fadc
Cover dispatch order guards and destination gating
roncodes Jul 28, 2026
a7387fc
Make geometry engine and geocoder injectable
roncodes Jul 29, 2026
7912c7b
Make datetime diff sql driver aware
roncodes Jul 29, 2026
421317d
Update analytics source assertion for portable sql
roncodes Jul 29, 2026
440bece
Harden sqlite detection for null connections
roncodes Jul 29, 2026
b31008c
Cover real auth can permission gates
roncodes Jul 29, 2026
4d06665
Cover place creation from injected geocoding results
roncodes Jul 29, 2026
3878f3a
Cover reverse geocoded creation and owner extraction
roncodes Jul 29, 2026
a479850
Cover customer password reset flows
roncodes Jul 29, 2026
ba9d8b8
Cover authenticated customer profile photo flows
roncodes Jul 29, 2026
1a897d5
Cover uploaded file proof capture branches
roncodes Jul 29, 2026
e2117e7
Cover integrated vendor bridge order flows
roncodes Jul 29, 2026
36f7cfb
Cover vendor bridge quote cancel and lifecycle flows
roncodes Jul 29, 2026
a195467
Cover order config lookup and status analytics seams
roncodes Jul 29, 2026
a76dd1e
Cover asset device and index resource seams
roncodes Jul 29, 2026
41b8901
Cover morph and tracking number helper seams
roncodes Jul 29, 2026
12b20c5
Cover entity and setting helper seams
roncodes Jul 29, 2026
d2cd454
Cover driver filter nearby and date branches
roncodes Jul 29, 2026
a8170f1
Cover place filter spatial scopes and waypoint labels
roncodes Jul 29, 2026
c468198
Cover work order and tracking status helper seams
roncodes Jul 29, 2026
035d0b0
Cover vehicle track context and distance command seams
roncodes Jul 29, 2026
50a664f
Cover contact guards and asset status capability queries
roncodes Jul 29, 2026
887b41c
Cover scheduling trait and orchestration unit conversions
roncodes Jul 29, 2026
2d52ed6
Cover order label rendering and config resolution
roncodes Jul 29, 2026
23eb4be
Cover internal service quote vendor bridge flows
roncodes Jul 29, 2026
77885be
Cover operational alert notify and guard branches
roncodes Jul 29, 2026
e47cb6f
Cover tracking fallback chain and telematic commands
roncodes Jul 29, 2026
b0d2c6a
Cover driver assignment ranking and query seams
roncodes Jul 29, 2026
91122fd
Cover capacity engine reasons and workload balancing
roncodes Jul 29, 2026
8552222
Cover tracking context stops statuses and guards
roncodes Jul 29, 2026
242421f
Cover vehicle and fuel report filter branches
roncodes Jul 29, 2026
99e870c
Cover fuel report imports and entity labels
roncodes Jul 29, 2026
71efebe
Cover metrics periods and manifest stop endpoints
roncodes Jul 29, 2026
cc4621a
Cover fleet resource relation preloading
roncodes Jul 29, 2026
d108858
Cover payload helpers and driver notification seams
roncodes Jul 29, 2026
19bc36b
Cover osrm mixed points polylines and caching
roncodes Jul 29, 2026
ade57c3
Cover vendor relations and import creation
roncodes Jul 29, 2026
f363d75
Cover small api controller helper batteries
roncodes Jul 29, 2026
c5491f1
Cover service rate helper battery
roncodes Jul 29, 2026
1d1f635
Cover issue resource linked order resolution
roncodes Jul 29, 2026
3ef15ac
Cover geofence dwell broadcast channels
roncodes Jul 29, 2026
6ea3409
Cover utils geojson and sql fallback branches
roncodes Jul 29, 2026
8d15800
Cover revenue trend bucketing and deltas
roncodes Jul 29, 2026
1e982d9
Cover unique tracking status validation rule
roncodes Jul 29, 2026
73b2625
Cover flow condition operators and hos query window
roncodes Jul 29, 2026
5917c43
Cover vehicle device attach detach error paths
roncodes Jul 29, 2026
785181e
Cover internal service rate filters and export
roncodes Jul 29, 2026
9a7fe28
Cover fuel provider async sync and lookup
roncodes Jul 29, 2026
9a9e562
Cover replay socket errors and fix timeout catch order
roncodes Jul 29, 2026
d82d48b
Cover algo helper arms and issue imports
roncodes Jul 29, 2026
d45c658
Cover maintainable intervals and history fallbacks
roncodes Jul 29, 2026
d814e83
Cover order filter dates bulk ids and eager scopes
roncodes Jul 29, 2026
7b6af8b
Cover search capability company scoped queries
roncodes Jul 29, 2026
367849e
Cover v1 order resource relation closures
roncodes Jul 29, 2026
ab0d553
Cover part and equipment helper batteries
roncodes Jul 29, 2026
b314f29
Cover analytics periods and place search fallbacks
roncodes Jul 29, 2026
913ba97
Cover route simulation lookups and pacing
roncodes Jul 29, 2026
7b5be21
Cover driver track sentry reporting paths
roncodes Jul 29, 2026
7554f61
Cover osrm tracking provider route mapping
roncodes Jul 29, 2026
ab83bf5
Cover lalamove market accessors and lookups
roncodes Jul 29, 2026
4443058
Cover work order observer real helpers
roncodes Jul 29, 2026
f4cb415
Cover driver avatars and remove dead pings relation
roncodes Jul 29, 2026
055dae3
Cover service area update border rebuilding
roncodes Jul 29, 2026
4654d07
Cover service stop resolution guard branches
roncodes Jul 29, 2026
0d0d4e3
Cover activity logic arms and event dispatch
roncodes Jul 29, 2026
18b8c1a
Cover vehicle export selection scoping
roncodes Jul 29, 2026
c1ac13d
Cover fuel transaction helper battery
roncodes Jul 29, 2026
5eeef16
Cover contact update and delete failure paths
roncodes Jul 29, 2026
a5adb1b
Cover device input morphs and coordinate pairs
roncodes Jul 29, 2026
7f036e9
Cover export scoping, legacy config helpers, and vendor order meta
roncodes Jul 29, 2026
9ebfd66
Cover service rate zone guards and fix parcel fallback sort
roncodes Jul 29, 2026
de81520
Cover customer auth guards photo seams and inline payloads
roncodes Jul 29, 2026
4377035
Cover internal driver photo custom field and error branches
roncodes Jul 29, 2026
09e408c
Cover fuel provider transaction lookup scoping
roncodes Jul 29, 2026
78eba96
Cover waypoint activity events driver pings and photo uploads
roncodes Jul 29, 2026
d9aba28
Fix nullable default order config and cover order flows
roncodes Jul 29, 2026
c4ec2a8
Cover api photo validation arms and comment failures
roncodes Jul 29, 2026
6a62b4a
Cover orchestration run modes and driver simulation routing
roncodes Jul 29, 2026
4c9c469
Drop unreachable order config refetch and cover relation fallbacks
roncodes Jul 29, 2026
0d42714
Fix inverted empty-geocoding guard in insertFromCoordinates
roncodes Jul 29, 2026
57657bc
Cover driver verification sentry reporting and create delegation
roncodes Jul 29, 2026
80c80b2
Cover tracking context geometry failures and skipped stops
roncodes Jul 29, 2026
f5720d2
Fix lalamove market passed as sandbox flag and cover payload guards
roncodes Jul 29, 2026
46de8ff
Cover vroom transport failures and settings fallbacks
roncodes Jul 29, 2026
a37f7c0
Sweep export collection scoping across every export class
roncodes Jul 29, 2026
6cf0dd9
Sweep filter date ranges and controller excel seams
roncodes Jul 29, 2026
3556bb0
Sweep api request authorization and resource relation resolution
roncodes Jul 29, 2026
c962de7
Cover spatial cast contracts and raw point reads
roncodes Jul 29, 2026
31b657a
Sweep observer and listener query seams
roncodes Jul 29, 2026
be9253a
Make fixPhone null-safe and sweep import delegation seams
roncodes Jul 29, 2026
594bb68
Let issue and fuel report imports omit location columns
roncodes Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions .github/workflows/server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,12 @@ jobs:
name: fleetops-coverage-clover
path: coverage/clover.xml
if-no-files-found: error

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage/clover.xml
disable_search: true
flags: backend
fail_ci_if_error: false
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
<a href="https://github.com/fleetbase/fleetops/blob/master/LICENSE.md">
<img src="https://img.shields.io/badge/license-AGPL--3.0--or--later-blue?style=flat-square" alt="License: AGPL-3.0-or-later">
</a>
<a href="https://codecov.io/gh/fleetbase/fleetops">
<img src="https://codecov.io/gh/fleetbase/fleetops/branch/main/graph/badge.svg" alt="Coverage">
</a>
<a href="https://www.npmjs.com/package/@fleetbase/fleetops-engine">
<img src="https://img.shields.io/badge/npm-%40fleetbase%2Ffleetops--engine-CB3837?style=flat-square" alt="NPM package">
</a>
Expand Down
8 changes: 6 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
],
"require": {
"php": "^8.0",
"barryvdh/laravel-dompdf": "^2.0",
"barryvdh/laravel-dompdf": "^3.1",
"brick/geo": "0.7.2",
"cknow/laravel-money": "^7.1",
"fleetbase/core-api": "*",
Expand Down Expand Up @@ -90,11 +90,15 @@
"@test:coverage:clover",
"@coverage:summary"
],
"coverage:check": [
"@test:coverage:clover",
"php scripts/coverage-summary.php coverage/clover.xml --fail-under=100"
],
"coverage:summary": "php scripts/coverage-summary.php coverage/clover.xml",
"lint": "php-cs-fixer fix -v",
"test:lint": "php-cs-fixer fix -v --dry-run",
"test:coverage": "php scripts/coverage-runner.php --coverage --coverage-text --colors=always",
"test:coverage:clover": "mkdir -p coverage && php scripts/coverage-runner.php --coverage-clover=coverage/clover.xml --colors=always",
"test:coverage:clover": "mkdir -p coverage && php scripts/coverage-file-runner.php --coverage-clover=coverage/clover.xml --colors=always",
"test:types": "phpstan analyse --ansi --memory-limit=4G",
"test:unit": "php scripts/pest-file-runner.php --colors=always",
"test": [
Expand Down
199 changes: 199 additions & 0 deletions scripts/coverage-file-runner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
<?php

declare(strict_types=1);

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Report\Clover;

// Merging hundreds of per-file coverage snapshots into one clover report
// needs more than the default 128M limit
ini_set('memory_limit', '-1');

$autoloadCandidates = [
getcwd() . '/server_vendor/autoload.php',
getcwd() . '/vendor/autoload.php',
];

$autoload = null;
foreach ($autoloadCandidates as $candidate) {
if (is_file($candidate)) {
$autoload = $candidate;
break;
}
}

if ($autoload === null) {
fwrite(STDERR, "Unable to find Composer autoload. Run composer install first.\n");
exit(1);
}

require_once $autoload;

$pestRunner = getcwd() . '/scripts/pest-runner.php';
if (!is_file($pestRunner)) {
fwrite(STDERR, "Unable to find Pest runner at scripts/pest-runner.php.\n");
exit(1);
}

$hasCoverageExtension = extension_loaded('xdebug') || extension_loaded('pcov');
if (!$hasCoverageExtension) {
fwrite(STDERR, "No PHP coverage driver is available.\n\n");
fwrite(STDERR, "Install or enable one of:\n");
fwrite(STDERR, " - Xdebug with XDEBUG_MODE=coverage\n");
fwrite(STDERR, " - PCOV\n");
fwrite(STDERR, "\n");
fwrite(STDERR, 'Current PHP binary: ' . PHP_BINARY . "\n");
exit(1);
}

putenv('XDEBUG_MODE=coverage');
$_ENV['XDEBUG_MODE'] = 'coverage';
$_SERVER['XDEBUG_MODE'] = 'coverage';

$args = array_slice($argv, 1);
$cloverPath = getcwd() . '/coverage/clover.xml';
$pestArgs = [];
$targets = [];

foreach ($args as $arg) {
if (str_starts_with($arg, '--coverage-clover=')) {
$path = substr($arg, strlen('--coverage-clover='));
$cloverPath = str_starts_with($path, '/') ? $path : getcwd() . '/' . $path;
continue;
}

if ($arg === '--coverage-clover') {
fwrite(STDERR, "Use --coverage-clover=path with coverage-file-runner.php.\n");
exit(1);
}

if ($arg !== '' && $arg[0] !== '-') {
$targets[] = $arg;
continue;
}

if (!str_starts_with($arg, '--coverage-')) {
$pestArgs[] = $arg;
}
}

if ($targets === []) {
$targets[] = 'server/tests';
}

$files = [];
foreach ($targets as $target) {
$path = getcwd() . '/' . ltrim($target, '/');
if (is_file($path)) {
$files[] = $path;
continue;
}

if (is_dir($path)) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)
);

foreach ($iterator as $fileInfo) {
if ($fileInfo->isFile() && $fileInfo->getExtension() === 'php') {
$files[] = $fileInfo->getPathname();
}
}
}
}

$files = array_values(array_unique($files));
sort($files);

if ($files === []) {
fwrite(STDERR, "Unable to find Pest test files for coverage.\n");
exit(1);
}

$coverageDir = dirname($cloverPath);
if (!is_dir($coverageDir) && !mkdir($coverageDir, 0777, true) && !is_dir($coverageDir)) {
fwrite(STDERR, "Unable to create coverage directory: {$coverageDir}\n");
exit(1);
}

$tmpDir = $coverageDir . '/.coverage-file-runner';
if (is_dir($tmpDir)) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($tmpDir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);

foreach ($iterator as $fileInfo) {
$fileInfo->isDir() ? rmdir($fileInfo->getPathname()) : unlink($fileInfo->getPathname());
}
} elseif (!mkdir($tmpDir, 0777, true) && !is_dir($tmpDir)) {
fwrite(STDERR, "Unable to create temporary coverage directory: {$tmpDir}\n");
exit(1);
}

$timeout = (float) (getenv('PEST_FILE_TIMEOUT') ?: 120);
$timeoutBinary = trim((string) shell_exec('command -v timeout'));
$memoryLimit = getenv('FLEETOPS_COVERAGE_MEMORY_LIMIT') ?: '-1';
$coverageFiles = [];

foreach ($files as $index => $file) {
$relativeFile = str_replace(getcwd() . '/', '', $file);
$coverageFile = $tmpDir . '/' . str_pad((string) $index, 4, '0', STR_PAD_LEFT) . '.cov';

fwrite(STDOUT, "::group::{$relativeFile} coverage\n");

$command = array_merge([
PHP_BINARY,
'-d',
'memory_limit=' . $memoryLimit,
$pestRunner,
], $pestArgs, [
'--coverage-php=' . $coverageFile,
$file,
]);

if ($timeoutBinary !== '') {
$command = array_merge([$timeoutBinary, "{$timeout}s"], $command);
}

fwrite(STDOUT, '$ ' . implode(' ', array_map('escapeshellarg', $command)) . "\n");
passthru(implode(' ', array_map('escapeshellarg', $command)), $exitCode);
fwrite(STDOUT, "::endgroup::\n");

if ($exitCode === 124) {
fwrite(STDERR, "\nTimed out after {$timeout} seconds while running {$relativeFile}.\n");
exit(1);
}

if ($exitCode !== 0) {
fwrite(STDERR, "\nPest coverage failed for {$relativeFile} with exit code {$exitCode}.\n");
exit($exitCode);
}

$coverageFiles[] = $coverageFile;
}

$merged = null;
foreach ($coverageFiles as $coverageFile) {
$coverage = include $coverageFile;

if (!$coverage instanceof CodeCoverage) {
fwrite(STDERR, "Invalid coverage artifact: {$coverageFile}\n");
exit(1);
}

if ($merged === null) {
$merged = $coverage;
continue;
}

$merged->merge($coverage);
}

if (!$merged instanceof CodeCoverage) {
fwrite(STDERR, "No coverage data was produced.\n");
exit(1);
}

(new Clover())->process($merged, $cloverPath);
fwrite(STDOUT, "Wrote Clover coverage to {$cloverPath}\n");
3 changes: 2 additions & 1 deletion scripts/coverage-runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
$args[] = 'server/tests';
}

$command = array_merge([PHP_BINARY, $pestRunner], $args);
$memoryLimit = getenv('FLEETOPS_COVERAGE_MEMORY_LIMIT') ?: '-1';
$command = array_merge([PHP_BINARY, '-d', 'memory_limit=' . $memoryLimit, $pestRunner], $args);
$escapedCommand = implode(' ', array_map('escapeshellarg', $command));

passthru($escapedCommand, $exitCode);
Expand Down
43 changes: 41 additions & 2 deletions scripts/coverage-summary.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,20 @@

declare(strict_types=1);

$cloverPath = $argv[1] ?? 'coverage/clover.xml';
$cloverPath = 'coverage/clover.xml';
$failUnder = null;

foreach (array_slice($argv, 1) as $arg) {
if (str_starts_with($arg, '--fail-under=')) {
$failUnder = (float) substr($arg, strlen('--fail-under='));

continue;
}

if ($arg !== '') {
$cloverPath = $arg;
}
}

if (!is_file($cloverPath)) {
fwrite(STDERR, "Coverage file not found: {$cloverPath}\n");
Expand All @@ -26,6 +39,27 @@ function intMetric(SimpleXMLElement $node, string $name): int
return (int) ($node->metrics[$name] ?? 0);
}

function hasMetric(SimpleXMLElement $node, string $name): bool
{
return isset($node->metrics[$name]);
}

function deriveCoveredClasses(SimpleXMLElement $project): int
{
$coveredClasses = 0;

foreach ($project->xpath('.//class') ?: [] as $class) {
$methods = intMetric($class, 'methods');
$coveredMethods = intMetric($class, 'coveredmethods');

if ($methods > 0 && $coveredMethods >= $methods) {
$coveredClasses++;
}
}

return $coveredClasses;
}

$project = $xml->project;
$metrics = $project->metrics;

Expand All @@ -34,7 +68,7 @@ function intMetric(SimpleXMLElement $node, string $name): int
$methods = (int) ($metrics['methods'] ?? 0);
$coveredMethods = (int) ($metrics['coveredmethods'] ?? 0);
$classes = (int) ($metrics['classes'] ?? 0);
$coveredClasses = (int) ($metrics['coveredclasses'] ?? 0);
$coveredClasses = hasMetric($project, 'coveredclasses') ? (int) $metrics['coveredclasses'] : deriveCoveredClasses($project);

$files = [];
$directories = [];
Expand Down Expand Up @@ -108,3 +142,8 @@ function intMetric(SimpleXMLElement $node, string $name): int
$relativePath = preg_replace('#^' . preg_quote(getcwd(), '#') . '/?#', '', $file['path']);
printf(" %6.2f%% %5d/%-5d %s\n", $file['percent'], $file['covered'], $file['statements'], $relativePath ?: $file['path']);
}

if ($failUnder !== null && coveragePercent($coveredStatements, $statements) < $failUnder) {
fwrite(STDERR, sprintf("\nCoverage %.2f%% is below the required %.2f%% line threshold.\n", coveragePercent($coveredStatements, $statements), $failUnder));
exit(1);
}
Loading
Loading