"""Advanced tests for settings bootstrap - template preservation and edge cases.""" import pytest from alfred.settings_bootstrap import ConfigSource, SettingsBootstrap @pytest.fixture def test_toml_with_all_types(tmp_path): """Create test TOML with all setting types.""" toml_content = """ [tool.poetry] name = "test" version = "1.0.0" [tool.poetry.dependencies] python = "^3.14" [tool.alfred.settings_schema.STRING_VAR] type = "string" source = "env" default = "default_string" description = "String variable" category = "test" [tool.alfred.settings_schema.INT_VAR] type = "integer" source = "env" default = 42 description = "Integer variable" category = "test" [tool.alfred.settings_schema.FLOAT_VAR] type = "float" source = "env" default = 3.14 description = "Float variable" category = "test" [tool.alfred.settings_schema.BOOL_VAR] type = "boolean" source = "env" default = true description = "Boolean variable" category = "test" [tool.alfred.settings_schema.SECRET_VAR] type = "secret" source = "generated" secret_rule = "16:hex" description = "Secret variable" category = "security" [tool.alfred.settings_schema.COMPUTED_VAR] type = "computed" source = "computed" compute_from = ["STRING_VAR", "INT_VAR"] compute_template = "{STRING_VAR}_{INT_VAR}" description = "Computed variable" category = "test" """ toml_path = tmp_path / "pyproject.toml" toml_path.write_text(toml_content) return tmp_path @pytest.fixture def test_env_example(tmp_path): """Create .env.example template.""" env_example_content = """# Test configuration STRING_VAR= INT_VAR= FLOAT_VAR= # Boolean settings BOOL_VAR= # Security SECRET_VAR= # Computed values COMPUTED_VAR= # Custom section CUSTOM_VAR=custom_value """ env_example_path = tmp_path / ".env.example" env_example_path.write_text(env_example_content) return env_example_path class TestTemplatePreservation: """Test that .env.example template structure is preserved.""" def test_preserves_comments(self, test_toml_with_all_types, test_env_example): """Test that comments from .env.example are preserved.""" from alfred.settings_schema import load_schema schema = load_schema(test_toml_with_all_types) source = ConfigSource.from_base_dir(test_toml_with_all_types) bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() env_content = source.env_path.read_text() # Check comments are preserved assert "# Test configuration" in env_content assert "# Boolean settings" in env_content assert "# Security" in env_content assert "# Computed values" in env_content def test_preserves_empty_lines(self, test_toml_with_all_types, test_env_example): """Test that empty lines from .env.example are preserved.""" from alfred.settings_schema import load_schema schema = load_schema(test_toml_with_all_types) source = ConfigSource.from_base_dir(test_toml_with_all_types) bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() env_content = source.env_path.read_text() lines = env_content.split("\n") # Check there are empty lines (structure preserved) assert "" in lines def test_preserves_variable_order(self, test_toml_with_all_types, test_env_example): """Test that variable order from .env.example is preserved.""" from alfred.settings_schema import load_schema schema = load_schema(test_toml_with_all_types) source = ConfigSource.from_base_dir(test_toml_with_all_types) bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() env_content = source.env_path.read_text() # Check order is preserved string_pos = env_content.find("STRING_VAR=") int_pos = env_content.find("INT_VAR=") float_pos = env_content.find("FLOAT_VAR=") bool_pos = env_content.find("BOOL_VAR=") assert string_pos < int_pos < float_pos < bool_pos class TestSecretPreservation: """Test that secrets are never overwritten.""" def test_preserves_existing_secrets( self, test_toml_with_all_types, test_env_example ): """Test that existing secrets are preserved across multiple bootstraps.""" from alfred.settings_schema import load_schema schema = load_schema(test_toml_with_all_types) source = ConfigSource.from_base_dir(test_toml_with_all_types) # First bootstrap - generates secret bootstrapper1 = SettingsBootstrap(source, schema) bootstrapper1.bootstrap() env_content_1 = source.env_path.read_text() secret_1 = [ line.split("=")[1] for line in env_content_1.split("\n") if line.startswith("SECRET_VAR=") ][0] # Second bootstrap - should preserve secret bootstrapper2 = SettingsBootstrap(source, schema) bootstrapper2.bootstrap() env_content_2 = source.env_path.read_text() secret_2 = [ line.split("=")[1] for line in env_content_2.split("\n") if line.startswith("SECRET_VAR=") ][0] assert secret_1 == secret_2 assert len(secret_1) == 32 # 16 hex bytes def test_multiple_secrets_preserved(self, tmp_path): """Test that multiple secrets are all preserved.""" toml_content = """ [tool.poetry] name = "test" version = "1.0.0" [tool.alfred.settings_schema.SECRET_1] type = "secret" source = "generated" secret_rule = "16:hex" category = "security" [tool.alfred.settings_schema.SECRET_2] type = "secret" source = "generated" secret_rule = "32:b64" category = "security" [tool.alfred.settings_schema.SECRET_3] type = "secret" source = "generated" secret_rule = "8:hex" category = "security" """ (tmp_path / "pyproject.toml").write_text(toml_content) (tmp_path / ".env.example").write_text("SECRET_1=\nSECRET_2=\nSECRET_3=\n") from alfred.settings_schema import load_schema schema = load_schema(tmp_path) source = ConfigSource.from_base_dir(tmp_path) # First bootstrap bootstrapper1 = SettingsBootstrap(source, schema) bootstrapper1.bootstrap() env_content_1 = source.env_path.read_text() # Second bootstrap bootstrapper2 = SettingsBootstrap(source, schema) bootstrapper2.bootstrap() env_content_2 = source.env_path.read_text() # All secrets should be identical assert env_content_1 == env_content_2 class TestCustomVariables: """Test that custom variables (not in schema) are preserved.""" def test_preserves_custom_variables_from_env( self, test_toml_with_all_types, test_env_example ): """Test that custom variables added to .env are preserved.""" from alfred.settings_schema import load_schema schema = load_schema(test_toml_with_all_types) source = ConfigSource.from_base_dir(test_toml_with_all_types) # First bootstrap bootstrapper1 = SettingsBootstrap(source, schema) bootstrapper1.bootstrap() # Add custom variables to .env with open(source.env_path, "a") as f: f.write("\nMY_CUSTOM_VAR=custom_value\n") f.write("ANOTHER_CUSTOM=another_value\n") # Second bootstrap bootstrapper2 = SettingsBootstrap(source, schema) bootstrapper2.bootstrap() env_content = source.env_path.read_text() # Custom variables should be preserved assert "MY_CUSTOM_VAR=custom_value" in env_content assert "ANOTHER_CUSTOM=another_value" in env_content def test_custom_variables_in_dedicated_section( self, test_toml_with_all_types, test_env_example ): """Test that custom variables are placed in a dedicated section.""" from alfred.settings_schema import load_schema schema = load_schema(test_toml_with_all_types) source = ConfigSource.from_base_dir(test_toml_with_all_types) # Create .env with custom variable source.env_path.write_text("MY_CUSTOM_VAR=test\n") bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() env_content = source.env_path.read_text() # Check custom section exists assert "# --- CUSTOM VARIABLES ---" in env_content assert "MY_CUSTOM_VAR=test" in env_content def test_preserves_custom_from_env_example( self, test_toml_with_all_types, test_env_example ): """Test that custom variables in .env.example are preserved.""" from alfred.settings_schema import load_schema schema = load_schema(test_toml_with_all_types) source = ConfigSource.from_base_dir(test_toml_with_all_types) bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() env_content = source.env_path.read_text() # CUSTOM_VAR is in .env.example but not in schema assert "CUSTOM_VAR=custom_value" in env_content class TestBooleanHandling: """Test that booleans are handled correctly.""" def test_booleans_written_as_lowercase( self, test_toml_with_all_types, test_env_example ): """Test that Python booleans are written as lowercase strings.""" from alfred.settings_schema import load_schema schema = load_schema(test_toml_with_all_types) source = ConfigSource.from_base_dir(test_toml_with_all_types) bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() env_content = source.env_path.read_text() # Boolean should be lowercase assert "BOOL_VAR=true" in env_content assert "BOOL_VAR=True" not in env_content assert "BOOL_VAR=TRUE" not in env_content def test_false_boolean_written_as_lowercase(self, tmp_path): """Test that False is written as 'false'.""" toml_content = """ [tool.poetry] name = "test" version = "1.0.0" [tool.alfred.settings_schema.BOOL_FALSE] type = "boolean" source = "env" default = false category = "test" """ (tmp_path / "pyproject.toml").write_text(toml_content) (tmp_path / ".env.example").write_text("BOOL_FALSE=\n") from alfred.settings_schema import load_schema schema = load_schema(tmp_path) source = ConfigSource.from_base_dir(tmp_path) bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() env_content = source.env_path.read_text() assert "BOOL_FALSE=false" in env_content assert "BOOL_FALSE=False" not in env_content def test_boolean_parsing_from_env(self, tmp_path): """Test that various boolean formats are parsed correctly.""" toml_content = """ [tool.poetry] name = "test" version = "1.0.0" [tool.alfred.settings_schema.BOOL_VAR] type = "boolean" source = "env" default = false category = "test" """ (tmp_path / "pyproject.toml").write_text(toml_content) (tmp_path / ".env.example").write_text("BOOL_VAR=\n") from alfred.settings_schema import load_schema schema = load_schema(tmp_path) source = ConfigSource.from_base_dir(tmp_path) # Test different boolean formats test_cases = [ ("true", True), ("TRUE", True), ("True", True), ("1", True), ("yes", True), ("false", False), ("FALSE", False), ("False", False), ("0", False), ("no", False), ] for input_val, expected in test_cases: source.env_path.write_text(f"BOOL_VAR={input_val}\n") bootstrapper = SettingsBootstrap(source, schema) bootstrapper._load_sources() bootstrapper._resolve_settings() assert bootstrapper.resolved_settings["BOOL_VAR"] == expected class TestComputedVariables: """Test that computed variables are calculated correctly.""" def test_computed_variables_written_to_env( self, test_toml_with_all_types, test_env_example ): """Test that computed variables are written with their computed values.""" from alfred.settings_schema import load_schema schema = load_schema(test_toml_with_all_types) source = ConfigSource.from_base_dir(test_toml_with_all_types) bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() env_content = source.env_path.read_text() # Computed variable should have its computed value assert "COMPUTED_VAR=default_string_42" in env_content def test_computed_uri_example(self, tmp_path): """Test computed URI (like MONGO_URI) is written correctly.""" toml_content = """ [tool.poetry] name = "test" version = "1.0.0" [tool.alfred.settings_schema.DB_HOST] type = "string" source = "env" default = "localhost" category = "database" [tool.alfred.settings_schema.DB_PORT] type = "integer" source = "env" default = 5432 category = "database" [tool.alfred.settings_schema.DB_USER] type = "string" source = "env" default = "user" category = "database" [tool.alfred.settings_schema.DB_PASSWORD] type = "secret" source = "generated" secret_rule = "16:hex" category = "security" [tool.alfred.settings_schema.DB_URI] type = "computed" source = "computed" compute_from = ["DB_USER", "DB_PASSWORD", "DB_HOST", "DB_PORT"] compute_template = "postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/db" category = "database" """ (tmp_path / "pyproject.toml").write_text(toml_content) (tmp_path / ".env.example").write_text( "DB_HOST=\nDB_PORT=\nDB_USER=\nDB_PASSWORD=\nDB_URI=\n" ) from alfred.settings_schema import load_schema schema = load_schema(tmp_path) source = ConfigSource.from_base_dir(tmp_path) bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() env_content = source.env_path.read_text() # Check URI is computed and written assert "DB_URI=postgresql://user:" in env_content assert "@localhost:5432/db" in env_content # Extract password from URI to verify it's the same as DB_PASSWORD import re uri_match = re.search(r"DB_URI=postgresql://user:([^@]+)@", env_content) password_match = re.search(r"DB_PASSWORD=([^\n]+)", env_content) assert uri_match and password_match assert uri_match.group(1) == password_match.group(1) class TestEdgeCases: """Test edge cases and error conditions.""" def test_missing_env_example(self, tmp_path): """Test that missing .env.example raises error.""" toml_content = """ [tool.poetry] name = "test" version = "1.0.0" [tool.alfred.settings_schema.TEST_VAR] type = "string" source = "env" default = "test" category = "test" """ (tmp_path / "pyproject.toml").write_text(toml_content) from alfred.settings_schema import load_schema schema = load_schema(tmp_path) source = ConfigSource.from_base_dir(tmp_path) bootstrapper = SettingsBootstrap(source, schema) with pytest.raises(FileNotFoundError, match=".env.example not found"): bootstrapper.bootstrap() def test_empty_env_example(self, tmp_path): """Test that empty .env.example works.""" toml_content = """ [tool.poetry] name = "test" version = "1.0.0" [tool.alfred.settings_schema.TEST_VAR] type = "string" source = "env" default = "test" category = "test" """ (tmp_path / "pyproject.toml").write_text(toml_content) (tmp_path / ".env.example").write_text("") from alfred.settings_schema import load_schema schema = load_schema(tmp_path) source = ConfigSource.from_base_dir(tmp_path) bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() # Should create .env even if .env.example is empty assert source.env_path.exists() def test_variable_with_equals_in_value(self, tmp_path): """Test that variables with '=' in their value are handled correctly.""" toml_content = """ [tool.poetry] name = "test" version = "1.0.0" [tool.alfred.settings_schema.URL_VAR] type = "string" source = "env" default = "http://example.com?key=value" category = "test" """ (tmp_path / "pyproject.toml").write_text(toml_content) (tmp_path / ".env.example").write_text("URL_VAR=\n") from alfred.settings_schema import load_schema schema = load_schema(tmp_path) source = ConfigSource.from_base_dir(tmp_path) bootstrapper = SettingsBootstrap(source, schema) bootstrapper.bootstrap() env_content = source.env_path.read_text() assert "URL_VAR=http://example.com?key=value" in env_content def test_preserves_existing_values_on_update(self, tmp_path): """Test that existing values are preserved when updating.""" toml_content = """ [tool.poetry] name = "test" version = "1.0.0" [tool.alfred.settings_schema.VAR1] type = "string" source = "env" default = "default1" category = "test" [tool.alfred.settings_schema.VAR2] type = "string" source = "env" default = "default2" category = "test" """ (tmp_path / "pyproject.toml").write_text(toml_content) (tmp_path / ".env.example").write_text("VAR1=\nVAR2=\n") from alfred.settings_schema import load_schema schema = load_schema(tmp_path) source = ConfigSource.from_base_dir(tmp_path) # First bootstrap bootstrapper1 = SettingsBootstrap(source, schema) bootstrapper1.bootstrap() # Modify values source.env_path.write_text("VAR1=custom1\nVAR2=custom2\n") # Second bootstrap bootstrapper2 = SettingsBootstrap(source, schema) bootstrapper2.bootstrap() env_content = source.env_path.read_text() # Custom values should be preserved assert "VAR1=custom1" in env_content assert "VAR2=custom2" in env_content class TestIntegration: """Integration tests with realistic scenarios.""" def test_full_workflow_like_alfred(self, tmp_path): """Test a full workflow similar to Alfred's actual usage.""" toml_content = """ [tool.poetry] name = "alfred" version = "0.1.7" [tool.poetry.dependencies] python = "^3.14" [tool.alfred.settings_schema.ALFRED_VERSION] type = "string" source = "toml" toml_path = "tool.poetry.version" category = "build" export_to_env_make = true [tool.alfred.settings_schema.HOST] type = "string" source = "env" default = "0.0.0.0" category = "app" [tool.alfred.settings_schema.PORT] type = "integer" source = "env" default = 3080 category = "app" [tool.alfred.settings_schema.JWT_SECRET] type = "secret" source = "generated" secret_rule = "32:hex" category = "security" [tool.alfred.settings_schema.MONGO_HOST] type = "string" source = "env" default = "mongodb" category = "database" [tool.alfred.settings_schema.MONGO_PASSWORD] type = "secret" source = "generated" secret_rule = "16:hex" category = "security" [tool.alfred.settings_schema.MONGO_URI] type = "computed" source = "computed" compute_from = ["MONGO_HOST", "MONGO_PASSWORD"] compute_template = "mongodb://user:{MONGO_PASSWORD}@{MONGO_HOST}:27017/db" category = "database" [tool.alfred.settings_schema.DEBUG_MODE] type = "boolean" source = "env" default = false category = "app" """ (tmp_path / "pyproject.toml").write_text(toml_content) env_example_content = """# Application settings HOST=0.0.0.0 PORT=3080 DEBUG_MODE=false # Security JWT_SECRET= # Database MONGO_HOST=mongodb MONGO_PASSWORD= MONGO_URI= # Build info ALFRED_VERSION= """ (tmp_path / ".env.example").write_text(env_example_content) from alfred.settings_schema import load_schema schema = load_schema(tmp_path) source = ConfigSource.from_base_dir(tmp_path) # First bootstrap bootstrapper1 = SettingsBootstrap(source, schema) bootstrapper1.bootstrap() env_content_1 = source.env_path.read_text() # Verify structure assert "# Application settings" in env_content_1 assert "HOST=0.0.0.0" in env_content_1 assert "PORT=3080" in env_content_1 assert "DEBUG_MODE=false" in env_content_1 # lowercase! assert "ALFRED_VERSION=0.1.7" in env_content_1 assert "JWT_SECRET=" in env_content_1 assert len([l for l in env_content_1.split("\n") if "JWT_SECRET=" in l][0]) > 20 assert "MONGO_URI=mongodb://user:" in env_content_1 # Second bootstrap - should preserve everything bootstrapper2 = SettingsBootstrap(source, schema) bootstrapper2.bootstrap() env_content_2 = source.env_path.read_text() # Everything should be identical assert env_content_1 == env_content_2 # Add custom variable with open(source.env_path, "a") as f: f.write("\nMY_CUSTOM_SETTING=test123\n") # Third bootstrap - should preserve custom bootstrapper3 = SettingsBootstrap(source, schema) bootstrapper3.bootstrap() env_content_3 = source.env_path.read_text() assert "MY_CUSTOM_SETTING=test123" in env_content_3 assert "# --- CUSTOM VARIABLES ---" in env_content_3