@@ -57,10 +57,10 @@ def reset_mock():
5757 MockFSBackend .reset ()
5858
5959
60- def _make_workspace ():
60+ def _make_workspace (path = "/tmp/test_ws" ):
6161 with patch ("branching.core.workspace.detect_fs_for_mount" ) as mock_detect :
62- mock_detect .return_value = MockFSBackend
63- return Workspace ("/tmp/test_ws" )
62+ mock_detect .return_value = ( MockFSBackend , Path ( path ))
63+ return Workspace (path )
6464
6565
6666class TestSpeculate :
@@ -1066,3 +1066,63 @@ def test_outcome_defaults(self):
10661066 assert o .winner is None
10671067 assert o .all_results == []
10681068 assert not o .committed
1069+
1070+
1071+ class SingleMountMockFSBackend (MockFSBackend ):
1072+ """Mock FS backend that uses single-mount semantics (like BranchFS)."""
1073+
1074+ @classmethod
1075+ def single_mount (cls ) -> bool :
1076+ return True
1077+
1078+
1079+ class TestSubBranching :
1080+ """Workspace(path) works when path is inside an existing mount."""
1081+
1082+ def test_workspace_from_branch_path (self ):
1083+ """Candidate constructs Workspace(branch_path) and sub-branches."""
1084+ mount_root = "/mnt/ws"
1085+ branch_path = "/mnt/ws/@uuid0"
1086+
1087+ with patch ("branching.core.workspace.detect_fs_for_mount" ) as mock_detect :
1088+ mock_detect .return_value = (SingleMountMockFSBackend , Path (mount_root ))
1089+ ws = Workspace (branch_path )
1090+
1091+ # Public API: path reflects where the candidate works
1092+ assert ws .path == Path (branch_path )
1093+
1094+ # Sub-branching works and creates branches on the real mount
1095+ b = ws .branch ("sub" )
1096+ assert b .name == "sub"
1097+ # create_branch receives the mount root, not the branch virtual path
1098+ with b :
1099+ assert SingleMountMockFSBackend ._branches_created [- 1 ] == "sub"
1100+
1101+ def test_workspace_at_mount_root_unchanged (self ):
1102+ """Normal usage (path == mount root) behaves the same as before."""
1103+ with patch ("branching.core.workspace.detect_fs_for_mount" ) as mock_detect :
1104+ mock_detect .return_value = (SingleMountMockFSBackend , Path ("/mnt/ws" ))
1105+ ws = Workspace ("/mnt/ws" )
1106+ assert ws .path == Path ("/mnt/ws" )
1107+
1108+ b = ws .branch ("feat" )
1109+ assert b .name == "feat"
1110+ with b :
1111+ assert SingleMountMockFSBackend ._branches_created [- 1 ] == "feat"
1112+
1113+ def test_sub_branch_speculate (self ):
1114+ """End-to-end: candidate sub-branches via Workspace and Speculate."""
1115+ mount_root = "/mnt/ws"
1116+ branch_path = "/mnt/ws/@uuid0"
1117+
1118+ with patch ("branching.core.workspace.detect_fs_for_mount" ) as mock_detect :
1119+ mock_detect .return_value = (SingleMountMockFSBackend , Path (mount_root ))
1120+ ws = Workspace (branch_path )
1121+
1122+ def candidate (path : Path ) -> bool :
1123+ return True
1124+
1125+ spec = Speculate ([candidate ], first_wins = True )
1126+ outcome = spec (ws )
1127+ assert outcome .committed
1128+ assert outcome .winner .success
0 commit comments