11import json
2+ import subprocess
23import time
34from pathlib import Path
45
78from create_python_app_core .git_cache import (
89 CacheMeta ,
910 download_repository ,
10- read_cache_meta ,
1111 write_cache_meta ,
1212)
1313from create_python_app_core .paths import ResolvedSource
1414
1515
16+ def _git (args : list [str ], * , cwd : Path ) -> str :
17+ return subprocess .check_output (
18+ ["git" , * args ], cwd = cwd , text = True , stderr = subprocess .STDOUT
19+ ).strip ()
20+
21+
22+ def _init_remote (path : Path ) -> None :
23+ path .mkdir (parents = True )
24+ _git (["init" , "-b" , "main" ], cwd = path )
25+ _git (["config" , "user.email" , "test@example.com" ], cwd = path )
26+ _git (["config" , "user.name" , "Test" ], cwd = path )
27+ (path / "README.md" ).write_text ("v1\n " , encoding = "utf-8" )
28+ (path / "extensions" / "legacy" ).mkdir (parents = True )
29+ (path / "extensions" / "legacy" / "ok.txt" ).write_text ("legacy\n " , encoding = "utf-8" )
30+ _git (["add" , "." ], cwd = path )
31+ _git (["commit" , "-m" , "init" ], cwd = path )
32+
33+
1634def test_file_source_returns_path (tmp_path : Path ) -> None :
1735 src = ResolvedSource (kind = "file" , url = f"file://{ tmp_path } " , local_path = tmp_path )
1836 assert download_repository (src ) == tmp_path
@@ -29,6 +47,8 @@ def test_meta_roundtrip(tmp_path: Path) -> None:
2947 entry = tmp_path / "e"
3048 meta = CacheMeta (url = "u" , ref = "main" , fetched_at = time .time (), commit = "abc" )
3149 write_cache_meta (entry , meta )
50+ from create_python_app_core .git_cache import read_cache_meta
51+
3252 loaded = read_cache_meta (entry )
3353 assert loaded is not None
3454 assert loaded .url == "u"
@@ -41,3 +61,77 @@ def test_skip_git_env(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
4161 with pytest .raises (CpaError ) as ei :
4262 download_repository (src , cache_root = tmp_path , refresh = "always" )
4363 assert ei .value .code == "CPA_SKIP_GIT"
64+
65+
66+ def test_refresh_without_ref_advances_to_remote_tip (tmp_path : Path ) -> None :
67+ work = tmp_path / "work"
68+ bare = tmp_path / "bare.git"
69+ cache = tmp_path / "cache"
70+
71+ _init_remote (work )
72+ _git (["clone" , "--bare" , str (work ), str (bare )], cwd = tmp_path )
73+
74+ src = ResolvedSource (kind = "git" , url = str (bare ))
75+ first = download_repository (src , cache_root = cache , refresh = "always" )
76+ first_sha = _git (["rev-parse" , "HEAD" ], cwd = first )
77+ assert (first / "extensions" / "legacy" / "ok.txt" ).is_file ()
78+ assert not (first / "extensions" / "all-github-setup" ).exists ()
79+
80+ _git (["clone" , str (bare ), str (tmp_path / "push" )], cwd = tmp_path )
81+ push = tmp_path / "push"
82+ _git (["config" , "user.email" , "test@example.com" ], cwd = push )
83+ _git (["config" , "user.name" , "Test" ], cwd = push )
84+ (push / "extensions" / "all-github-setup" ).mkdir (parents = True )
85+ (push / "extensions" / "all-github-setup" / "ok.txt" ).write_text (
86+ "new\n " , encoding = "utf-8"
87+ )
88+ _git (["add" , "." ], cwd = push )
89+ _git (["commit" , "-m" , "rename extension" ], cwd = push )
90+ _git (["push" , "origin" , "main" ], cwd = push )
91+ new_sha = _git (["rev-parse" , "HEAD" ], cwd = push )
92+ assert new_sha != first_sha
93+
94+ refreshed = download_repository (src , cache_root = cache , refresh = "always" )
95+ assert refreshed == first
96+ assert _git (["rev-parse" , "HEAD" ], cwd = refreshed ) == new_sha
97+ assert (refreshed / "extensions" / "all-github-setup" / "ok.txt" ).is_file ()
98+
99+
100+ def test_missing_subdir_forces_refresh_when_meta_is_fresh (tmp_path : Path ) -> None :
101+ work = tmp_path / "work"
102+ bare = tmp_path / "bare.git"
103+ cache = tmp_path / "cache"
104+ _init_remote (work )
105+ _git (["clone" , "--bare" , str (work ), str (bare )], cwd = tmp_path )
106+
107+ src = ResolvedSource (
108+ kind = "git" ,
109+ url = str (bare ),
110+ subdir = "extensions/all-github-setup" ,
111+ )
112+ entry = download_repository (src , cache_root = cache , refresh = "always" )
113+ write_cache_meta (
114+ entry ,
115+ CacheMeta (
116+ url = str (bare ),
117+ ref = None ,
118+ fetched_at = time .time (),
119+ commit = _git (["rev-parse" , "HEAD" ], cwd = entry ),
120+ ),
121+ )
122+ assert not (entry / "extensions" / "all-github-setup" ).exists ()
123+
124+ _git (["clone" , str (bare ), str (tmp_path / "push" )], cwd = tmp_path )
125+ push = tmp_path / "push"
126+ _git (["config" , "user.email" , "test@example.com" ], cwd = push )
127+ _git (["config" , "user.name" , "Test" ], cwd = push )
128+ (push / "extensions" / "all-github-setup" ).mkdir (parents = True )
129+ (push / "extensions" / "all-github-setup" / "ok.txt" ).write_text (
130+ "new\n " , encoding = "utf-8"
131+ )
132+ _git (["add" , "." ], cwd = push )
133+ _git (["commit" , "-m" , "add all-github-setup" ], cwd = push )
134+ _git (["push" , "origin" , "main" ], cwd = push )
135+
136+ updated = download_repository (src , cache_root = cache , refresh = "stale" )
137+ assert (updated / "extensions" / "all-github-setup" / "ok.txt" ).is_file ()
0 commit comments