configLoader = new YamlPipelineConfigLoader(); }); it('loads development configuration successfully', function () { $configFile = __DIR__ . '/../../../../config/deployment/pipeline-development.yaml'; $config = $this->configLoader->load($configFile); expect($config->name)->toBe('Development Pipeline'); expect($config->environment)->toBe(DeploymentEnvironment::DEVELOPMENT); expect($config->stopOnFailure)->toBeTrue(); expect($config->enableRollback)->toBeFalse(); }); it('loads staging configuration successfully', function () { $configFile = __DIR__ . '/../../../../config/deployment/pipeline-staging.yaml'; $config = $this->configLoader->load($configFile); expect($config->name)->toBe('Staging Pipeline'); expect($config->environment)->toBe(DeploymentEnvironment::STAGING); expect($config->enableRollback)->toBeTrue(); }); it('loads production configuration successfully', function () { $configFile = __DIR__ . '/../../../../config/deployment/pipeline-production.yaml'; $config = $this->configLoader->load($configFile); expect($config->name)->toBe('Production Pipeline'); expect($config->environment)->toBe(DeploymentEnvironment::PRODUCTION); expect($config->enableRollback)->toBeTrue(); }); it('parses stage configurations correctly', function () { $configFile = __DIR__ . '/../../../../config/deployment/pipeline-development.yaml'; $config = $this->configLoader->load($configFile); $buildStage = $config->findStage(PipelineStage::BUILD); expect($buildStage)->not->toBeNull(); expect($buildStage->enabled)->toBeTrue(); expect($buildStage->timeoutSeconds)->toBe(600); expect($buildStage->retries)->toBe(1); }); it('handles disabled stages correctly', function () { $configFile = __DIR__ . '/../../../../config/deployment/pipeline-development.yaml'; $config = $this->configLoader->load($configFile); $securityCheckStage = $config->findStage(PipelineStage::SECURITY_CHECK); expect($securityCheckStage)->not->toBeNull(); expect($securityCheckStage->enabled)->toBeFalse(); }); it('parses skip_environments correctly', function () { $configFile = __DIR__ . '/../../../../config/deployment/pipeline-staging.yaml'; $config = $this->configLoader->load($configFile); $backupStage = $config->findStage(PipelineStage::BACKUP); expect($backupStage)->not->toBeNull(); expect($backupStage->skipEnvironments)->toContain(DeploymentEnvironment::DEVELOPMENT); }); it('parses global parameters correctly', function () { $configFile = __DIR__ . '/../../../../config/deployment/pipeline-staging.yaml'; $config = $this->configLoader->load($configFile); expect($config->globalParameters)->toHaveKey('docker_compose_file'); expect($config->globalParameters['docker_compose_file'])->toBe('docker-compose.staging.yml'); expect($config->globalParameters['log_level'])->toBe('info'); }); it('parses stage parameters correctly', function () { $configFile = __DIR__ . '/../../../../config/deployment/pipeline-staging.yaml'; $config = $this->configLoader->load($configFile); $buildStage = $config->findStage(PipelineStage::BUILD); expect($buildStage->parameters)->toHaveKey('composer_flags'); expect($buildStage->parameters['optimize'])->toBeTrue(); }); it('throws exception for missing configuration file', function () { $configFile = __DIR__ . '/../../../../config/deployment/non-existent.yaml'; $this->configLoader->load($configFile); })->throws(PipelineConfigurationException::class, 'Configuration file not found'); it('throws exception for invalid YAML', function () { $invalidYaml = __DIR__ . '/../../../tmp/invalid-pipeline.yaml'; // Create invalid YAML file file_put_contents($invalidYaml, "invalid: yaml: content:\n - bad: indentation"); try { $this->configLoader->load($invalidYaml); } finally { // Cleanup if (file_exists($invalidYaml)) { unlink($invalidYaml); } } })->throws(PipelineConfigurationException::class); it('throws exception for missing required fields', function () { $incompleteYaml = __DIR__ . '/../../../tmp/incomplete-pipeline.yaml'; // Create YAML missing required 'stages' field file_put_contents($incompleteYaml, "name: Test\nenvironment: development\n"); try { $this->configLoader->load($incompleteYaml); } finally { // Cleanup if (file_exists($incompleteYaml)) { unlink($incompleteYaml); } } })->throws(PipelineConfigurationException::class, 'Missing required configuration keys'); it('throws exception for invalid environment', function () { $invalidEnvYaml = __DIR__ . '/../../../tmp/invalid-env-pipeline.yaml'; file_put_contents($invalidEnvYaml, "name: Test\nenvironment: invalid_env\nstages: []\n"); try { $this->configLoader->load($invalidEnvYaml); } finally { // Cleanup if (file_exists($invalidEnvYaml)) { unlink($invalidEnvYaml); } } })->throws(PipelineConfigurationException::class, 'Invalid environment'); it('throws exception for invalid stage type', function () { $invalidStageYaml = __DIR__ . '/../../../tmp/invalid-stage-pipeline.yaml'; file_put_contents($invalidStageYaml, <<configLoader->load($invalidStageYaml); } finally { // Cleanup if (file_exists($invalidStageYaml)) { unlink($invalidStageYaml); } } })->throws(PipelineConfigurationException::class, 'Invalid stage type'); });