Files
wild-pc/core/tests/test_git.py
Paul Payne 05b28cb584 Rename Castle -> Wild PC across the repo
Repo-side rename only (Phases 1-3 of the migration plan); the live box
(~/.castle, systemd units, /data/castle, domains) is a separate cutover.

- Slug `castle` -> `wildpc`: CLI command, module names (wildpc_core/cli/api),
  dist names, entry point `wildpc = wildpc_cli.main:main`.
- Identifiers: CastleConfig/NATSClient/DirError/MDNS -> Wildpc*.
- Env/constants: CASTLE_* -> WILDPC_*; ~/.castle -> ~/.wildpc, castle.yaml ->
  wildpc.yaml, /data/castle -> /data/wildpc.
- Systemd UNIT_PREFIX castle- -> wildpc-; own programs castle-api/gateway/etc.
- Display prose "Castle" -> "Wild PC" in docs, agent-guide files, README, frontend.
- Package dirs and bootstrap yaml renamed via git mv; lockfiles regenerated;
  redundant nested uv.lock files dropped (workspace root lock is authoritative).

Tests: core 273, cli 47, wildpc-api 120 all pass. Frontend type-checks + builds.
Fixed a stale test fixture (secret_env_path kind arg) broken pre-rename.
2026-07-18 22:55:08 -07:00

124 lines
3.7 KiB
Python

"""Tests for git working-copy status/sync (core/src/wildpc_core/git.py)."""
from __future__ import annotations
import subprocess
from pathlib import Path
import pytest
from wildpc_core import git as G
# Identity so commits succeed without touching the user's global git config.
_ENV = {
"GIT_AUTHOR_NAME": "t",
"GIT_AUTHOR_EMAIL": "t@t",
"GIT_COMMITTER_NAME": "t",
"GIT_COMMITTER_EMAIL": "t@t",
"GIT_CONFIG_GLOBAL": "/dev/null",
"GIT_CONFIG_SYSTEM": "/dev/null",
}
def _git(cwd: Path, *args: str) -> None:
subprocess.run(
["git", "-C", str(cwd), *args],
check=True,
capture_output=True,
text=True,
env={**_base_env(), **_ENV},
)
def _base_env() -> dict[str, str]:
import os
return dict(os.environ)
def _commit(cwd: Path, fname: str, text: str) -> None:
(cwd / fname).write_text(text)
_git(cwd, "add", fname)
_git(cwd, "commit", "-m", f"add {fname}")
@pytest.fixture
def repos(tmp_path: Path):
"""An `upstream` repo and a `work` clone of it (tracking upstream/main)."""
upstream = tmp_path / "upstream"
upstream.mkdir()
_git(upstream, "init", "-q", "-b", "main")
_commit(upstream, "a.txt", "one")
work = tmp_path / "work"
_git(tmp_path, "clone", "-q", str(upstream), str(work))
return upstream, work
def test_non_repo_is_benign(tmp_path: Path) -> None:
assert G.is_git_repo(tmp_path / "nope") is False
st = G.git_status(tmp_path / "nope")
assert st.is_repo is False and st.branch is None
ok, out = G.pull(tmp_path / "nope")
assert ok is False and "not a git" in out
def test_status_clean_and_up_to_date(repos) -> None:
_, work = repos
st = G.git_status(work, fetch=True)
assert st.is_repo and st.branch == "main"
assert st.dirty is False
assert st.behind == 0 and st.ahead == 0
assert st.upstream and st.upstream.endswith("main")
def test_behind_then_pull_fast_forwards(repos) -> None:
upstream, work = repos
_commit(upstream, "b.txt", "two") # advance the remote
st = G.git_status(work, fetch=True)
assert st.behind == 1 and st.ahead == 0
ok, out = G.pull(work)
assert ok is True, out
assert (work / "b.txt").exists()
after = G.git_status(work, fetch=True)
assert after.behind == 0 and after.dirty is False
def test_conflicting_dirty_tree_blocks_pull(repos) -> None:
"""A pull that would overwrite a locally-modified file is refused (ff-only never
merges), leaving the working copy untouched with git's own message."""
upstream, work = repos
_commit(upstream, "a.txt", "upstream change") # remote touches a.txt...
(work / "a.txt").write_text("local uncommitted change") # ...so does the work tree
assert G.git_status(work, fetch=False).dirty is True
ok, out = G.pull(work)
assert ok is False and out # "local changes would be overwritten"
assert (work / "a.txt").read_text() == "local uncommitted change"
def test_diverged_branch_blocks_ff_pull(repos) -> None:
"""Local commits the remote doesn't have → --ff-only refuses (no merge commit)."""
upstream, work = repos
_commit(upstream, "b.txt", "remote two")
_commit(work, "c.txt", "local two") # work now has a commit upstream lacks
st = G.git_status(work, fetch=True)
assert st.behind == 1 and st.ahead == 1
ok, out = G.pull(work)
assert ok is False and out
def test_detached_head_reported(repos) -> None:
_, work = repos
head = subprocess.run(
["git", "-C", str(work), "rev-parse", "HEAD"],
capture_output=True,
text=True,
env={**_base_env(), **_ENV},
).stdout.strip()
_git(work, "checkout", "-q", head)
st = G.git_status(work, fetch=False)
assert st.detached is True and st.branch is None