@@ -862,3 +862,134 @@ def test_global_rejects_tilde_local_path(self):
862862 assert "not supported at user scope" in result .output
863863 finally :
864864 os .chdir (self .original_dir )
865+
866+ # ---------------------------------------------------------------------------
867+ # Generic-host SSH-first validation tests
868+ # ---------------------------------------------------------------------------
869+
870+ class TestGenericHostSshFirstValidation :
871+ """Tests for the SSH-first ls-remote logic added for generic (non-GitHub/ADO) hosts."""
872+
873+ def _make_completed_process (self , returncode , stderr = "" ):
874+ """Return a minimal subprocess.CompletedProcess-like mock."""
875+ mock = MagicMock ()
876+ mock .returncode = returncode
877+ mock .stderr = stderr
878+ mock .stdout = ""
879+ return mock
880+
881+ @patch ("subprocess.run" )
882+ def test_generic_host_tries_ssh_first_and_succeeds (self , mock_run ):
883+ """SSH URL is tried first for generic hosts and used when it succeeds."""
884+ from apm_cli .commands .install import _validate_package_exists
885+
886+ # SSH probe succeeds on the first call
887+ mock_run .return_value = self ._make_completed_process (returncode = 0 )
888+
889+ result = _validate_package_exists (
890+ "git@git.example.org:org/group/repo.git" , verbose = False
891+ )
892+
893+ assert result is True
894+ # subprocess.run must have been called at least once
895+ assert mock_run .call_count >= 1
896+ # First call must use the SSH URL
897+ first_call_cmd = mock_run .call_args_list [0 ][0 ][0 ]
898+ assert any ("git@git.example.org" in arg for arg in first_call_cmd ), (
899+ f"Expected SSH URL in first ls-remote call, got: { first_call_cmd } "
900+ )
901+
902+ @patch ("subprocess.run" )
903+ def test_generic_host_falls_back_to_https_when_ssh_fails (self , mock_run ):
904+ """HTTPS fallback is used for generic hosts when SSH ls-remote fails."""
905+ from apm_cli .commands .install import _validate_package_exists
906+
907+ # SSH probe fails, HTTPS succeeds
908+ mock_run .side_effect = [
909+ self ._make_completed_process (returncode = 128 , stderr = "ssh: connect to host" ),
910+ self ._make_completed_process (returncode = 0 ),
911+ ]
912+
913+ result = _validate_package_exists (
914+ "git@git.example.org:org/group/repo.git" , verbose = False
915+ )
916+
917+ assert result is True
918+ assert mock_run .call_count == 2
919+ # First call: SSH
920+ first_cmd = mock_run .call_args_list [0 ][0 ][0 ]
921+ assert any ("git@git.example.org" in arg for arg in first_cmd ), (
922+ f"Expected SSH URL in first call, got: { first_cmd } "
923+ )
924+ # Second call: HTTPS
925+ second_cmd = mock_run .call_args_list [1 ][0 ][0 ]
926+ assert any ("https://git.example.org" in arg for arg in second_cmd ), (
927+ f"Expected HTTPS URL in second call, got: { second_cmd } "
928+ )
929+
930+ @patch ("subprocess.run" )
931+ def test_generic_host_returns_false_when_both_transports_fail (self , mock_run ):
932+ """Validation returns False when both SSH and HTTPS fail for a generic host."""
933+ from apm_cli .commands .install import _validate_package_exists
934+
935+ mock_run .return_value = self ._make_completed_process (
936+ returncode = 128 , stderr = "fatal: could not read Username"
937+ )
938+
939+ result = _validate_package_exists (
940+ "git@git.example.org:org/group/repo.git" , verbose = False
941+ )
942+
943+ assert result is False
944+ assert mock_run .call_count == 2 # tried SSH then HTTPS
945+
946+ @patch ("subprocess.run" )
947+ def test_github_host_skips_ssh_attempt (self , mock_run ):
948+ """GitHub.com repositories do NOT go through the SSH-first ls-remote path."""
949+
950+ import urllib .request
951+ import urllib .error
952+
953+ from apm_cli .commands .install import _validate_package_exists
954+
955+ with patch ("urllib.request.urlopen" ) as mock_urlopen :
956+ mock_urlopen .side_effect = urllib .error .HTTPError (
957+ url = "https://api.github.com/repos/owner/repo" ,
958+ code = 404 , msg = "Not Found" , hdrs = {}, fp = None ,
959+ )
960+ result = _validate_package_exists ("owner/repo" , verbose = False )
961+
962+ assert result is False
963+ # No ls-remote call should have been made for a github.com host
964+ ls_remote_calls = [
965+ call for call in mock_run .call_args_list
966+ if "ls-remote" in (call [0 ][0 ] if call [0 ] else [])
967+ ]
968+ assert len (ls_remote_calls ) == 0 , (
969+ f"Expected no ls-remote calls for github.com, got: { ls_remote_calls } "
970+ )
971+
972+ @patch ("subprocess.run" )
973+ def test_ghes_host_skips_ssh_attempt (self , mock_run ):
974+ """A GHES host is treated as GitHub, not generic SSH probe is skipped."""
975+ from apm_cli .commands .install import _validate_package_exists
976+
977+ mock_run .return_value = self ._make_completed_process (returncode = 0 )
978+
979+ result = _validate_package_exists (
980+ "company.ghe.com/team/internal-repo" , verbose = False
981+ )
982+
983+ assert result is True
984+ ls_remote_calls = [
985+ call for call in mock_run .call_args_list
986+ if "ls-remote" in (call [0 ][0 ] if call [0 ] else [])
987+ ]
988+ assert len (ls_remote_calls ) == 1 , (
989+ f"Expected exactly 1 ls-remote call for GHES host, got: { ls_remote_calls } "
990+ )
991+ only_cmd = ls_remote_calls [0 ][0 ][0 ]
992+ # Must use HTTPS, not SSH
993+ assert all ("git@" not in arg for arg in only_cmd ), (
994+ f"Expected HTTPS-only URL for GHES host, got: { only_cmd } "
995+ )
0 commit comments