"""The instance keeps its own SECRET_KEY in the data directory. A deployment that never sets SECRET_KEY must get the same key on every start, or everything sealed under the first one (forge tokens, the backup secret) stops opening after the first container recreate. """ import stat from unittest.mock import patch import pytest from grimoire import config from grimoire.config import SECRET_KEY_FILENAME, load_or_create_secret_key from grimoire.services import secrets def test_the_first_start_writes_a_key_only_its_owner_can_read(tmp_path): key = load_or_create_secret_key(tmp_path) path = tmp_path / SECRET_KEY_FILENAME assert key and len(key) == 64 assert path.read_text().strip() == key assert stat.S_IMODE(path.stat().st_mode) == 0o600 assert [p.name for p in tmp_path.iterdir()] == [SECRET_KEY_FILENAME] def test_every_later_start_reads_the_same_key_and_what_it_sealed_still_opens(tmp_path): first = load_or_create_secret_key(tmp_path) with patch.object(secrets.Config, "SECRET_KEY", first): sealed = secrets.seal("forge-token") second = load_or_create_secret_key(tmp_path) assert second == first with patch.object(secrets.Config, "SECRET_KEY", second): assert secrets.unseal(sealed) == "forge-token" def test_no_data_directory_means_no_kept_key(tmp_path): assert load_or_create_secret_key(tmp_path / "absent") is None def test_an_empty_key_file_is_refused_rather_than_replaced(tmp_path): (tmp_path / SECRET_KEY_FILENAME).write_text("\n") with pytest.raises(RuntimeError, match="is empty"): load_or_create_secret_key(tmp_path) assert (tmp_path / SECRET_KEY_FILENAME).read_text() == "\n" def test_an_explicit_key_wins_and_leaves_the_kept_one_alone(tmp_path, monkeypatch): monkeypatch.setenv("DATA_DIR", str(tmp_path)) monkeypatch.delenv("SECRET_KEY_FILE", raising=False) monkeypatch.delenv("SECRET_KEY", raising=False) kept, source = config._secret_key() assert source == "data" monkeypatch.setenv("SECRET_KEY", "an-operator-key") assert config._secret_key() == ("an-operator-key", "env") monkeypatch.delenv("SECRET_KEY") assert config._secret_key() == (kept, "data") def test_a_secret_key_file_that_is_missing_stops_the_start(tmp_path, monkeypatch): monkeypatch.setenv("DATA_DIR", str(tmp_path)) monkeypatch.delenv("SECRET_KEY", raising=False) monkeypatch.setenv("SECRET_KEY_FILE", str(tmp_path / "no-such-secret")) with pytest.raises(RuntimeError, match="SECRET_KEY_FILE"): config._secret_key() assert not (tmp_path / SECRET_KEY_FILENAME).exists() def test_with_nowhere_to_keep_a_key_the_process_gets_a_temporary_one(tmp_path, monkeypatch): monkeypatch.setenv("DATA_DIR", str(tmp_path / "absent")) monkeypatch.delenv("SECRET_KEY", raising=False) monkeypatch.delenv("SECRET_KEY_FILE", raising=False) key, source = config._secret_key() assert source == "ephemeral" assert key not in config.PLACEHOLDER_SECRET_KEYS and len(key) == 64