storedValue; } } class SelfserveWashCompletionDepartmentLaneFake extends \objects\department_lanes_o { public function __construct(string $machineRelayId = 'relay-machine', string $cleanerRelayId = 'relay-cleaner') { $this->relay_machine_id = new SelfserveWashCompletionRelayValueFake($machineRelayId); $this->relay_machine_cleaner_id = new SelfserveWashCompletionRelayValueFake($cleanerRelayId); $this->relay_machine_program_picker_id = new SelfserveWashCompletionRelayValueFake(''); } public function exists(): bool { return true; } } class SelfserveWashCompletionRelayLaneFake extends selfserve_lane { /** @var selfserve_lane_relay[] */ public array $statusReads = []; /** @var array */ public array $relayWrites = []; public function __construct(bool $reportedOn) { $this->id = 77; $this->reportedOn = $reportedOn; $this->department_lane = new SelfserveWashCompletionDepartmentLaneFake(); } private bool $reportedOn; public function getRelayStatus(selfserve_lane_relay $relay): array { $this->statusReads[] = $relay; return ['on' => $this->reportedOn]; } public function setRelayStatusHard(selfserve_lane_relay $relay, bool $on): bool { $this->relayWrites[] = [$relay, $on]; return true; } } class SelfserveWashCompletionFlowHarness extends selfserve_wash_flow { public function __construct() {} public function turnOffConfiguredRelay(selfserve_lane $lane, selfserve_lane_relay $relay): void { $this->turnOffRelayIfConfigured($lane, $relay); } } it('forces machine and cleaner relays off when a self-serve wash session is completed', function (): void { $washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php')); expect($washFlow)->not->toBeFalse(); expect($washFlow)->toContain('$this->disableMachineRelayForCompletedWash($laneId);'); expect($washFlow)->toContain('bool $disableRelays = true'); expect($washFlow)->toContain('if ($disableRelays) {'); $methodOffset = strpos($washFlow, 'protected function disableMachineRelayForCompletedWash'); expect($methodOffset)->not->toBeFalse(); $methodBody = substr($washFlow, (int)$methodOffset, 1500); expect($methodBody)->toContain('$this->turnOffRelayIfConfigured($lane, selfserve_lane_relay::MACHINE);'); expect($methodBody)->toContain('$this->turnOffRelayIfConfigured($lane, selfserve_lane_relay::MACHINE_CLEANER);'); $helperOffset = strpos($washFlow, 'protected function turnOffRelayIfConfigured'); expect($helperOffset)->not->toBeFalse(); $helperBody = substr($washFlow, (int)$helperOffset, 1500); expect($helperBody)->toContain('$lane->setRelayStatusHard($relay, false);'); expect($helperBody)->not->toContain('$lane->getRelayStatus($relay)'); }); it('always dispatches completion relay off for configured machine relays without a status precheck', function (): void { $lane = new SelfserveWashCompletionRelayLaneFake(reportedOn: false); $flow = new SelfserveWashCompletionFlowHarness(); $flow->turnOffConfiguredRelay($lane, selfserve_lane_relay::MACHINE); $flow->turnOffConfiguredRelay($lane, selfserve_lane_relay::MACHINE_CLEANER); expect($lane->statusReads)->toBe([]) ->and($lane->relayWrites)->toBe([ [selfserve_lane_relay::MACHINE, false], [selfserve_lane_relay::MACHINE_CLEANER, false], ]); }); it('normal STOP completion skips duplicate completion relay cleanup after STOP already disabled relays', function (): void { $commandTrait = file_get_contents(app_path('modules/selfserve/traits/selfserve_lane_command_t.php')); expect($commandTrait)->not->toBeFalse(); $methodOffset = strpos($commandTrait, 'protected function completeLatestSessionForStop(): void'); expect($methodOffset)->not->toBeFalse(); $methodBody = substr($commandTrait, (int)$methodOffset, 1500); expect($methodBody)->toContain(<<<'PHP' (new \modules\selfserve\classes\selfserve_wash_flow())->completeLatestSessionForLane( $this->id, $this->getLicensePlate() ?: null, $this->getCustomerNumber() ?: null, method_exists($this, 'getLastInvoiceOrderId') ? $this->getLastInvoiceOrderId() : null, false ); PHP); });