Files
api/services/nginx/app/tests/Unit/Selfserve/SelfserveRouteWiringTest.php
T
Jeppe Bundgaard acdff75311 Add requestBooleanFlag helper and enhance session synchronization logic
- Introduce `requestBooleanFlag` method for consistent boolean parameter handling with default values.
- Add `activate_machine` and `sync_relay_state` parameters to `synchronizeSession` for more flexible relay and machine activation control.
- Update methods, routes, and tests to integrate the new session synchronization parameters effectively.
- Enhance debugging support with additional metadata in simulation and payload captures.
2026-04-28 16:54:57 +02:00

304 lines
20 KiB
PHP

<?php
it('wires self-serve machine types, eligibility, summaries, and machine-start webhook routes', function (): void {
$machineTypesRoute = file_get_contents(app_path('routes/departmentSelfserveMachineTypesRoute.php'));
$vehicleConditionsRoute = file_get_contents(app_path('routes/departmentSelfserveVehicleConditionsRoute.php'));
$webhookRoute = file_get_contents(app_path('routes/machineButtonPressRoute.php'));
expect($machineTypesRoute)->not->toBeFalse();
expect($machineTypesRoute)->toContain('/department/selfserve/machine-types');
expect($vehicleConditionsRoute)->not->toBeFalse();
expect($vehicleConditionsRoute)->toContain('/department/selfserve/vehicle/allowed');
expect($vehicleConditionsRoute)->toContain('/department/selfserve/washes/summary');
expect($vehicleConditionsRoute)->toContain('synchronizeSession');
expect($webhookRoute)->not->toBeFalse();
expect($webhookRoute)->toContain('/relay/button/press/post');
expect($webhookRoute)->toContain('recordMachineStartWebhook');
});
it('keeps machine type support wired into lanes, tasks, and conditions routes', function (): void {
$lanesRoute = file_get_contents(app_path('routes/departmentLanesRoute.php'));
$tasksRoute = file_get_contents(app_path('routes/departmentSelfserveTasksRoute.php'));
$conditionsRoute = file_get_contents(app_path('routes/departmentSelfserveConditionsRoute.php'));
$questionsRoute = file_get_contents(app_path('routes/departmentSelfserveQuestionsRoute.php'));
expect($lanesRoute)->toContain('machine_type_id');
expect($tasksRoute)->toContain('machine_type_id');
expect($conditionsRoute)->toContain('machine_type_id');
expect($questionsRoute)->toContain('department') // shared questions still use the legacy columns with default 0 scope
->toContain('lane')
->toContain('product');
});
it('wires self-serve config draft/publish/rollback lifecycle endpoints', function (): void {
$configRoute = file_get_contents(app_path('routes/departmentSelfserveConfigVersionsRoute.php'));
expect($configRoute)->not->toBeFalse();
expect($configRoute)->toContain('/department/selfserve/config/versions');
expect($configRoute)->toContain('/department/selfserve/config/history');
expect($configRoute)->toContain('/department/selfserve/config/active');
expect($configRoute)->toContain('/department/selfserve/config/draft');
expect($configRoute)->toContain('/department/selfserve/config/validate');
expect($configRoute)->toContain('/department/selfserve/config/publish');
expect($configRoute)->toContain('/department/selfserve/config/rollback');
expect($configRoute)->toContain('publish_department_selfserve_config_versions');
expect($configRoute)->toContain('rollback_department_selfserve_config_versions');
});
it('wires the all-in-one self-serve studio replacement endpoints', function (): void {
$studioRoute = file_get_contents(app_path('routes/departmentSelfserveStudioRoute.php'));
$studioGraph = file_get_contents(app_path('modules/selfserve/classes/selfserve_studio_graph.php'));
expect($studioRoute)->not->toBeFalse();
expect($studioRoute)->toContain('/department/selfserve/studio/graph');
expect($studioRoute)->toContain('/department/selfserve/studio/layout');
expect($studioRoute)->toContain('/department/selfserve/studio/validate');
expect($studioRoute)->toContain('/department/selfserve/studio/simulate');
expect($studioRoute)->toContain('/department/selfserve/studio/publish');
expect($studioRoute)->toContain('/department/selfserve/studio/rollback');
expect($studioRoute)->toContain('/department/selfserve/studio/gateway-action');
expect($studioRoute)->toContain('modules_shelly_config');
expect($studioRoute)->toContain('modules_selfserve_sessions_force_stop');
expect($studioGraph)->not->toBeFalse();
expect($studioGraph)->toContain('department_selfserve_studio_layouts');
expect($studioGraph)->toContain('buildGatewayWorkspace');
expect($studioGraph)->toContain('runGatewayAction');
expect($studioGraph)->toContain('layout_affects_runtime');
expect($studioGraph)->toContain('resolved');
});
it('wires machine wash included minutes into self-serve module config', function (): void {
$selfserveConfig = file_get_contents(app_path('modules/selfserve/selfserve_c.php'));
expect($selfserveConfig)->not->toBeFalse();
expect($selfserveConfig)->toContain('selfserve_machine_wash_minutes_included_c');
expect($selfserveConfig)->toContain('machine_wash_minutes_included');
});
it('keeps legacy self-serve CRUD routes syncing canonical drafts', function (): void {
$questionsRoute = file_get_contents(app_path('routes/departmentSelfserveQuestionsRoute.php'));
$conditionsRoute = file_get_contents(app_path('routes/departmentSelfserveConditionsRoute.php'));
$rulesRoute = file_get_contents(app_path('routes/departmentSelfserveConditionRulesRoute.php'));
$tasksRoute = file_get_contents(app_path('routes/departmentSelfserveTasksRoute.php'));
expect($questionsRoute)->toContain('syncDraftFromLegacyForDepartment');
expect($conditionsRoute)->toContain('syncDraftFromLegacyForDepartment');
expect($rulesRoute)->toContain('syncDraftFromLegacyForDepartment');
expect($tasksRoute)->toContain('syncDraftFromLegacyForDepartment');
});
it('wires machine relay status get and set endpoints', function (): void {
$moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
expect($moduleSelfServeRoute)->not->toBeFalse();
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine/status');
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine/set');
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine_program_picker/status');
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine_program_picker/set');
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine_cleaner/status');
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine_cleaner/set');
expect($moduleSelfServeRoute)->toContain('getMachineRelayStatus');
expect($moduleSelfServeRoute)->toContain('setMachineRelayStatus');
expect($moduleSelfServeRoute)->toContain('getMachineProgramPickerRelayStatus');
expect($moduleSelfServeRoute)->toContain('setMachineProgramPickerRelayStatus');
expect($moduleSelfServeRoute)->toContain('getMachineCleanerRelayStatus');
expect($moduleSelfServeRoute)->toContain('setMachineCleanerRelayStatus');
expect($moduleSelfServeRoute)->toContain('setMachineCleanerRelayStatusHard(true)');
expect($moduleSelfServeRoute)->toContain('applyShellyTransportOverride($lane)');
expect($moduleSelfServeRoute)->toContain("'transport' => \$this->requestedShellyTransportOverride()");
expect($moduleSelfServeRoute)->toContain('buildRelayStatusResponse');
});
it('applies Shelly transport overrides across self-serve relay side-effect routes', function (): void {
$moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
expect($moduleSelfServeRoute)->not->toBeFalse();
expect(substr_count($moduleSelfServeRoute, 'applyShellyTransportOverride($lane)'))->toBeGreaterThanOrEqual(17);
expect($moduleSelfServeRoute)->toContain('$lane->execute($command, $args);');
expect($moduleSelfServeRoute)->toContain('$lane->setAllowedServicesFromVisibleTasks($allowed_services);');
expect($moduleSelfServeRoute)->toContain('$lane->open($gate, $toggle_after);');
expect($moduleSelfServeRoute)->toContain('$lane->turnOnRelay(selfserve_lane_relay::MACHINE, $duration);');
expect($moduleSelfServeRoute)->toContain('$lane->turnOnRelay(selfserve_lane_relay::MACHINE_PROGRAM_PICKER, $duration);');
expect($moduleSelfServeRoute)->toContain('$lane->turnOnRelay(selfserve_lane_relay::MACHINE_CLEANER, $duration);');
expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOnMachineRelay($duration);');
expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOffMachineRelay();');
expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOnRelay(selfserve_lane_relay::MACHINE_PROGRAM_PICKER, $duration);');
expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOffRelay(selfserve_lane_relay::MACHINE_PROGRAM_PICKER);');
expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOnRelay(selfserve_lane_relay::MACHINE_CLEANER, $duration);');
expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOffRelay(selfserve_lane_relay::MACHINE_CLEANER);');
});
it('wires allowed services route through machine relay visibility sync', function (): void {
$moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
expect($moduleSelfServeRoute)->not->toBeFalse();
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/services/allowed');
expect($moduleSelfServeRoute)->toContain('setAllowedServicesFromVisibleTasks($allowed_services)');
expect($moduleSelfServeRoute)->not->toContain('syncMachineRelayFromVisibleServices($allowed_services, true)');
expect($moduleSelfServeRoute)->toContain("'relay_sync' => \$relay_sync");
});
it('wires self-serve lane gate open endpoint', function (): void {
$moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
expect($moduleSelfServeRoute)->not->toBeFalse();
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/gate/open');
expect($moduleSelfServeRoute)->toContain('modules_selfserve_lane_gate_open');
expect($moduleSelfServeRoute)->toContain('selfserve_lane_port::ENTRANCE');
expect($moduleSelfServeRoute)->toContain('selfserve_lane_port::EXIT');
expect($moduleSelfServeRoute)->toContain('$toggle_after = $this->requestedRelayToggleAfter(1);');
expect($moduleSelfServeRoute)->toContain('$lane->open($gate, $toggle_after)');
expect($moduleSelfServeRoute)->toContain("'toggle_after' => \$toggle_after");
expect($moduleSelfServeRoute)->toContain('private function requestedRelayToggleAfter');
expect($moduleSelfServeRoute)->toContain('Failed to open self-serve lane gate');
expect($moduleSelfServeRoute)->toContain("'Failed to open ' . strtolower(\$gate->name) . ' gate.'");
expect($moduleSelfServeRoute)->not->toContain('Failed to open lane gate: ');
});
it('wires self-serve property gate command permissions', function (): void {
$moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
$commandTrait = file_get_contents(app_path('modules/selfserve/traits/selfserve_lane_command_t.php'));
expect($moduleSelfServeRoute)->not->toBeFalse();
expect($moduleSelfServeRoute)->toContain('case selfserve_lane_command::OPEN_PROPERTY_ACCESS_GATE');
expect($moduleSelfServeRoute)->toContain('case selfserve_lane_command::OPEN_PROPERTY_EXIT_GATE');
expect($moduleSelfServeRoute)->toContain('modules_selfserve_lane_command_execute_open_property_access_gate');
expect($moduleSelfServeRoute)->toContain('modules_selfserve_lane_command_execute_open_property_exit_gate');
expect($moduleSelfServeRoute)->toContain('Failed to execute self-serve property gate command');
expect($commandTrait)->not->toBeFalse();
expect($commandTrait)->toContain('Failed to open property access gate.');
expect($commandTrait)->toContain('Failed to open property exit gate.');
expect($commandTrait)->not->toContain('Failed to open property access gate: ');
});
it('wires in-progress self-serve wash details endpoint', function (): void {
$moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
expect($moduleSelfServeRoute)->not->toBeFalse();
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/wash/in-progress');
expect($moduleSelfServeRoute)->toContain('modules_selfserve_lane_wash_in_progress_view');
expect($moduleSelfServeRoute)->toContain("'in_progress' => true");
expect($moduleSelfServeRoute)->toContain("'in_progress' => false");
expect($moduleSelfServeRoute)->toContain('selfserve_lane_status::OCCUPIED');
expect($moduleSelfServeRoute)->toContain('selfserve_lane_state::IN_WASH');
expect($moduleSelfServeRoute)->toContain('getWashStartTime');
expect($moduleSelfServeRoute)->toContain('machine_wash_minutes_included');
expect($moduleSelfServeRoute)->toContain("'included_minutes' =>");
expect($moduleSelfServeRoute)->toContain("'machine_relay_enabled' =>");
expect($moduleSelfServeRoute)->toContain("'machine_relay_enabled_at' =>");
expect($moduleSelfServeRoute)->toContain("'wash_started_at' =>");
});
it('wires self-serve session management endpoints and OpenAPI coverage', function (): void {
$moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
$openApiFiles = [app_path('openapi.yaml')];
$rootOpenApiFile = dirname(app_path(), 3) . '/openapi.yaml';
if (is_file($rootOpenApiFile)) {
$openApiFiles[] = $rootOpenApiFile;
}
expect($moduleSelfServeRoute)->not->toBeFalse();
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/sessions');
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/sessions/{id}');
expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/force/stop');
expect($moduleSelfServeRoute)->toContain('modules_selfserve_sessions_view');
expect($moduleSelfServeRoute)->toContain('modules_selfserve_sessions_force_stop');
expect($moduleSelfServeRoute)->toContain('modules_selfserve_sessions_force_stop_bill');
expect($moduleSelfServeRoute)->toContain('listObjectsWithPaginationIfSet');
expect($moduleSelfServeRoute)->toContain('getSessionSummary($session_id)');
expect($moduleSelfServeRoute)->toContain('forceStopLane($lane_id, $session_id, $bill, $reason, $user_id)');
foreach ($openApiFiles as $openApiFile) {
$openApi = file_get_contents($openApiFile);
expect($openApi)->not->toBeFalse();
expect($openApi)->toContain('/modules/self-serve/sessions:');
expect($openApi)->toContain('/modules/self-serve/sessions/{id}:');
expect($openApi)->toContain('/modules/self-serve/lane/force/stop:');
expect($openApi)->toContain('listSelfServeSessions');
expect($openApi)->toContain('getSelfServeSessionDetail');
expect($openApi)->toContain('forceStopSelfServeLane');
expect($openApi)->toContain('SelfserveForceStopResponse');
expect($openApi)->toContain('FORCE_STOPPED');
expect($openApi)->toContain('SESSION_FORCE_STOPPED');
}
});
it('keeps self-serve force stop distinct from normal STOP relay and gate behavior', function (): void {
$washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));
$sessionObject = file_get_contents(app_path('objects/selfserve_wash_sessions_o.php'));
$statusEnum = file_get_contents(app_path('modules/selfserve/helpers/selfserve_wash_session_status.php'));
$eventEnum = file_get_contents(app_path('modules/selfserve/helpers/selfserve_wash_event_type.php'));
expect($washFlow)->not->toBeFalse();
expect($sessionObject)->not->toBeFalse();
expect($statusEnum)->toContain('FORCE_STOPPED');
expect($eventEnum)->toContain('SESSION_FORCE_STOPPED');
expect($sessionObject)->toContain('markForceStopped');
expect($sessionObject)->toContain("metadata_json->set(\$existing)");
$start = strpos($washFlow, 'public function forceStopLane');
$end = strpos($washFlow, 'protected function buildEligibilitySnapshot', $start);
expect($start)->not->toBeFalse();
expect($end)->not->toBeFalse();
$forceStopSource = substr($washFlow, $start, $end - $start);
expect($forceStopSource)->toContain('$lane->invoice()');
expect($forceStopSource)->toContain('getLastInvoiceOrderId');
expect($forceStopSource)->toContain('markForceStopped');
expect($forceStopSource)->toContain('SESSION_FORCE_STOPPED');
expect($forceStopSource)->toContain('selfserve_lane_command::RESET');
expect($forceStopSource)->toContain('runtime_before_reset');
expect($forceStopSource)->not->toContain('selfserve_lane_command::STOP');
expect($forceStopSource)->not->toContain('selfserve_lane_port::EXIT');
expect($forceStopSource)->not->toContain('open(');
expect($forceStopSource)->not->toContain('turnOffRelaysAfterStop');
expect($forceStopSource)->not->toContain('disableMachineRelayForCompletedWash');
expect($forceStopSource)->not->toContain('getRelayStatus');
expect($forceStopSource)->not->toContain('MACHINE_PROGRAM_PICKER');
expect($forceStopSource)->not->toContain('addVehicleTypeProductToInvoiceIfNeeded');
expect($forceStopSource)->not->toContain('addVehicleTypeProductToLastInvoiceOrder');
});
it('wires vehicle type override into self-serve preview and synchronization routes', function (): void {
$vehicleConditionsRoute = file_get_contents(app_path('routes/departmentSelfserveVehicleConditionsRoute.php'));
$washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));
expect($vehicleConditionsRoute)->not->toBeFalse();
expect($vehicleConditionsRoute)->toContain('vehicle_type_id');
expect($vehicleConditionsRoute)->toContain('vehicle_type');
expect($vehicleConditionsRoute)->toContain('resolveVehicleTypeIdFromQuery');
expect($vehicleConditionsRoute)->toContain('resolveVehicleTypeIdFromRequest');
expect($vehicleConditionsRoute)->toContain('normalizeVehicleTypeOverride');
expect($vehicleConditionsRoute)->toContain('shouldRefreshSummaryForVehicleType');
expect($vehicleConditionsRoute)->toContain('previewVehicleEligibility($lane_id, $reg, $customer_number, $vehicle_type_id)');
expect($vehicleConditionsRoute)->toContain('synchronizeSession($lane_id, $reg, $customer_number, false, $vehicle_type_id, false)');
expect($vehicleConditionsRoute)->toContain('requestBooleanFlag');
expect($vehicleConditionsRoute)->toContain('synchronizeSession($lane, $reg, $customer_id, $activate_machine, $vehicle_type_id, $sync_relay_state)');
expect($vehicleConditionsRoute)->toContain('synchronizeSession($lane_id, $reg, $customer_id, $activate_machine, $vehicle_type_id, $sync_relay_state)');
expect($washFlow)->not->toBeFalse();
expect($washFlow)->toContain('resolveVehicleTypeId($vehicle, $vehicleTypeIdOverride)');
expect($washFlow)->toContain("'vehicle_type_id' => \$snapshot['vehicle_type_id']");
expect($washFlow)->toContain("\$session->vehicle_type_id->set(\$snapshot['vehicle_type_id']);");
expect($washFlow)->toContain("\$session->vehicle_id->set(\$snapshot['vehicle']['id'] ?? null);");
});
it('keeps read-only self-serve preview and summary refreshes from touching relay hardware', function (): void {
$vehicleConditionsRoute = file_get_contents(app_path('routes/departmentSelfserveVehicleConditionsRoute.php'));
expect($vehicleConditionsRoute)->not->toBeFalse();
expect($vehicleConditionsRoute)->toContain('$flow->synchronizeSession($lane_id, $reg, $customer_number, false, $vehicle_type_id, false);');
expect($vehicleConditionsRoute)->toContain('$summary = $flow->synchronizeSession(');
expect($vehicleConditionsRoute)->toContain('$summary = $flow->synchronizeSession($lane_id, $reg, $customer_number, false, $vehicle_type_id, false);');
expect($vehicleConditionsRoute)->toContain('$activate_machine = $this->requestBooleanFlag(\'activate_machine\', true);');
expect($vehicleConditionsRoute)->toContain('$sync_relay_state = $this->requestBooleanFlag(\'sync_relay_state\', true);');
expect($vehicleConditionsRoute)->toContain('$this->getWashFlow()->synchronizeSession($lane, $reg, $customer_id, $activate_machine, $vehicle_type_id, $sync_relay_state);');
expect($vehicleConditionsRoute)->toContain('$this->getWashFlow()->synchronizeSession($lane_id, $reg, $customer_id, $activate_machine, $vehicle_type_id, $sync_relay_state);');
});