@@ -888,3 +888,123 @@ def test_run_error(mock_bundler_run: MagicMock, mock_ensure_uv: MagicMock) -> No
888888 result = run (args )
889889
890890 assert result == 2
891+
892+
893+ def test_venv_site_packages_lib_exists_no_python (tmp_path : Path ) -> None :
894+ """Test venv_site_packages when lib/ exists but has no python*/site-packages."""
895+ # Create lib dir without python subdirs, plus Windows structure
896+ lib = tmp_path / "lib"
897+ lib .mkdir ()
898+ # No python* directories inside lib
899+
900+ # Create Windows structure as fallback
901+ sp = tmp_path / "Lib" / "site-packages"
902+ sp .mkdir (parents = True )
903+
904+ result = venv_site_packages (tmp_path )
905+ assert result == sp
906+
907+
908+ @patch ("cosmofy.bundle.subprocess.check_output" )
909+ def test_bundler_uv_sync_script_no_venv (
910+ mock_check_output : MagicMock , tmp_path : Path
911+ ) -> None :
912+ """Test Bundler.uv_sync with script but no venv (venv=None)."""
913+ script_path = tmp_path / "script.py"
914+ script_path .write_text ("print('hello')" )
915+ mock_check_output .return_value = '{"sync": {"environment": {"path": ""}}}'
916+
917+ args = baton .parse (Args , split ("" ))
918+ bundler = Bundler (args )
919+ # Call with venv=None and script provided
920+ bundler .uv_sync (pkg = "script.py" , version = "3.11" , venv = None , script = script_path )
921+
922+ call_args = mock_check_output .call_args [0 ][0 ]
923+ assert "--script" in call_args
924+ # Should NOT have --active since venv is None
925+ assert "--active" not in call_args
926+
927+
928+ @patch ("cosmofy.bundle.Bundler.bundle_entry_points" )
929+ @patch ("cosmofy.bundle.Bundler.uv_version" )
930+ @patch ("cosmofy.bundle.get_version" )
931+ @patch ("cosmofy.bundle.Bundler.get_cosmo_python" )
932+ def test_bundler_run_with_output_dir_set (
933+ mock_get_cosmo : MagicMock ,
934+ mock_get_version : MagicMock ,
935+ mock_uv_version : MagicMock ,
936+ mock_bundle_eps : MagicMock ,
937+ tmp_path : Path ,
938+ ) -> None :
939+ """Test Bundler.run when output_dir is already set."""
940+ cosmo_python = tmp_path / "python"
941+ cosmo_python .write_bytes (b"python" )
942+ mock_get_cosmo .return_value = cosmo_python
943+ mock_get_version .return_value = "3.11.0"
944+ mock_uv_version .return_value = ("mypackage" , "1.0.0" )
945+ mock_bundle_eps .return_value = {}
946+
947+ output_dir = tmp_path / "custom_dist"
948+ output_dir .mkdir ()
949+
950+ # Provide output_dir explicitly
951+ args = baton .parse (Args , split (f"--output-dir { output_dir } " ))
952+ bundler = Bundler (args )
953+ bundler .run ()
954+
955+ # Should use the provided output_dir, not find_project_root() / "dist"
956+ assert args .output_dir == output_dir
957+
958+
959+ @patch ("cosmofy.bundle.Bundler.bundle_script" )
960+ @patch ("cosmofy.bundle.get_version" )
961+ @patch ("cosmofy.bundle.Bundler.get_cosmo_python" )
962+ def test_bundler_run_scripts_with_output_dir (
963+ mock_get_cosmo : MagicMock ,
964+ mock_get_version : MagicMock ,
965+ mock_bundle_script : MagicMock ,
966+ tmp_path : Path ,
967+ ) -> None :
968+ """Test Bundler.run with scripts and output_dir set."""
969+ cosmo_python = tmp_path / "python"
970+ cosmo_python .write_bytes (b"python" )
971+ mock_get_cosmo .return_value = cosmo_python
972+ mock_get_version .return_value = "3.11.0"
973+
974+ script = tmp_path / "myscript.py"
975+ script .write_text ("print('hello')" )
976+
977+ output_dir = tmp_path / "dist"
978+ output_dir .mkdir ()
979+
980+ args = baton .parse (Args , split (f"--script { script } --output-dir { output_dir } " ))
981+ bundler = Bundler (args )
982+ result = bundler .run ()
983+
984+ # Result should have script path using output_dir
985+ assert "scripts" in result
986+
987+
988+ @patch ("cosmofy.bundle.ensure_uv" )
989+ @patch ("cosmofy.bundle.Bundler.run" )
990+ def test_run_json_output (
991+ mock_bundler_run : MagicMock ,
992+ mock_ensure_uv : MagicMock ,
993+ capsys : pytest .CaptureFixture [str ],
994+ ) -> None :
995+ """Test run function with JSON output format."""
996+ from cosmofy .bundle import run as bundle_run
997+
998+ mock_ensure_uv .return_value = True
999+ mock_bundler_run .return_value = {"entry_points" : ["dist/mycmd" ], "scripts" : []}
1000+
1001+ args = baton .parse (Args , split ("--output-format json" ))
1002+ result = bundle_run (args )
1003+
1004+ assert result == 0
1005+ captured = capsys .readouterr ()
1006+ import json
1007+
1008+ data = json .loads (captured .out )
1009+ assert "entry_points" in data
1010+ assert data ["entry_points" ] == ["dist/mycmd" ]
0 commit comments