168 lines
6.2 KiB
Python
168 lines
6.2 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import pytest
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
|
|
|
import bootstrap_clone_kb_gate as bootstrap # noqa: E402
|
|
|
|
|
|
def _identity(name: str, identifier: str, *, disposable: bool) -> dict[str, Any]:
|
|
return {
|
|
"id": identifier,
|
|
"name": name,
|
|
"image": "postgres:16",
|
|
"labels": (
|
|
{bootstrap.bound.DISPOSABLE_LABEL_KEY: bootstrap.bound.DISPOSABLE_LABEL_VALUE}
|
|
if disposable
|
|
else {}
|
|
),
|
|
"network_mode": "none" if disposable else "bridge",
|
|
"ports": {},
|
|
"mount_sources": [f"/{name}-data"],
|
|
"started_at": f"{name}-start",
|
|
}
|
|
|
|
|
|
def test_bootstrap_installs_gate_verifies_logins_and_writes_only_private_secret_files(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
tmp_path.chmod(0o700)
|
|
target = _identity("clone", "clone-id", disposable=True)
|
|
production = _identity(bootstrap.bound.PRODUCTION_CONTAINER, "production-id", disposable=False)
|
|
monkeypatch.setattr(
|
|
bootstrap.bound,
|
|
"container_identity",
|
|
lambda name: dict(production if name == bootstrap.bound.PRODUCTION_CONTAINER else target),
|
|
)
|
|
monkeypatch.setattr(
|
|
bootstrap.bound,
|
|
"validate_disposable_target_identity",
|
|
lambda observed, live: {"all": observed["id"] != live["id"], "network_none": True},
|
|
)
|
|
passwords = iter(("review-test-secret", "apply-test-secret"))
|
|
monkeypatch.setattr(bootstrap.secrets, "token_urlsafe", lambda _length: next(passwords))
|
|
sql_calls: list[dict[str, Any]] = []
|
|
monkeypatch.setattr(
|
|
bootstrap,
|
|
"_psql",
|
|
lambda container, database, sql, **kwargs: sql_calls.append(
|
|
{"container": container, "database": database, "sql": sql, **kwargs}
|
|
),
|
|
)
|
|
login_calls: list[tuple[str, str, str, str]] = []
|
|
monkeypatch.setattr(
|
|
bootstrap,
|
|
"_verify_login",
|
|
lambda container, database, role, password: login_calls.append((container, database, role, password)),
|
|
)
|
|
manifest = {
|
|
"approval_table": "kb_stage.kb_proposal_approvals",
|
|
"review_principals_table": "kb_stage.kb_review_principals",
|
|
"review_role_exists": True,
|
|
"apply_role_exists": True,
|
|
}
|
|
monkeypatch.setattr(bootstrap.bound, "_psql_json", lambda *_args, **_kwargs: dict(manifest))
|
|
|
|
result = bootstrap.bootstrap("clone", "teleo", tmp_path)
|
|
|
|
assert result["status"] == "ready"
|
|
assert result["bound_container_id"] == "clone-id"
|
|
assert result["gate_manifest"] == manifest
|
|
assert result["secret_values_or_hashes_recorded"] is False
|
|
assert login_calls == [
|
|
("clone-id", "teleo", "kb_review", "review-test-secret"),
|
|
("clone-id", "teleo", "kb_apply", "apply-test-secret"),
|
|
]
|
|
assert len(sql_calls) == 3
|
|
assert sql_calls[-1]["secret_values"] == ("review-test-secret", "apply-test-secret")
|
|
review_file = tmp_path / "kb-review.env"
|
|
apply_file = tmp_path / "kb-apply.env"
|
|
assert review_file.read_text(encoding="utf-8") == "KB_REVIEW_PASSWORD=review-test-secret\n"
|
|
assert apply_file.read_text(encoding="utf-8") == "KB_APPLY_DB_PASSWORD=apply-test-secret\n"
|
|
assert review_file.stat().st_mode & 0o777 == 0o600
|
|
assert apply_file.stat().st_mode & 0o777 == 0o600
|
|
serialized = json.dumps(result, sort_keys=True)
|
|
assert "review-test-secret" not in serialized
|
|
assert "apply-test-secret" not in serialized
|
|
|
|
|
|
@pytest.mark.parametrize("mode", [0o755, 0o750])
|
|
def test_bootstrap_rejects_nonprivate_output_directory(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
mode: int,
|
|
) -> None:
|
|
tmp_path.chmod(mode)
|
|
target = _identity("clone", "clone-id", disposable=True)
|
|
production = _identity(bootstrap.bound.PRODUCTION_CONTAINER, "production-id", disposable=False)
|
|
monkeypatch.setattr(
|
|
bootstrap.bound,
|
|
"container_identity",
|
|
lambda name: dict(production if name == bootstrap.bound.PRODUCTION_CONTAINER else target),
|
|
)
|
|
monkeypatch.setattr(bootstrap.bound, "validate_disposable_target_identity", lambda *_args: {"all": True})
|
|
|
|
with pytest.raises(bootstrap.bound.CheckpointError, match="private directory"):
|
|
bootstrap.bootstrap("clone", "teleo", tmp_path)
|
|
|
|
|
|
def test_bootstrap_refuses_existing_or_symlinked_secret_paths(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
tmp_path.chmod(0o700)
|
|
target = _identity("clone", "clone-id", disposable=True)
|
|
production = _identity(bootstrap.bound.PRODUCTION_CONTAINER, "production-id", disposable=False)
|
|
monkeypatch.setattr(
|
|
bootstrap.bound,
|
|
"container_identity",
|
|
lambda name: dict(production if name == bootstrap.bound.PRODUCTION_CONTAINER else target),
|
|
)
|
|
monkeypatch.setattr(bootstrap.bound, "validate_disposable_target_identity", lambda *_args: {"all": True})
|
|
(tmp_path / "kb-review.env").symlink_to(tmp_path / "missing-target")
|
|
|
|
with pytest.raises(bootstrap.bound.CheckpointError, match="already exist"):
|
|
bootstrap.bootstrap("clone", "teleo", tmp_path)
|
|
|
|
|
|
def test_write_secret_is_exclusive_and_does_not_follow_symlinks(tmp_path: Path) -> None:
|
|
target = tmp_path / "target"
|
|
target.write_text("unchanged\n", encoding="utf-8")
|
|
link = tmp_path / "secret.env"
|
|
link.symlink_to(target)
|
|
|
|
with pytest.raises(FileExistsError):
|
|
bootstrap._write_secret(link, "SECRET", "value")
|
|
assert target.read_text(encoding="utf-8") == "unchanged\n"
|
|
|
|
|
|
def test_main_rejects_production_without_calling_bootstrap(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
capsys: pytest.CaptureFixture[str],
|
|
) -> None:
|
|
called = False
|
|
|
|
def unexpected(*_args: Any, **_kwargs: Any) -> dict[str, Any]:
|
|
nonlocal called
|
|
called = True
|
|
return {}
|
|
|
|
monkeypatch.setattr(bootstrap, "bootstrap", unexpected)
|
|
result = bootstrap.main(
|
|
["--container", bootstrap.bound.PRODUCTION_CONTAINER, "--db", "teleo", "--output-dir", str(tmp_path)]
|
|
)
|
|
|
|
assert result == 2
|
|
assert called is False
|
|
output = json.loads(capsys.readouterr().out)
|
|
assert output["status"] == "rejected"
|
|
assert "non-production" in output["error"]
|