chore: import upstream snapshot with attribution
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def _load_update_module():
|
||||
module_path = Path(__file__).resolve().parents[2] / "scripts" / "update.py"
|
||||
module_name = "update_script_under_test"
|
||||
sys.modules.pop(module_name, None)
|
||||
spec = importlib.util.spec_from_file_location(module_name, module_path)
|
||||
assert spec and spec.loader
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[module_name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def _run(args: list[str], cwd: Path) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
args,
|
||||
cwd=cwd,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
encoding="utf-8",
|
||||
errors="replace",
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
def _git(cwd: Path, *args: str) -> str:
|
||||
return _run(["git", *args], cwd).stdout.strip()
|
||||
|
||||
|
||||
def _write(path: Path, text: str) -> None:
|
||||
path.write_text(text, encoding="utf-8")
|
||||
|
||||
|
||||
def _commit(repo: Path, message: str) -> None:
|
||||
_git(repo, "add", ".")
|
||||
_git(repo, "commit", "-m", message)
|
||||
|
||||
|
||||
def _configure_user(repo: Path) -> None:
|
||||
_git(repo, "config", "user.email", "tests@example.com")
|
||||
_git(repo, "config", "user.name", "DeepTutor Tests")
|
||||
|
||||
|
||||
def _create_checkout(tmp_path: Path, branch: str = "main") -> tuple[Path, Path]:
|
||||
remote = tmp_path / "remote.git"
|
||||
seed = tmp_path / "seed"
|
||||
checkout = tmp_path / "checkout"
|
||||
|
||||
_run(["git", "init", "--bare", str(remote)], tmp_path)
|
||||
_run(["git", "init", "-b", branch, str(seed)], tmp_path)
|
||||
_configure_user(seed)
|
||||
_write(seed / "app.txt", "v1\n")
|
||||
_commit(seed, "initial")
|
||||
_git(seed, "remote", "add", "origin", str(remote))
|
||||
_git(seed, "push", "-u", "origin", branch)
|
||||
|
||||
_run(["git", "clone", "--branch", branch, str(remote), str(checkout)], tmp_path)
|
||||
_configure_user(checkout)
|
||||
return seed, checkout
|
||||
|
||||
|
||||
def _push_remote_commit(seed: Path, branch: str, text: str = "v2\n") -> None:
|
||||
_write(seed / "app.txt", text)
|
||||
_commit(seed, "remote update")
|
||||
_git(seed, "push", "origin", branch)
|
||||
|
||||
|
||||
def test_update_fast_forwards_the_current_non_main_branch(tmp_path: Path) -> None:
|
||||
update = _load_update_module()
|
||||
seed, checkout = _create_checkout(tmp_path, branch="dev")
|
||||
_push_remote_commit(seed, "dev")
|
||||
|
||||
exit_code = update.main(["--repo", str(checkout), "--yes"])
|
||||
|
||||
assert exit_code == 0
|
||||
assert _git(checkout, "branch", "--show-current") == "dev"
|
||||
assert (checkout / "app.txt").read_text(encoding="utf-8") == "v2\n"
|
||||
assert _git(checkout, "rev-parse", "HEAD") == _git(checkout, "rev-parse", "origin/dev")
|
||||
|
||||
|
||||
def test_update_refuses_tracked_local_changes(tmp_path: Path) -> None:
|
||||
update = _load_update_module()
|
||||
seed, checkout = _create_checkout(tmp_path)
|
||||
_push_remote_commit(seed, "main")
|
||||
_write(checkout / "app.txt", "local edit\n")
|
||||
|
||||
exit_code = update.main(["--repo", str(checkout), "--yes"])
|
||||
|
||||
assert exit_code == 1
|
||||
assert (checkout / "app.txt").read_text(encoding="utf-8") == "local edit\n"
|
||||
assert _git(checkout, "rev-parse", "HEAD") != _git(checkout, "rev-parse", "origin/main")
|
||||
|
||||
|
||||
def test_update_refuses_diverged_branches(tmp_path: Path) -> None:
|
||||
update = _load_update_module()
|
||||
seed, checkout = _create_checkout(tmp_path)
|
||||
_push_remote_commit(seed, "main")
|
||||
_write(checkout / "app.txt", "local commit\n")
|
||||
_commit(checkout, "local update")
|
||||
|
||||
exit_code = update.main(["--repo", str(checkout), "--yes"])
|
||||
|
||||
assert exit_code == 1
|
||||
assert (checkout / "app.txt").read_text(encoding="utf-8") == "local commit\n"
|
||||
assert _git(checkout, "rev-parse", "HEAD") != _git(checkout, "rev-parse", "origin/main")
|
||||
|
||||
|
||||
def test_dependency_hints_cover_backend_and_frontend_manifests() -> None:
|
||||
update = _load_update_module()
|
||||
|
||||
hints = update.dependency_hints(
|
||||
["pyproject.toml", "requirements/server.txt", "web/package-lock.json"]
|
||||
)
|
||||
|
||||
assert len(hints) == 2
|
||||
assert "Backend dependencies changed" in hints[0]
|
||||
assert "Frontend dependencies changed" in hints[1]
|
||||
Reference in New Issue
Block a user