Skip to content

Improve Fleet-Ops server test coverage - #277

Draft
roncodes wants to merge 650 commits into
mainfrom
feature/fleetops-server-coverage-100
Draft

Improve Fleet-Ops server test coverage#277
roncodes wants to merge 650 commits into
mainfrom
feature/fleetops-server-coverage-100

Conversation

@roncodes

@roncodes roncodes commented Jul 19, 2026

Copy link
Copy Markdown
Member

Summary

This is a progressive backend-only coverage PR for Fleet-Ops ./server.

  • Wires backend coverage reporting into the Composer workflow with a 100% fail-under gate.
  • Expands categorized server tests under server/tests/Unit/... and server/tests/Feature/... instead of adding more flat root-level coverage files.
  • Adds coverage for exports, imports, value objects, events, exceptions, registries, analytics options, mail, notifications, provider registration contracts, driver model helpers, internal driver controller helpers, and LiveController operations monitor snapshot behavior.
  • Extends the shared test bootstrap with additive shims so controller error branches (response()->apiError(...)) and job dispatch branches (Job::dispatch/dispatchIf) are reachable under test — previously these fatally errored and structurally blocked coverage on nearly every controller.
  • Covers the API DriverController protected helper methods, the self-contained route/order driving-simulation methods (no-route and successful-dispatch branches), and the driver-lookup/organization not-found error branches via an in-memory SQLite fixture.

Latest Local Coverage

Fresh host run from COMPOSER_PROCESS_TIMEOUT=0 composer coverage:baseline:

Metric Coverage Covered / Total
Lines 99.26% 33,872 / 34,124 statements
Methods 96.04% 4,174 / 4,346 methods
Classes 77.93% 406 / 521 classes

Coverage gate verification:

  • php scripts/coverage-summary.php coverage/clover.xml --fail-under=100 correctly fails while coverage is below 100%.
  • The fail-under gate remains red by design until backend line coverage reaches 100%.

Latest Progress

  • Line coverage is 99.26%, up from 79.53% at the start of this campaign. The last stretch came from pattern sweeps — one data-driven test per repeated code shape — covering export collection() scoping (17 classes), queryWithRequest across every API controller (19 classes), import createFromImport delegation (13 classes), filter date ranges, Excel download/import seams, request authorize() gates, resource relation resolution, observer/listener query seams, and assorted controller/command/job seams.
  • Twelve production bugs found and fixed. Highlights: Place::insertFromCoordinates() guarded empty reverse-geocoding results with !$results->count() === 0, comparing a bool to an int so the guard never fired — with no results the code fell through to an address array carrying location 0,0, which array_merge layered over the caller's real coordinates, silently inserting places at Null Island instead of returning false as documented. Lalamove::getQuotationForMarket() called instance(null, null, $market), putting the market in the bool $sandbox slot — without strict_types the string coerced to true, so market-scoped quotations ran against the sandbox host with the market silently dropped. OrderConfig::default() declared a non-nullable self return while returning first(), so companies without a stored transport config fataled instead of having one provisioned. ServiceRate called Collection::sortByDesc() with no argument, throwing whenever a parcel outsized every fee tier.
  • Remaining residue (~390 statements) concentrates in the deep internal/api Order, Driver and Orchestration controller flows, the shared queryWithRequest pipeline, and harness-unreachable defensive catches.

Validation

Run on the host runtime (asdf PHP 8.4 with Xdebug, XDEBUG_MODE=coverage), no Docker:

  • php -l server/tests/Feature/Http/Api/DriverControllerHelpersTest.php
  • php scripts/pest-runner.php server/tests/Feature/Http/Api/DriverControllerHelpersTest.php
  • vendor/bin/php-cs-fixer fix --using-cache=no --sequential server/tests/Feature/Http/Api/DriverControllerHelpersTest.php scripts/pest-bootstrap.php
  • composer test:lint
  • composer test:unit
  • COMPOSER_PROCESS_TIMEOUT=0 composer coverage:baseline
  • php scripts/coverage-summary.php coverage/clover.xml --fail-under=100
  • git diff --check
  • git diff --cached --check

Current Lowest Coverage Targets

Next backend coverage slices, from the fresh local Clover report (highest absolute uncovered):

File Uncovered statements
server/src/Http/Controllers/Api/v1/OrderController.php 21
server/src/Http/Controllers/Internal/v1/OrchestrationController.php 19
server/src/Http/Controllers/Api/v1/DriverController.php 16
server/src/Models/Place.php 15
server/src/Http/Controllers/Internal/v1/OrderController.php 7

A growing share of what remains is provably unreachable rather than merely untested — shadowed instanceof chains (the spatial casts check GeometryInterface before each concrete type), ?type returns that never actually yield null (Find::httpResourceForModel always falls back to FleetbaseResource), and guards whose conditions cannot evaluate true. These are documented in the tests that probe them.

Practical ceiling

A growing share of what remains is provably unreachable rather than untested. Confirmed by direct analysis so far:

Location Why it cannot execute
Casts/{Point,Polygon,MultiPolygon} instanceof GeometryInterface (or Expression) is tested before each concrete spatial type, so the later arms are shadowed
Http/Resources/v1/{Maintenance,MaintenanceSchedule,WorkOrder,Order} Find::httpResourceForModel() always resolves a class, falling back to FleetbaseResource, so the JsonResource fallbacks never run
Models/Place::createFromMixed re-tests isCoordinatesStrict() inside a branch already gated on it
Models/Place::findExistingSharedPlace $location is initialised to a SpatialPoint and the catch assigns one, so the non-point guard cannot fire
Support/Utils::coordsToCircle the 0..360 loop closes the circle exactly, so the closing-point guard never triggers
Http/Controllers/Internal/v1/GeocoderController guards the result of getPointFromCoordinates(), declared : Point (non-nullable)
Http/Controllers/Internal/v1/MetricsController guards $request->date() against not being a DateTime; it returns Carbon, which extends it
Http/Controllers/Api/v1/DriverController null-company fallback sits after the same variable is already dereferenced
Integrations/Lalamove::__callStatic instance is a defined public static, so PHP never routes it through __callStatic

Two things previously written off as untestable turned out not to be, and are now covered: the queryWithRequest seam on all 19 API controllers (it needed only a session store, route-resolver stub and a couple of request macros), and the Lalamove API response handling (its post/request helpers are private, but the Guzzle client is an injectable property, so a MockHandler exercises the real code path without network access).

Two further lines (Internal/v1/OrderController 887-888) are reachable only once the upstream findByIdOrFail defect above is fixed, and are deliberately left uncovered rather than shimmed green.

A handful more are blocked by genuinely external dependencies — Firebase-backed push payloads, the Illuminate\Encryption\Encrypter (absent from this vendor tree), and MySQL-only st_distance_sphere spatial predicates that SQLite cannot evaluate.

Known upstream defect (needs a core-api patch)

Model::findByIdOrFail() in fleetbase/core-api throws BadMethodCallException instead of the ModelNotFoundException it documents, because it calls a getModelNotFoundException() method that does not exist on Eloquent's Builder. Four call sites in this package have catch (ModelNotFoundException) blocks that can never fire, so missing records surface as HTTP 500s. Details, affected lines and a suggested fix are in this PR comment. Two lines in Internal/v1/OrderController.php are intentionally left uncovered pending that fix rather than shimmed green.

Notes

  • This PR is intentionally still progressive and remains below the 100% final target.
  • Local validation is run before every push so broken test slices are not published.
  • Coverage work is pushed in batches of a few validated commits so progress is not stranded locally.
  • The largest remaining uncovered surfaces are the big API/Internal Order and Driver controllers; many of their error/dispatch branches are now reachable thanks to the bootstrap shim additions.

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

roncodes and others added 29 commits July 28, 2026 02:16
Exercise the public create() endpoint against an in-memory SQLite fixture:
order config resolution with invalid-type rejection, string payload
resolution, driver/vehicle/facilitator assignment via public ids, customer
assignment from both string ids and array input with contact creation,
adhoc normalization, orchestrator priority defaults, missing-payload
rejection, order persistence with relation loading, and finalize dispatch.
Raises Api/v1/OrderController line coverage from 63.88% to 65.34%.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Exercise the device event processed-marking endpoint with first/repeat/missing
branches (standing up a token auth guard and hasher for the activity logger),
the getting-started response helpers and company-status delegation seam, and
the order-config lookup, error response, and delete branches including the
core-service protection and soft-delete paths.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Exercise the IssuesInsights widget against an in-memory SQLite fixture:
category aggregation with uncategorized fallback, priority histograms, open
counts, resolution windows, and empty-data shapes. Also covers the
ProcessAllocationJob query helpers: unassigned order filtering with id
scoping, online-driver vehicle availability, engine and allocation option
settings defaults, public id lookups, and the log seam.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Exercise the PetroApp provider against faked http responses: connection
testing across success, unauthorized, and transport-exception branches,
paginated retrieval with multi-page traversal and station data keys, runtime
error propagation on failed responses, transaction listing with bill
normalization and fallback transaction ids, and header/base-url credential
variants. PetroAppFuelProvider is now at 100% line coverage.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Exercise quotation-to-service-quote construction: amount/item/metadata
building with wrapped-data unwrapping, integrated vendor linkage, persistence
with items, market resolution, constructor credential/company-market
branches, and static call proxying. Fixes two latent bugs surfaced by the
tests: Lalamove::instance() passed its sandbox and market arguments to the
constructor in swapped order (previously pinned as-broken by the lifecycle
test, now asserted fixed), and serviceQuoteFromQuotation() declared a
non-nullable return while returning null for empty quotations. Raises
Lalamove line coverage from 60.06% to 66.77%.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Exercise the Place model against an in-memory SQLite fixture with a geocoder
fake: shared-place deduplication across owned/incomplete/matching/spatial
branches, uuid insertion with fillable filtering and session stamping,
geocoding lookup fallbacks to plain street records, coordinate-based creation
from points and arrays, and avatar resolution across raw urls, named
defaults, and unknown uuid keys. Raises Models/Place line coverage from
61.38% to 84.40%.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Jobs/GeofenceDwellAndBulkNotifyTest.php covering the
CheckGeofenceDwell job (fired dwell events, exited-state short circuit,
missing-subject and missing-geofence warning branches for both driver and
vehicle subjects), the NotifyBulkAssignedDriver job (matched-order
notification loop with failure logging and missing-driver early exit), the
Customer model global type scope with customer_/contact_ public id
normalization, and the public NavigatorController driver onboard settings
endpoint including the empty-settings fallback.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Listeners/HandleOrderDispatchedHelpersTest.php
exercising the real HandleOrderDispatched protected helper bodies against
SQLite: the dispatch-failed lifecycle event emission, tracking-status
dispatch-activity existence check, the nearby-available-drivers spatial
query with its company/user/driver whereHas chain (registering the
users.driver relation through the core Expandable expand() mechanism,
since the core model __call bypasses Eloquent relation resolvers), and
the assigned/adhoc driver notification helpers via a notification
dispatcher fake.

Adds server/tests/Unit/Http/Filter/FuelProviderTransactionFilterTest.php
covering the public-relation resolution pipeline: missing-identifier
early returns, public-id and internal-id uuid resolution with company
scoping, and the internal-request raw-uuid allowance.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Notifications/OrderDispatchedChannelsTest.php
covering the OrderDispatched notification's broadcast payload wrapping the
serialized order resource, the mail seam with waypoint/order tracking
resolution, and the fcm/apn push delegation seams.

Adds server/tests/Unit/Http/Resources/CustomerResourceTest.php covering
the public Customer resource serialization with the customer_-prefixed
public id, the orders-count subquery, the company payload projection with
currency casing, and the missing-company fallback.

Adds server/tests/Unit/Http/Resources/PayloadResourceTest.php covering
Payload resource serialization with route ETAs resolved through a tracker
registered via the Expandable expand() mechanism, the private place and
waypoint builders in both ETA modes, the non-collection waypoints
fallback, and the webhook projection.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php
covering the API DriverController authentication surface against SQLite:
password login with wrong-password rejection and personal-access-token
issuance, phone login with the SMS-transport failure falling back to the
mail-faked email verification channel plus the no-channel error, and
verification-code checking across unknown-identity, invalid-code,
config-bypass, and stored-code success paths with auth-token persistence.
The users.driver relation needed by whereHas('driver') is registered via
the core Expandable expand() mechanism.

Adds server/tests/Unit/Notifications/MonitoringNotificationsTest.php
covering the LateDeparture, ProlongedStoppage, and RouteDeviation mail
bodies and database payloads, and the WaypointCompleted broadcast channel
construction with order-channel expansion, no-order fallback, and push
delegation seams.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Integrations/Lalamove/LalamoveServiceTypeTest.php
covering the LalamoveServiceType value object: dynamic hydration,
restriction fallbacks through __get, the instance-call all() proxy with
unknown-method null returns, and static find by key string and callback.

Adds server/tests/Unit/Support/OperationsPulseTest.php covering the
OperationsPulse analytics snapshot against SQLite: tile aggregation for
active orders, drivers online, vehicles deployed with the driver whereHas
constraint, open issues, completed-today windows with the +100 delta cap,
empty-company null deltas, and the signed rounded delta percentage edges.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Http/Resources/WorkOrderResourceTest.php covering
the WorkOrder API resource serialization with computed maintenance fields,
the Ember polymorphic type injection for maintenance-subject and
facilitator slugs with empty passthroughs, and the morph resource
transformer's null and JsonResource-fallback branches.

Adds server/tests/Unit/Console/SyncTelematicsCommandTest.php covering the
fleetops:sync-telematics command with the process lock skipped: the
no-pollable-providers early exit, chunked job queueing over active and
connected telematics rows with company scoping, and provider filtering by
requested keys, discovery support, and webhook exclusion.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Api/DriverControllerOrganizationsTest.php
covering currentOrganization with the company-session fallback chain and
the unresolvable-company error branch, and listOrganizations returning
every company the driver's user account belongs to through company_users.

Adds server/tests/Feature/Http/Api/DriverControllerSessionHelpersTest.php
covering the protected session/persistence helpers: session and current
company resolution, the company-from-request seam, user info application,
user/driver/device persistence with firstOrCreate dedupe, uuid and point
utilities, null file resolution, driver lookup with resource wrapping,
and the queryWithRequest vendor filter pipeline (whose query param is
matched against vendors.public_id by the controller callback and
drivers.vendor_uuid by DriverFilter).

The switchOrganization endpoint remains structurally blocked: loading the
core SwitchOrganizationRequest fatals against the harness FormRequest
authorize() signature.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Api/PurchaseRateControllerOrderCreationTest.php
covering createOrderFromServiceQuote against SQLite: the default-type
order path, payload construction from preliminary data with pickup,
dropoff, and return places persisted, adoption of the quote's payload and
service-rate type, and the integrated-vendor failure branch returning an
api error response with no order written.

Exercising this surface uncovered two latent bugs, both fixed:

- The preliminary-data block called setDropoff($return) instead of
  setReturn($return), which fatals with a TypeError for quotes without a
  return place (the common case) and silently overwrote the dropoff with
  the return place otherwise. The return place is now assigned through
  setReturn and only when present.
- The integrated-vendor catch branch returns response()->apiError(...)
  from a method declared to return ?Order, fataling with a TypeError on
  every vendor failure. The declaration is now Order|JsonResponse|null,
  matching the existing caller which already type-checks the result.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Internal/LiveControllerEndpointsTest.php
covering the LiveController map feeds against SQLite with a tagged-cache
fake: active-order destination coordinates (hydrating real Point
instances from packed WKB so getCurrentDestinationLocation's lat/lng
filter executes), active routes with the nested driver/tracking/payload
constraints and the unassigned-driver drop, the live order query with
exclusion filtering, viewport-bounded driver and vehicle listings with
the spatial location guards, and filtered place listings.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Internal/SearchControllerEndpointTest.php
covering the internal global search endpoint against SQLite with an
admin session user bypassing per-type permission checks: the blank-query
short circuit, requested-type parsing, order search through the
tracking-number relation subquery, driver search through the user
relation, the generic column search used by vehicles and fleets with
limits, and the unmatched-query empty result.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Support/ResolvesOrderServiceStopsDbTest.php
covering the ResolvesOrderServiceStops helpers the fake-based trait test
cannot reach, against SQLite: proof resolution by instance/public-id with
invalid-input rejection, endpoint stop completion via stored tracking
statuses, next-incomplete-stop advancement persisting the payload's
current destination, current-stop activity detection through tracking
status codes for both endpoint and waypoint stops, endpoint
tracking-number creation with barcode fakes and payload linkage,
endpoint activity insertion writing tracking statuses and relinking the
number, tracking-number status lookups preferring the linked status
uuid, and updateCurrentServiceStopActivity's location guard,
endpoint-insertion, and skip branches.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds four categorized tests covering small remaining surfaces against
SQLite:

- server/tests/Unit/Observers/FleetOpsCompanyUserAndServiceAreaObserversTest.php:
  CompanyUserObserver driver deletion on membership removal and status
  syncing driven by wasChanged, and ServiceAreaObserver zone cascade
  deletion with the country-border polygon helper seam.
- server/tests/Unit/Listeners/HandleOrderDispatchFailedListenerTest.php:
  the order-creator lookup and failure notification hand-off through a
  notification dispatcher fake, plus the missing-creator skip.
- server/tests/Unit/Console/AssignDriverRolesCommandTest.php: company
  traversal with the users.driver expansion, the admin skip, the
  role-assignment error branch, and the empty-company quiet run.
- server/tests/Unit/Support/AiCapabilityPermissionsTest.php: the shared
  AbstractFleetOpsAICapability helpers through OperationalQueryCapability —
  admin permission bypass, gate delegation seam, search-term extraction
  with stop-word filtering, and the multi-column like matcher.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Models/SensorReadingsTest.php covering the Sensor
model reading pipeline against SQLite with the token-guard auth manager
and disabled activity log: out-of-threshold readings opening a single
threshold alert without duplication, normal readings resolving open
alerts, severity mapping and alert message generation, and subject
position creation from latitude/longitude and location-keyed attributes.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Internal/VendorControllerEndpointsTest.php
covering the internal VendorController against SQLite with an excel fake:
the export download with filename/format handling, the distinct status
listing, the import pipeline with resolved files and the invalid-file
error branch, the vendor/driver/contact lookup helpers including trashed
and or-fail variants, the contact resource payload projection, and the
vendor personnel updateOrCreate/list/create/delete helpers.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Internal/ContactControllerEndpointsTest.php
covering the internal ContactController against SQLite with an excel
fake: export downloads, the import pipeline with the invalid-file error
branch, contact lookup and vendor-conversion helpers (creation,
personnel linkage, transaction wrapper, vendor resource payload), the
customer context migration rewriting orders and customer-portal issue
metadata onto the converted vendor, and the customer portal
welcome-email guard branches with extension detection.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Support/OperationalQueryDistributionTest.php
covering the OperationalQueryCapability driver geofence distribution
against SQLite with spatial containment stand-ins: the empty-fleet short
circuit, online and updated_at filter application, packed-WKB point
hydration, and per-service-area/zone containment counting as an admin
session user.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Notifications/OrderFailedChannelsTest.php covering
the OrderFailed notification title construction with the
waypoint-tracking-number preference, broadcast channel construction
across company/api/order channels, and the fcm/apn delegation seams.

Adds server/tests/Unit/Console/AssignCustomerRolesCommandTest.php
covering the fleetops:assign-customer-roles command traversal with user
resolution, the role-assignment error branch, and the quiet empty run.

Adds server/tests/Unit/Models/IntegratedVendorLifecycleTest.php covering
the IntegratedVendor created/updated/deleted boot hooks resolving the
lalamove provider, credential access, the provider/api bridges, and the
webhook-url mutator's explicit and derived-default branches.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Http/Resources/MaintenanceResourcesTest.php
covering both maintenance API resources: the Ember maintenance-subject
and facilitator type injections with empty passthroughs, the morph
transformer null and JsonResource fallbacks, and full serialization of
loaded polymorphic subject/maintainable relations through the whenLoaded
callbacks against SQLite.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Api/OrderControllerStartActivityTest.php
covering the API OrderController startOrder and updateActivity endpoints
against SQLite with a real transport order-config flow: the
unknown-order, already-started, missing-driver, adhoc-without-driver,
and not-dispatched error branches; the skip-dispatch success path that
starts the order, assigns the driver's current job, fires OrderStarted,
and delegates into updateActivity with the started activity; and the
updateActivity unknown/completed rejections plus the dispatched-activity
branch firing OrderDispatchFailed when no driver is assigned.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Api/OrderControllerLifecycleActionsTest.php
covering the API OrderController lifecycle endpoints against SQLite with
the transport order-config flow: getNextActivity resolving flow steps
with the 404 branch and proof-of-delivery flag/method injection on
completing activities, completeOrder's incomplete-waypoint guard,
cancelOrder transitioning the order to canceled, and setDestination's
validation and current-service-stop persistence.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Api/OrderControllerPersistenceHelpersTest.php
covering the API OrderController protected persistence helpers against
SQLite: customer contact firstOrCreate dedupe, the company timezone
fallback, order/proof/file creation, the finalize-order job dispatch
through the chainable dispatch shim, the routing-engine delegation seam,
storage writes via a filesystem fake, proof subject scoping for order
and entity subjects, entity editing settings lookup, and the
order/proof/comment resource wrappers.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php
covering the internal OrderController against SQLite with the transport
order-config flow: start's unknown/already-started/driverless guards and
the success path assigning the driver's current job and firing
OrderStarted, updateActivity's proof-of-delivery requirement with the
bypass flag, the dispatched-activity failure without an assigned driver,
lifecycle activity updates writing tracking statuses, next-activity flow
resolution, and setDestination's single-stop rejection plus
multi-waypoint validation and persistence.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds server/tests/Unit/Support/FuelEfficiencyAndCanceledHandlersTest.php
covering the FuelEfficiency weekly cost-per-distance aggregation with a
YEARWEEK SQLite stand-in and the empty-series fallback, the
HandleOrderCanceled listener's driver lookup and notification helpers
through a dispatcher fake with the unassigned fallback, and the
OrderCompleted notification broadcast channels plus fcm/apn delegation
seams.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
roncodes and others added 6 commits July 29, 2026 23:13
These 19 seams were previously written off as harness-blocked; they only
needed a session store, route resolver stub, getController/or macros and
a directives table.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@roncodes

Copy link
Copy Markdown
Member Author

Upstream defect found during this coverage work — needs a core-api patch

Not fixable in this PR (the code lives in the fleetbase/core-api dependency), recording it here so it isn't lost.

What

Fleetbase\Models\Model::findByIdOrFail()core-api/src/Models/Model.php:224 — ends with:

throw (new static())->newModelQuery()->getModel()->newQuery()->getModel()::query()
    ->getModel()::query()->getModelNotFoundException($cls, [$identifier]);

getModelNotFoundException() does not exist on Illuminate\Database\Eloquent\Builder. It appears nowhere in illuminate/*.

Effect

The method's own docblock declares @throws \Illuminate\Database\Eloquent\ModelNotFoundException, but a missing record actually raises:

BadMethodCallException: Call to undefined method Illuminate\Database\Eloquent\Builder::getModelNotFoundException()

Confirmed at runtime, not just by reading — calling Vendor::findByIdOrFail('vendor_does_not_exist') against a seeded schema throws the BadMethodCallException.

Why it matters for Fleet-Ops

Callers that follow the documented contract wrap the call in catch (ModelNotFoundException $e). Those catch blocks can never fire, so a lookup for a nonexistent record escapes as an uncaught BadMethodCallException (HTTP 500) instead of the intended not-found response.

Affected call sites in this package:

File Line Consequence
server/src/Http/Controllers/Internal/v1/OrderController.php 886 nextActivitycatch (ModelNotFoundException) returning 'No order found.' is dead; unknown order id 500s
server/src/Http/Controllers/Internal/v1/OrderController.php 1417 findOrderForDriverPing
server/src/Http/Controllers/Internal/v1/VendorController.php 313 vendor lookup
server/src/Http/Controllers/Internal/v1/VendorController.php 318 contact lookup

Suggested fix (in core-api)

Throw the exception directly rather than routing through a nonexistent builder method:

throw (new ModelNotFoundException())->setModel(static::class, [$identifier]);

Coverage note

The two lines at Internal/v1/OrderController.php:887-888 are deliberately left uncovered in this PR. They are reachable only if findByIdOrFail throws what it documents. A test-harness shim could make them green, but that would hide a defect that is still live in production, so they remain uncovered pending the upstream fix.

roncodes and others added 12 commits July 30, 2026 00:05
The pest config() shim only accepted string keys, so production code using
Laravel's array setter form hit a TypeError. Six notification tests were
asserting that TypeError as though it were real behaviour; with the shim
fixed they now assert the genuine failure.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@roncodes

Copy link
Copy Markdown
Member Author

Second defect found during coverage work — single quote with no servicable rates returns a 500

Unlike the findByIdOrFail issue, this one is in this package and could be fixed here. Recording it rather than folding a behaviour change into a coverage PR.

What

Api\v1\ServiceQuoteController::query() ends its service-rate branch with:

if ($single) {
    $bestQuote = $this->bestQuote($serviceQuotes);

    return $this->serviceQuoteResource($bestQuote);
}

bestQuote() is collect($serviceQuotes)->sortBy('amount')->first() — it returns null for an empty collection. serviceQuoteResource() is typed serviceQuoteResource(ServiceQuote $serviceQuote), non-nullable.

Effect

Requesting single=1 when no service rate is servicable for the given waypoints raises:

TypeError: serviceQuoteResource(): Argument #1 ($serviceQuote) must be of type
Fleetbase\FleetOps\Models\ServiceQuote, null given

That surfaces as an HTTP 500 rather than a meaningful "no quotes available" response. Hit while building a fixture with two company service rates that were not servicable for the payload's waypoints — a realistic configuration, not a contrived one.

The same shape appears twice in the file (the equivalent block around line 155 and again around line 329), so both entry points are affected.

Suggested fix

Return an explicit empty-quote response instead of passing null through:

if ($single) {
    $bestQuote = $this->bestQuote($serviceQuotes);

    if (!$bestQuote instanceof ServiceQuote) {
        return response()->apiError('No service quotes available for this route.', 404);
    }

    return $this->serviceQuoteResource($bestQuote);
}

Coverage note

The four lines in the second block (ServiceQuoteController 287, 297, 329, 331) remain uncovered. Reaching them needs a fixture where company service rates are both servicable for the waypoints and quotable end to end; the attempt for this PR produced quotes only through the earlier, already-covered block.

roncodes and others added 10 commits July 30, 2026 02:32
Place::insertFromMixed() called insertFromGeocodingLookup() for any plain
address string, but no such method existed on Place or any ancestor, so the
call always raised BadMethodCallException. Define it as the insert-side twin
of createFromGeocodingLookup, mirroring insertFromGoogleAddress.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants