runImport([$this->hotel(costUnit: null, countryCode: 'ch')]); $this->assertSame(Command::FAILURE, $tester->getStatusCode()); $this->assertStringContainsString('Kostenstellen', $tester->getDisplay()); $this->assertStringNotContainsString('Länderkennungen', $tester->getDisplay()); } public function testItRefusesToRunWhenNoHotelReportsACountryCode(): void { $tester = $this->runImport([$this->hotel(costUnit: '4711', countryCode: null)]); $this->assertSame(Command::FAILURE, $tester->getStatusCode()); $this->assertStringContainsString('Länderkennungen', $tester->getDisplay()); } public function testItNamesBothFieldsWhenNeitherIsReported(): void { $tester = $this->runImport([$this->hotel(costUnit: null, countryCode: null)]); $this->assertSame(Command::FAILURE, $tester->getStatusCode()); $this->assertStringContainsString('Kostenstellen und Länderkennungen', $tester->getDisplay()); } public function testAnEmptyStringDoesNotCountAsAReportedField(): void { $tester = $this->runImport([$this->hotel(costUnit: '', countryCode: '')]); $this->assertSame(Command::FAILURE, $tester->getStatusCode()); $this->assertStringContainsString('Kostenstellen und Länderkennungen', $tester->getDisplay()); } public function testOneHotelCarryingAFieldIsEnoughToPassTheGuard(): void { // most hotels legitimately have no kostenstelle upstream, so the guard asks whether // the field is reported at all, not whether every hotel fills it $tester = $this->runImport([ $this->hotel(costUnit: null, countryCode: null), $this->hotel(costUnit: '4711', countryCode: 'ch'), ]); $display = $tester->getDisplay(); $this->assertStringNotContainsString('Kostenstellen', $display); $this->assertStringNotContainsString('Länderkennungen', $display); } public function testItRefusesToRunWithoutAnyHotelBaseData(): void { $tester = $this->runImport([]); $this->assertSame(Command::FAILURE, $tester->getStatusCode()); $this->assertStringContainsString('Hotel-Stammdaten', $tester->getDisplay()); } /** * @param list $hotels */ private function runImport(array $hotels): CommandTester { $keyed = []; foreach ($hotels as $index => $hotel) { $keyed[$index + 1] = $hotel; } $tester = new CommandTester(new BpnImportCommand( $this->refusingConnection(), $this->provider(HotelDataProvider::class, $keyed), $this->provider(PickupDataProvider::class, []), new NullLogger(), // an auto stubbed listing, so it yields no files. The guards under test all run // before it, and the one case that gets past them stops there without writing. $this->createMock(FilesystemOperator::class), )); $tester->execute([]); return $tester; } /** * @template T of HotelDataProvider|PickupDataProvider * * @param class-string $class * * @return T&MockObject */ private function provider(string $class, array $all): object { $provider = $this->createMock($class); $provider->method('getAll')->willReturn($all); return $provider; } /** * Any query at all means the guard let a write through, which is the whole failure this * covers, so the connection fails the test rather than the command. */ private function refusingConnection(): Connection&MockObject { $connection = $this->createMock(Connection::class); $connection->expects($this->never())->method($this->anything()); return $connection; } private function hotel(?string $costUnit, ?string $countryCode): Hotel { return (new Hotel()) ->setBusProId(163959) ->setCode('LPJGR') ->setName('Sportclub Jenatsch') ->setCostUnit($costUnit) ->setCountryCode($countryCode) ; } }