value; } } class SelfserveLanePortDepartmentLaneFake { public SelfserveLanePortValueFake $relay_in_id; public SelfserveLanePortValueFake $relay_out_id; public function __construct(string $relayInId, string $relayOutId) { $this->relay_in_id = new SelfserveLanePortValueFake($relayInId); $this->relay_out_id = new SelfserveLanePortValueFake($relayOutId); } } class SelfserveLanePortSwitchFake extends shelly_device_switch { /** @var array */ public array $switchCalls = []; public function switch(bool $on, ?bool $skip_toggle_after = false): array|object|null { $this->switchCalls[] = [ 'id' => $this->id, 'on' => $on, ]; return ['ok' => true]; } } class SelfservePortSwitchOutputFake extends shelly_device_switch { /** @var array */ public array $switchCalls = []; protected function sendShellySwitchRequest(array $parameters): array|object|null { $this->switchCalls[] = $parameters; return ['ok' => true]; } } class SelfserveLanePortControllerHarness { use selfserve_lane_port_controller_t; public int $id = 55; public object $department_lane; public SelfserveLanePortSwitchFake $switchFake; public selfserve_lane_status $laneStatus; public selfserve_lane_state $laneState; public function __construct(string $relayInId, string $relayOutId) { $this->department_lane = new SelfserveLanePortDepartmentLaneFake($relayInId, $relayOutId); $this->switchFake = new SelfserveLanePortSwitchFake(); $this->laneStatus = selfserve_lane_status::AVAILABLE; $this->laneState = selfserve_lane_state::IDLE; } protected function createShellySwitchDevice(): shelly_device_switch { return $this->switchFake; } public function getLaneStatus(): selfserve_lane_status { return $this->laneStatus; } public function setLaneState(selfserve_lane_state $state): void { $this->laneState = $state; } public function logLaneAction(...$args): void {} } it('opens exit port by switching relay_in_id on', function (): void { $lane = new SelfserveLanePortControllerHarness( relayInId: 'relay-in-123', relayOutId: 'relay-out-123' ); $result = $lane->shellyOpenPort(selfserve_lane_port::EXIT); expect($result)->toBeTrue(); expect($lane->switchFake->switchCalls)->toBe([ [ 'id' => 'relay-in-123', 'on' => true, ], ]); }); it('opens entrance port by switching relay_out_id on', function (): void { $lane = new SelfserveLanePortControllerHarness( relayInId: 'relay-in-123', relayOutId: 'relay-out-123' ); $result = $lane->shellyOpenPort(selfserve_lane_port::ENTRANCE); expect($result)->toBeTrue(); expect($lane->switchFake->switchCalls)->toBe([ [ 'id' => 'relay-out-123', 'on' => true, ], ]); }); it('keeps demo relay gate-open queued but skips Shelly switch calls', function (): void { $lane = new SelfserveLanePortControllerHarness( relayInId: 'demo-relay-in', relayOutId: 'demo-relay-out' ); $result = $lane->open(selfserve_lane_port::EXIT); expect($result)->toBeTrue(); expect($lane->laneState)->toBe(selfserve_lane_state::EXIT_PORT_OPEN_QUEUED); expect($lane->switchFake->switchCalls)->toBe([]); }); it('does not print Shelly switch responses to output', function (): void { $switch = new SelfservePortSwitchOutputFake(); $switch->id = 'relay-port-123'; ob_start(); $switch->switch(true); $output = (string)ob_get_clean(); expect($output)->toBe(''); expect($switch->switchCalls)->toHaveCount(1); });