tenantService = $this->createMock(TenantService::class); $this->tester = new CommandTester( new TenantDeleteCommand($this->tenantService, new NullLogger()) ); } private function givenTenant(?array $domains = ['acme.test', 'acme.example']): TenantObject { $tenant = (new TenantObject()) ->setId('6a5d84498ac04b41fa0c43a2') ->setIdentifier('acme') ->setLabel('Acme Inc') ->setEnabled(true); if ($domains !== null) { $tenant->setDomains(new DomainCollection($domains)); } $this->tenantService->method('fetchById')->with('acme')->willReturn($tenant); return $tenant; } public function testFailsWhenTenantNotFound(): void { $this->tenantService->method('fetchById')->willReturn(null); $this->tenantService->expects($this->never())->method('destroy'); $status = $this->tester->execute(['identifier' => 'missing']); $this->assertSame(Command::FAILURE, $status); $this->assertStringContainsString('not found', $this->tester->getDisplay()); } public function testForceDeletesWithoutPrompt(): void { $tenant = $this->givenTenant(); $this->tenantService->expects($this->once())->method('destroy')->with($tenant); $status = $this->tester->execute(['identifier' => 'acme', '--force' => true]); $this->assertSame(Command::SUCCESS, $status); $this->assertStringContainsString('deleted successfully', $this->tester->getDisplay()); } public function testDeletesWhenConfirmationMatchesPrimaryDomain(): void { $tenant = $this->givenTenant(); $this->tenantService->expects($this->once())->method('destroy')->with($tenant); $this->tester->setInputs(['acme.test']); $status = $this->tester->execute(['identifier' => 'acme'], ['interactive' => true]); $this->assertSame(Command::SUCCESS, $status); $this->assertStringContainsString("Type 'acme.test' to confirm deletion", $this->tester->getDisplay()); $this->assertStringContainsString('deleted successfully', $this->tester->getDisplay()); } public function testCancelsWhenConfirmationDoesNotMatch(): void { $this->givenTenant(); $this->tenantService->expects($this->never())->method('destroy'); $this->tester->setInputs(['wrong.test']); $status = $this->tester->execute(['identifier' => 'acme'], ['interactive' => true]); $this->assertSame(Command::FAILURE, $status); $this->assertStringContainsString('Confirmation did not match', $this->tester->getDisplay()); } public function testConfirmationFallsBackToIdentifierWhenTenantHasNoDomains(): void { $tenant = $this->givenTenant(null); $this->tenantService->expects($this->once())->method('destroy')->with($tenant); $this->tester->setInputs(['acme']); $status = $this->tester->execute(['identifier' => 'acme'], ['interactive' => true]); $this->assertSame(Command::SUCCESS, $status); $this->assertStringContainsString("Type 'acme' to confirm deletion", $this->tester->getDisplay()); } public function testCancelsWhenRunNonInteractivelyWithoutForce(): void { $this->givenTenant(); $this->tenantService->expects($this->never())->method('destroy'); $status = $this->tester->execute(['identifier' => 'acme'], ['interactive' => false]); $this->assertSame(Command::FAILURE, $status); } public function testFailsWhenDestroyThrows(): void { $this->givenTenant(); $this->tenantService ->method('destroy') ->willThrowException(new \RuntimeException('datastore unavailable')); $status = $this->tester->execute(['identifier' => 'acme', '--force' => true]); $this->assertSame(Command::FAILURE, $status); $this->assertStringContainsString('datastore unavailable', $this->tester->getDisplay()); } }