Add .gitattributes for binary files and extend order booking update tests. Enhance dynamic image and routing logic with program_picker support.
This commit is contained in:
@@ -623,12 +623,266 @@ it('builds guided simulator debug payload with blockers and canvas annotations',
|
||||
->and($debug['questions'][0]['state'])->toBe('missing')
|
||||
->and($debug['questions'][0]['answer_source'])->toBe('override')
|
||||
->and($debug['tasks'][0]['state'])->toBe('blocked')
|
||||
->and($debug['tasks'][0]['reason'])->toBe('Task Fold mirrors blocked because gate QUESTION Are mirrors folded? expected true, actual missing.')
|
||||
->and($debug['tasks'][0]['causes'][0])->toMatchArray([
|
||||
'kind' => 'question',
|
||||
'id' => 11,
|
||||
'label' => 'Are mirrors folded?',
|
||||
'expected' => true,
|
||||
'actual' => null,
|
||||
])
|
||||
->and($debug['dynamic_image_buttons'][0])->toMatchArray([
|
||||
'kind' => 'dynamic_image_button',
|
||||
'label' => '1',
|
||||
'state' => 'hidden',
|
||||
'task_id' => 41,
|
||||
])
|
||||
->and(array_column($debug['decisions'], 'kind'))->toContain('question')
|
||||
->and(array_column($debug['decisions'], 'kind'))->toContain('task')
|
||||
->and(array_column($debug['decisions'], 'kind'))->toContain('signal')
|
||||
->and(array_column($debug['decisions'], 'kind'))->toContain('dynamic_image_button')
|
||||
->and(array_column($debug['recommendations'], 'title'))->toContain('Answer required questions')
|
||||
->and(array_column($debug['recommendations'], 'title'))->toContain('Configure lane machine relay')
|
||||
->and($debug['graph_annotations']['nodes']['question:11']['state'])->toBe('warning')
|
||||
->and($debug['graph_annotations']['nodes']['lane:7']['state'])->toBe('error');
|
||||
});
|
||||
|
||||
it('adds program picker before mapped machine buttons in simulator debug decisions', function (): void {
|
||||
$service = selfserve_wash_flow_without_constructor();
|
||||
|
||||
$debug = $service->buildStudioDebugPayload(6, [
|
||||
'lane' => [
|
||||
'id' => 7,
|
||||
'department' => 6,
|
||||
'name' => 'Lane 7',
|
||||
'relay_machine_id' => 'M-7',
|
||||
'relay_machine_program_picker_id' => 'PICKER-7',
|
||||
],
|
||||
'machine_type' => ['id' => 1001, 'name' => 'Portal'],
|
||||
'vehicle' => null,
|
||||
'reg' => 'TEST123',
|
||||
'customer_number' => null,
|
||||
'vehicle_type_id' => 4,
|
||||
'answers' => [],
|
||||
'questions' => [],
|
||||
'tasks' => [
|
||||
['id' => 41, 'task' => 'Choose program', 'services' => ['PROGRAM_PICKER'], 'buttons' => [], 'dynamic_images_vehicle_type' => 4, 'order_priority' => 1],
|
||||
['id' => 42, 'task' => 'Press first button', 'services' => ['MACHINE'], 'buttons' => [0], 'dynamic_images_vehicle_type' => 4, 'order_priority' => 2],
|
||||
],
|
||||
'allowed_services' => ['PROGRAM_PICKER', 'MACHINE'],
|
||||
'machine_available' => true,
|
||||
'all_visible_questions_answered' => true,
|
||||
'allowed' => true,
|
||||
'config_version_id' => 90,
|
||||
'config_source' => 'draft',
|
||||
'evaluation_trace' => [
|
||||
'visible_question_ids' => [],
|
||||
'visibility_condition_results' => [],
|
||||
'condition_results' => [],
|
||||
'task_gates' => [
|
||||
['task_id' => 41, 'gate_type' => 'ALWAYS', 'gate_ref_id' => null, 'satisfied' => true],
|
||||
['task_id' => 42, 'gate_type' => 'ALWAYS', 'gate_ref_id' => null, 'satisfied' => true],
|
||||
],
|
||||
],
|
||||
'debug_candidates' => [
|
||||
'questions' => [],
|
||||
'conditions' => [],
|
||||
'rules' => [],
|
||||
'tasks' => [
|
||||
['id' => 41, 'task' => 'Choose program', 'gate_type' => 'ALWAYS', 'gate_ref_id' => null, 'services' => ['PROGRAM_PICKER'], 'buttons' => [], 'dynamic_images_vehicle_type' => 4, 'order_priority' => 1],
|
||||
['id' => 42, 'task' => 'Press first button', 'gate_type' => 'ALWAYS', 'gate_ref_id' => null, 'services' => ['MACHINE'], 'buttons' => [0], 'dynamic_images_vehicle_type' => 4, 'order_priority' => 2],
|
||||
],
|
||||
],
|
||||
], [
|
||||
'lookups' => [
|
||||
'labels' => [
|
||||
'departments' => ['6' => 'Roskilde'],
|
||||
'lanes' => ['7' => 'Lane 7'],
|
||||
'vehicle_types' => ['4' => 'Program 4'],
|
||||
'machine_types' => ['1001' => 'Portal'],
|
||||
'tasks' => ['41' => 'Choose program', '42' => 'Press first button'],
|
||||
],
|
||||
],
|
||||
'gateway_workspace' => [
|
||||
'gateways' => [
|
||||
[
|
||||
'id' => 701,
|
||||
'label' => 'Roskilde Edge',
|
||||
'status' => 'ONLINE',
|
||||
'bindings' => [
|
||||
['relay_id' => 'PICKER-7', 'label' => 'Program picker relay', 'role' => 'PROGRAM_PICKER', 'services' => ['PROGRAM_PICKER']],
|
||||
['relay_id' => 'M-7', 'label' => 'Machine relay', 'role' => 'MACHINE', 'services' => ['MACHINE']],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$dynamicImageDecisions = array_values(array_filter(
|
||||
$debug['decisions'],
|
||||
static fn(array $decision): bool => ($decision['kind'] ?? null) === 'dynamic_image_button'
|
||||
));
|
||||
|
||||
expect(array_column($debug['dynamic_image_buttons'], 'button'))->toBe(['program_picker', 0])
|
||||
->and(array_column($debug['dynamic_image_buttons'], 'label'))->toBe(['Program picker', '0'])
|
||||
->and(array_column($dynamicImageDecisions, 'label'))->toBe(['Program picker', '0']);
|
||||
});
|
||||
|
||||
it('adds exact hidden question, skipped action, signal, and button decision causes', function (): void {
|
||||
$service = selfserve_wash_flow_without_constructor();
|
||||
|
||||
$debug = $service->buildStudioDebugPayload(6, [
|
||||
'lane' => [
|
||||
'id' => 7,
|
||||
'department' => 6,
|
||||
'name' => 'Lane 7',
|
||||
'relay_machine_id' => 'M-7',
|
||||
],
|
||||
'machine_type' => ['id' => 1001, 'name' => 'Portal'],
|
||||
'vehicle' => null,
|
||||
'reg' => 'TEST123',
|
||||
'customer_number' => null,
|
||||
'vehicle_type_id' => 2,
|
||||
'answers' => [11 => false],
|
||||
'answer_sources' => [11 => 'override'],
|
||||
'questions' => [],
|
||||
'tasks' => [],
|
||||
'allowed_services' => [],
|
||||
'machine_available' => true,
|
||||
'all_visible_questions_answered' => true,
|
||||
'allowed' => false,
|
||||
'config_version_id' => 90,
|
||||
'config_source' => 'draft',
|
||||
'evaluation_trace' => [
|
||||
'visible_question_ids' => [11],
|
||||
'visibility_condition_results' => [21 => false],
|
||||
'condition_results' => [21 => false],
|
||||
'visibility_expression_traces' => [
|
||||
21 => [
|
||||
'type' => 'condition',
|
||||
'condition_id' => 21,
|
||||
'result' => false,
|
||||
'expression' => [
|
||||
'type' => 'group',
|
||||
'operator' => 'ALL',
|
||||
'result' => false,
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'predicate',
|
||||
'subject_type' => 'question',
|
||||
'subject_id' => 11,
|
||||
'operator' => 'IS_TRUE',
|
||||
'actual_value' => false,
|
||||
'result' => false,
|
||||
'reason' => 'Predicate did not pass.',
|
||||
],
|
||||
],
|
||||
'reason' => 'Group did not pass.',
|
||||
],
|
||||
],
|
||||
],
|
||||
'condition_expression_traces' => [
|
||||
21 => [
|
||||
'type' => 'condition',
|
||||
'condition_id' => 21,
|
||||
'result' => false,
|
||||
'expression' => [
|
||||
'type' => 'predicate',
|
||||
'subject_type' => 'question',
|
||||
'subject_id' => 11,
|
||||
'operator' => 'IS_TRUE',
|
||||
'actual_value' => false,
|
||||
'result' => false,
|
||||
'reason' => 'Predicate did not pass.',
|
||||
],
|
||||
],
|
||||
],
|
||||
'task_gates' => [
|
||||
['task_id' => 41, 'gate_type' => 'QUESTION', 'gate_ref_id' => 11, 'satisfied' => false],
|
||||
],
|
||||
],
|
||||
'debug_candidates' => [
|
||||
'questions' => [
|
||||
['id' => 11, 'question' => 'Are mirrors folded?', 'condition_id' => null, 'order_priority' => 1],
|
||||
['id' => 12, 'question' => 'Is the lift lowered?', 'condition_id' => 21, 'order_priority' => 2],
|
||||
],
|
||||
'conditions' => [
|
||||
['id' => 21, 'name' => 'Trailer present', 'condition_id' => null],
|
||||
],
|
||||
'rules' => [
|
||||
['id' => 31, 'condition_id' => 21, 'type' => 'IS_TRUE', 'object_type' => 'question', 'object_id' => 11, 'name' => 'Mirror answer'],
|
||||
],
|
||||
'tasks' => [
|
||||
['id' => 41, 'task' => 'Fold mirrors', 'gate_type' => 'QUESTION', 'gate_ref_id' => 11, 'services' => ['MACHINE'], 'buttons' => ['start'], 'order_priority' => 1],
|
||||
],
|
||||
'actions' => [
|
||||
[
|
||||
'id' => 81,
|
||||
'name' => 'Open entry on start',
|
||||
'event' => 'wash_start_command',
|
||||
'wash_mode' => 'both',
|
||||
'operation' => 'open_lane_entrance_port',
|
||||
'condition_id' => 21,
|
||||
'order_priority' => 1,
|
||||
],
|
||||
],
|
||||
'visible_answers' => [11 => false],
|
||||
],
|
||||
], [
|
||||
'lookups' => [
|
||||
'labels' => [
|
||||
'departments' => ['6' => 'Roskilde'],
|
||||
'lanes' => ['7' => 'Lane 7'],
|
||||
'vehicle_types' => ['2' => 'Forvogn'],
|
||||
'machine_types' => ['1001' => 'Portal'],
|
||||
'questions' => ['11' => 'Are mirrors folded?', '12' => 'Is the lift lowered?'],
|
||||
'conditions' => ['21' => 'Trailer present'],
|
||||
'tasks' => ['41' => 'Fold mirrors'],
|
||||
],
|
||||
],
|
||||
'gateway_workspace' => [
|
||||
'gateways' => [
|
||||
[
|
||||
'id' => 701,
|
||||
'label' => 'Roskilde Edge',
|
||||
'status' => 'ONLINE',
|
||||
'bindings' => [
|
||||
['relay_id' => 'M-7', 'label' => 'Machine relay', 'role' => 'MACHINE', 'services' => ['MACHINE']],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$hiddenQuestion = array_values(array_filter($debug['questions'], static fn(array $question): bool => (int)$question['id'] === 12))[0] ?? [];
|
||||
$taskDecision = array_values(array_filter($debug['decisions'], static fn(array $decision): bool => ($decision['kind'] ?? '') === 'task'))[0] ?? [];
|
||||
$actionDecision = array_values(array_filter($debug['decisions'], static fn(array $decision): bool => ($decision['kind'] ?? '') === 'action'))[0] ?? [];
|
||||
$signalDecision = array_values(array_filter($debug['decisions'], static fn(array $decision): bool => ($decision['kind'] ?? '') === 'signal' && ($decision['state'] ?? '') === 'blocked'))[0] ?? [];
|
||||
$buttonDecision = array_values(array_filter($debug['decisions'], static fn(array $decision): bool => ($decision['kind'] ?? '') === 'dynamic_image_button'))[0] ?? [];
|
||||
|
||||
expect($hiddenQuestion['state'])->toBe('hidden')
|
||||
->and($hiddenQuestion['reason'])->toBe('Question Is the lift lowered? hidden because visibility condition Trailer present expected true, actual false.')
|
||||
->and($debug['conditions'][0]['causes'][0])->toMatchArray([
|
||||
'kind' => 'question',
|
||||
'id' => 11,
|
||||
'label' => 'Are mirrors folded?',
|
||||
'expected' => true,
|
||||
'actual' => false,
|
||||
])
|
||||
->and($taskDecision['reason'])->toBe('Task Fold mirrors blocked because gate QUESTION Are mirrors folded? expected true, actual false.')
|
||||
->and($actionDecision['state'])->toBe('skipped')
|
||||
->and($actionDecision['causes'][0])->toMatchArray([
|
||||
'kind' => 'condition',
|
||||
'id' => 21,
|
||||
'label' => 'Trailer present',
|
||||
'expected' => true,
|
||||
'actual' => false,
|
||||
])
|
||||
->and($signalDecision['causes'][0]['reason'])->toContain('blocked')
|
||||
->and($buttonDecision['state'])->toBe('hidden')
|
||||
->and($buttonDecision['causes'][0]['label'])->toBe('Fold mirrors');
|
||||
});
|
||||
|
||||
it('projects visible question answer paths into grouped task service and signal outcomes', function (): void {
|
||||
$service = selfserve_studio_graph_without_constructor();
|
||||
$simulate = function (array $overrides): array {
|
||||
|
||||
Reference in New Issue
Block a user