- Implement DeterministicBuildConfig class for reproducible builds - Compute config hash with normalized JSON and sorted inputs - Derive SOURCE_DATE_EPOCH from config hash (no wall clock dependency) - Create archiso profile with fixed locale, timezone, compression settings - Add tests verifying hash determinism and order independence
62 lines
2.2 KiB
Python
62 lines
2.2 KiB
Python
"""Tests for deterministic build configuration."""
|
|
|
|
from backend.app.services.deterministic import DeterministicBuildConfig
|
|
|
|
|
|
class TestDeterministicBuildConfig:
|
|
"""Test that same inputs produce same outputs."""
|
|
|
|
def test_hash_deterministic(self) -> None:
|
|
"""Same config produces same hash."""
|
|
config = {
|
|
"packages": ["vim", "git", "base"],
|
|
"overlays": [
|
|
{
|
|
"name": "test",
|
|
"files": [{"path": "/etc/test", "content": "hello"}],
|
|
}
|
|
],
|
|
}
|
|
|
|
hash1 = DeterministicBuildConfig.compute_config_hash(config)
|
|
hash2 = DeterministicBuildConfig.compute_config_hash(config)
|
|
|
|
assert hash1 == hash2
|
|
|
|
def test_hash_order_independent(self) -> None:
|
|
"""Package order doesn't affect hash."""
|
|
config1 = {"packages": ["vim", "git", "base"], "overlays": []}
|
|
config2 = {"packages": ["base", "git", "vim"], "overlays": []}
|
|
|
|
hash1 = DeterministicBuildConfig.compute_config_hash(config1)
|
|
hash2 = DeterministicBuildConfig.compute_config_hash(config2)
|
|
|
|
assert hash1 == hash2
|
|
|
|
def test_hash_different_configs(self) -> None:
|
|
"""Different configs produce different hashes."""
|
|
config1 = {"packages": ["vim"], "overlays": []}
|
|
config2 = {"packages": ["emacs"], "overlays": []}
|
|
|
|
hash1 = DeterministicBuildConfig.compute_config_hash(config1)
|
|
hash2 = DeterministicBuildConfig.compute_config_hash(config2)
|
|
|
|
assert hash1 != hash2
|
|
|
|
def test_source_date_epoch_deterministic(self) -> None:
|
|
"""Same hash produces same timestamp."""
|
|
config_hash = "abc123def456"
|
|
|
|
epoch1 = DeterministicBuildConfig.get_source_date_epoch(config_hash)
|
|
epoch2 = DeterministicBuildConfig.get_source_date_epoch(config_hash)
|
|
|
|
assert epoch1 == epoch2
|
|
|
|
def test_source_date_epoch_in_range(self) -> None:
|
|
"""Timestamp is within reasonable range."""
|
|
config_hash = "abc123def456"
|
|
|
|
epoch = DeterministicBuildConfig.get_source_date_epoch(config_hash)
|
|
|
|
# Should be between 2020 and 2030
|
|
assert 1577836800 <= epoch <= 1924991999
|