9
9
import pytest
10
10
11
11
from libvcs ._internal .run import run
12
+ from libvcs .cmd .git import Git
13
+ from libvcs .exc import CommandError
14
+ from libvcs .pytest_plugin import (
15
+ DEFAULT_GIT_INITIAL_BRANCH ,
16
+ _create_git_remote_repo ,
17
+ )
12
18
13
19
if t .TYPE_CHECKING :
14
20
import pathlib
@@ -176,3 +182,220 @@ def test_git_bare_repo_sync_and_commit(
176
182
# Test
177
183
result = pytester .runpytest (str (first_test_filename ))
178
184
result .assert_outcomes (passed = 2 )
185
+
186
+
187
+ def test_create_git_remote_repo_basic (tmp_path : pathlib .Path ) -> None :
188
+ """Test basic git repository creation."""
189
+ repo_path = tmp_path / "test-repo"
190
+
191
+ result = _create_git_remote_repo (repo_path , init_cmd_args = [])
192
+
193
+ assert result == repo_path
194
+ assert repo_path .exists ()
195
+ assert (repo_path / ".git" ).exists ()
196
+
197
+
198
+ def test_create_git_remote_repo_bare (tmp_path : pathlib .Path ) -> None :
199
+ """Test bare git repository creation."""
200
+ repo_path = tmp_path / "test-repo.git"
201
+
202
+ result = _create_git_remote_repo (repo_path , init_cmd_args = ["--bare" ])
203
+
204
+ assert result == repo_path
205
+ assert repo_path .exists ()
206
+ assert (repo_path / "HEAD" ).exists ()
207
+ assert not (repo_path / ".git" ).exists ()
208
+
209
+
210
+ def test_create_git_remote_repo_with_initial_branch (
211
+ tmp_path : pathlib .Path ,
212
+ monkeypatch : pytest .MonkeyPatch ,
213
+ ) -> None :
214
+ """Test repository creation with custom initial branch.
215
+
216
+ This test checks both modern Git (2.30.0+) and fallback behavior.
217
+ """
218
+ repo_path = tmp_path / "test-repo"
219
+
220
+ # Track Git.init calls
221
+ init_calls : list [dict [str , t .Any ]] = []
222
+
223
+ def mock_init (self : Git , * args : t .Any , ** kwargs : t .Any ) -> str :
224
+ init_calls .append ({"args" : args , "kwargs" : kwargs })
225
+
226
+ # Simulate old Git that doesn't support --initial-branch
227
+ if kwargs .get ("initial_branch" ):
228
+ msg = "error: unknown option `initial-branch'"
229
+ raise CommandError (
230
+ msg ,
231
+ returncode = 1 ,
232
+ cmd = ["git" , "init" , "--initial-branch=main" ],
233
+ )
234
+
235
+ # Create the repo directory to simulate successful init
236
+ self .path .mkdir (exist_ok = True )
237
+ (self .path / ".git" ).mkdir (exist_ok = True )
238
+ return "Initialized empty Git repository"
239
+
240
+ monkeypatch .setattr (Git , "init" , mock_init )
241
+
242
+ result = _create_git_remote_repo (repo_path , initial_branch = "develop" )
243
+
244
+ # Should have tried twice: once with initial_branch, once without
245
+ assert len (init_calls ) == 2
246
+ assert init_calls [0 ]["kwargs" ].get ("initial_branch" ) == "develop"
247
+ assert "initial_branch" not in init_calls [1 ]["kwargs" ]
248
+ assert result == repo_path
249
+
250
+
251
+ def test_create_git_remote_repo_modern_git (
252
+ tmp_path : pathlib .Path ,
253
+ monkeypatch : pytest .MonkeyPatch ,
254
+ ) -> None :
255
+ """Test repository creation with Git 2.30.0+ that supports --initial-branch."""
256
+ repo_path = tmp_path / "test-repo"
257
+
258
+ init_calls : list [dict [str , t .Any ]] = []
259
+
260
+ def mock_init (self : Git , * args : t .Any , ** kwargs : t .Any ) -> str :
261
+ init_calls .append ({"args" : args , "kwargs" : kwargs })
262
+ # Simulate successful init with --initial-branch support
263
+ self .path .mkdir (exist_ok = True )
264
+ (self .path / ".git" ).mkdir (exist_ok = True )
265
+ branch = kwargs .get ("initial_branch" , "master" )
266
+ return f"Initialized empty Git repository with initial branch '{ branch } '"
267
+
268
+ monkeypatch .setattr (Git , "init" , mock_init )
269
+
270
+ result = _create_git_remote_repo (repo_path , initial_branch = "main" )
271
+
272
+ # Should only call init once since it succeeded
273
+ assert len (init_calls ) == 1
274
+ assert init_calls [0 ]["kwargs" ].get ("initial_branch" ) == "main"
275
+ assert result == repo_path
276
+
277
+
278
+ @pytest .mark .parametrize (
279
+ ("env_var" , "param" , "expected_branch" ),
280
+ [
281
+ ("custom-env" , None , "custom-env" ), # Use env var
282
+ ("custom-env" , "param-override" , "param-override" ), # Param overrides env
283
+ (None , "explicit-param" , "explicit-param" ), # Use param
284
+ (None , None , DEFAULT_GIT_INITIAL_BRANCH ), # Use default
285
+ ],
286
+ )
287
+ def test_create_git_remote_repo_branch_configuration (
288
+ tmp_path : pathlib .Path ,
289
+ monkeypatch : pytest .MonkeyPatch ,
290
+ env_var : str | None ,
291
+ param : str | None ,
292
+ expected_branch : str ,
293
+ ) -> None :
294
+ """Test initial branch configuration hierarchy."""
295
+ # Always reload the module to ensure fresh state
296
+ import sys
297
+
298
+ if "libvcs.pytest_plugin" in sys .modules :
299
+ del sys .modules ["libvcs.pytest_plugin" ]
300
+
301
+ if env_var :
302
+ monkeypatch .setenv ("LIBVCS_GIT_DEFAULT_INITIAL_BRANCH" , env_var )
303
+
304
+ # Import after setting env var
305
+ from libvcs .pytest_plugin import _create_git_remote_repo
306
+
307
+ repo_path = tmp_path / "test-repo"
308
+
309
+ # Track what branch was used
310
+ used_branch = None
311
+
312
+ def mock_init (self : Git , * args : t .Any , ** kwargs : t .Any ) -> str :
313
+ nonlocal used_branch
314
+ used_branch = kwargs .get ("initial_branch" )
315
+ self .path .mkdir (exist_ok = True )
316
+ (self .path / ".git" ).mkdir (exist_ok = True )
317
+ return "Initialized"
318
+
319
+ monkeypatch .setattr (Git , "init" , mock_init )
320
+
321
+ _create_git_remote_repo (repo_path , initial_branch = param )
322
+
323
+ assert used_branch == expected_branch
324
+
325
+
326
+ def test_create_git_remote_repo_post_init_callback (tmp_path : pathlib .Path ) -> None :
327
+ """Test that post-init callback is executed."""
328
+ repo_path = tmp_path / "test-repo"
329
+ callback_executed = False
330
+ callback_path = None
331
+
332
+ def post_init_callback (
333
+ remote_repo_path : pathlib .Path , env : t .Any = None
334
+ ) -> None :
335
+ nonlocal callback_executed , callback_path
336
+ callback_executed = True
337
+ callback_path = remote_repo_path
338
+ (remote_repo_path / "callback-marker.txt" ).write_text ("executed" )
339
+
340
+ _create_git_remote_repo (
341
+ repo_path ,
342
+ remote_repo_post_init = post_init_callback ,
343
+ init_cmd_args = [], # Create non-bare repo for easier testing
344
+ )
345
+
346
+ assert callback_executed
347
+ assert callback_path == repo_path
348
+ assert (repo_path / "callback-marker.txt" ).exists ()
349
+ assert (repo_path / "callback-marker.txt" ).read_text () == "executed"
350
+
351
+
352
+ def test_create_git_remote_repo_permission_error (
353
+ tmp_path : pathlib .Path ,
354
+ monkeypatch : pytest .MonkeyPatch ,
355
+ ) -> None :
356
+ """Test handling of permission errors."""
357
+ repo_path = tmp_path / "test-repo"
358
+
359
+ def mock_init (self : Git , * args : t .Any , ** kwargs : t .Any ) -> str :
360
+ msg = "fatal: cannot mkdir .git: Permission denied"
361
+ raise CommandError (
362
+ msg ,
363
+ returncode = 128 ,
364
+ cmd = ["git" , "init" ],
365
+ )
366
+
367
+ monkeypatch .setattr (Git , "init" , mock_init )
368
+
369
+ with pytest .raises (CommandError ) as exc_info :
370
+ _create_git_remote_repo (repo_path )
371
+
372
+ assert "Permission denied" in str (exc_info .value )
373
+
374
+
375
+ @pytest .mark .skipif (
376
+ not shutil .which ("git" ),
377
+ reason = "git is not available" ,
378
+ )
379
+ def test_create_git_remote_repo_integration (tmp_path : pathlib .Path ) -> None :
380
+ """Integration test with real git command."""
381
+ repo_path = tmp_path / "integration-repo"
382
+
383
+ result = _create_git_remote_repo (repo_path , initial_branch = "development" )
384
+
385
+ assert result == repo_path
386
+ assert repo_path .exists ()
387
+
388
+ # Check actual git status
389
+ git = Git (path = repo_path )
390
+
391
+ # Get git version to determine what to check
392
+ try :
393
+ version = git .version ()
394
+ if version .major > 2 or (version .major == 2 and version .minor >= 30 ):
395
+ # Can check branch name on modern Git
396
+ branch_output = git .run (["symbolic-ref" , "HEAD" ])
397
+ assert "refs/heads/development" in branch_output
398
+ except Exception :
399
+ # Just verify it's a valid repo
400
+ status = git .run (["status" , "--porcelain" ])
401
+ assert isinstance (status , str )
0 commit comments