# #region Test.Git.Url.Edge [C:3] [TYPE Module] [SEMANTICS test, git, url, parse, coverage] # @BRIEF Edge case tests for GitServiceUrlMixin — URL parsing, host extraction, alignment, identity parsing. # @RELATION BINDS_TO -> [GitServiceUrlMixin] import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src")) from unittest.mock import MagicMock, patch import pytest from src.services.git._url import GitServiceUrlMixin @pytest.fixture def mixin(): return GitServiceUrlMixin() class TestExtractHttpHost: def test_none_or_empty(self, mixin): assert mixin._extract_http_host(None) is None assert mixin._extract_http_host("") is None assert mixin._extract_http_host(" ") is None def test_non_http_scheme(self, mixin): assert mixin._extract_http_host("ssh://git@example.com") is None assert mixin._extract_http_host("git@example.com:repo.git") is None def test_http_with_port(self, mixin): result = mixin._extract_http_host("https://example.com:8443/repo") assert result == "example.com:8443" def test_http_without_port(self, mixin): result = mixin._extract_http_host("https://example.com/repo") assert result == "example.com" def test_parse_error(self, mixin): # urlparse doesn't typically raise, but test the except path result = mixin._extract_http_host(None) assert result is None def test_no_hostname(self, mixin): with patch("src.services.git._url.urlparse") as mock_parse: parsed = MagicMock() parsed.scheme = "http" parsed.hostname = None mock_parse.return_value = parsed result = mixin._extract_http_host("http:///path") assert result is None class TestStripUrlCredentials: def test_empty(self, mixin): assert mixin._strip_url_credentials("") == "" def test_no_credentials(self, mixin): url = "https://example.com/repo.git" assert mixin._strip_url_credentials(url) == url def test_with_credentials(self, mixin): url = "https://user:pass@example.com/repo.git" result = mixin._strip_url_credentials(url) assert "user" not in result assert "pass" not in result assert "example.com/repo.git" in result def test_non_http_scheme(self, mixin): url = "ssh://git@example.com/repo.git" assert mixin._strip_url_credentials(url) == url def test_parse_error(self, mixin): with patch("src.services.git._url.urlparse", side_effect=Exception("parse error")): url = "https://example.com/repo" result = mixin._strip_url_credentials(url) assert result == url class TestReplaceHostInUrl: def test_empty_source(self, mixin): assert mixin._replace_host_in_url("", "https://new.com") is None def test_empty_config(self, mixin): assert mixin._replace_host_in_url("https://old.com/repo", "") is None def test_non_http_scheme(self, mixin): assert mixin._replace_host_in_url("ssh://old.com", "https://new.com") is None def test_no_hostname(self, mixin): result = mixin._replace_host_in_url("http:///path", "https://new.com") assert result is None def test_successful_replacement(self, mixin): result = mixin._replace_host_in_url( "https://old.com:8080/org/repo.git", "https://new.com:8443" ) assert result is not None assert "new.com:8443" in result assert "old.com" not in result def test_preserves_credentials(self, mixin): result = mixin._replace_host_in_url( "https://user:pass@old.com/org/repo.git", "https://new.com" ) assert result is not None assert "user" in result assert "new.com" in result def test_parse_error(self, mixin): with patch("src.services.git._url.urlparse", side_effect=Exception("parse error")): result = mixin._replace_host_in_url("https://old.com", "https://new.com") assert result is None class TestParseRemoteRepoIdentity: def test_empty_url(self, mixin): with pytest.raises(Exception, match="empty"): mixin._parse_remote_repo_identity("") def test_ssh_url(self, mixin): result = mixin._parse_remote_repo_identity("git@github.com:owner/repo.git") assert result["owner"] == "owner" assert result["repo"] == "repo" assert result["full_name"] == "owner/repo" def test_https_url(self, mixin): result = mixin._parse_remote_repo_identity("https://github.com/owner/repo.git") assert result["owner"] == "owner" assert result["repo"] == "repo" def test_nested_path(self, mixin): result = mixin._parse_remote_repo_identity("https://github.com/org/team/repo.git") assert result["owner"] == "org" assert result["repo"] == "repo" assert result["namespace"] == "org/team" def test_no_git_suffix(self, mixin): result = mixin._parse_remote_repo_identity("https://github.com/owner/repo") assert result["repo"] == "repo" assert result["full_name"] == "owner/repo" def test_too_few_parts(self, mixin): with pytest.raises(Exception, match="Cannot parse"): mixin._parse_remote_repo_identity("https://github.com/owner") def test_ssh_with_no_colon(self, mixin): """SSH URL with no colon after host — currently raises (format unsupported).""" with pytest.raises(Exception, match="Cannot parse"): mixin._parse_remote_repo_identity("git@github.com/owner/repo.git") class TestDeriveServerUrlFromRemote: def test_empty(self, mixin): assert mixin._derive_server_url_from_remote("") is None def test_ssh_url_returns_none(self, mixin): assert mixin._derive_server_url_from_remote("git@github.com:org/repo.git") is None def test_non_http_scheme(self, mixin): assert mixin._derive_server_url_from_remote("ftp://example.com/repo") is None def test_no_hostname(self, mixin): assert mixin._derive_server_url_from_remote("http:///path") is None def test_success_https(self, mixin): result = mixin._derive_server_url_from_remote("https://github.com/org/repo.git") assert result == "https://github.com" def test_with_port(self, mixin): result = mixin._derive_server_url_from_remote("https://gitea.local:3000/org/repo.git") assert result == "https://gitea.local:3000" class TestNormalizeGitServerUrl: def test_empty_raises(self, mixin): with pytest.raises(Exception, match="required"): mixin._normalize_git_server_url("") def test_strips_trailing_slash(self, mixin): result = mixin._normalize_git_server_url("https://github.com/") assert result == "https://github.com" def test_preserves_no_slash(self, mixin): result = mixin._normalize_git_server_url("https://github.com") assert result == "https://github.com" class TestAlignOriginHostWithConfig: def test_no_config_host(self, mixin): result = mixin._align_origin_host_with_config(1, MagicMock(), None, None, None) assert result is None def test_no_origin_host(self, mixin): result = mixin._align_origin_host_with_config( 1, MagicMock(), "https://new.com", "", None ) assert result is None def test_hosts_match(self, mixin): result = mixin._align_origin_host_with_config( 1, MagicMock(), "https://same.com", "https://same.com/repo", None ) assert result is None def test_hosts_mismatch_sets_url(self, mixin): origin = MagicMock() result = mixin._align_origin_host_with_config( 1, origin, "https://new.com", "https://old.com/repo", None ) assert result is not None assert "new.com" in result origin.set_url.assert_called_once() def test_set_url_failure(self, mixin): origin = MagicMock() origin.set_url.side_effect = Exception("set_url failed") result = mixin._align_origin_host_with_config( 1, origin, "https://new.com", "https://old.com/repo", None ) assert result is None def test_sets_aligned_url(self, mixin): origin = MagicMock() with patch("src.services.git._url.SessionLocal") as mock_sl: mock_db = MagicMock() mock_sl.return_value = mock_db mock_db_repo = MagicMock() mock_db.query.return_value.filter.return_value.first.return_value = mock_db_repo result = mixin._align_origin_host_with_config( 1, origin, "https://new.com", "https://old.com/repo", None ) assert result is not None mock_db_repo.remote_url = mixin._strip_url_credentials(result) assert mock_db.commit.called def test_db_update_failure(self, mixin): origin = MagicMock() with patch("src.services.git._url.SessionLocal", side_effect=Exception("DB error")): result = mixin._align_origin_host_with_config( 1, origin, "https://new.com", "https://old.com/repo", None ) assert result is not None # still returns aligned URL def test_uses_binding_remote_url_when_current_empty(self, mixin): origin = MagicMock() result = mixin._align_origin_host_with_config( 1, origin, "https://new.com", "", "https://old.com/repo" ) assert result is not None assert "new.com" in result # #endregion Test.Git.Url.Edge