Files
2026-07-13 13:12:33 +08:00

118 lines
4.0 KiB
Python

"""Behavior pins for the shared migration secret ``.env`` writer.
Complements ``test_secret_file_perms.py`` (which pins mode/atomicity/error
propagation through the full migrators): these tests cover the two
compatibility guarantees the consolidated writer adds — symlinked ``.env``
files are written through (the link survives), and a writable ``.env``
inside a non-writable directory falls back to an in-place rewrite instead
of failing the migration partway through.
"""
from __future__ import annotations
import json
import os
import stat
from pathlib import Path
import pytest
from opensquilla.migration.env_file import write_secret_env_file
def test_write_secret_env_file_writes_through_symlink(tmp_path: Path) -> None:
real = tmp_path / "dotfiles" / "env-store"
real.parent.mkdir()
real.write_text("EXISTING_KEY=keep-me\n", encoding="utf-8")
link = tmp_path / ".env"
link.symlink_to(real)
write_secret_env_file(link, ["EXISTING_KEY=keep-me", "NEW_KEY=synthetic"])
# The dotfiles-managed link must survive and the target must receive
# the merged content — replacing the link with a regular file silently
# disconnects the user's env store.
assert link.is_symlink()
content = real.read_text(encoding="utf-8")
assert "EXISTING_KEY=keep-me" in content
assert "NEW_KEY=synthetic" in content
if os.name != "nt":
assert stat.S_IMODE(os.stat(real).st_mode) == 0o600
@pytest.mark.skipif(os.name == "nt", reason="POSIX permission bits are meaningless on Windows")
@pytest.mark.skipif(
hasattr(os, "geteuid") and os.geteuid() == 0, reason="root bypasses file modes"
)
def test_write_secret_env_file_falls_back_when_directory_not_writable(
tmp_path: Path,
) -> None:
home = tmp_path / "opensquilla-home"
home.mkdir()
env_path = home / ".env"
env_path.write_text("OLD_KEY=old\n", encoding="utf-8")
os.chmod(home, 0o555) # directory read/execute only; file stays writable
try:
write_secret_env_file(env_path, ["OLD_KEY=old", "NEW_KEY=synthetic"])
finally:
os.chmod(home, 0o755)
content = env_path.read_text(encoding="utf-8")
assert "NEW_KEY=synthetic" in content
assert "OLD_KEY=old" in content
@pytest.mark.skipif(os.name == "nt", reason="POSIX permission bits are meaningless on Windows")
@pytest.mark.skipif(
hasattr(os, "geteuid") and os.geteuid() == 0, reason="root bypasses file modes"
)
def test_write_secret_env_file_unwritable_dir_without_file_still_raises(
tmp_path: Path,
) -> None:
home = tmp_path / "opensquilla-home"
home.mkdir()
os.chmod(home, 0o555)
try:
with pytest.raises(PermissionError):
write_secret_env_file(home / ".env", ["KEY=synthetic"])
finally:
os.chmod(home, 0o755)
def test_openclaw_migration_preserves_symlinked_env(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""End-to-end: a dotfiles-managed ~/.opensquilla/.env symlink survives a
secrets migration, with the merged content written through the link."""
from opensquilla.migration.openclaw import MigrationOptions, OpenClawMigrator
source = tmp_path / ".openclaw"
source.mkdir()
(source / "openclaw.json").write_text(
json.dumps({"agents": {"defaults": {}}}), encoding="utf-8"
)
(source / ".env").write_text("OPENAI_API_KEY=sk-dummy-openclaw\n", encoding="utf-8")
home = tmp_path / "opensquilla-home"
home.mkdir()
monkeypatch.setenv("OPENSQUILLA_STATE_DIR", str(home))
monkeypatch.delenv("OPENSQUILLA_GATEWAY_CONFIG_PATH", raising=False)
real = tmp_path / "dotfiles-env"
real.write_text("EXISTING_KEY=keep-me\n", encoding="utf-8")
(home / ".env").symlink_to(real)
OpenClawMigrator(
MigrationOptions(
source=source,
config_path=tmp_path / "config.toml",
apply=True,
migrate_secrets=True,
)
).migrate()
assert (home / ".env").is_symlink()
content = real.read_text(encoding="utf-8")
assert "EXISTING_KEY=keep-me" in content
assert "OPENAI_API_KEY=sk-dummy-openclaw" in content