runtimeSyncResult = [ 'command' => 'module:sync', 'status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored_total' => 2, 'duration_ms' => 88, 'steps' => [ 'migrate' => ['status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0], 'permissions-sync' => ['status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 1], 'build' => ['status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0], 'assets-sync' => ['status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 1], ], 'message' => 'done', ]; $command = new RuntimeSyncCommand($runner); ob_start(); $exitCode = $command->execute([], ['format' => 'json']); $output = (string) ob_get_clean(); self::assertSame(0, $exitCode); /** @var array $decoded */ $decoded = json_decode($output, true, 512, JSON_THROW_ON_ERROR); self::assertSame('module:sync', $decoded['command']); self::assertSame('ok', $decoded['status']); self::assertSame(0, $decoded['exit_code']); self::assertArrayHasKey('steps', $decoded); self::assertArrayHasKey('migrate', $decoded['steps']); self::assertArrayHasKey('assets-sync', $decoded['steps']); } public function testRuntimeSyncInvalidFormatReturnsOne(): void { $command = new RuntimeSyncCommand(new FakeModuleRunner()); $exitCode = $command->execute([], ['format' => 'xml']); self::assertSame(1, $exitCode); } public function testDeactivateDoesNotMutateGlobalArgvAndPassesOptions(): void { $runner = new FakeModuleRunner(); $runner->deactivateResult = [ 'command' => 'module:deactivate', 'status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0, 'message' => 'ok', ]; $command = new DeactivateCommand($runner); $argvBefore = ['bin/console', 'list']; $GLOBALS['argv'] = $argvBefore; $exitCode = $command->execute(['addressbook'], ['confirm' => true]); self::assertSame(0, $exitCode); self::assertSame('addressbook', $runner->deactivateModuleId); self::assertTrue($runner->deactivateConfirm); self::assertFalse($runner->deactivateDryRun); self::assertSame($argvBefore, $GLOBALS['argv']); } } final class FakeModuleRunner implements ModuleRunnerInterface { /** @var array */ public array $runtimeSyncResult = [ 'command' => 'module:sync', 'status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored_total' => 0, 'duration_ms' => 0, 'steps' => [ 'migrate' => ['status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0], 'permissions-sync' => ['status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0], 'build' => ['status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0], 'assets-sync' => ['status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0], ], 'message' => 'ok', ]; /** @var array */ public array $deactivateResult = [ 'command' => 'module:deactivate', 'status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0, 'message' => 'ok', ]; public string $deactivateModuleId = ''; public bool $deactivateConfirm = false; public bool $deactivateDryRun = false; public function migrate(): array { return [ 'command' => 'module:migrate', 'status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0, 'message' => 'ok', ]; } public function permissionsSync(): array { return [ 'command' => 'module:permissions-sync', 'status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0, 'created' => 0, 'updated' => 0, 'unchanged' => 0, 'deactivated' => 0, 'total' => 0, 'message' => 'ok', ]; } public function build(): array { return [ 'command' => 'module:build', 'status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0, 'core_entries' => 0, 'module_entries' => 0, 'message' => 'ok', ]; } public function assetsSync(): array { return [ 'command' => 'module:assets-sync', 'status' => 'ok', 'exit_code' => 0, 'vendor_warnings_ignored' => 0, 'mode' => 'symlink', 'created' => 0, 'updated' => 0, 'removed' => 0, 'unchanged' => 0, 'message' => 'ok', ]; } public function deactivate(string $moduleId, bool $confirm, bool $dryRun): array { $this->deactivateModuleId = $moduleId; $this->deactivateConfirm = $confirm; $this->deactivateDryRun = $dryRun; return $this->deactivateResult; } public function runtimeSync(): array { return $this->runtimeSyncResult; } }