@fleetbase/fleetops-engine 0.6.58 → 0.6.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (701) hide show
  1. package/README.md +3 -0
  2. package/RELEASE.md +36 -0
  3. package/addon/components/customer/form.hbs +0 -49
  4. package/addon/components/entity/card.hbs +1 -0
  5. package/addon/components/modals/order-label.hbs +1 -1
  6. package/addon/components/place/form.hbs +1 -1
  7. package/addon/components/vehicle/details/schedules.hbs +2 -2
  8. package/addon/components/vehicle/details/schedules.js +7 -1
  9. package/addon/controllers/maintenance/schedules/index/edit.js +10 -1
  10. package/addon/services/entity-actions.js +32 -0
  11. package/addon/services/vehicle-actions.js +4 -2
  12. package/addon/styles/fleetops-engine.css +0 -1
  13. package/composer.json +8 -3
  14. package/extension.json +1 -1
  15. package/index.js +7 -1
  16. package/package.json +2 -1
  17. package/scripts/coverage-file-runner.php +240 -0
  18. package/scripts/coverage-runner.php +2 -1
  19. package/scripts/coverage-summary.php +41 -2
  20. package/scripts/pest-bootstrap.php +237 -3
  21. package/scripts/pest-file-runner.php +13 -1
  22. package/scripts/pest-runner.php +57 -2
  23. package/server/config/fleetops.php +44 -0
  24. package/server/config/geocoder.php +1 -1
  25. package/server/migrations/2026_08_08_000001_make_sku_and_provider_transaction_unique_indexes_soft_delete_aware.php +152 -0
  26. package/server/resources/views/labels/entity-label.php +4 -4
  27. package/server/src/Casts/MultiPolygon.php +8 -3
  28. package/server/src/Casts/OrderConfigEntities.php +10 -3
  29. package/server/src/Casts/Point.php +10 -6
  30. package/server/src/Casts/Polygon.php +5 -2
  31. package/server/src/Console/Commands/AssignCustomerRoles.php +28 -6
  32. package/server/src/Console/Commands/AssignDriverRoles.php +36 -6
  33. package/server/src/Console/Commands/AuditCustomerUserConflicts.php +6 -1
  34. package/server/src/Console/Commands/DispatchAdhocOrders.php +12 -2
  35. package/server/src/Console/Commands/FixCustomerCompanies.php +46 -11
  36. package/server/src/Console/Commands/FixDriverCompanies.php +18 -3
  37. package/server/src/Console/Commands/FixLegacyOrderConfigs.php +31 -6
  38. package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +94 -42
  39. package/server/src/Console/Commands/PurgeUnpurchasedServiceQuotes.php +42 -12
  40. package/server/src/Console/Commands/ReplayVehicleLocations.php +120 -55
  41. package/server/src/Console/Commands/SendDriverNotification.php +25 -4
  42. package/server/src/Console/Commands/SendMaintenanceReminders.php +43 -23
  43. package/server/src/Console/Commands/SimulateOrderRouteNavigation.php +36 -6
  44. package/server/src/Console/Commands/TrackOrderDistanceAndTime.php +6 -1
  45. package/server/src/Events/DriverLocationChanged.php +1 -1
  46. package/server/src/Events/DriverSimulatedLocationChanged.php +1 -1
  47. package/server/src/Events/EntityActivityChanged.php +1 -1
  48. package/server/src/Events/EntityCompleted.php +1 -1
  49. package/server/src/Events/EntityDriverAssigned.php +1 -1
  50. package/server/src/Events/FuelProviderTransactionImported.php +1 -1
  51. package/server/src/Events/FuelProviderTransactionMatched.php +1 -1
  52. package/server/src/Events/FuelProviderTransactionUnmatched.php +1 -1
  53. package/server/src/Events/FuelReportCreatedFromProvider.php +1 -1
  54. package/server/src/Events/GeofenceDwelled.php +1 -1
  55. package/server/src/Events/GeofenceEntered.php +1 -1
  56. package/server/src/Events/GeofenceExited.php +1 -1
  57. package/server/src/Events/VehicleLocationChanged.php +1 -1
  58. package/server/src/Events/WaypointActivityChanged.php +1 -1
  59. package/server/src/Events/WaypointCompleted.php +1 -1
  60. package/server/src/Exceptions/IntegratedVendorException.php +2 -2
  61. package/server/src/Exceptions/TelematicRateLimitExceededException.php +1 -1
  62. package/server/src/Http/Controllers/Api/v1/ContactController.php +89 -22
  63. package/server/src/Http/Controllers/Api/v1/CustomerController.php +346 -101
  64. package/server/src/Http/Controllers/Api/v1/DeviceController.php +48 -14
  65. package/server/src/Http/Controllers/Api/v1/DriverController.php +224 -64
  66. package/server/src/Http/Controllers/Api/v1/EntityController.php +112 -66
  67. package/server/src/Http/Controllers/Api/v1/EquipmentController.php +40 -10
  68. package/server/src/Http/Controllers/Api/v1/FleetController.php +54 -14
  69. package/server/src/Http/Controllers/Api/v1/FuelReportController.php +54 -14
  70. package/server/src/Http/Controllers/Api/v1/FuelTransactionController.php +36 -11
  71. package/server/src/Http/Controllers/Api/v1/GeofenceController.php +73 -23
  72. package/server/src/Http/Controllers/Api/v1/IssueController.php +54 -14
  73. package/server/src/Http/Controllers/Api/v1/LabelController.php +61 -22
  74. package/server/src/Http/Controllers/Api/v1/NavigatorController.php +32 -15
  75. package/server/src/Http/Controllers/Api/v1/OrderConfigController.php +35 -5
  76. package/server/src/Http/Controllers/Api/v1/OrderController.php +388 -203
  77. package/server/src/Http/Controllers/Api/v1/PartController.php +40 -10
  78. package/server/src/Http/Controllers/Api/v1/PayloadController.php +90 -30
  79. package/server/src/Http/Controllers/Api/v1/PlaceController.php +188 -91
  80. package/server/src/Http/Controllers/Api/v1/PurchaseRateController.php +100 -36
  81. package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +96 -26
  82. package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +169 -81
  83. package/server/src/Http/Controllers/Api/v1/ServiceRateController.php +104 -75
  84. package/server/src/Http/Controllers/Api/v1/TrackingNumberController.php +92 -14
  85. package/server/src/Http/Controllers/Api/v1/TrackingStatusController.php +66 -16
  86. package/server/src/Http/Controllers/Api/v1/VehicleController.php +121 -67
  87. package/server/src/Http/Controllers/Api/v1/VendorController.php +54 -14
  88. package/server/src/Http/Controllers/Api/v1/WorkOrderController.php +47 -12
  89. package/server/src/Http/Controllers/Api/v1/ZoneController.php +84 -24
  90. package/server/src/Http/Controllers/Concerns/ResolvesReviewAccountBypass.php +62 -0
  91. package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +15 -5
  92. package/server/src/Http/Controllers/Internal/v1/ContactController.php +113 -28
  93. package/server/src/Http/Controllers/Internal/v1/CustomerController.php +36 -11
  94. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +9 -4
  95. package/server/src/Http/Controllers/Internal/v1/DriverController.php +168 -70
  96. package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +18 -3
  97. package/server/src/Http/Controllers/Internal/v1/FleetController.php +137 -60
  98. package/server/src/Http/Controllers/Internal/v1/FleetOpsLookupController.php +24 -9
  99. package/server/src/Http/Controllers/Internal/v1/FuelReportController.php +18 -3
  100. package/server/src/Http/Controllers/Internal/v1/GeocoderController.php +26 -8
  101. package/server/src/Http/Controllers/Internal/v1/GettingStartedController.php +12 -2
  102. package/server/src/Http/Controllers/Internal/v1/IntegratedVendorController.php +27 -6
  103. package/server/src/Http/Controllers/Internal/v1/IssueController.php +82 -29
  104. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +34 -26
  105. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +141 -97
  106. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +28 -8
  107. package/server/src/Http/Controllers/Internal/v1/MetricsController.php +21 -12
  108. package/server/src/Http/Controllers/Internal/v1/MorphController.php +90 -18
  109. package/server/src/Http/Controllers/Internal/v1/NavigatorController.php +88 -35
  110. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +149 -39
  111. package/server/src/Http/Controllers/Internal/v1/OrderConfigController.php +54 -18
  112. package/server/src/Http/Controllers/Internal/v1/OrderController.php +298 -86
  113. package/server/src/Http/Controllers/Internal/v1/PartController.php +18 -3
  114. package/server/src/Http/Controllers/Internal/v1/PaymentController.php +46 -14
  115. package/server/src/Http/Controllers/Internal/v1/PlaceController.php +31 -5
  116. package/server/src/Http/Controllers/Internal/v1/ProofController.php +88 -48
  117. package/server/src/Http/Controllers/Internal/v1/SearchController.php +1 -1
  118. package/server/src/Http/Controllers/Internal/v1/SensorController.php +6 -1
  119. package/server/src/Http/Controllers/Internal/v1/ServiceAreaController.php +6 -1
  120. package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +255 -105
  121. package/server/src/Http/Controllers/Internal/v1/SettingController.php +88 -35
  122. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +1 -4
  123. package/server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php +42 -14
  124. package/server/src/Http/Controllers/Internal/v1/VendorController.php +103 -33
  125. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +42 -12
  126. package/server/src/Http/Filter/DriverFilter.php +2 -2
  127. package/server/src/Http/Filter/MaintenanceScheduleFilter.php +40 -0
  128. package/server/src/Http/Filter/OrderFilter.php +1 -1
  129. package/server/src/Http/Filter/VehicleFilter.php +21 -7
  130. package/server/src/Http/Middleware/AuthenticateCustomerToken.php +12 -2
  131. package/server/src/Http/Requests/BulkDispatchRequest.php +3 -9
  132. package/server/src/Http/Requests/CancelOrderRequest.php +2 -6
  133. package/server/src/Http/Requests/CreateContactRequest.php +2 -6
  134. package/server/src/Http/Requests/CreateCustomerRequest.php +9 -1
  135. package/server/src/Http/Requests/CreateDeviceRequest.php +2 -2
  136. package/server/src/Http/Requests/CreateDriverRequest.php +3 -9
  137. package/server/src/Http/Requests/CreateEntityRequest.php +2 -6
  138. package/server/src/Http/Requests/CreateEquipmentRequest.php +2 -2
  139. package/server/src/Http/Requests/CreateFleetRequest.php +2 -6
  140. package/server/src/Http/Requests/CreateFuelReportRequest.php +16 -9
  141. package/server/src/Http/Requests/CreateFuelTransactionRequest.php +66 -3
  142. package/server/src/Http/Requests/CreateIssueRequest.php +2 -6
  143. package/server/src/Http/Requests/CreateOrderRequest.php +7 -8
  144. package/server/src/Http/Requests/CreatePartRequest.php +41 -3
  145. package/server/src/Http/Requests/CreatePayloadRequest.php +7 -9
  146. package/server/src/Http/Requests/CreatePlaceRequest.php +2 -6
  147. package/server/src/Http/Requests/CreatePurchaseRateRequest.php +2 -6
  148. package/server/src/Http/Requests/CreateSensorRequest.php +2 -2
  149. package/server/src/Http/Requests/CreateServiceAreaRequest.php +2 -6
  150. package/server/src/Http/Requests/CreateServiceQuoteRequest.php +2 -6
  151. package/server/src/Http/Requests/CreateServiceRateRequest.php +2 -6
  152. package/server/src/Http/Requests/CreateTrackingNumberRequest.php +2 -6
  153. package/server/src/Http/Requests/CreateTrackingStatusRequest.php +2 -6
  154. package/server/src/Http/Requests/CreateVehicleRequest.php +2 -6
  155. package/server/src/Http/Requests/CreateVendorRequest.php +2 -6
  156. package/server/src/Http/Requests/CreateWorkOrderRequest.php +2 -2
  157. package/server/src/Http/Requests/CreateZoneRequest.php +2 -6
  158. package/server/src/Http/Requests/DecodeTrackingNumberQR.php +2 -6
  159. package/server/src/Http/Requests/DriverSimulationRequest.php +2 -6
  160. package/server/src/Http/Requests/Internal/AssignOrderRequest.php +3 -7
  161. package/server/src/Http/Requests/Internal/CreateDriverRequest.php +4 -12
  162. package/server/src/Http/Requests/Internal/CreateOrderConfigRequest.php +2 -6
  163. package/server/src/Http/Requests/Internal/CreateOrderRequest.php +7 -8
  164. package/server/src/Http/Requests/Internal/FleetActionRequest.php +18 -12
  165. package/server/src/Http/Requests/Internal/UpdateDriverRequest.php +6 -3
  166. package/server/src/Http/Requests/QueryServiceQuotesRequest.php +2 -6
  167. package/server/src/Http/Requests/ScheduleOrderRequest.php +2 -6
  168. package/server/src/Http/Requests/UpdateIssueRequest.php +1 -3
  169. package/server/src/Http/Resources/v1/Customer.php +2 -2
  170. package/server/src/Http/Resources/v1/Maintenance.php +3 -4
  171. package/server/src/Http/Resources/v1/MaintenanceSchedule.php +3 -4
  172. package/server/src/Http/Resources/v1/Order.php +3 -7
  173. package/server/src/Http/Resources/v1/PurchaseRate.php +4 -6
  174. package/server/src/Http/Resources/v1/TrackingNumber.php +33 -0
  175. package/server/src/Http/Resources/v1/TrackingStatus.php +4 -6
  176. package/server/src/Http/Resources/v1/WorkOrder.php +3 -4
  177. package/server/src/Imports/ContactImport.php +10 -1
  178. package/server/src/Imports/DriverImport.php +10 -1
  179. package/server/src/Imports/EquipmentImport.php +6 -1
  180. package/server/src/Imports/FleetImport.php +10 -1
  181. package/server/src/Imports/FuelReportImport.php +10 -1
  182. package/server/src/Imports/IssueImport.php +10 -1
  183. package/server/src/Imports/MaintenanceImport.php +6 -1
  184. package/server/src/Imports/MaintenanceScheduleImport.php +6 -1
  185. package/server/src/Imports/PartImport.php +6 -1
  186. package/server/src/Imports/PlaceImport.php +10 -1
  187. package/server/src/Imports/VehicleImport.php +10 -1
  188. package/server/src/Imports/VendorImport.php +10 -1
  189. package/server/src/Imports/WorkOrderImport.php +6 -1
  190. package/server/src/Integrations/Lalamove/Lalamove.php +3 -7
  191. package/server/src/Integrations/Lalamove/LalamoveServiceType.php +5 -8
  192. package/server/src/Jobs/CheckGeofenceDwell.php +41 -16
  193. package/server/src/Jobs/FinalizeApiOrderCreation.php +17 -2
  194. package/server/src/Jobs/FinalizeInternalOrderCreation.php +11 -1
  195. package/server/src/Jobs/NotifyBulkAssignedDriver.php +29 -16
  196. package/server/src/Jobs/ProcessAllocationJob.php +59 -25
  197. package/server/src/Jobs/ReplayPositions.php +17 -10
  198. package/server/src/Jobs/SendPositionReplay.php +18 -7
  199. package/server/src/Jobs/SimulateDrivingRoute.php +14 -3
  200. package/server/src/Jobs/SyncFuelProviderTransactionsJob.php +12 -2
  201. package/server/src/Listeners/HandleDeliveryCompletion.php +11 -1
  202. package/server/src/Listeners/HandleGeofenceDwelled.php +6 -1
  203. package/server/src/Listeners/HandleGeofenceEntered.php +4 -2
  204. package/server/src/Listeners/HandleGeofenceExited.php +6 -1
  205. package/server/src/Listeners/HandleOrderCanceled.php +18 -3
  206. package/server/src/Listeners/HandleOrderDispatchFailed.php +12 -2
  207. package/server/src/Listeners/HandleOrderDispatched.php +61 -26
  208. package/server/src/Listeners/HandleOrderDriverAssigned.php +12 -2
  209. package/server/src/Listeners/HandleUserRemovedFromCompany.php +19 -6
  210. package/server/src/Listeners/NotifyDriverOnShiftChange.php +24 -4
  211. package/server/src/Listeners/NotifyOrderEvent.php +11 -6
  212. package/server/src/Models/Contact.php +6 -7
  213. package/server/src/Models/Device.php +26 -3
  214. package/server/src/Models/Driver.php +0 -6
  215. package/server/src/Models/Entity.php +4 -4
  216. package/server/src/Models/Equipment.php +1 -1
  217. package/server/src/Models/FuelReport.php +26 -1
  218. package/server/src/Models/IntegratedVendor.php +2 -2
  219. package/server/src/Models/Issue.php +5 -1
  220. package/server/src/Models/MaintenanceSchedule.php +25 -10
  221. package/server/src/Models/Order.php +18 -15
  222. package/server/src/Models/OrderConfig.php +1 -1
  223. package/server/src/Models/Payload.php +9 -16
  224. package/server/src/Models/Place.php +20 -13
  225. package/server/src/Models/Sensor.php +24 -0
  226. package/server/src/Models/ServiceArea.php +3 -6
  227. package/server/src/Models/ServiceQuote.php +5 -0
  228. package/server/src/Models/ServiceRate.php +13 -6
  229. package/server/src/Models/Telematic.php +2 -2
  230. package/server/src/Models/TrackingNumber.php +55 -10
  231. package/server/src/Models/TrackingStatus.php +24 -4
  232. package/server/src/Models/Waypoint.php +44 -10
  233. package/server/src/Models/Zone.php +3 -6
  234. package/server/src/Notifications/DriverArrivedAtGeofence.php +1 -1
  235. package/server/src/Notifications/OrderDispatched.php +3 -9
  236. package/server/src/Notifications/OrderPing.php +3 -9
  237. package/server/src/Observers/CategoryObserver.php +6 -1
  238. package/server/src/Observers/CompanyObserver.php +5 -0
  239. package/server/src/Observers/CompanyUserObserver.php +12 -2
  240. package/server/src/Observers/DriverObserver.php +19 -4
  241. package/server/src/Observers/FleetObserver.php +6 -1
  242. package/server/src/Observers/PurchaseRateObserver.php +66 -11
  243. package/server/src/Observers/ServiceAreaObserver.php +12 -2
  244. package/server/src/Observers/ServiceRateObserver.php +21 -6
  245. package/server/src/Observers/TrackingNumberObserver.php +19 -4
  246. package/server/src/Observers/UserObserver.php +6 -1
  247. package/server/src/Observers/VehicleObserver.php +27 -7
  248. package/server/src/Observers/WorkOrderObserver.php +27 -7
  249. package/server/src/Orchestration/Engines/DriverAssignmentEngine.php +8 -3
  250. package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +2 -2
  251. package/server/src/Providers/FleetOpsServiceProvider.php +12 -0
  252. package/server/src/Providers/NotificationServiceProvider.php +6 -0
  253. package/server/src/Support/Ai/Capabilities/AssetStatusCapability.php +36 -26
  254. package/server/src/Support/Ai/Capabilities/CreateOrderPreviewCapability.php +18 -5
  255. package/server/src/Support/Ai/Capabilities/OrderInsightsCapability.php +6 -1
  256. package/server/src/Support/Ai/Capabilities/SearchResourcesCapability.php +26 -6
  257. package/server/src/Support/Analytics/FuelEfficiency.php +9 -3
  258. package/server/src/Support/Analytics/FuelProviderSummary.php +14 -5
  259. package/server/src/Support/Analytics/GeofenceViolations.php +40 -20
  260. package/server/src/Support/Analytics/IssuesInsights.php +38 -18
  261. package/server/src/Support/Analytics/MaintenanceOverview.php +51 -21
  262. package/server/src/Support/Analytics/OnTimeDelivery.php +3 -2
  263. package/server/src/Support/Analytics/OperationsPulse.php +59 -35
  264. package/server/src/Support/Analytics/OrdersByStatus.php +12 -7
  265. package/server/src/Support/Analytics/RevenueTrend.php +16 -6
  266. package/server/src/Support/Analytics/TopDrivers.php +3 -2
  267. package/server/src/Support/FleetOps.php +109 -104
  268. package/server/src/Support/FuelProviders/FuelProviderService.php +10 -1
  269. package/server/src/Support/Geocoding.php +52 -14
  270. package/server/src/Support/GeofenceIntersectionService.php +47 -27
  271. package/server/src/Support/LiveOrderQuery.php +6 -1
  272. package/server/src/Support/Metrics/ActiveRevenueQuery.php +3 -2
  273. package/server/src/Support/Metrics/FuelCostsMetric.php +6 -1
  274. package/server/src/Support/ResolvesOrderServiceStops.php +10 -0
  275. package/server/src/Support/Telematics/Providers/GeotabProvider.php +9 -4
  276. package/server/src/Support/Utils.php +122 -16
  277. package/server/src/Tracking/Providers/GoogleRoutesTrackingProvider.php +6 -1
  278. package/server/src/Tracking/Providers/OsrmTrackingProvider.php +11 -6
  279. package/server/src/Tracking/TrackingProviderManager.php +2 -3
  280. package/server/src/Tracking/TrackingProviderRegistry.php +8 -4
  281. package/server/src/Traits/PayloadAccessors.php +6 -1
  282. package/server/src/routes.php +24 -17
  283. package/server/tests/AiOperationalQueryCapabilityTest.php +457 -1
  284. package/server/tests/AiPreviewCapabilityContractsTest.php +180 -0
  285. package/server/tests/AnalyticsRoutesTest.php +380 -1
  286. package/server/tests/AnalyticsWidgetContractsTest.php +740 -0
  287. package/server/tests/ApiContactControllerContractsTest.php +284 -0
  288. package/server/tests/ApiCustomerControllerContractsTest.php +1130 -0
  289. package/server/tests/ApiDriverControllerContractsTest.php +603 -0
  290. package/server/tests/ApiEntityControllerContractsTest.php +329 -0
  291. package/server/tests/ApiEquipmentControllerContractsTest.php +239 -0
  292. package/server/tests/ApiFleetControllerContractsTest.php +176 -0
  293. package/server/tests/ApiFuelReportControllerContractsTest.php +217 -0
  294. package/server/tests/ApiIssueControllerContractsTest.php +217 -0
  295. package/server/tests/ApiOrderControllerContractsTest.php +1030 -0
  296. package/server/tests/ApiPartControllerContractsTest.php +251 -0
  297. package/server/tests/ApiPayloadControllerContractsTest.php +314 -0
  298. package/server/tests/ApiPlaceControllerContractsTest.php +541 -0
  299. package/server/tests/ApiPurchaseRateControllerContractsTest.php +283 -0
  300. package/server/tests/ApiServiceAreaZoneControllerContractsTest.php +442 -0
  301. package/server/tests/ApiServiceQuoteControllerContractsTest.php +322 -0
  302. package/server/tests/ApiTrackingNumberControllerContractsTest.php +225 -0
  303. package/server/tests/ApiTrackingStatusControllerContractsTest.php +254 -0
  304. package/server/tests/ApiVehicleControllerContractsTest.php +368 -0
  305. package/server/tests/ApiVendorControllerContractsTest.php +199 -0
  306. package/server/tests/AssetAndPlaceSearchContractsTest.php +220 -0
  307. package/server/tests/BackendSmallModelContractsTest.php +354 -0
  308. package/server/tests/CapacityAllocationEngineTest.php +56 -0
  309. package/server/tests/CommandContractsTest.php +2695 -0
  310. package/server/tests/CompactResourceSerializationTest.php +2838 -0
  311. package/server/tests/ControllerFilterContractsTest.php +1273 -0
  312. package/server/tests/ControllerHelperContractsTest.php +1740 -0
  313. package/server/tests/ControllerSerializationContractsTest.php +141 -0
  314. package/server/tests/CoverageSummaryScriptTest.php +93 -0
  315. package/server/tests/CustomerContactIdentitySafetyTest.php +1 -6
  316. package/server/tests/CustomerEndpointTest.php +169 -2
  317. package/server/tests/DriverFilterExecutionTest.php +263 -0
  318. package/server/tests/DriverGeofenceProcessingTest.php +153 -0
  319. package/server/tests/DriverResourceSerializationTest.php +245 -0
  320. package/server/tests/DriverSchedulingTraitContractsTest.php +259 -0
  321. package/server/tests/EventContractsTest.php +1259 -0
  322. package/server/tests/ExceptionContractsTest.php +81 -0
  323. package/server/tests/ExpansionContractsTest.php +242 -0
  324. package/server/tests/ExportCoverageTest.php +116 -0
  325. package/server/tests/Feature/Http/Api/ContactControllerPhotoAndDeleteTest.php +199 -0
  326. package/server/tests/Feature/Http/Api/CustomerControllerHelperSeamsTest.php +831 -0
  327. package/server/tests/Feature/Http/Api/DeviceControllerContractsTest.php +347 -0
  328. package/server/tests/Feature/Http/Api/DeviceControllerCrudTest.php +176 -0
  329. package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +530 -0
  330. package/server/tests/Feature/Http/Api/DriverControllerHelpersTest.php +240 -0
  331. package/server/tests/Feature/Http/Api/DriverControllerLookupTest.php +186 -0
  332. package/server/tests/Feature/Http/Api/DriverControllerOrganizationsTest.php +112 -0
  333. package/server/tests/Feature/Http/Api/DriverControllerSessionHelpersTest.php +207 -0
  334. package/server/tests/Feature/Http/Api/DriverControllerSwitchOrgAndGeofenceTest.php +329 -0
  335. package/server/tests/Feature/Http/Api/DriverControllerTrackTest.php +383 -0
  336. package/server/tests/Feature/Http/Api/EntityControllerHelpersTest.php +150 -0
  337. package/server/tests/Feature/Http/Api/FuelTransactionControllerContractsTest.php +352 -0
  338. package/server/tests/Feature/Http/Api/GeofenceControllerContractsTest.php +461 -0
  339. package/server/tests/Feature/Http/Api/LabelControllerSubjectsTest.php +148 -0
  340. package/server/tests/Feature/Http/Api/OrderConfigControllerTest.php +112 -0
  341. package/server/tests/Feature/Http/Api/OrderControllerCreateTest.php +279 -0
  342. package/server/tests/Feature/Http/Api/OrderControllerIntegratedVendorTest.php +295 -0
  343. package/server/tests/Feature/Http/Api/OrderControllerLifecycleActionsTest.php +279 -0
  344. package/server/tests/Feature/Http/Api/OrderControllerLookupHelpersTest.php +207 -0
  345. package/server/tests/Feature/Http/Api/OrderControllerNearbyFiltersTest.php +282 -0
  346. package/server/tests/Feature/Http/Api/OrderControllerPersistenceHelpersTest.php +216 -0
  347. package/server/tests/Feature/Http/Api/OrderControllerQueryFiltersTest.php +184 -0
  348. package/server/tests/Feature/Http/Api/OrderControllerStartActivityTest.php +663 -0
  349. package/server/tests/Feature/Http/Api/PayloadControllerHelpersTest.php +134 -0
  350. package/server/tests/Feature/Http/Api/PlaceAndServiceQuoteSeamsTest.php +479 -0
  351. package/server/tests/Feature/Http/Api/PurchaseRateControllerHelpersTest.php +176 -0
  352. package/server/tests/Feature/Http/Api/PurchaseRateControllerOrderCreationTest.php +268 -0
  353. package/server/tests/Feature/Http/Api/SensorControllerCrudTest.php +239 -0
  354. package/server/tests/Feature/Http/Api/ServiceAreaControllerBorderAndSeamsTest.php +187 -0
  355. package/server/tests/Feature/Http/Api/ServiceQuoteControllerQueryTest.php +405 -0
  356. package/server/tests/Feature/Http/Api/ServiceQuoteControllerVendorBridgeTest.php +212 -0
  357. package/server/tests/Feature/Http/Api/ServiceRateControllerContractsTest.php +263 -0
  358. package/server/tests/Feature/Http/Api/SmallApiControllerHelpersTest.php +272 -0
  359. package/server/tests/Feature/Http/Api/TrackingNumberControllerHelpersTest.php +160 -0
  360. package/server/tests/Feature/Http/Api/TrackingStatusControllerHelpersTest.php +165 -0
  361. package/server/tests/Feature/Http/Api/VehicleControllerTrackingTest.php +483 -0
  362. package/server/tests/Feature/Http/Api/WorkOrderControllerContractsTest.php +326 -0
  363. package/server/tests/Feature/Http/Api/WorkOrderControllerHelpersTest.php +130 -0
  364. package/server/tests/Feature/Http/Api/ZoneControllerBordersAndSeamsTest.php +294 -0
  365. package/server/tests/Feature/Http/FleetOpsRouteRegistrationTest.php +189 -0
  366. package/server/tests/Feature/Http/Internal/AnalyticsControllerSeamsTest.php +53 -0
  367. package/server/tests/Feature/Http/Internal/ContactControllerContractsTest.php +240 -0
  368. package/server/tests/Feature/Http/Internal/ContactControllerCustomerGuardsTest.php +128 -0
  369. package/server/tests/Feature/Http/Internal/ContactControllerEndpointsTest.php +294 -0
  370. package/server/tests/Feature/Http/Internal/CustomerControllerCredentialsTest.php +196 -0
  371. package/server/tests/Feature/Http/Internal/DeviceControllerContractsTest.php +370 -0
  372. package/server/tests/Feature/Http/Internal/DeviceControllerExportAndFiltersTest.php +151 -0
  373. package/server/tests/Feature/Http/Internal/DriverControllerContractsTest.php +1167 -0
  374. package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +233 -0
  375. package/server/tests/Feature/Http/Internal/DriverControllerExistingUserAdoptionTest.php +710 -0
  376. package/server/tests/Feature/Http/Internal/DriverControllerHelpersTest.php +218 -0
  377. package/server/tests/Feature/Http/Internal/FleetAssignmentsAndTriggersTest.php +198 -0
  378. package/server/tests/Feature/Http/Internal/FleetOpsLookupControllerDbTest.php +90 -0
  379. package/server/tests/Feature/Http/Internal/HubControllerTest.php +206 -0
  380. package/server/tests/Feature/Http/Internal/IssueControllerContractsTest.php +404 -0
  381. package/server/tests/Feature/Http/Internal/IssueControllerExportImportTest.php +118 -0
  382. package/server/tests/Feature/Http/Internal/LiveControllerEndpointsTest.php +246 -0
  383. package/server/tests/Feature/Http/Internal/LiveControllerTest.php +778 -0
  384. package/server/tests/Feature/Http/Internal/MaintenanceScheduleControllerContractsTest.php +263 -0
  385. package/server/tests/Feature/Http/Internal/MaintenanceScheduleControllerSurfacesTest.php +214 -0
  386. package/server/tests/Feature/Http/Internal/MetricsControllerContractsTest.php +249 -0
  387. package/server/tests/Feature/Http/Internal/MetricsControllerHelpersTest.php +66 -0
  388. package/server/tests/Feature/Http/Internal/MorphControllerHelpersTest.php +125 -0
  389. package/server/tests/Feature/Http/Internal/NavigatorControllerHelpersTest.php +201 -0
  390. package/server/tests/Feature/Http/Internal/OrchestrationControllerContractsTest.php +928 -0
  391. package/server/tests/Feature/Http/Internal/OrchestrationControllerHelpersTest.php +214 -0
  392. package/server/tests/Feature/Http/Internal/OrchestrationControllerImportOrdersTest.php +340 -0
  393. package/server/tests/Feature/Http/Internal/OrderConfigControllerContractsTest.php +314 -0
  394. package/server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php +663 -0
  395. package/server/tests/Feature/Http/Internal/OrderControllerCapturePhotoTest.php +507 -0
  396. package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +1742 -0
  397. package/server/tests/Feature/Http/Internal/OrderControllerCreateRecordTest.php +488 -0
  398. package/server/tests/Feature/Http/Internal/OrderControllerHelperSeamsTest.php +298 -0
  399. package/server/tests/Feature/Http/Internal/OrderControllerHelpersTest.php +267 -0
  400. package/server/tests/Feature/Http/Internal/OrderControllerImportFromFilesTest.php +179 -0
  401. package/server/tests/Feature/Http/Internal/OrderControllerUpstreamNotFoundTest.php +80 -0
  402. package/server/tests/Feature/Http/Internal/PaymentControllerPaymentsTest.php +154 -0
  403. package/server/tests/Feature/Http/Internal/PlaceControllerTest.php +252 -0
  404. package/server/tests/Feature/Http/Internal/PositionControllerReplayMetricsTest.php +163 -0
  405. package/server/tests/Feature/Http/Internal/ProofControllerContractsTest.php +219 -0
  406. package/server/tests/Feature/Http/Internal/SearchControllerEndpointTest.php +151 -0
  407. package/server/tests/Feature/Http/Internal/ServiceQuoteControllerHelpersTest.php +208 -0
  408. package/server/tests/Feature/Http/Internal/ServiceQuoteControllerQueryRecordTest.php +217 -0
  409. package/server/tests/Feature/Http/Internal/ServiceQuoteControllerVendorBridgeTest.php +239 -0
  410. package/server/tests/Feature/Http/Internal/ServiceQuotePreliminaryQueryTest.php +271 -0
  411. package/server/tests/Feature/Http/Internal/SettingControllerHelpersTest.php +133 -0
  412. package/server/tests/Feature/Http/Internal/SmallInternalControllersTest.php +180 -0
  413. package/server/tests/Feature/Http/Internal/TelematicControllerEndpointsTest.php +417 -0
  414. package/server/tests/Feature/Http/Internal/VehicleControllerEndpointsTest.php +361 -0
  415. package/server/tests/Feature/Http/Internal/VendorControllerEndpointsTest.php +203 -0
  416. package/server/tests/Feature/Http/Internal/WorkOrderAndConfigSurfacesTest.php +289 -0
  417. package/server/tests/Feature/Http/Internal/WorkOrderControllerContractsTest.php +199 -0
  418. package/server/tests/Feature/Http/TelematicWebhookIngestTest.php +343 -0
  419. package/server/tests/Feature/Http/Telematics/TelematicWebhookControllerProcessingTest.php +254 -0
  420. package/server/tests/FleetOpsSupportContractsTest.php +187 -0
  421. package/server/tests/FlespiProviderContractsTest.php +244 -0
  422. package/server/tests/FlowResourceTest.php +295 -0
  423. package/server/tests/FuelProviderConnectionControllerContractsTest.php +303 -0
  424. package/server/tests/FuelProviderImplementationTest.php +753 -0
  425. package/server/tests/FuelProviderResourceContractsTest.php +171 -0
  426. package/server/tests/FuelProviderTransactionControllerContractsTest.php +198 -0
  427. package/server/tests/GeofenceIntersectionServiceContractsTest.php +164 -0
  428. package/server/tests/HOSConstraintContractsTest.php +128 -0
  429. package/server/tests/HubAndGettingStartedContractsTest.php +158 -0
  430. package/server/tests/ImportContractsTest.php +191 -0
  431. package/server/tests/InternalCustomerControllerContractsTest.php +238 -0
  432. package/server/tests/InternalFleetControllerContractsTest.php +389 -0
  433. package/server/tests/InternalImportExportControllerContractsTest.php +289 -0
  434. package/server/tests/InternalMetricsControllerContractsTest.php +69 -0
  435. package/server/tests/InternalPlaceWorkOrderControllerContractsTest.php +207 -0
  436. package/server/tests/InternalServiceQuoteCheckoutControllerContractsTest.php +470 -0
  437. package/server/tests/InternalVendorControllerContractsTest.php +331 -0
  438. package/server/tests/InventoryTelemetryModelContractsTest.php +516 -0
  439. package/server/tests/IssueTimelineContractsTest.php +191 -0
  440. package/server/tests/LabelPdfRenderingTest.php +57 -0
  441. package/server/tests/LalamoveIntegrationContractsTest.php +167 -0
  442. package/server/tests/LiveOrderQueryTest.php +145 -18
  443. package/server/tests/LookupAndGeocoderControllerContractsTest.php +357 -0
  444. package/server/tests/MaintainableTraitContractsTest.php +245 -0
  445. package/server/tests/MaintenanceScheduleControllerContractsTest.php +101 -0
  446. package/server/tests/MaintenanceWarrantyContractsTest.php +237 -0
  447. package/server/tests/ManifestControllerContractsTest.php +310 -0
  448. package/server/tests/MetricsRegistryTest.php +596 -10
  449. package/server/tests/MiddlewareContractsTest.php +150 -0
  450. package/server/tests/ModelAccessorContractsTest.php +3201 -0
  451. package/server/tests/MorphControllerContractsTest.php +410 -0
  452. package/server/tests/NavigatorControllerContractsTest.php +206 -0
  453. package/server/tests/NotificationAndMailContractsTest.php +677 -0
  454. package/server/tests/ObserverContractsTest.php +1414 -0
  455. package/server/tests/OperationalAlgorithmContractsTest.php +852 -0
  456. package/server/tests/OperationsMonitorEndpointTest.php +2 -1
  457. package/server/tests/OrchestrationPayloadBuilderTest.php +87 -0
  458. package/server/tests/OrderActivityFlowRegressionTest.php +354 -7
  459. package/server/tests/OrderDispatchListenerContractsTest.php +577 -0
  460. package/server/tests/OrderFilterExecutionTest.php +244 -0
  461. package/server/tests/OrderQueuedClosureRegressionTest.php +2 -1
  462. package/server/tests/PaymentControllerContractsTest.php +199 -0
  463. package/server/tests/PointResolutionTest.php +54 -0
  464. package/server/tests/ProcessAllocationJobContractsTest.php +224 -0
  465. package/server/tests/ProofAndSensorControllerContractsTest.php +44 -0
  466. package/server/tests/ProviderContractsTest.php +409 -0
  467. package/server/tests/PublicApiControllerInputContractsTest.php +294 -0
  468. package/server/tests/QueueJobContractsTest.php +764 -0
  469. package/server/tests/ReportSchemaContractsTest.php +271 -0
  470. package/server/tests/RequestContractsTest.php +1058 -0
  471. package/server/tests/RouteRegistrationExecutionTest.php +178 -0
  472. package/server/tests/RouteSequencingEngineTest.php +179 -0
  473. package/server/tests/RulesAndCastsTest.php +246 -0
  474. package/server/tests/SamsaraProviderContractsTest.php +272 -0
  475. package/server/tests/SearchControllerContractsTest.php +311 -0
  476. package/server/tests/ServiceRateRouteValidationTest.php +76 -0
  477. package/server/tests/SettingControllerContractsTest.php +410 -0
  478. package/server/tests/SmallControllerContractsTest.php +480 -0
  479. package/server/tests/SupportAnalyticsCustomerAuthContractsTest.php +221 -0
  480. package/server/tests/SupportJobAndAiCoverageTest.php +812 -0
  481. package/server/tests/SupportServiceContractsTest.php +287 -0
  482. package/server/tests/SupportValueObjectsTest.php +155 -0
  483. package/server/tests/TelematicControllerContractsTest.php +185 -0
  484. package/server/tests/TelematicProviderRegistryTest.php +167 -0
  485. package/server/tests/TelematicServiceHelperContractsTest.php +315 -0
  486. package/server/tests/TelematicWebhookControllerContractsTest.php +253 -0
  487. package/server/tests/TelematicsHardeningTest.php +642 -86
  488. package/server/tests/TrackingIntelligenceTest.php +644 -1
  489. package/server/tests/TrackingNumberTraitContractsTest.php +106 -0
  490. package/server/tests/Unit/Casts/SpatialCastBranchesTest.php +75 -0
  491. package/server/tests/Unit/Console/AssignCustomerRolesCommandTest.php +102 -0
  492. package/server/tests/Unit/Console/AssignDriverRolesCommandTest.php +113 -0
  493. package/server/tests/Unit/Console/Commands/FixLegacyOrderConfigsHelpersTest.php +108 -0
  494. package/server/tests/Unit/Console/Commands/ProcessOperationalAlertsCommandTest.php +265 -0
  495. package/server/tests/Unit/Console/Commands/ReplayVehicleLocationsCommandTest.php +320 -0
  496. package/server/tests/Unit/Console/Commands/SimulateGeofenceEventsCommandTest.php +207 -0
  497. package/server/tests/Unit/Console/FixCommandsAndMaintenanceControllerTest.php +256 -0
  498. package/server/tests/Unit/Console/FleetOpsCommandsTest.php +198 -0
  499. package/server/tests/Unit/Console/ReplayVehicleLocationsCommandTest.php +252 -0
  500. package/server/tests/Unit/Console/SendMaintenanceRemindersTest.php +212 -0
  501. package/server/tests/Unit/Console/SimulateGeofenceEventsCommandTest.php +232 -0
  502. package/server/tests/Unit/Console/SyncTelematicsCommandTest.php +136 -0
  503. package/server/tests/Unit/Constraints/HOSConstraintTest.php +158 -0
  504. package/server/tests/Unit/Events/EventModelRecordTest.php +93 -0
  505. package/server/tests/Unit/Events/OrderDispatchFailedTest.php +34 -0
  506. package/server/tests/Unit/Exports/ExportCollectionsTest.php +142 -0
  507. package/server/tests/Unit/Exports/OperationalExportMappingTest.php +651 -0
  508. package/server/tests/Unit/Exports/VehicleExportTest.php +284 -0
  509. package/server/tests/Unit/Flow/EventFiringTest.php +95 -0
  510. package/server/tests/Unit/Http/Api/QueryWithRequestSeamsTest.php +198 -0
  511. package/server/tests/Unit/Http/Api/SensorInputTest.php +95 -0
  512. package/server/tests/Unit/Http/Filter/DeviceFilterTest.php +199 -0
  513. package/server/tests/Unit/Http/Filter/EmptyValueGuardsTest.php +66 -0
  514. package/server/tests/Unit/Http/Filter/FilterDateRangesTest.php +72 -0
  515. package/server/tests/Unit/Http/Filter/FuelProviderTransactionFilterTest.php +172 -0
  516. package/server/tests/Unit/Http/Filter/InternalRelationUuidLookupTest.php +135 -0
  517. package/server/tests/Unit/Http/Filter/IssueFilterTest.php +129 -0
  518. package/server/tests/Unit/Http/Filter/MaintenanceScheduleFilterTest.php +69 -0
  519. package/server/tests/Unit/Http/Filter/PartFilterTest.php +173 -0
  520. package/server/tests/Unit/Http/Internal/ControllerDelegationSeamsTest.php +136 -0
  521. package/server/tests/Unit/Http/Internal/ControllerExcelSeamsTest.php +89 -0
  522. package/server/tests/Unit/Http/Middleware/SetupDriverSessionTest.php +32 -0
  523. package/server/tests/Unit/Http/Requests/CreateFuelReportRequestTest.php +68 -0
  524. package/server/tests/Unit/Http/Requests/FleetActionRequestTest.php +80 -0
  525. package/server/tests/Unit/Http/Requests/RequestAuthorizationTest.php +50 -0
  526. package/server/tests/Unit/Http/Resources/CustomerResourceTest.php +108 -0
  527. package/server/tests/Unit/Http/Resources/EntityCustomerTypeTest.php +35 -0
  528. package/server/tests/Unit/Http/Resources/FleetResourceTest.php +102 -0
  529. package/server/tests/Unit/Http/Resources/IndexOrderResourceTest.php +210 -0
  530. package/server/tests/Unit/Http/Resources/IndexVehicleResourceTest.php +194 -0
  531. package/server/tests/Unit/Http/Resources/MaintenanceResourcesTest.php +182 -0
  532. package/server/tests/Unit/Http/Resources/OrderAndDriverResourceTest.php +121 -0
  533. package/server/tests/Unit/Http/Resources/OrderConfigResourceTest.php +47 -0
  534. package/server/tests/Unit/Http/Resources/PayloadResourceTest.php +111 -0
  535. package/server/tests/Unit/Http/Resources/RelationResourceFallbacksTest.php +32 -0
  536. package/server/tests/Unit/Http/Resources/ResourceRelationResolutionTest.php +55 -0
  537. package/server/tests/Unit/Http/Resources/TrackingNumberResourceTest.php +174 -0
  538. package/server/tests/Unit/Http/Resources/WaypointResourceTest.php +295 -0
  539. package/server/tests/Unit/Http/Resources/WorkOrderResourceTest.php +164 -0
  540. package/server/tests/Unit/Imports/ImportDelegationTest.php +151 -0
  541. package/server/tests/Unit/Integrations/Lalamove/LalamoveDeliveryStopTest.php +79 -0
  542. package/server/tests/Unit/Integrations/Lalamove/LalamoveLifecycleTest.php +211 -0
  543. package/server/tests/Unit/Integrations/Lalamove/LalamoveQuoteAndServiceOrderTest.php +381 -0
  544. package/server/tests/Unit/Integrations/Lalamove/LalamoveServiceTypeTest.php +51 -0
  545. package/server/tests/Unit/Integrations/LalamoveQuotationTest.php +194 -0
  546. package/server/tests/Unit/Integrations/LalamoveResponseHandlingTest.php +58 -0
  547. package/server/tests/Unit/Jobs/GeofenceDwellAndBulkNotifyTest.php +190 -0
  548. package/server/tests/Unit/Jobs/ReplayPositionsTest.php +57 -0
  549. package/server/tests/Unit/Jobs/SimulateWaypointReachedTest.php +92 -0
  550. package/server/tests/Unit/Listeners/HandleGeofenceEnteredTest.php +256 -0
  551. package/server/tests/Unit/Listeners/HandleOrderDispatchFailedListenerTest.php +101 -0
  552. package/server/tests/Unit/Listeners/HandleOrderDispatchedConcreteTest.php +203 -0
  553. package/server/tests/Unit/Listeners/HandleOrderDispatchedHelpersTest.php +186 -0
  554. package/server/tests/Unit/Listeners/HandleUserRemovedFromCompanyTest.php +65 -0
  555. package/server/tests/Unit/Listeners/ListenerDelegationSeamsTest.php +83 -0
  556. package/server/tests/Unit/Models/AssetTest.php +469 -0
  557. package/server/tests/Unit/Models/ContactCustomerUserTest.php +370 -0
  558. package/server/tests/Unit/Models/ContactTest.php +577 -0
  559. package/server/tests/Unit/Models/DeviceEventTest.php +481 -0
  560. package/server/tests/Unit/Models/DeviceLastPositionDefaultTest.php +128 -0
  561. package/server/tests/Unit/Models/DriverModelHelpersTest.php +268 -0
  562. package/server/tests/Unit/Models/DriverTest.php +401 -0
  563. package/server/tests/Unit/Models/EntityTest.php +171 -0
  564. package/server/tests/Unit/Models/EquipmentTest.php +434 -0
  565. package/server/tests/Unit/Models/FleetTest.php +153 -0
  566. package/server/tests/Unit/Models/FuelProviderTransactionTest.php +82 -0
  567. package/server/tests/Unit/Models/FuelReportImportAndLabelsTest.php +193 -0
  568. package/server/tests/Unit/Models/ImportRowFallbackSeamsTest.php +100 -0
  569. package/server/tests/Unit/Models/IntegratedVendorLifecycleTest.php +203 -0
  570. package/server/tests/Unit/Models/IntegratedVendorTest.php +135 -0
  571. package/server/tests/Unit/Models/IssueTest.php +160 -0
  572. package/server/tests/Unit/Models/MaintenanceScheduleTest.php +319 -0
  573. package/server/tests/Unit/Models/MaintenanceTest.php +277 -0
  574. package/server/tests/Unit/Models/MaintenanceWorkOrderImportTest.php +161 -0
  575. package/server/tests/Unit/Models/ManifestStopTest.php +121 -0
  576. package/server/tests/Unit/Models/ManifestTest.php +163 -0
  577. package/server/tests/Unit/Models/ModelLookupFallbackSeamsTest.php +149 -0
  578. package/server/tests/Unit/Models/OrderConfigTest.php +341 -0
  579. package/server/tests/Unit/Models/OrderDynamicContractsTest.php +190 -0
  580. package/server/tests/Unit/Models/OrderPayloadConfigAndDistanceTest.php +426 -0
  581. package/server/tests/Unit/Models/OrderPayloadRouteTest.php +227 -0
  582. package/server/tests/Unit/Models/OrderPurchaseAndDriversTest.php +188 -0
  583. package/server/tests/Unit/Models/OrderTest.php +917 -0
  584. package/server/tests/Unit/Models/PartTest.php +399 -0
  585. package/server/tests/Unit/Models/PayloadTest.php +710 -0
  586. package/server/tests/Unit/Models/PayloadWaypointsAndEntitiesTest.php +317 -0
  587. package/server/tests/Unit/Models/PlaceCreationAndImportTest.php +384 -0
  588. package/server/tests/Unit/Models/PlaceGeocodingResultsTest.php +253 -0
  589. package/server/tests/Unit/Models/PlaceSharedAndGeocodingTest.php +271 -0
  590. package/server/tests/Unit/Models/PlaceTest.php +589 -0
  591. package/server/tests/Unit/Models/PositionTest.php +49 -0
  592. package/server/tests/Unit/Models/RouteTest.php +55 -0
  593. package/server/tests/Unit/Models/SensorLastPositionDefaultTest.php +130 -0
  594. package/server/tests/Unit/Models/SensorReadingsTest.php +168 -0
  595. package/server/tests/Unit/Models/SensorTest.php +284 -0
  596. package/server/tests/Unit/Models/ServiceAreaGeometryTest.php +129 -0
  597. package/server/tests/Unit/Models/ServiceAreaTest.php +125 -0
  598. package/server/tests/Unit/Models/ServiceQuoteTest.php +617 -0
  599. package/server/tests/Unit/Models/ServiceRateFeeTest.php +73 -0
  600. package/server/tests/Unit/Models/ServiceRatePersistenceAndServicabilityTest.php +428 -0
  601. package/server/tests/Unit/Models/ServiceRateTest.php +533 -0
  602. package/server/tests/Unit/Models/SmallModelSurfacesTest.php +294 -0
  603. package/server/tests/Unit/Models/TrackingNumberTest.php +199 -0
  604. package/server/tests/Unit/Models/TrackingStatusTest.php +131 -0
  605. package/server/tests/Unit/Models/VehicleDeviceTest.php +115 -0
  606. package/server/tests/Unit/Models/VehiclePositionsAndImportTest.php +220 -0
  607. package/server/tests/Unit/Models/VehicleTest.php +266 -0
  608. package/server/tests/Unit/Models/VendorRelationsAndImportTest.php +107 -0
  609. package/server/tests/Unit/Models/WarrantyTest.php +187 -0
  610. package/server/tests/Unit/Models/WaypointTest.php +366 -0
  611. package/server/tests/Unit/Models/WorkOrderTest.php +272 -0
  612. package/server/tests/Unit/Models/ZoneTest.php +83 -0
  613. package/server/tests/Unit/Notifications/MonitoringNotificationsTest.php +136 -0
  614. package/server/tests/Unit/Notifications/OrderAssignedChannelsTest.php +53 -0
  615. package/server/tests/Unit/Notifications/OrderDispatchedChannelsTest.php +81 -0
  616. package/server/tests/Unit/Notifications/OrderFailedChannelsTest.php +73 -0
  617. package/server/tests/Unit/Notifications/OrderPingChannelsTest.php +69 -0
  618. package/server/tests/Unit/Notifications/TerminalOrderNotificationsTest.php +185 -0
  619. package/server/tests/Unit/Observers/FleetOpsCompanyUserAndServiceAreaObserversTest.php +133 -0
  620. package/server/tests/Unit/Observers/ObserverHelperExecutionTest.php +135 -0
  621. package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +267 -0
  622. package/server/tests/Unit/Observers/PurchaseRateObserverAndOrderJobsTest.php +169 -0
  623. package/server/tests/Unit/Orchestration/Engines/DriverAssignmentEngineTest.php +393 -0
  624. package/server/tests/Unit/Orchestration/Engines/VroomOrchestrationEngineLifecycleTest.php +397 -0
  625. package/server/tests/Unit/Orchestration/OrchestrationEngineRegistryTest.php +45 -0
  626. package/server/tests/Unit/Orchestration/VroomEngineTransportAndSettingsTest.php +253 -0
  627. package/server/tests/Unit/Providers/FleetOpsAiRegistrationTest.php +59 -0
  628. package/server/tests/Unit/Rules/ResolvableVehicleTest.php +85 -0
  629. package/server/tests/Unit/Support/Ai/AssetStatusCapabilityQueriesTest.php +120 -0
  630. package/server/tests/Unit/Support/Ai/Capabilities/CreateOrderPreviewCapabilityTest.php +385 -0
  631. package/server/tests/Unit/Support/Ai/Capabilities/OrderInsightsCapabilityTest.php +226 -0
  632. package/server/tests/Unit/Support/Ai/Capabilities/SearchResourcesCapabilityTest.php +237 -0
  633. package/server/tests/Unit/Support/AiCapabilityPermissionsTest.php +124 -0
  634. package/server/tests/Unit/Support/AlgoEdgeBranchesTest.php +24 -0
  635. package/server/tests/Unit/Support/Analytics/GeofenceViolationsTest.php +109 -0
  636. package/server/tests/Unit/Support/Analytics/LiveFleetTest.php +205 -0
  637. package/server/tests/Unit/Support/Analytics/MaintenanceOverviewTest.php +236 -0
  638. package/server/tests/Unit/Support/Analytics/OnTimeDeliveryAndTopDriversTest.php +250 -0
  639. package/server/tests/Unit/Support/CreateOrderPreviewDraftingTest.php +235 -0
  640. package/server/tests/Unit/Support/CustomerAuthTest.php +132 -0
  641. package/server/tests/Unit/Support/FuelEfficiencyAndCanceledHandlersTest.php +164 -0
  642. package/server/tests/Unit/Support/FuelProviderRegistryResolutionTest.php +51 -0
  643. package/server/tests/Unit/Support/FuelProviderServiceMatchingTest.php +280 -0
  644. package/server/tests/Unit/Support/GeocodingInjectedClientTest.php +268 -0
  645. package/server/tests/Unit/Support/GeocodingLocateTest.php +32 -0
  646. package/server/tests/Unit/Support/GeofenceIntersectionServiceTest.php +188 -0
  647. package/server/tests/Unit/Support/GettingStartedTest.php +164 -0
  648. package/server/tests/Unit/Support/GuardReturnSeamsTest.php +172 -0
  649. package/server/tests/Unit/Support/IntegratedVendorsResolverTest.php +250 -0
  650. package/server/tests/Unit/Support/IssuesInsightsAndAllocationJobTest.php +156 -0
  651. package/server/tests/Unit/Support/MagicAccessorSeamsTest.php +54 -0
  652. package/server/tests/Unit/Support/MaintainableAndOperationalAlertsTest.php +215 -0
  653. package/server/tests/Unit/Support/Metrics/AvgOrderValueMetricTest.php +308 -0
  654. package/server/tests/Unit/Support/Metrics/CountMetricsTest.php +280 -0
  655. package/server/tests/Unit/Support/Metrics/FuelCostsMetricTest.php +102 -0
  656. package/server/tests/Unit/Support/Metrics/MetricQuerySeamsTest.php +129 -0
  657. package/server/tests/Unit/Support/Metrics/OrdersScheduledMetricTest.php +146 -0
  658. package/server/tests/Unit/Support/MoreQuerySeamsTest.php +410 -0
  659. package/server/tests/Unit/Support/OperationalQueryDistributionTest.php +152 -0
  660. package/server/tests/Unit/Support/OperationsPulseTest.php +130 -0
  661. package/server/tests/Unit/Support/OptimizeOrderRouteCapabilityTest.php +147 -0
  662. package/server/tests/Unit/Support/OrchestrationPayloadBuilderTest.php +135 -0
  663. package/server/tests/Unit/Support/OrchestrationRouteAndVehicleTasksTest.php +254 -0
  664. package/server/tests/Unit/Support/OsrmProofAndIssueFilterTest.php +247 -0
  665. package/server/tests/Unit/Support/PetroAppFuelProviderTest.php +165 -0
  666. package/server/tests/Unit/Support/PlaceSearchRankingTest.php +191 -0
  667. package/server/tests/Unit/Support/QuerySeamsTest.php +309 -0
  668. package/server/tests/Unit/Support/ResolvesOrderServiceStopsDbTest.php +303 -0
  669. package/server/tests/Unit/Support/ResolvesOrderServiceStopsTest.php +382 -0
  670. package/server/tests/Unit/Support/SkipAndFallbackGuardsTest.php +70 -0
  671. package/server/tests/Unit/Support/TelematicCredentialResolutionTest.php +31 -0
  672. package/server/tests/Unit/Support/TelematicServiceTest.php +256 -0
  673. package/server/tests/Unit/Support/Telematics/Providers/AbstractProviderTest.php +154 -0
  674. package/server/tests/Unit/Support/Telematics/Providers/AfaqyProviderTest.php +191 -0
  675. package/server/tests/Unit/Support/Telematics/Providers/AfaqyProviderTransportTest.php +138 -0
  676. package/server/tests/Unit/Support/Telematics/Providers/GeotabProviderTest.php +329 -0
  677. package/server/tests/Unit/Support/Telematics/Providers/ProviderTransportSeamsTest.php +133 -0
  678. package/server/tests/Unit/Support/Telematics/Providers/SafeeProviderTest.php +487 -0
  679. package/server/tests/Unit/Support/Telematics/TelematicProviderRegistryTest.php +39 -0
  680. package/server/tests/Unit/Support/Telematics/TelematicServiceLifecycleTest.php +711 -0
  681. package/server/tests/Unit/Support/Telematics/TelematicServiceTelemetryTest.php +364 -0
  682. package/server/tests/Unit/Support/UtilsAdditionalTest.php +256 -0
  683. package/server/tests/Unit/Support/UtilsGeoMatrixAndVendorTest.php +382 -0
  684. package/server/tests/Unit/Tracking/TrackingContextTest.php +114 -0
  685. package/server/tests/Unit/Tracking/TrackingIntelligenceAndContextTest.php +477 -0
  686. package/server/tests/Unit/Tracking/TrackingProviderManagerFallbacksTest.php +208 -0
  687. package/server/tests/Unit/Traits/HasTrackingNumberTest.php +303 -0
  688. package/server/tests/Unit/Traits/PayloadAccessorsTest.php +150 -0
  689. package/server/tests/UtilsContractsTest.php +244 -0
  690. package/server/tests/VehicleControllerHelperContractsTest.php +488 -0
  691. package/server/tests/VehicleDataParsingTest.php +45 -0
  692. package/server/tests/VehicleResourceContractsTest.php +313 -0
  693. package/translations/bg-bg.yaml +2 -0
  694. package/translations/en-us.yaml +2 -0
  695. package/translations/es-pa.yaml +2 -0
  696. package/translations/fr-fr.yaml +2 -0
  697. package/translations/mn-mn.yaml +2 -0
  698. package/translations/pt-br.yaml +2 -0
  699. package/translations/ru-ru.yaml +2 -0
  700. package/server/tests/DeviceFilterTest.php +0 -44
  701. package/server/tests/LiveControllerViewportTest.php +0 -52
@@ -11,6 +11,239 @@ use Illuminate\Http\Client\ConnectionException;
11
11
  use Illuminate\Support\Carbon;
12
12
  use Illuminate\Support\Facades\Http;
13
13
 
14
+ class FleetOpsAfaqyProviderProbe extends AfaqyProvider
15
+ {
16
+ public function setCredentialsForTest(array $credentials): void
17
+ {
18
+ $this->credentials = $credentials;
19
+ }
20
+
21
+ public function buildAuthenticatedRequestForTest(string $endpoint, array $payload = [], bool $tokenInQuery = false): array
22
+ {
23
+ return $this->buildAuthenticatedRequest($endpoint, $payload, $tokenInQuery);
24
+ }
25
+
26
+ public function setTokenForTest(string $token): void
27
+ {
28
+ $this->setToken($token);
29
+ }
30
+
31
+ public function canRefreshTokenForTest(): bool
32
+ {
33
+ return $this->canRefreshToken();
34
+ }
35
+
36
+ public function providerErrorMessageForTest(array $json): ?string
37
+ {
38
+ return $this->providerErrorMessage($json);
39
+ }
40
+
41
+ public function transportErrorContextForTest(string $endpoint, array $payload, ConnectionException $e, bool $retryAttempted, float $startedAt, int $timeout, int $connectTimeout): array
42
+ {
43
+ return $this->transportErrorContext($endpoint, $payload, $e, $retryAttempted, $startedAt, $timeout, $connectTimeout);
44
+ }
45
+
46
+ public function compactLastUpdateForTest(array $lastUpdate): array
47
+ {
48
+ return $this->compactLastUpdate($lastUpdate);
49
+ }
50
+
51
+ public function parseTimestampForTest($value): ?string
52
+ {
53
+ return $this->parseTimestamp($value);
54
+ }
55
+
56
+ public function normalizeSensorTypeForTest(array $payload, string $sensorName): string
57
+ {
58
+ return $this->normalizeSensorType($payload, $sensorName);
59
+ }
60
+
61
+ public function sensorIdentityPartForTest(string $value): string
62
+ {
63
+ return $this->sensorIdentityPart($value);
64
+ }
65
+ }
66
+
67
+ class FleetOpsSafeeProviderProbe extends SafeeProvider
68
+ {
69
+ public array $postCalls = [];
70
+ public array $postResponses = [];
71
+ public array $postExceptions = [];
72
+
73
+ public function setCredentialsForTest(array $credentials): void
74
+ {
75
+ $this->credentials = $credentials;
76
+ }
77
+
78
+ public function setAuthContextForTest(array $authContext): void
79
+ {
80
+ $this->authContext = $authContext;
81
+ }
82
+
83
+ public function setTelematicMetaForTest(array $meta): void
84
+ {
85
+ $telematic = new Telematic();
86
+ $telematic->meta = $meta;
87
+ $this->telematic = $telematic;
88
+ }
89
+
90
+ public function queuePostResponse(string $endpoint, array $response): void
91
+ {
92
+ $this->postResponses[$endpoint][] = $response;
93
+ }
94
+
95
+ public function queuePostException(string $endpoint, Throwable $exception): void
96
+ {
97
+ $this->postExceptions[$endpoint][] = $exception;
98
+ }
99
+
100
+ public function resolveBaseUrlForTest(): string
101
+ {
102
+ return $this->resolveBaseUrl();
103
+ }
104
+
105
+ public function filledCredentialForTest(string $key): ?string
106
+ {
107
+ return $this->filledCredential($key);
108
+ }
109
+
110
+ public function buildAuthContextForTest(string $tokenUrl): array
111
+ {
112
+ return $this->buildAuthContext($tokenUrl);
113
+ }
114
+
115
+ public function safeDiagnosticMetadataForTest(): array
116
+ {
117
+ return $this->safeDiagnosticMetadata();
118
+ }
119
+
120
+ public function resolveTelemetryWindowForTest(array $options = []): array
121
+ {
122
+ return $this->resolveTelemetryWindow($options);
123
+ }
124
+
125
+ public function currentSafeeTimestampForTest(): float
126
+ {
127
+ return $this->currentSafeeTimestamp();
128
+ }
129
+
130
+ public function resolveListInfoPayloadForTest(array $options = []): array
131
+ {
132
+ return $this->resolveListInfoPayload($options);
133
+ }
134
+
135
+ public function resolveListedVehicleIdsForTest(array $vehicles): array
136
+ {
137
+ return $this->resolveListedVehicleIds($vehicles);
138
+ }
139
+
140
+ public function summarizeVehicleIdentitiesForTest(array $vehicles, array $uniqueVehicleIds): array
141
+ {
142
+ return $this->summarizeVehicleIdentities($vehicles, $uniqueVehicleIds);
143
+ }
144
+
145
+ public function fetchLastStatesByVehicleForTest(array $vehicleIds, ?array &$endpointStats = null): array
146
+ {
147
+ return $this->fetchLastStatesByVehicle($vehicleIds, $endpointStats);
148
+ }
149
+
150
+ public function enrichVehicleSnapshotForTest(array $vehicle, array $window, array &$endpointStats): array
151
+ {
152
+ return $this->enrichVehicleSnapshot($vehicle, $window, $endpointStats);
153
+ }
154
+
155
+ public function fetchVehicleEndpointForTest(string $endpoint, array $payload, string $name, mixed $vehicleId, array &$endpointStats, mixed $default = null): mixed
156
+ {
157
+ return $this->fetchVehicleEndpoint($endpoint, $payload, $name, $vehicleId, $endpointStats, $default);
158
+ }
159
+
160
+ public function currentTelemetryPayloadForTest(array $payload): ?array
161
+ {
162
+ return $this->currentTelemetryPayload($payload);
163
+ }
164
+
165
+ public function resolveVehicleIdForTest(array $payload): mixed
166
+ {
167
+ return $this->resolveVehicleId($payload);
168
+ }
169
+
170
+ public function resolveCanonicalVehicleIdForTest(array $payload, array $identity, array $current = []): mixed
171
+ {
172
+ return $this->resolveCanonicalVehicleId($payload, $identity, $current);
173
+ }
174
+
175
+ public function resolveVehicleNameForTest(array $identity, array $current = [], mixed $vehicleId = null): string
176
+ {
177
+ return $this->resolveVehicleName($identity, $current, $vehicleId);
178
+ }
179
+
180
+ public function sanitizeProviderMessageForTest(string $message): string
181
+ {
182
+ return $this->sanitizeProviderMessage($message);
183
+ }
184
+
185
+ public function extractPositionForTest(array $payload): array
186
+ {
187
+ return $this->extractPosition($payload);
188
+ }
189
+
190
+ public function parseTimestampForTest($value): ?string
191
+ {
192
+ return $this->parseTimestamp($value);
193
+ }
194
+
195
+ public function normalizeVehicleStatusForTest(?string $status): string
196
+ {
197
+ return $this->normalizeVehicleStatus($status);
198
+ }
199
+
200
+ public function resolveOnlineForTest(array $payload): ?bool
201
+ {
202
+ return $this->resolveOnline($payload);
203
+ }
204
+
205
+ public function extractOdometerForTest(array $payload): mixed
206
+ {
207
+ return $this->extractOdometer($payload);
208
+ }
209
+
210
+ public function extractIgnitionForTest(array $payload): ?bool
211
+ {
212
+ return $this->extractIgnition($payload);
213
+ }
214
+
215
+ public function extractFuelLevelForTest(array $payload): mixed
216
+ {
217
+ return $this->extractFuelLevel($payload);
218
+ }
219
+
220
+ public function extractTelemetrySensorsForTest(array $payload): array
221
+ {
222
+ return $this->extractTelemetrySensors($payload);
223
+ }
224
+
225
+ public function extractSensorMapForTest(array $payload, array $keys): array
226
+ {
227
+ return $this->extractSensorMap($payload, $keys);
228
+ }
229
+
230
+ public function makeSafeeSensorPayloadForTest(array $sourcePayload, mixed $vehicleId, mixed $plateNo, string $type, string $name, mixed $value, ?string $unit = null): array
231
+ {
232
+ return $this->makeSafeeSensorPayload($sourcePayload, $vehicleId, $plateNo, $type, $name, $value, $unit);
233
+ }
234
+
235
+ protected function safeePost(string $endpoint, array|stdClass $payload = [], bool $dataEndpoint = false): array
236
+ {
237
+ $this->postCalls[] = compact('endpoint', 'payload', 'dataEndpoint');
238
+
239
+ if (!empty($this->postExceptions[$endpoint])) {
240
+ throw array_shift($this->postExceptions[$endpoint]);
241
+ }
242
+
243
+ return array_shift($this->postResponses[$endpoint]) ?? [];
244
+ }
245
+ }
246
+
14
247
  test('device event model and migration expose lifecycle fields used by telematics workflows', function () {
15
248
  $model = file_get_contents(__DIR__ . '/../src/Models/DeviceEvent.php');
16
249
  $migration = file_get_contents(__DIR__ . '/../migrations/2026_06_06_000001_harden_device_events_telematics_contract.php');
@@ -86,20 +319,6 @@ test('telematics service requires provider identity and stores idempotent event
86
319
  ->toContain('meta->provider_account_id');
87
320
  });
88
321
 
89
- test('device details render consistent telematics connection state and timestamp', function () {
90
- $details = file_get_contents(__DIR__ . '/../../addon/components/device/details.hbs');
91
- $header = file_get_contents(__DIR__ . '/../../addon/components/device/panel-header.hbs');
92
-
93
- expect($details)
94
- ->toContain('format-date-fns this.lastSeenLabel "dd MMM yyyy, HH:mm"')
95
- ->not->toContain('format-date @resource.last_online_at');
96
-
97
- expect($header)
98
- ->toContain('this.connectionStatus')
99
- ->toContain('this.lastOnlineAt')
100
- ->not->toContain('@resource.online "online"');
101
- });
102
-
103
322
  test('native providers normalize device payloads to canonical FleetOps keys', function () {
104
323
  $providers = [
105
324
  file_get_contents(__DIR__ . '/../src/Support/Telematics/Providers/AfaqyProvider.php'),
@@ -766,6 +985,227 @@ test('safee sensors normalize stable parent scoped identities and latest values'
766
985
  expect($closed['value'])->toBe('Closed');
767
986
  });
768
987
 
988
+ test('safee helper methods normalize credentials metadata and sync windows', function () {
989
+ Carbon::setTestNow(Carbon::parse('2026-06-23T09:15:40.250000Z'));
990
+
991
+ try {
992
+ $provider = new FleetOpsSafeeProviderProbe();
993
+ $provider->setCredentialsForTest([
994
+ 'api_base_url' => ' https://tenant.safee.test/ ',
995
+ 'server_uri' => 'https://ignored.safee.test',
996
+ 'realm_id' => 'fleet-realm',
997
+ 'blank' => ' ',
998
+ 'array_value' => ['not-string'],
999
+ ]);
1000
+ $provider->setAuthContextForTest([
1001
+ 'auth_host' => 'https://auth.safee.test',
1002
+ 'auth_path' => '',
1003
+ 'realm_id' => 'fleet-realm',
1004
+ ]);
1005
+ $provider->setTelematicMetaForTest([
1006
+ 'safee_last_telemetry_synced_at' => 1782206000.5,
1007
+ ]);
1008
+
1009
+ expect($provider->resolveBaseUrlForTest())->toBe('https://tenant.safee.test')
1010
+ ->and($provider->filledCredentialForTest('blank'))->toBeNull()
1011
+ ->and($provider->filledCredentialForTest('array_value'))->toBeNull()
1012
+ ->and($provider->buildAuthContextForTest('https://auth.safee.test/auth/realms/fleet/protocol/openid-connect/token'))->toBe([
1013
+ 'auth_host' => 'https://auth.safee.test',
1014
+ 'auth_path' => '/auth/realms/fleet/protocol/openid-connect/token',
1015
+ 'realm_id' => 'fleet-realm',
1016
+ ])
1017
+ ->and($provider->safeDiagnosticMetadataForTest())->toBe([
1018
+ 'auth_host' => 'https://auth.safee.test',
1019
+ 'realm_id' => 'fleet-realm',
1020
+ ])
1021
+ ->and($provider->currentSafeeTimestampForTest())->toBe(1782206140.25)
1022
+ ->and($provider->resolveTelemetryWindowForTest())->toBe([
1023
+ 'startDate' => 1782205880.5,
1024
+ 'endDate' => 1782206140.25,
1025
+ ])
1026
+ ->and($provider->resolveTelemetryWindowForTest([
1027
+ 'start_date' => -50,
1028
+ 'end_date' => 1782206100.5,
1029
+ ]))->toBe([
1030
+ 'startDate' => 0,
1031
+ 'endDate' => 1782206100.5,
1032
+ ])
1033
+ ->and($provider->resolveListInfoPayloadForTest([
1034
+ 'filter' => (object) ['status' => 'ACTIVE'],
1035
+ 'page_size' => 25,
1036
+ 'page_index' => 2,
1037
+ ]))->toBe([
1038
+ 'status' => 'ACTIVE',
1039
+ 'pageSize' => 25,
1040
+ 'pageIndex' => 2,
1041
+ ]);
1042
+ } finally {
1043
+ Carbon::setTestNow();
1044
+ }
1045
+ });
1046
+
1047
+ test('safee endpoint helpers account for identities enrichment and failures', function () {
1048
+ $provider = new FleetOpsSafeeProviderProbe();
1049
+ $vehicles = [
1050
+ ['id' => 105],
1051
+ ['_safee' => ['vehicle_id' => 106]],
1052
+ ['_safee' => ['identity' => ['id' => 105]]],
1053
+ ['plateNo' => 'missing'],
1054
+ 'not-array',
1055
+ ];
1056
+
1057
+ $ids = $provider->resolveListedVehicleIdsForTest($vehicles);
1058
+
1059
+ expect($ids)->toBe([105, 106])
1060
+ ->and($provider->summarizeVehicleIdentitiesForTest($vehicles, $ids))->toBe([
1061
+ 'unique_vehicle_ids' => 2,
1062
+ 'missing_vehicle_ids' => 2,
1063
+ 'duplicate_vehicle_ids' => ['105' => 2],
1064
+ ]);
1065
+
1066
+ $provider->queuePostResponse('/api/v2/vehicle/last-state', [
1067
+ 'result' => [
1068
+ ['vehicle' => ['id' => 105], 'status' => 'online'],
1069
+ 'ignored',
1070
+ ['id' => 106, 'status' => 'offline'],
1071
+ ],
1072
+ ]);
1073
+ $stats = ['failures' => []];
1074
+
1075
+ $lastStates = $provider->fetchLastStatesByVehicleForTest([105, 106], $stats);
1076
+
1077
+ expect($lastStates['105']['status'])->toBe('online')
1078
+ ->and($lastStates['106']['status'])->toBe('offline');
1079
+
1080
+ $provider->queuePostException('/api/v2/vehicle/last-state', new RuntimeException('token=abc123 password=secret failed'));
1081
+ $failedStats = ['failures' => []];
1082
+
1083
+ expect($provider->fetchLastStatesByVehicleForTest([107], $failedStats))->toBe([])
1084
+ ->and($failedStats['failures'][0])->toMatchArray([
1085
+ 'endpoint' => '/api/v2/vehicle/last-state',
1086
+ 'vehicle_id' => null,
1087
+ 'message' => 'token=[redacted] password=[redacted] failed',
1088
+ ]);
1089
+
1090
+ $provider->queuePostResponse('/api/v2/vehicle/positions', [
1091
+ 'result' => [
1092
+ ['id' => 'p1'],
1093
+ ['id' => 'p2'],
1094
+ ],
1095
+ ]);
1096
+ $endpointStats = [
1097
+ 'last_info_fetched' => 0,
1098
+ 'positions_fetched' => 0,
1099
+ 'events_fetched' => 0,
1100
+ 'failures' => [],
1101
+ ];
1102
+
1103
+ expect($provider->fetchVehicleEndpointForTest('/api/v2/vehicle/positions', ['vehicleId' => 105], 'positions', 105, $endpointStats, []))->toBe([
1104
+ ['id' => 'p1'],
1105
+ ['id' => 'p2'],
1106
+ ])
1107
+ ->and($endpointStats['positions_fetched'])->toBe(2)
1108
+ ->and($provider->fetchVehicleEndpointForTest('/api/v2/vehicle/events', ['vehicleId' => null], 'events', null, $endpointStats, []))->toBe([]);
1109
+
1110
+ $provider->queuePostResponse('/api/v2/vehicle/last-info', [
1111
+ 'result' => [
1112
+ 'id' => 105,
1113
+ 'plateNo' => 'ABC-1234',
1114
+ 'date' => 1782206140.5,
1115
+ 'temperaturePerType' => ['Cargo' => 4.5],
1116
+ 'doorPerType' => ['Rear' => 'Closed'],
1117
+ 'humidityPerType' => ['Cabin' => 44],
1118
+ ],
1119
+ ]);
1120
+ $provider->queuePostException('/api/v2/vehicle/positions', new RuntimeException('client_secret=secret failed'));
1121
+ $provider->queuePostResponse('/api/v2/vehicle/events', ['result' => [['id' => 'event-1']]]);
1122
+
1123
+ $snapshotStats = [
1124
+ 'last_info_fetched' => 0,
1125
+ 'positions_fetched' => 0,
1126
+ 'events_fetched' => 0,
1127
+ 'failures' => [],
1128
+ ];
1129
+ $snapshot = $provider->enrichVehicleSnapshotForTest(['id' => 105, 'plateNo' => 'ABC-1234'], [
1130
+ 'startDate' => 1782206000.5,
1131
+ 'endDate' => 1782206140.5,
1132
+ ], $snapshotStats);
1133
+
1134
+ expect($snapshot['_safee']['current_info']['id'])->toBe(105)
1135
+ ->and($snapshot['_safee']['positions'])->toBe([])
1136
+ ->and($snapshot['_safee']['events'])->toBe([['id' => 'event-1']])
1137
+ ->and($snapshot['sensors'])->toHaveCount(3)
1138
+ ->and($snapshotStats)->toMatchArray([
1139
+ 'last_info_fetched' => 1,
1140
+ 'events_fetched' => 1,
1141
+ 'failures' => [
1142
+ [
1143
+ 'endpoint' => '/api/v2/vehicle/positions',
1144
+ 'vehicle_id' => 105,
1145
+ 'message' => 'client_secret=[redacted] failed',
1146
+ ],
1147
+ ],
1148
+ ]);
1149
+ });
1150
+
1151
+ test('safee normalization helpers cover status position sensor and timestamp variants', function () {
1152
+ $provider = new FleetOpsSafeeProviderProbe();
1153
+
1154
+ expect($provider->currentTelemetryPayloadForTest([
1155
+ '_safee' => [
1156
+ 'current_state' => ['status' => 'offline', 'speed' => 10],
1157
+ 'current_info' => ['speed' => 12, 'odometer' => 500],
1158
+ ],
1159
+ ]))->toBe([
1160
+ 'status' => 'offline',
1161
+ 'speed' => 12,
1162
+ 'odometer' => 500,
1163
+ ])
1164
+ ->and($provider->currentTelemetryPayloadForTest(['_safee' => ['current_info' => ['speed' => 7]]]))->toBe(['speed' => 7])
1165
+ ->and($provider->currentTelemetryPayloadForTest(['_safee' => ['current_state' => ['speed' => 6]]]))->toBe(['speed' => 6])
1166
+ ->and($provider->currentTelemetryPayloadForTest(['_safee' => []]))->toBeNull()
1167
+ ->and($provider->resolveVehicleIdForTest(['_safee' => ['vehicle_id' => 105]]))->toBe(105)
1168
+ ->and($provider->resolveVehicleIdForTest(['vehicle' => ['id' => 106]]))->toBe(106)
1169
+ ->and($provider->resolveCanonicalVehicleIdForTest([], ['_safee' => ['identity' => ['id' => 107]]]))->toBe(107)
1170
+ ->and($provider->resolveVehicleNameForTest([], ['vehicle' => ['name' => 'Truck Name']], null))->toBe('Truck Name')
1171
+ ->and($provider->resolveVehicleNameForTest([], [], 108))->toBe('Safee Vehicle 108')
1172
+ ->and($provider->resolveVehicleNameForTest([], [], null))->toBe('Unknown Safee Vehicle')
1173
+ ->and($provider->sanitizeProviderMessageForTest('access_token=abc&password=secret client_secret=top'))->toBe('access_token=[redacted]&password=[redacted] client_secret=[redacted]')
1174
+ ->and($provider->extractPositionForTest(['loc' => ['coordinates' => [55.2, 25.2]]]))->toBe(['lat' => 25.2, 'lng' => 55.2])
1175
+ ->and($provider->parseTimestampForTest(null))->toBeNull()
1176
+ ->and($provider->parseTimestampForTest(1782206140500))->toBe(Carbon::createFromTimestamp(1782206140.5)->toDateTimeString())
1177
+ ->and($provider->parseTimestampForTest('2026-06-23T09:15:40Z'))->toBe('2026-06-23 09:15:40')
1178
+ ->and($provider->normalizeVehicleStatusForTest(null))->toBe('active')
1179
+ ->and($provider->normalizeVehicleStatusForTest('expired'))->toBe('inactive')
1180
+ ->and($provider->normalizeVehicleStatusForTest('moving'))->toBe('active')
1181
+ ->and($provider->resolveOnlineForTest(['online' => 'false']))->toBeFalse()
1182
+ ->and($provider->resolveOnlineForTest(['online' => 'not-bool']))->toBeTrue()
1183
+ ->and($provider->resolveOnlineForTest(['vehicleStatus' => 'deleted']))->toBeFalse()
1184
+ ->and($provider->resolveOnlineForTest([]))->toBeNull()
1185
+ ->and($provider->extractOdometerForTest(['canbus' => ['odometer' => 10]]))->toBe(10)
1186
+ ->and($provider->extractOdometerForTest(['vehicleCanbus' => ['odometer' => 11]]))->toBe(11)
1187
+ ->and($provider->extractIgnitionForTest(['event' => ['code' => 'ignition_on']]))->toBeTrue()
1188
+ ->and($provider->extractIgnitionForTest(['event' => ['code' => 'ignition_off']]))->toBeFalse()
1189
+ ->and($provider->extractIgnitionForTest(['ignition' => '1']))->toBeTrue()
1190
+ ->and($provider->extractIgnitionForTest([]))->toBeNull()
1191
+ ->and($provider->extractFuelLevelForTest(['fuel' => ['level' => 80]]))->toBe(80)
1192
+ ->and($provider->extractFuelLevelForTest(['vehicleFuel' => ['level' => 81]]))->toBe(81)
1193
+ ->and($provider->extractSensorMapForTest(['temperaturePerType' => ['Cargo' => 4.5]], ['temperature', 'temperaturePerType']))->toBe(['Cargo' => 4.5])
1194
+ ->and($provider->extractSensorMapForTest([], ['temperature']))->toBe([]);
1195
+
1196
+ $sensorPayload = $provider->makeSafeeSensorPayloadForTest([
1197
+ 'deviceTime' => '2026-06-23T09:15:40Z',
1198
+ ], null, null, 'door', 'Rear', 'Open');
1199
+
1200
+ expect($sensorPayload)->toMatchArray([
1201
+ 'internal_id' => 'safee:unknown_vehicle:door:Rear',
1202
+ 'external_id' => 'safee:unknown_vehicle:door:Rear',
1203
+ 'recorded_at' => '2026-06-23T09:15:40Z',
1204
+ 'deviceTime' => '2026-06-23T09:15:40Z',
1205
+ 'source' => 'door',
1206
+ ]);
1207
+ });
1208
+
769
1209
  test('geotab latest log record drives device and event telemetry', function () {
770
1210
  $payload = [
771
1211
  'id' => 'device-1',
@@ -886,26 +1326,6 @@ test('geotab polling fetches recent log records and merges latest record into de
886
1326
  ]);
887
1327
  })->skip('Requires isolated Laravel HTTP client fake state in the full application harness.');
888
1328
 
889
- test('telematics details use public id for consumer webhook URLs and do not read ember uuid', function () {
890
- $component = file_get_contents(__DIR__ . '/../../addon/components/telematic/details.js');
891
- $template = file_get_contents(__DIR__ . '/../../addon/components/telematic/details.hbs');
892
-
893
- expect($component)
894
- ->toContain('const id = this.args.resource?.public_id;')
895
- ->toContain('return null;')
896
- ->not->toContain('this.args.resource?.uuid')
897
- ->not->toContain('this.args.resource?.public_id ?? this.args.resource?.id');
898
-
899
- expect($template)
900
- ->toContain('this.hasWebhookUrl')
901
- ->toContain('Webhook URL unavailable until this integration has a public ID.')
902
- ->toContain('Provider polling');
903
-
904
- expect($component)
905
- ->toContain('last_sync_job_id')
906
- ->toContain('last_sync_error');
907
- });
908
-
909
1329
  test('native telematics providers expose local provider icons with a descriptor fallback', function () {
910
1330
  $config = include __DIR__ . '/../config/telematics.php';
911
1331
  $iconPath = '/engines-dist/images/telematics/providers/';
@@ -1073,6 +1493,175 @@ test('afaqy sensors reject event shaped payloads without scalar sensor values',
1073
1493
  ]))->toThrow(InvalidArgumentException::class);
1074
1494
  });
1075
1495
 
1496
+ test('afaqy provider helpers build authenticated requests and sanitized diagnostics', function () {
1497
+ Carbon::setTestNow(Carbon::parse('2026-06-23 10:00:00'));
1498
+
1499
+ try {
1500
+ $provider = new FleetOpsAfaqyProviderProbe();
1501
+ $provider->setCredentialsForTest([
1502
+ 'token' => 'static-token',
1503
+ 'username' => 'user',
1504
+ 'password' => 'secret',
1505
+ ]);
1506
+
1507
+ [$queryUrl, $queryBody] = $provider->buildAuthenticatedRequestForTest('/units/lists?existing=1', [
1508
+ 'data' => ['limit' => 25, 'offset' => 50],
1509
+ ], true);
1510
+ [$bodyUrl, $body] = $provider->buildAuthenticatedRequestForTest('/units/view', [
1511
+ 'data' => ['id' => 'unit-1'],
1512
+ ]);
1513
+
1514
+ $provider->setTokenForTest('fresh-token');
1515
+
1516
+ expect($queryUrl)->toBe('https://api.afaqy.sa/units/lists?existing=1&token=static-token')
1517
+ ->and($queryBody)->toBe(['data' => ['limit' => 25, 'offset' => 50]])
1518
+ ->and($bodyUrl)->toBe('https://api.afaqy.sa/units/view')
1519
+ ->and($body)->toBe(['token' => 'static-token', 'data' => ['id' => 'unit-1']])
1520
+ ->and($provider->canRefreshTokenForTest())->toBeTrue()
1521
+ ->and($provider->providerErrorMessageForTest(['error' => ['message' => 'Nested failure']]))->toBe('Nested failure')
1522
+ ->and($provider->providerErrorMessageForTest(['error_description' => 'Description failure']))->toBe('Description failure')
1523
+ ->and($provider->providerErrorMessageForTest(['error' => ['not_scalar' => true]]))->toBeNull()
1524
+ ->and($provider->parseTimestampForTest(null))->toBeNull()
1525
+ ->and($provider->parseTimestampForTest(1719136800000))->toBe('2024-06-23 10:00:00')
1526
+ ->and($provider->parseTimestampForTest('2026-06-23T09:00:00Z'))->toBe('2026-06-23 09:00:00')
1527
+ ->and($provider->compactLastUpdateForTest([
1528
+ 'dtt' => '2026-06-23T09:00:00Z',
1529
+ 'lat' => 25.2,
1530
+ 'lng' => 55.2,
1531
+ 'speed' => 64,
1532
+ 'angle' => 180,
1533
+ 'alt' => 12,
1534
+ 'params' => ['sat' => 8, 'protocol' => 'wialon'],
1535
+ ]))->toMatchArray([
1536
+ 'occurred_at' => '2026-06-23 09:00:00',
1537
+ 'lat' => 25.2,
1538
+ 'lng' => 55.2,
1539
+ 'speed' => 64,
1540
+ 'heading' => 180,
1541
+ 'altitude' => 12,
1542
+ 'satellites' => 8,
1543
+ 'protocol' => 'wialon',
1544
+ ])
1545
+ ->and($provider->transportErrorContextForTest('/units/lists', [
1546
+ 'data' => ['limit' => 500, 'offset' => 100],
1547
+ ], new ConnectionException('timeout with 12345 bytes received'), true, microtime(true) - 0.25, 120, 15))->toMatchArray([
1548
+ 'provider' => 'afaqy',
1549
+ 'endpoint' => '/units/lists',
1550
+ 'requested_limit' => 500,
1551
+ 'requested_offset' => 100,
1552
+ 'timeout' => 120,
1553
+ 'connect_timeout' => 15,
1554
+ 'bytes_received' => 12345,
1555
+ 'retry_attempted' => true,
1556
+ 'transport_error' => 'connection_exception',
1557
+ ]);
1558
+ } finally {
1559
+ Carbon::setTestNow();
1560
+ }
1561
+ });
1562
+
1563
+ test('afaqy normalization covers device event and sensor type variants', function () {
1564
+ $provider = new FleetOpsAfaqyProviderProbe();
1565
+
1566
+ $payload = [
1567
+ '_id' => 'unit-123',
1568
+ 'name' => null,
1569
+ 'imei' => 'imei-123',
1570
+ 'sim_number' => 'sim-123',
1571
+ 'device_serial' => 'serial-123',
1572
+ 'active' => true,
1573
+ 'driver_id' => 'driver-provider-id',
1574
+ 'device' => 'tracker-model',
1575
+ 'profile' => [
1576
+ 'plate_number' => 'ABC-123',
1577
+ 'vehicle_type' => 'truck',
1578
+ 'fuel_type' => 'diesel',
1579
+ 'vin' => 'vin-123',
1580
+ ],
1581
+ 'counters' => [
1582
+ 'odometer' => 12345,
1583
+ 'last_acc' => '1',
1584
+ ],
1585
+ 'last_update' => [
1586
+ 'dtt' => '2026-06-23T09:00:00Z',
1587
+ 'lat' => 25.2,
1588
+ 'lng' => 55.2,
1589
+ 'speed' => 64,
1590
+ 'angle' => 180,
1591
+ 'alt' => 12,
1592
+ 'params' => ['fuel' => 73, 'acc' => 'true'],
1593
+ ],
1594
+ ];
1595
+
1596
+ $device = $provider->normalizeDevice($payload);
1597
+ $event = $provider->normalizeEvent($payload + [
1598
+ 'event' => 'ignition_on',
1599
+ 'message' => 'Ignition on',
1600
+ ]);
1601
+
1602
+ expect($device)->toMatchArray([
1603
+ 'device_id' => 'unit-123',
1604
+ 'external_id' => 'unit-123',
1605
+ 'name' => 'ABC-123',
1606
+ 'provider' => 'afaqy',
1607
+ 'model' => 'tracker-model',
1608
+ 'imei' => 'imei-123',
1609
+ 'phone' => 'sim-123',
1610
+ 'vin' => 'vin-123',
1611
+ 'status' => 'active',
1612
+ 'online' => true,
1613
+ 'last_seen_at' => '2026-06-23 09:00:00',
1614
+ 'location' => ['lat' => 25.2, 'lng' => 55.2],
1615
+ ])
1616
+ ->and(data_get($device, 'meta.capabilities'))->toBe([
1617
+ 'tracking' => true,
1618
+ 'odometer' => true,
1619
+ 'fuel_level' => true,
1620
+ 'ignition_state' => true,
1621
+ ])
1622
+ ->and($event)->toMatchArray([
1623
+ 'external_id' => 'unit-123',
1624
+ 'device_id' => 'unit-123',
1625
+ 'event_type' => 'ignition_on',
1626
+ 'message' => 'Ignition on',
1627
+ 'occurred_at' => '2026-06-23 09:00:00',
1628
+ 'online' => true,
1629
+ 'location' => ['lat' => 25.2, 'lng' => 55.2],
1630
+ 'speed' => 64,
1631
+ 'heading' => 180,
1632
+ 'altitude' => 12,
1633
+ 'odometer' => 12345,
1634
+ 'ignition' => true,
1635
+ 'fuel_level' => 73,
1636
+ ]);
1637
+
1638
+ expect($provider->normalizeSensorTypeForTest(['type' => 'temperature'], 'Cargo temperature'))->toBe('temperature')
1639
+ ->and($provider->normalizeSensorTypeForTest(['type' => 'humidity'], 'Cabin humidity'))->toBe('humidity')
1640
+ ->and($provider->normalizeSensorTypeForTest(['param' => 'di2'], 'Digital input 2'))->toBe('digital')
1641
+ ->and($provider->normalizeSensorTypeForTest(['param' => 'ai1'], 'Analog input 1'))->toBe('analog')
1642
+ ->and($provider->normalizeSensorTypeForTest(['type' => 'battery'], 'Battery voltage'))->toBe('voltage')
1643
+ ->and($provider->normalizeSensorTypeForTest(['type' => 'custom sensor'], 'Custom Sensor'))->toBe('custom_sensor_custom_sensor')
1644
+ ->and($provider->sensorIdentityPartForTest(' Sensor / Name #1 '))->toBe('sensor_name_1');
1645
+
1646
+ $sensor = $provider->normalizeSensor([
1647
+ 'unit_id' => 'unit-123',
1648
+ 'id' => 'temp-1',
1649
+ 'sensor' => ['name' => 'Temperature 1'],
1650
+ 'last_update' => [
1651
+ 'value' => 4.5,
1652
+ 'dtt' => '2026-06-23T09:10:00Z',
1653
+ ],
1654
+ ]);
1655
+
1656
+ expect($sensor)->toMatchArray([
1657
+ 'internal_id' => 'afaqy:unit-123:temp_1',
1658
+ 'name' => 'Temperature 1',
1659
+ 'type' => 'temperature',
1660
+ 'value' => 4.5,
1661
+ 'recorded_at' => '2026-06-23 09:10:00',
1662
+ ]);
1663
+ });
1664
+
1076
1665
  test('afaqy bad sensor cleanup migration targets only afaqy telematics sensors', function () {
1077
1666
  $migration = file_get_contents(__DIR__ . '/../migrations/2026_06_23_000001_delete_bad_afaqy_telematics_sensors.php');
1078
1667
 
@@ -1315,7 +1904,6 @@ test('telematics device sync records provider pagination and skipped device coun
1315
1904
  test('telematics polling command is registered and scheduled for discovery providers by default', function () {
1316
1905
  $command = file_get_contents(__DIR__ . '/../src/Console/Commands/SyncTelematics.php');
1317
1906
  $provider = file_get_contents(__DIR__ . '/../src/Providers/FleetOpsServiceProvider.php');
1318
- $details = file_get_contents(__DIR__ . '/../../addon/components/telematic/details.hbs');
1319
1907
 
1320
1908
  expect($command)
1321
1909
  ->toContain("protected \$signature = 'fleetops:sync-telematics")
@@ -1330,10 +1918,6 @@ test('telematics polling command is registered and scheduled for discovery provi
1330
1918
  expect($provider)
1331
1919
  ->toContain('Console\\Commands\\SyncTelematics::class')
1332
1920
  ->toContain("command('fleetops:sync-telematics')->everyMinute()");
1333
-
1334
- expect($details)
1335
- ->toContain('Provider polling')
1336
- ->toContain('Fleet-Ops polls this provider for device snapshots and telemetry updates.');
1337
1921
  });
1338
1922
 
1339
1923
  test('native endpoint fields are advanced optional overrides with provider defaults', function () {
@@ -1465,27 +2049,6 @@ test('telematics activity logging excludes large json and spatial payloads', fun
1465
2049
  ->not->toContain("'location',");
1466
2050
  });
1467
2051
 
1468
- test('telematics setup renders endpoint overrides only in advanced settings', function () {
1469
- $formTemplate = file_get_contents(__DIR__ . '/../../addon/components/telematic/form.hbs');
1470
- $settingsTemplate = file_get_contents(__DIR__ . '/../../addon/components/telematic/settings.hbs');
1471
- $formComponent = file_get_contents(__DIR__ . '/../../addon/components/telematic/form.js');
1472
- $settingsComponent = file_get_contents(__DIR__ . '/../../addon/components/telematic/settings.js');
1473
-
1474
- foreach ([$formComponent, $settingsComponent] as $component) {
1475
- expect($component)
1476
- ->toContain('advancedCredentialFields')
1477
- ->toContain('field.advanced || field.is_endpoint')
1478
- ->toContain('!field.advanced && !field.is_endpoint');
1479
- }
1480
-
1481
- foreach ([$formTemplate, $settingsTemplate] as $template) {
1482
- expect($template)
1483
- ->toContain('Advanced connection settings')
1484
- ->toContain('this.advancedCredentialFields')
1485
- ->toContain('field.default_value');
1486
- }
1487
- });
1488
-
1489
2052
  test('telematics details logs are routed and use persisted safe data only', function () {
1490
2053
  $routes = file_get_contents(__DIR__ . '/../src/routes.php');
1491
2054
  $controller = file_get_contents(__DIR__ . '/../src/Http/Controllers/Internal/v1/TelematicController.php');
@@ -1510,32 +2073,6 @@ test('telematics details logs are routed and use persisted safe data only', func
1510
2073
  ->not->toContain('Log::');
1511
2074
  });
1512
2075
 
1513
- test('telematics provider status display maps active and connected to connected', function () {
1514
- $indexController = file_get_contents(__DIR__ . '/../../addon/controllers/connectivity/telematics/index.js');
1515
- $detailsController = file_get_contents(__DIR__ . '/../../addon/controllers/connectivity/telematics/details.js');
1516
- $statusCell = file_get_contents(__DIR__ . '/../../addon/components/cell/telematic-status.js');
1517
-
1518
- expect($indexController)
1519
- ->toContain("cellComponent: 'cell/telematic-status'");
1520
-
1521
- expect($detailsController)
1522
- ->toContain("case 'active':\n return 'Connected';");
1523
-
1524
- expect($statusCell)
1525
- ->toContain("case 'active':")
1526
- ->toContain("case 'connected':")
1527
- ->toContain("return 'Connected';");
1528
- });
1529
-
1530
- test('telematics overview attention items render as full width stacked alerts', function () {
1531
- $template = file_get_contents(__DIR__ . '/../../addon/components/telematic/details.hbs');
1532
-
1533
- expect($template)
1534
- ->toContain('<section class="flex flex-col gap-3">')
1535
- ->toContain('class="w-full rounded-md border border-yellow-200 bg-yellow-50 p-3')
1536
- ->not->toContain('lg:grid-cols-2');
1537
- });
1538
-
1539
2076
  test('device attachment morph types are normalized and legacy aliases are tolerated', function () {
1540
2077
  $migration = file_get_contents(__DIR__ . '/../migrations/2026_06_15_000001_normalize_device_attachable_vehicle_morph_types.php');
1541
2078
  $command = file_get_contents(__DIR__ . '/../src/Console/Commands/FixInvalidPolymorphicRelationTypeNamespaces.php');
@@ -1589,3 +2126,22 @@ function telematics_activity_log_method(string $model): string
1589
2126
 
1590
2127
  return $matches['body'] ?? '';
1591
2128
  }
2129
+
2130
+ if (!function_exists('Fleetbase\FleetOps\Models\activity')) {
2131
+ eval('namespace Fleetbase\FleetOps\Models; function activity($logName = null) { $GLOBALS["fleetopsTelematicActivities"][] = $logName; return new class { public array $properties = []; public function performedOn($subject) { return $this; } public function withProperties(array $properties) { $this->properties = $properties; $GLOBALS["fleetopsTelematicActivityProps"][] = $properties; return $this; } public function log(string $message) { $GLOBALS["fleetopsTelematicActivityLogs"][] = $message; return true; } }; }');
2132
+ }
2133
+
2134
+ test('telematic send command logs the activity and reports success', function () {
2135
+ $GLOBALS['fleetopsTelematicActivities'] = [];
2136
+ $GLOBALS['fleetopsTelematicActivityProps'] = [];
2137
+ $GLOBALS['fleetopsTelematicActivityLogs'] = [];
2138
+
2139
+ $telematic = new Telematic();
2140
+ $telematic->setRawAttributes(['uuid' => 'telematic-cmd-1', 'company_uuid' => 'company-1', 'provider' => 'flespi'], true);
2141
+
2142
+ expect($telematic->sendCommand('reboot', ['delay' => 5]))->toBeTrue()
2143
+ ->and($GLOBALS['fleetopsTelematicActivities'])->toBe(['telematic_command'])
2144
+ ->and($GLOBALS['fleetopsTelematicActivityProps'][0]['command'])->toBe('reboot')
2145
+ ->and($GLOBALS['fleetopsTelematicActivityProps'][0]['parameters'])->toBe(['delay' => 5])
2146
+ ->and($GLOBALS['fleetopsTelematicActivityLogs'][0])->toContain('reboot');
2147
+ });