@fleetbase/fleetops-engine 0.6.67 → 0.6.69
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.
- package/RELEASE.md +11 -15
- package/addon/components/contact/form.hbs +13 -2
- package/addon/components/customer/details.hbs +17 -17
- package/addon/components/customer/form.hbs +13 -2
- package/addon/components/customer/form.js +0 -56
- package/addon/components/driver/details.hbs +13 -19
- package/addon/components/driver/form.hbs +20 -35
- package/addon/components/driver/form.js +0 -58
- package/addon/components/fuel-integration/form.hbs +2 -2
- package/addon/components/fuel-integration/form.js +8 -0
- package/addon/components/fuel-integration/hub.js +1 -1
- package/addon/components/modals/driver-form.hbs +2 -2
- package/addon/components/modals/reset-profile-credentials.hbs +23 -0
- package/addon/components/modals/reset-profile-credentials.js +70 -0
- package/addon/components/profile-identity-hint.hbs +13 -0
- package/addon/components/profile-identity-hint.js +49 -0
- package/addon/controllers/management/drivers/index.js +4 -0
- package/addon/extension.js +4 -2
- package/addon/services/contact-actions.js +50 -89
- package/addon/services/driver-actions.js +98 -0
- package/addon/templates/connectivity/fuel-providers/details/index.hbs +1 -1
- package/addon/utils/fuel-transaction.js +2 -0
- package/addon/utils/profile-login-actions.js +172 -0
- package/app/components/modals/{reset-customer-credentials.js → reset-profile-credentials.js} +1 -1
- package/app/components/profile-identity-hint.js +1 -0
- package/composer.json +1 -1
- package/docs/SAFEE.md +5 -0
- package/docs/TELEMATICS_QUEUES.md +59 -8
- package/extension.json +1 -1
- package/package.json +3 -3
- package/scripts/pest-bootstrap.php +12 -0
- package/server/config/fuel-providers.php +53 -0
- package/server/migrations/2026_09_21_000001_convert_profile_only_users_to_managed_accounts.php +132 -0
- package/server/resources/views/mail/driver-credentials.blade.php +21 -0
- package/server/src/Console/Commands/FixCustomerCompanies.php +1 -1
- package/server/src/Console/Commands/FixDriverCompanies.php +1 -1
- package/server/src/Exceptions/ProfileIdentityConflictException.php +34 -0
- package/server/src/Http/Controllers/Api/v1/CustomerController.php +14 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +56 -39
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +5 -17
- package/server/src/Http/Controllers/Internal/v1/CustomerController.php +40 -4
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +182 -162
- package/server/src/Http/Controllers/Internal/v1/FleetOpsLookupController.php +47 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +156 -27
- package/server/src/Http/Requests/CreateDriverRequest.php +6 -2
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +9 -18
- package/server/src/Http/Resources/v1/Contact.php +3 -0
- package/server/src/Http/Resources/v1/Driver.php +3 -0
- package/server/src/Jobs/PollTelematicTelemetry.php +36 -0
- package/server/src/Jobs/SyncTelematicDevicesJob.php +2 -2
- package/server/src/Mail/DriverCredentialsMail.php +55 -0
- package/server/src/Models/Contact.php +55 -90
- package/server/src/Models/Sensor.php +3 -5
- package/server/src/Observers/ContactObserver.php +2 -22
- package/server/src/Observers/DriverObserver.php +5 -6
- package/server/src/Providers/FleetOpsServiceProvider.php +15 -0
- package/server/src/Support/Ai/Capabilities/AbstractFleetOpsAICapability.php +62 -5
- package/server/src/Support/Ai/Capabilities/AssetStatusCapability.php +24 -5
- package/server/src/Support/Ai/Capabilities/CreateOrderPreviewCapability.php +6 -2
- package/server/src/Support/Ai/Capabilities/OrderInsightsCapability.php +40 -7
- package/server/src/Support/Ai/Capabilities/SearchResourcesCapability.php +86 -24
- package/server/src/Support/Ai/FleetOpsAiConsoleCommands.php +122 -0
- package/server/src/Support/Ai/Tools/CreateOrderTool.php +86 -0
- package/server/src/Support/Ai/Tools/ImportOrdersTool.php +53 -0
- package/server/src/Support/Ai/Tools/OptimizeOrderRouteTool.php +70 -0
- package/server/src/Support/Ai/Tools/SearchResourcesTool.php +73 -0
- package/server/src/Support/FuelProviders/Providers/SascoFuelProvider.php +272 -0
- package/server/src/Support/ProfileAccountManager.php +405 -0
- package/server/src/Support/Telematics/TelematicService.php +6 -4
- package/server/src/Support/Telematics/Telemetry/Inbox.php +27 -9
- package/server/src/routes.php +5 -0
- package/server/tests/AiOperationalQueryCapabilityTest.php +1 -0
- package/server/tests/ApiCustomerControllerContractsTest.php +21 -0
- package/server/tests/ApiDriverControllerContractsTest.php +102 -20
- package/server/tests/CommandContractsTest.php +4 -4
- package/server/tests/ControllerHelperContractsTest.php +3 -11
- package/server/tests/Feature/Http/AfaqyRealtimeIngestionTest.php +5 -0
- package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +36 -0
- package/server/tests/Feature/Http/Api/DriverControllerSessionHelpersTest.php +19 -6
- package/server/tests/Feature/Http/Api/ProfileOnlyUserConversionMigrationTest.php +201 -0
- package/server/tests/Feature/Http/Internal/ContactControllerEndpointsTest.php +6 -4
- package/server/tests/Feature/Http/Internal/CustomerControllerCredentialsTest.php +6 -2
- package/server/tests/Feature/Http/Internal/DriverControllerContractsTest.php +23 -0
- package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +42 -17
- package/server/tests/Feature/Http/Internal/DriverControllerExistingUserAdoptionTest.php +168 -277
- package/server/tests/Feature/Http/Internal/DriverControllerLoginManagementTest.php +716 -0
- package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +10 -3
- package/server/tests/Feature/Http/Internal/OrderControllerImportFromFilesTest.php +18 -0
- package/server/tests/Feature/Http/Internal/OrderControllerTenantScopingTest.php +411 -0
- package/server/tests/Feature/Http/Internal/OrderControllerUpstreamNotFoundTest.php +8 -20
- package/server/tests/Feature/Http/ManualTelemetryPollingTest.php +88 -4
- package/server/tests/InternalCustomerControllerContractsTest.php +32 -1
- package/server/tests/ModelAccessorContractsTest.php +82 -20
- package/server/tests/ObserverContractsTest.php +27 -2
- package/server/tests/RequestContractsTest.php +19 -11
- package/server/tests/SupportJobAndAiCoverageTest.php +3 -3
- package/server/tests/TelematicControllerContractsTest.php +22 -4
- package/server/tests/Unit/Models/ContactCustomerUserTest.php +90 -14
- package/server/tests/Unit/Models/ContactTest.php +48 -5
- package/server/tests/Unit/Models/SensorTest.php +2 -2
- package/server/tests/Unit/Models/SmallModelSurfacesTest.php +31 -32
- package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +7 -2
- package/server/tests/Unit/Providers/FleetOpsAiRegistrationTest.php +15 -2
- package/server/tests/Unit/Support/Ai/AssetStatusCapabilityQueriesTest.php +42 -0
- package/server/tests/Unit/Support/Ai/Capabilities/OrderInsightsCapabilityTest.php +19 -1
- package/server/tests/Unit/Support/Ai/Capabilities/SearchResourcesCapabilityTest.php +85 -1
- package/server/tests/Unit/Support/Ai/FleetOpsAiConsoleCommandsTest.php +31 -0
- package/server/tests/Unit/Support/Ai/Tools/FleetOpsAiToolsTest.php +246 -0
- package/server/tests/Unit/Support/AiCapabilityPermissionsTest.php +3 -3
- package/server/tests/Unit/Support/ProfileAccountManagerTest.php +643 -0
- package/server/tests/Unit/Support/SascoFuelProviderTest.php +338 -0
- package/translations/en-us.yaml +37 -2
- package/translations/uk-ua.yaml +3475 -0
- package/addon/components/modals/reset-customer-credentials.hbs +0 -18
- package/addon/components/modals/reset-customer-credentials.js +0 -55
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Fleetbase\Ai\Models\AiTask;
|
|
4
|
+
use Fleetbase\Ai\Support\AiAudience;
|
|
5
|
+
use Fleetbase\Ai\Support\AiToolContext;
|
|
6
|
+
use Fleetbase\FleetOps\Models\OrderConfig;
|
|
7
|
+
use Fleetbase\FleetOps\Support\Ai\Tools\CreateOrderTool;
|
|
8
|
+
use Fleetbase\FleetOps\Support\Ai\Tools\SearchResourcesTool;
|
|
9
|
+
|
|
10
|
+
class FleetOpsCreateOrderToolProbe extends CreateOrderTool
|
|
11
|
+
{
|
|
12
|
+
public array $resolvedPlaces = [];
|
|
13
|
+
|
|
14
|
+
public function exposeBuildDraft(AiTask $task, array $input = []): array
|
|
15
|
+
{
|
|
16
|
+
return $this->buildDraft($task, $input);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected function can(string $permission): bool
|
|
20
|
+
{
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected function draftFromPrompt(string $prompt): array
|
|
25
|
+
{
|
|
26
|
+
return array_filter([
|
|
27
|
+
'order_config_uuid' => 'order-config-uuid',
|
|
28
|
+
'type' => 'transport',
|
|
29
|
+
'payload' => $prompt !== '' ? ['pickup_query' => 'Parsed from prompt'] : [],
|
|
30
|
+
'dispatched' => false,
|
|
31
|
+
], fn ($value) => $value !== []) + ['payload' => []];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected function resolvePlace(?string $query): ?array
|
|
35
|
+
{
|
|
36
|
+
return $this->resolvedPlaces[$query] ?? null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
protected function resolveOrderConfig(array $draft): ?OrderConfig
|
|
40
|
+
{
|
|
41
|
+
$config = new OrderConfig();
|
|
42
|
+
$config->setRawAttributes(['uuid' => 'order-config-uuid', 'key' => 'transport', 'name' => 'Transport'], true);
|
|
43
|
+
|
|
44
|
+
return $config;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected function resolveDriver(array $draft): ?Fleetbase\FleetOps\Models\Driver
|
|
48
|
+
{
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected function resolveVehicle(array $draft): ?Fleetbase\FleetOps\Models\Vehicle
|
|
53
|
+
{
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class FleetOpsSearchResourcesToolProbe extends SearchResourcesTool
|
|
59
|
+
{
|
|
60
|
+
public array $searched = [];
|
|
61
|
+
|
|
62
|
+
protected function searchAll(array $terms, ?array $types = null): array
|
|
63
|
+
{
|
|
64
|
+
$this->searched[] = [$terms, $types];
|
|
65
|
+
|
|
66
|
+
return ['query_terms' => $terms, 'results' => []];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
test('create order tool prepares one draft preview per call from structured fields without parsing the prompt', function () {
|
|
71
|
+
$tool = new FleetOpsCreateOrderToolProbe();
|
|
72
|
+
$task = new AiTask(['prompt' => 'Create some dummy orders with made up data', 'metadata' => []]);
|
|
73
|
+
$context = new AiToolContext($task, new AiAudience(false, ['fleet-ops create order']));
|
|
74
|
+
|
|
75
|
+
$first = $tool->invoke($task, [
|
|
76
|
+
'pickup' => 'Kabelweg 57, Amsterdam',
|
|
77
|
+
'dropoff' => 'Danzigerkade 15, Amsterdam',
|
|
78
|
+
'scheduled_at' => '2026-09-16T09:30:00+02:00',
|
|
79
|
+
'driver' => ' ',
|
|
80
|
+
'notes' => 'Test order',
|
|
81
|
+
], $context);
|
|
82
|
+
$second = $tool->invoke($task, ['pickup' => 'Diemerhof 32, Diemen', 'dropoff' => 'Herikerbergweg 238, Amsterdam', 'dispatch' => true], $context);
|
|
83
|
+
|
|
84
|
+
expect($first['preview_id'])->toBe('preview-1')
|
|
85
|
+
->and($second['preview_id'])->toBe('preview-2')
|
|
86
|
+
->and($first['status'])->toBe('draft_shown_to_user')
|
|
87
|
+
->and($first['message'])->toContain('does not exist until the user reviews the card')
|
|
88
|
+
->and($context->actionPreviews)->toHaveCount(2)
|
|
89
|
+
->and($context->actionPreviews[0]['draft']['payload']['pickup_query'])->toBe('Kabelweg 57, Amsterdam')
|
|
90
|
+
->and($context->actionPreviews[0]['draft']['scheduled_at'])->toBe('2026-09-16T09:30:00+02:00')
|
|
91
|
+
->and($context->actionPreviews[0]['draft']['notes'])->toBe('Test order')
|
|
92
|
+
->and($context->actionPreviews[0]['draft'])->not->toHaveKey('driver_query')
|
|
93
|
+
->and($context->actionPreviews[0]['draft']['dispatched'])->toBeFalse()
|
|
94
|
+
->and($context->actionPreviews[1]['draft']['payload']['pickup_query'])->toBe('Diemerhof 32, Diemen')
|
|
95
|
+
->and($context->actionPreviews[1]['draft']['dispatched'])->toBeTrue()
|
|
96
|
+
->and(json_encode($context->actionPreviews))->not->toContain('Parsed from prompt')
|
|
97
|
+
->and($tool->toolName())->toBe('propose_create_order')
|
|
98
|
+
->and($tool->key())->toBe('fleet-ops.create_order')
|
|
99
|
+
->and($tool->toolDescription())->toContain('NOT created by this tool')
|
|
100
|
+
->and($tool->toolParameters()['required'])->toBe(['pickup', 'dropoff'])
|
|
101
|
+
->and($tool->availableFor($context))->toBeTrue()
|
|
102
|
+
->and($tool->availableFor(new AiToolContext($task, new AiAudience(false, []))))->toBeFalse();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('create order drafts refresh from the edited preview and still parse prompts in keyword mode', function () {
|
|
106
|
+
$tool = new FleetOpsCreateOrderToolProbe();
|
|
107
|
+
$task = new AiTask(['prompt' => 'create order', 'metadata' => ['action_previews' => [['draft' => ['notes' => 'first preview']]]]]);
|
|
108
|
+
|
|
109
|
+
$refreshed = $tool->exposeBuildDraft($task, ['existing_draft' => ['notes' => 'second preview'], 'draft' => ['dispatched' => true]]);
|
|
110
|
+
$keyword = $tool->exposeBuildDraft($task, ['dispatched' => false]);
|
|
111
|
+
|
|
112
|
+
expect($refreshed['notes'])->toBe('second preview')
|
|
113
|
+
->and($refreshed['dispatched'])->toBeTrue()
|
|
114
|
+
->and($refreshed['payload']['pickup_query'])->toBe('Parsed from prompt')
|
|
115
|
+
->and($keyword['notes'])->toBe('first preview')
|
|
116
|
+
->and($keyword)->not->toHaveKey('existing_draft');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('search tool searches the requested phrase and identifiers for the chosen record types', function () {
|
|
120
|
+
$tool = new FleetOpsSearchResourcesToolProbe();
|
|
121
|
+
$task = new AiTask(['prompt' => 'where is Jane']);
|
|
122
|
+
$context = new AiToolContext($task, new AiAudience(false, ['fleet-ops see driver']));
|
|
123
|
+
|
|
124
|
+
$tool->invoke($task, ['query' => 'Jane Doe', 'types' => ['drivers', 'pets']], $context);
|
|
125
|
+
$tool->invoke($task, ['query' => 'order order_yhkejdnzgz'], $context);
|
|
126
|
+
|
|
127
|
+
expect($tool->searched[0])->toBe([['Jane Doe', 'Doe'], ['drivers']])
|
|
128
|
+
->and($tool->searched[1])->toBe([['order order_yhkejdnzgz', 'order_yhkejdnzgz'], null])
|
|
129
|
+
->and($tool->invoke($task, ['query' => ' x '], $context))->toBe(['error' => 'Provide an identifier or name to search for.'])
|
|
130
|
+
->and($tool->toolName())->toBe('fleetops_search')
|
|
131
|
+
->and($tool->key())->toBe('fleet-ops.search_resources')
|
|
132
|
+
->and($tool->toolDescription())->toContain('particular record')
|
|
133
|
+
->and($tool->toolParameters()['properties']['types']['items']['enum'])->toBe(SearchResourcesTool::TYPES)
|
|
134
|
+
->and($tool->availableFor($context))->toBeTrue()
|
|
135
|
+
->and($tool->availableFor(new AiToolContext($task, new AiAudience(false, []))))->toBeFalse();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('search tool narrows the real search to the requested types', function () {
|
|
139
|
+
$tool = new class extends SearchResourcesTool {
|
|
140
|
+
public array $called = [];
|
|
141
|
+
|
|
142
|
+
protected function orders(array $terms): array
|
|
143
|
+
{
|
|
144
|
+
$this->called[] = 'orders';
|
|
145
|
+
|
|
146
|
+
return [['id' => 'order_1']];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
protected function drivers(array $terms): array
|
|
150
|
+
{
|
|
151
|
+
$this->called[] = 'drivers';
|
|
152
|
+
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
$result = $tool->invoke(new AiTask(['prompt' => '']), ['query' => 'ORDER-1', 'types' => ['orders']], new AiToolContext(new AiTask(), new AiAudience(true)));
|
|
158
|
+
|
|
159
|
+
expect($tool->called)->toBe(['orders'])
|
|
160
|
+
->and($result['results'])->toBe(['orders' => [['id' => 'order_1']]]);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
class FleetOpsOptimizeOrderRouteToolProbe extends Fleetbase\FleetOps\Support\Ai\Tools\OptimizeOrderRouteTool
|
|
164
|
+
{
|
|
165
|
+
public array $previewedPrompts = [];
|
|
166
|
+
|
|
167
|
+
public function preview(AiTask $task, array $input = []): array
|
|
168
|
+
{
|
|
169
|
+
$this->previewedPrompts[] = $task->prompt;
|
|
170
|
+
|
|
171
|
+
return [
|
|
172
|
+
'action' => $this->key(),
|
|
173
|
+
'ready' => true,
|
|
174
|
+
'message' => 'Fleetbase AI prepared an optimized waypoint sequence. Review it before applying.',
|
|
175
|
+
'missing_fields' => [],
|
|
176
|
+
'fields' => [['label' => 'Order', 'value' => 'order_abc']],
|
|
177
|
+
];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
test('optimize route tool proposes a reviewable preview for the order the model named', function () {
|
|
182
|
+
$tool = new FleetOpsOptimizeOrderRouteToolProbe();
|
|
183
|
+
$task = new AiTask(['prompt' => 'can you optimise the route please', 'metadata' => []]);
|
|
184
|
+
$context = new AiToolContext($task, new AiAudience(false, ['fleet-ops optimize order', 'fleet-ops update-route-for order']));
|
|
185
|
+
|
|
186
|
+
$result = $tool->invoke($task, ['order' => 'order_abc'], $context);
|
|
187
|
+
|
|
188
|
+
expect($result['preview_id'])->toBe('preview-1')
|
|
189
|
+
->and($result['ready'])->toBeTrue()
|
|
190
|
+
->and($result['status'])->toBe('proposal_shown_to_user')
|
|
191
|
+
->and($result['fields'])->toBe([['label' => 'Order', 'value' => 'order_abc']])
|
|
192
|
+
->and($result['missing_fields'])->toBe([])
|
|
193
|
+
// The capability finds the order in the prompt text, so the tool hands it the id the model gave.
|
|
194
|
+
->and($tool->previewedPrompts)->toBe(['order_abc'])
|
|
195
|
+
->and($task->prompt)->toBe('can you optimise the route please')
|
|
196
|
+
->and($context->actionPreviews)->toHaveCount(1)
|
|
197
|
+
->and($tool->toolName())->toBe('propose_optimize_order_route')
|
|
198
|
+
->and($tool->key())->toBe('fleet-ops.optimize_order_route')
|
|
199
|
+
->and($tool->toolDescription())->toContain('NOT changed by this tool')
|
|
200
|
+
->and($tool->toolParameters()['required'])->toBe(['order'])
|
|
201
|
+
->and($tool->availableFor($context))->toBeTrue()
|
|
202
|
+
->and($tool->availableFor(new AiToolContext($task, new AiAudience(false, ['fleet-ops optimize order']))))->toBeFalse();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('optimize route tool refuses to build a preview without an order', function () {
|
|
206
|
+
$tool = new FleetOpsOptimizeOrderRouteToolProbe();
|
|
207
|
+
$task = new AiTask(['prompt' => 'optimise a route', 'metadata' => []]);
|
|
208
|
+
$context = new AiToolContext($task, new AiAudience(false, ['fleet-ops optimize order', 'fleet-ops update-route-for order']));
|
|
209
|
+
|
|
210
|
+
expect($tool->invoke($task, ['order' => ' '], $context))->toBe([
|
|
211
|
+
'error' => 'invalid_arguments',
|
|
212
|
+
'message' => 'An order id is required to optimize a route.',
|
|
213
|
+
])
|
|
214
|
+
->and($tool->invoke($task, [], $context)['error'])->toBe('invalid_arguments')
|
|
215
|
+
->and($context->actionPreviews)->toBe([])
|
|
216
|
+
->and($tool->previewedPrompts)->toBe([]);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
class FleetOpsImportOrdersToolProbe extends Fleetbase\FleetOps\Support\Ai\Tools\ImportOrdersTool
|
|
220
|
+
{
|
|
221
|
+
protected function can(string $permission): bool
|
|
222
|
+
{
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
test('import tool reports what an import needs and never claims to have imported anything', function () {
|
|
228
|
+
$tool = new FleetOpsImportOrdersToolProbe();
|
|
229
|
+
$task = new AiTask(['prompt' => 'can I import orders from a spreadsheet', 'metadata' => []]);
|
|
230
|
+
$context = new AiToolContext($task, new AiAudience(false, ['fleet-ops import order']));
|
|
231
|
+
|
|
232
|
+
$result = $tool->invoke($task, [], $context);
|
|
233
|
+
|
|
234
|
+
expect($result['accepted_sources'])->toBe(['xlsx', 'csv'])
|
|
235
|
+
->and($result['minimum_columns'])->toContain('pickup address or pickup place')
|
|
236
|
+
->and($result['can_import_here'])->toBeFalse()
|
|
237
|
+
->and($result['message'])->toContain('will not process uploaded spreadsheets')
|
|
238
|
+
->and($context->actionPreviews)->toBe([])
|
|
239
|
+
->and($tool->toolName())->toBe('fleetops_import_requirements')
|
|
240
|
+
->and($tool->key())->toBe('fleet-ops.import_orders_preview')
|
|
241
|
+
->and($tool->toolDescription())->toContain('does NOT import anything')
|
|
242
|
+
->and($tool->toolParameters()['properties'])->toBeInstanceOf(stdClass::class)
|
|
243
|
+
->and(json_encode($tool->toolParameters()['properties']))->toBe('{}')
|
|
244
|
+
->and($tool->availableFor($context))->toBeTrue()
|
|
245
|
+
->and($tool->availableFor(new AiToolContext($task, new AiAudience(false, []))))->toBeFalse();
|
|
246
|
+
});
|
|
@@ -96,7 +96,7 @@ test('permission checks for non admin users delegate to the gate', function () {
|
|
|
96
96
|
->and(fn () => fleetopsAiPermissionsHelper('canAll')->invoke($capability, ['fleet-ops list order']))->toThrow(TypeError::class);
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
test('search terms extract identifiers and fall back to the raw prompt', function () {
|
|
99
|
+
test('search terms extract identifiers and never fall back to the raw prompt', function () {
|
|
100
100
|
fleetopsAiPermissionsBoot();
|
|
101
101
|
$capability = new OperationalQueryCapability();
|
|
102
102
|
$searchTerms = fleetopsAiPermissionsHelper('searchTerms');
|
|
@@ -105,8 +105,8 @@ test('search terms extract identifiers and fall back to the raw prompt', functio
|
|
|
105
105
|
expect($terms)->toContain('TRK-12345', 'atlas99')
|
|
106
106
|
->and($terms)->not->toContain('find', 'order');
|
|
107
107
|
|
|
108
|
-
//
|
|
109
|
-
expect($searchTerms->invoke($capability, 'find order'))->toBe([
|
|
108
|
+
// Prompts without a record reference produce no search terms, so nothing is searched
|
|
109
|
+
expect($searchTerms->invoke($capability, 'find order'))->toBe([]);
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
test('where like any matches across columns and terms', function () {
|