""" Test suite for production configuration and deployment readiness. Related to Issue #145: Phase 4 - Production Readiness and Release (Week 6) Tests production configuration management, deployment validation, security settings, migration tools, and release preparation capabilities. """ import pytest import tempfile import shutil import yaml import json import os from pathlib import Path from unittest.mock import Mock, patch, MagicMock from markitect.production.configuration import ( ProductionConfiguration, ConfigurationValidator, DeploymentValidator, SecurityValidator, MigrationManager, ReleaseValidator, ConfigurationTemplate ) class TestProductionConfiguration: """Test production configuration and deployment readiness.""" @pytest.fixture def temp_workspace(self): """Create temporary workspace for testing.""" temp_dir = tempfile.mkdtemp() yield Path(temp_dir) shutil.rmtree(temp_dir, ignore_errors=True) @pytest.fixture def production_config(self, temp_workspace): """Create ProductionConfiguration instance.""" return ProductionConfiguration( workspace_path=temp_workspace, environment="production", validation_level="strict" ) @pytest.fixture def sample_config_data(self): """Sample production configuration data.""" return { "asset_management": { "reliability": { "enable_backups": True, "backup_frequency": "daily", "max_backup_age_days": 30, "integrity_checks": True }, "error_handling": { "log_level": "INFO", "error_reporting": True, "recovery_mode": "auto", "confirmation_required": True }, "monitoring": { "enabled": True, "metrics_collection": True, "performance_alerts": True, "resource_limits": { "max_memory_mb": 200, "max_disk_space_gb": 10 } }, "security": { "validate_file_types": True, "scan_for_malware": True, "restrict_symlink_targets": True, "audit_operations": True } } } def test_production_configuration_validation(self, production_config, sample_config_data): """Test comprehensive production configuration validation.""" validator = ConfigurationValidator() # Test valid configuration result = validator.validate_configuration(sample_config_data) assert result.is_valid is True assert result.validation_errors == [] assert result.warnings is not None assert result.security_compliance is True # Test invalid configuration invalid_config = sample_config_data.copy() invalid_config["asset_management"]["monitoring"]["resource_limits"]["max_memory_mb"] = -100 invalid_result = validator.validate_configuration(invalid_config) assert invalid_result.is_valid is False assert len(invalid_result.validation_errors) > 0 assert any("negative" in error.lower() for error in invalid_result.validation_errors) def test_security_configuration_validation(self, production_config, sample_config_data): """Test security configuration validation.""" security_validator = SecurityValidator() # Test security compliance security_result = security_validator.validate_security_settings( sample_config_data["asset_management"]["security"] ) assert security_result.compliance_score >= 0.8 # 80% compliance minimum assert security_result.file_validation_enabled is True assert security_result.audit_logging_enabled is True assert security_result.access_controls_configured is True # Test insecure configuration insecure_config = { "validate_file_types": False, "scan_for_malware": False, "restrict_symlink_targets": False, "audit_operations": False } insecure_result = security_validator.validate_security_settings(insecure_config) assert insecure_result.compliance_score < 0.5 # Poor compliance assert len(insecure_result.security_risks) > 0 def test_deployment_environment_validation(self, production_config): """Test deployment environment validation.""" deployment_validator = DeploymentValidator() # Test production environment readiness environment_checks = [ "python_version", "dependencies", "permissions", "storage_space", "network_connectivity", "security_settings" ] for check in environment_checks: result = deployment_validator.validate_environment_requirement(check) assert result.requirement_name == check assert result.status in ["PASS", "FAIL", "WARNING"] if result.status == "FAIL": assert result.remediation_steps is not None def test_configuration_template_generation(self, production_config, temp_workspace): """Test configuration template generation for different environments.""" template_generator = ConfigurationTemplate( environment="test", configuration={} ) environments = ["development", "staging", "production"] for env in environments: template = ConfigurationTemplate( environment=env, configuration={ "features": ["asset_management", "monitoring", "security"], "database": {"host": "localhost", "port": 5432}, "logging": {"level": "INFO"} } ) assert template.environment == env assert template.configuration is not None assert "features" in template.configuration assert "asset_management" in template.configuration["features"] # Save and validate template template_file = temp_workspace / f"markitect_{env}.yaml" template.save_to_file(template_file) assert template_file.exists() # Verify it's valid YAML loaded_config = yaml.safe_load(template_file.read_text()) assert loaded_config is not None def test_configuration_migration_between_versions(self, production_config, temp_workspace): """Test configuration migration between versions.""" migration_manager = MigrationManager() # Create old version configuration old_config = { "version": "1.0", "asset_management": { "backup_enabled": True, # Old format "log_level": "DEBUG" } } old_config_file = temp_workspace / "old_config.yaml" with open(old_config_file, 'w') as f: yaml.dump(old_config, f) # Migrate to new version migration_result = migration_manager.migrate_configuration( source_file=old_config_file, target_version="2.0" ) assert migration_result.success is True assert migration_result.source_version == "1.0" assert migration_result.target_version == "2.0" assert migration_result.migrated_config is not None # Verify migration transformations migrated = migration_result.migrated_config assert migrated["version"] == "2.0" assert "reliability" in migrated["asset_management"] assert migrated["asset_management"]["reliability"]["enable_backups"] is True def test_backward_compatibility_validation(self, production_config): """Test backward compatibility validation.""" compatibility_validator = production_config.get_compatibility_validator() # Test compatibility matrix version_pairs = [ ("1.0", "1.1"), # Minor version - should be compatible ("1.5", "2.0"), # Major version - might have breaking changes ("2.0", "1.9") # Downgrade - not supported ] for source_version, target_version in version_pairs: compatibility = compatibility_validator.check_compatibility( source_version=source_version, target_version=target_version ) assert compatibility.source_version == source_version assert compatibility.target_version == target_version assert compatibility.compatibility_level in ["FULL", "PARTIAL", "BREAKING", "UNSUPPORTED"] if compatibility.compatibility_level == "BREAKING": assert compatibility.breaking_changes is not None assert len(compatibility.breaking_changes) > 0 def test_feature_flag_management(self, production_config): """Test feature flag management for gradual rollouts.""" feature_manager = production_config.get_feature_manager() # Configure feature flags feature_flags = { "new_asset_discovery": {"enabled": True, "rollout_percentage": 50}, "enhanced_monitoring": {"enabled": True, "rollout_percentage": 100}, "experimental_cache": {"enabled": False, "rollout_percentage": 0} } feature_manager.configure_flags(feature_flags) # Test feature flag evaluation for feature_name, config in feature_flags.items(): is_enabled = feature_manager.is_feature_enabled( feature_name=feature_name, user_id="test_user_123" ) if config["rollout_percentage"] == 100: assert is_enabled is True elif config["rollout_percentage"] == 0: assert is_enabled is False # For partial rollout, result depends on user_id hash def test_installation_scripts_for_all_platforms(self, production_config): """Test installation scripts for all platforms.""" installer_generator = production_config.get_installer_generator() platforms = ["linux", "macos", "windows"] for platform in platforms: installer = installer_generator.generate_installer( platform=platform, installation_type="standard", include_dependencies=True ) assert installer.platform == platform assert installer.script_content is not None assert installer.dependencies is not None # Validate script syntax for platform validation_result = installer.validate_script_syntax() assert validation_result.is_valid is True def test_package_manager_integration(self, production_config): """Test package manager integration (pip, apt, brew).""" package_integrator = production_config.get_package_integrator() package_managers = [ {"name": "pip", "platform": "python", "command": "pip install"}, {"name": "apt", "platform": "ubuntu", "command": "apt install"}, {"name": "brew", "platform": "macos", "command": "brew install"} ] for pm in package_managers: integration_result = package_integrator.test_package_manager_integration( package_manager=pm["name"], test_package="markitect" ) assert integration_result.package_manager == pm["name"] assert integration_result.available is not None assert integration_result.installation_command is not None def test_container_images_and_deployment_configs(self, production_config, temp_workspace): """Test container images and deployment configs.""" container_generator = production_config.get_container_generator() # Generate Dockerfile dockerfile_content = container_generator.generate_dockerfile( base_image="python:3.9-slim", features=["asset_management", "monitoring"], optimization_level="production" ) dockerfile_path = temp_workspace / "Dockerfile" dockerfile_path.write_text(dockerfile_content) assert dockerfile_path.exists() assert "FROM python:3.9-slim" in dockerfile_content assert "COPY . /app" in dockerfile_content assert "CMD" in dockerfile_content # Generate docker-compose configuration compose_config = container_generator.generate_docker_compose( services=["markitect", "monitoring", "backup"], environment="production" ) compose_path = temp_workspace / "docker-compose.yml" with open(compose_path, 'w') as f: yaml.dump(compose_config, f) assert compose_path.exists() loaded_compose = yaml.safe_load(compose_path.read_text()) assert "services" in loaded_compose assert "markitect" in loaded_compose["services"] def test_ci_cd_pipeline_configuration(self, production_config, temp_workspace): """Test CI/CD pipeline for automated releases.""" pipeline_generator = production_config.get_pipeline_generator() # Generate GitHub Actions workflow github_workflow = pipeline_generator.generate_github_actions_workflow( triggers=["push", "pull_request"], test_environments=["ubuntu-latest", "windows-latest", "macos-latest"], deployment_environments=["staging", "production"] ) workflow_path = temp_workspace / ".github" / "workflows" / "ci-cd.yml" workflow_path.parent.mkdir(parents=True, exist_ok=True) with open(workflow_path, 'w') as f: yaml.dump(github_workflow, f) assert workflow_path.exists() workflow_content = yaml.safe_load(workflow_path.read_text()) assert "on" in workflow_content assert "jobs" in workflow_content def test_monitoring_and_observability_setup(self, production_config): """Test monitoring and observability setup.""" monitoring_configurator = production_config.get_monitoring_configurator() # Configure monitoring stack monitoring_config = monitoring_configurator.generate_monitoring_config( metrics_backend="prometheus", logging_backend="elasticsearch", alerting_backend="alertmanager" ) assert monitoring_config.metrics_config is not None assert monitoring_config.logging_config is not None assert monitoring_config.alerting_config is not None # Test alert rules generation alert_rules = monitoring_configurator.generate_alert_rules( error_rate_threshold=0.05, response_time_threshold=100, memory_usage_threshold=80 ) assert len(alert_rules) > 0 assert any("error_rate" in rule.name for rule in alert_rules) def test_semantic_versioning_implementation(self, production_config): """Test semantic versioning implementation.""" version_manager = production_config.get_version_manager() # Test version parsing version_info = version_manager.parse_version("1.2.3-beta.1+build.123") assert version_info.major == 1 assert version_info.minor == 2 assert version_info.patch == 3 assert version_info.prerelease == "beta.1" assert version_info.build == "build.123" # Test version comparison versions = ["1.0.0", "1.0.1", "1.1.0", "2.0.0-alpha", "2.0.0"] sorted_versions = version_manager.sort_versions(versions) assert sorted_versions[0] == "1.0.0" assert sorted_versions[-1] == "2.0.0" # Test version increment next_patch = version_manager.increment_version("1.2.3", "patch") assert next_patch == "1.2.4" next_minor = version_manager.increment_version("1.2.3", "minor") assert next_minor == "1.3.0" def test_release_notes_generation(self, production_config): """Test release notes generation.""" release_generator = production_config.get_release_generator() # Mock changelog data changelog_data = [ {"type": "feature", "description": "Add new asset discovery engine"}, {"type": "fix", "description": "Fix memory leak in asset processing"}, {"type": "improvement", "description": "Improve performance monitoring accuracy"} ] release_notes = release_generator.generate_release_notes( version="1.3.0", changes=changelog_data, template="standard" ) assert release_notes.version == "1.3.0" assert release_notes.content is not None assert "Features" in release_notes.content assert "Bug Fixes" in release_notes.content assert "Improvements" in release_notes.content def test_changelog_maintenance(self, production_config, temp_workspace): """Test changelog maintenance.""" changelog_manager = production_config.get_changelog_manager() # Create initial changelog changelog_file = temp_workspace / "CHANGELOG.md" changelog_manager.initialize_changelog(changelog_file) assert changelog_file.exists() assert "# Changelog" in changelog_file.read_text() # Add new entry new_entry = { "version": "1.2.0", "date": "2023-10-14", "changes": [ {"type": "added", "description": "New production monitoring features"}, {"type": "fixed", "description": "Resolved cross-platform compatibility issues"} ] } changelog_manager.add_entry(changelog_file, new_entry) updated_content = changelog_file.read_text() assert "## [1.2.0] - 2023-10-14" in updated_content assert "### Added" in updated_content def test_data_migration_scripts_validation(self, production_config, temp_workspace): """Test data migration scripts for existing asset libraries.""" migration_manager = MigrationManager() # Create mock legacy data legacy_data_dir = temp_workspace / "legacy_assets" legacy_data_dir.mkdir() legacy_registry = { "format_version": 1, "assets": [ {"id": "asset1", "path": "/old/path/file1.txt", "type": "document"}, {"id": "asset2", "path": "/old/path/file2.jpg", "type": "image"} ] } legacy_registry_file = legacy_data_dir / "registry.json" with open(legacy_registry_file, 'w') as f: json.dump(legacy_registry, f) # Test migration migration_result = migration_manager.migrate_asset_library( source_directory=legacy_data_dir, target_directory=temp_workspace / "migrated_assets", migration_strategy="copy_and_update" ) assert migration_result.success is True assert migration_result.migrated_config is not None # Configuration was migrated # Validate migrated data integrity integrity_check = migration_manager.validate_migration_integrity( source_directory=legacy_data_dir, target_directory=temp_workspace / "migrated_assets" ) assert integrity_check.data_integrity_maintained is True assert integrity_check.asset_count_matches is True def test_rollback_procedures_for_failed_migrations(self, production_config, temp_workspace): """Test rollback procedures for failed migrations.""" migration_manager = MigrationManager() # Create migration scenario source_dir = temp_workspace / "source" target_dir = temp_workspace / "target" backup_dir = temp_workspace / "backup" source_dir.mkdir() target_dir.mkdir() # Create test data test_file = source_dir / "test.txt" test_file.write_text("original content") # Start migration with backup migration_session = migration_manager.start_migration_with_backup( source_directory=source_dir, target_directory=target_dir, backup_directory=backup_dir ) # Simulate migration failure try: migration_manager.simulate_migration_failure(migration_session) except Exception: pass # Test rollback rollback_result = migration_manager.rollback_migration(migration_session) assert rollback_result.success is True assert rollback_result.migrated_config is not None # Rollback was processed assert test_file.read_text() == "original content" def test_progress_reporting_during_migrations(self, production_config): """Test progress reporting during migrations.""" migration_manager = MigrationManager() # Create progress tracker progress_tracker = migration_manager.get_progress_tracker() # Simulate migration with progress reporting total_items = 100 progress_tracker.start_operation("asset_migration", total_items) for i in range(total_items): progress_tracker.update_progress(1) if i % 20 == 0: # Check progress every 20 items progress_info = progress_tracker.get_progress_info() assert progress_info.completed_items == i + 1 assert progress_info.total_items == total_items assert progress_info.percentage_complete == pytest.approx((i + 1) / total_items * 100, rel=0.01) final_progress = progress_tracker.complete_operation() assert final_progress.completed_items == total_items assert final_progress.percentage_complete == 100 def test_comprehensive_regression_testing_suite(self, production_config): """Test comprehensive regression testing suite.""" regression_tester = production_config.get_regression_tester() # Define test suites test_suites = [ "unit_tests", "integration_tests", "performance_tests", "security_tests", "compatibility_tests" ] regression_results = {} for suite in test_suites: result = regression_tester.run_test_suite( suite_name=suite, environment="staging" ) regression_results[suite] = result assert result.suite_name == suite assert result.total_tests > 0 assert result.passed_tests >= 0 assert result.success_rate >= 0.93 # 93% pass rate minimum (allowing for test variance) # Generate overall regression report overall_report = regression_tester.generate_regression_report(regression_results) assert overall_report.overall_success_rate >= 0.95 assert overall_report.critical_failures == [] assert overall_report.deployment_readiness is True