261 lines
7.9 KiB
Python
261 lines
7.9 KiB
Python
"""Regression tests for CLI profile resolution before home .env loading."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from typer.testing import CliRunner
|
|
|
|
from opensquilla.cli.main import app
|
|
from opensquilla.paths import default_opensquilla_home
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
def _write_profile(home: Path, name: str, env_lines: list[str]) -> Path:
|
|
profile_dir = home / name
|
|
profile_dir.mkdir(parents=True, exist_ok=True)
|
|
(profile_dir / ".env").write_text("\n".join(env_lines) + "\n", encoding="utf-8")
|
|
return profile_dir
|
|
|
|
|
|
def test_cli_profile_loads_selected_profile_env(monkeypatch, tmp_path: Path) -> None:
|
|
home = tmp_path / "profiles"
|
|
_write_profile(home, "default", ["PROFILE_MARK=loaded-default"])
|
|
_write_profile(home, "coder", ["CODER_MARK=loaded-coder"])
|
|
|
|
for key in (
|
|
"OPENSQUILLA_HOME",
|
|
"OPENSQUILLA_PROFILE",
|
|
"OPENSQUILLA_STATE_DIR",
|
|
"PROFILE_MARK",
|
|
"CODER_MARK",
|
|
):
|
|
monkeypatch.delenv(key, raising=False)
|
|
monkeypatch.setenv("OPENSQUILLA_HOME", str(home))
|
|
|
|
result = runner.invoke(app, ["--profile", "coder", "providers", "list", "--json"])
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert os.environ["OPENSQUILLA_PROFILE"] == "coder"
|
|
assert os.environ["CODER_MARK"] == "loaded-coder"
|
|
assert "PROFILE_MARK" not in os.environ
|
|
assert default_opensquilla_home() == home / "coder"
|
|
|
|
|
|
def test_cli_without_profile_keeps_legacy_home(monkeypatch, tmp_path: Path) -> None:
|
|
legacy_home = tmp_path / ".opensquilla"
|
|
legacy_home.mkdir()
|
|
(legacy_home / ".env").write_text("LEGACY_MARK=loaded\n", encoding="utf-8")
|
|
|
|
for key in (
|
|
"OPENSQUILLA_HOME",
|
|
"OPENSQUILLA_PROFILE",
|
|
"OPENSQUILLA_STATE_DIR",
|
|
"LEGACY_MARK",
|
|
):
|
|
monkeypatch.delenv(key, raising=False)
|
|
monkeypatch.setenv("HOME", str(tmp_path))
|
|
|
|
result = runner.invoke(app, ["providers", "list", "--json"])
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert os.environ["LEGACY_MARK"] == "loaded"
|
|
assert default_opensquilla_home() == legacy_home
|
|
|
|
|
|
def test_cli_does_not_reload_same_home_env_after_key_removed(
|
|
monkeypatch, tmp_path: Path
|
|
) -> None:
|
|
legacy_home = tmp_path / ".opensquilla"
|
|
legacy_home.mkdir()
|
|
(legacy_home / ".env").write_text("RELOAD_MARK=loaded\n", encoding="utf-8")
|
|
|
|
for key in (
|
|
"OPENSQUILLA_HOME",
|
|
"OPENSQUILLA_PROFILE",
|
|
"OPENSQUILLA_STATE_DIR",
|
|
"RELOAD_MARK",
|
|
):
|
|
monkeypatch.delenv(key, raising=False)
|
|
monkeypatch.setenv("HOME", str(tmp_path))
|
|
|
|
result = runner.invoke(app, ["providers", "list", "--json"])
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert os.environ["RELOAD_MARK"] == "loaded"
|
|
|
|
monkeypatch.delenv("RELOAD_MARK", raising=False)
|
|
|
|
result = runner.invoke(app, ["providers", "list", "--json"])
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert "RELOAD_MARK" not in os.environ
|
|
|
|
|
|
def test_cli_rejects_invalid_profile(monkeypatch) -> None:
|
|
monkeypatch.delenv("OPENSQUILLA_STATE_DIR", raising=False)
|
|
|
|
result = runner.invoke(app, ["--profile", "../escape", "providers", "list"])
|
|
|
|
assert result.exit_code != 0
|
|
assert "lowercase letters" in result.output
|
|
|
|
|
|
def test_env_load_uses_provided_home(monkeypatch, tmp_path: Path) -> None:
|
|
from opensquilla.env import load_env
|
|
|
|
home = tmp_path / "custom"
|
|
home.mkdir(parents=True)
|
|
(home / ".env").write_text("CUSTOM_MARK=ok\n", encoding="utf-8")
|
|
|
|
for key in (
|
|
"OPENSQUILLA_HOME",
|
|
"OPENSQUILLA_PROFILE",
|
|
"OPENSQUILLA_STATE_DIR",
|
|
"CUSTOM_MARK",
|
|
):
|
|
monkeypatch.delenv(key, raising=False)
|
|
|
|
assert load_env(home=home) >= 1
|
|
assert os.environ["CUSTOM_MARK"] == "ok"
|
|
|
|
|
|
def test_cli_profile_env_wins_over_legacy_home_for_same_key(
|
|
monkeypatch, tmp_path: Path
|
|
) -> None:
|
|
legacy_home = tmp_path / ".opensquilla"
|
|
profiles_root = legacy_home / "profiles"
|
|
legacy_home.mkdir()
|
|
(legacy_home / ".env").write_text("PROFILE_SHARED_MARK=legacy\n", encoding="utf-8")
|
|
_write_profile(profiles_root, "coder", ["PROFILE_SHARED_MARK=coder"])
|
|
|
|
for key in (
|
|
"OPENSQUILLA_HOME",
|
|
"OPENSQUILLA_PROFILE",
|
|
"OPENSQUILLA_STATE_DIR",
|
|
"PROFILE_SHARED_MARK",
|
|
):
|
|
monkeypatch.delenv(key, raising=False)
|
|
monkeypatch.setenv("HOME", str(tmp_path))
|
|
cwd = tmp_path / "cwd"
|
|
cwd.mkdir()
|
|
monkeypatch.chdir(cwd)
|
|
|
|
result = runner.invoke(app, ["--profile", "coder", "providers", "list", "--json"])
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert os.environ["PROFILE_SHARED_MARK"] == "coder"
|
|
assert default_opensquilla_home() == profiles_root / "coder"
|
|
|
|
|
|
def test_cli_profile_env_wins_on_cold_start_import(tmp_path: Path) -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
cwd = tmp_path / "cwd"
|
|
cwd.mkdir()
|
|
legacy_home = tmp_path / ".opensquilla"
|
|
profiles_root = legacy_home / "profiles"
|
|
legacy_home.mkdir()
|
|
(legacy_home / ".env").write_text("PROFILE_SHARED_MARK=legacy\n", encoding="utf-8")
|
|
_write_profile(profiles_root, "coder", ["PROFILE_SHARED_MARK=coder"])
|
|
|
|
env = os.environ.copy()
|
|
env.update({"HOME": str(tmp_path)})
|
|
env.pop("OPENSQUILLA_HOME", None)
|
|
env.pop("OPENSQUILLA_PROFILE", None)
|
|
env.pop("OPENSQUILLA_STATE_DIR", None)
|
|
env.pop("PROFILE_SHARED_MARK", None)
|
|
pythonpath = [
|
|
str(repo_root / "src"),
|
|
str(repo_root),
|
|
env.get("PYTHONPATH", ""),
|
|
]
|
|
env["PYTHONPATH"] = os.pathsep.join(path for path in pythonpath if path)
|
|
|
|
script = """
|
|
import os
|
|
import sys
|
|
from typer.testing import CliRunner
|
|
|
|
sys.argv = ["opensquilla", "--profile", "coder", "providers", "list", "--json"]
|
|
from opensquilla.cli.main import app
|
|
|
|
cold_start_mark = os.environ.get("PROFILE_SHARED_MARK", "")
|
|
if cold_start_mark != "coder":
|
|
print(cold_start_mark)
|
|
raise SystemExit(2)
|
|
|
|
result = CliRunner().invoke(app, ["--profile", "coder", "providers", "list", "--json"])
|
|
if result.exit_code != 0:
|
|
print(result.output)
|
|
raise SystemExit(result.exit_code)
|
|
|
|
print(os.environ.get("PROFILE_SHARED_MARK", ""))
|
|
raise SystemExit(0 if os.environ.get("PROFILE_SHARED_MARK") == "coder" else 2)
|
|
"""
|
|
result = subprocess.run(
|
|
[sys.executable, "-c", script],
|
|
cwd=cwd,
|
|
env=env,
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
|
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
|
|
|
|
def test_cli_profile_from_cwd_env_wins_over_legacy_home_on_cold_start(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
cwd = tmp_path / "cwd"
|
|
cwd.mkdir()
|
|
(cwd / ".env").write_text("OPENSQUILLA_PROFILE=coder\n", encoding="utf-8")
|
|
legacy_home = tmp_path / ".opensquilla"
|
|
profiles_root = legacy_home / "profiles"
|
|
legacy_home.mkdir()
|
|
(legacy_home / ".env").write_text("PROFILE_SHARED_MARK=legacy\n", encoding="utf-8")
|
|
_write_profile(profiles_root, "coder", ["PROFILE_SHARED_MARK=coder"])
|
|
|
|
env = os.environ.copy()
|
|
env.update({"HOME": str(tmp_path)})
|
|
env.pop("OPENSQUILLA_HOME", None)
|
|
env.pop("OPENSQUILLA_PROFILE", None)
|
|
env.pop("OPENSQUILLA_STATE_DIR", None)
|
|
env.pop("PROFILE_SHARED_MARK", None)
|
|
pythonpath = [
|
|
str(repo_root / "src"),
|
|
str(repo_root),
|
|
env.get("PYTHONPATH", ""),
|
|
]
|
|
env["PYTHONPATH"] = os.pathsep.join(path for path in pythonpath if path)
|
|
|
|
script = """
|
|
import os
|
|
from opensquilla.paths import default_opensquilla_home
|
|
|
|
from opensquilla.cli.main import app
|
|
|
|
if os.environ.get("PROFILE_SHARED_MARK") != "coder":
|
|
print(os.environ.get("PROFILE_SHARED_MARK", ""))
|
|
raise SystemExit(2)
|
|
if default_opensquilla_home().name != "coder":
|
|
print(default_opensquilla_home())
|
|
raise SystemExit(3)
|
|
raise SystemExit(0)
|
|
"""
|
|
result = subprocess.run(
|
|
[sys.executable, "-c", script],
|
|
cwd=cwd,
|
|
env=env,
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
|
|
assert result.returncode == 0, result.stdout + result.stderr
|