"""Focused tests for pure URL resolution behavior in the executable module.""" import importlib.machinery import importlib.util import os import pathlib import subprocess import sys import tempfile import unittest from unittest import mock SCRIPT_PATH = pathlib.Path(__file__).parents[1] / "git-tui" LOADER = importlib.machinery.SourceFileLoader("git_tui", str(SCRIPT_PATH)) SPEC = importlib.util.spec_from_loader(LOADER.name, LOADER) assert SPEC is not None git_tui = importlib.util.module_from_spec(SPEC) sys.modules[LOADER.name] = git_tui LOADER.exec_module(git_tui) class GiteaRemoteUrlTests(unittest.TestCase): def setUp(self) -> None: self.app = git_tui.GitTuiApp.__new__(git_tui.GitTuiApp) def test_resolves_ssh_url_from_https_base_url(self) -> None: remote_url = self.app.resolve_gitea_remote_url( "owner/project", "https://git.example.com", transport="ssh" ) self.assertEqual(remote_url, "git@git.example.com:owner/project.git") def test_resolves_custom_ssh_user_and_port(self) -> None: remote_url = self.app.resolve_gitea_remote_url( "owner/project", "https://git.example.com/gitea", transport="ssh", ssh_user="forge", ssh_port="2222", ) self.assertEqual( remote_url, "ssh://forge@git.example.com:2222/owner/project.git", ) def test_still_supports_https(self) -> None: remote_url = self.app.resolve_gitea_remote_url( "owner/project", "https://git.example.com", transport="https" ) self.assertEqual( remote_url, "https://git.example.com/owner/project.git", ) def test_rejects_invalid_ssh_port(self) -> None: remote_url = self.app.resolve_gitea_remote_url( "owner/project", "https://git.example.com", transport="ssh", ssh_port="not-a-port", ) self.assertEqual(remote_url, "") class RepositorySyncTests(unittest.TestCase): def setUp(self) -> None: self.app = git_tui.GitTuiApp.__new__(git_tui.GitTuiApp) self.app.status_message = "" self.app.show_output = lambda *_args: None self.app.show_command_result = lambda *_args: None self.original_cwd = pathlib.Path.cwd() self.temp_dir = tempfile.TemporaryDirectory() self.root = pathlib.Path(self.temp_dir.name) def tearDown(self) -> None: os.chdir(self.original_cwd) self.temp_dir.cleanup() def git(self, cwd: pathlib.Path, *args: str) -> str: completed = subprocess.run( ["git", *args], cwd=cwd, check=True, capture_output=True, text=True, ) return completed.stdout.strip() def init_worktree(self, path: pathlib.Path) -> None: path.mkdir() self.git(path, "init", "-b", "main") self.git(path, "config", "user.name", "Test User") self.git(path, "config", "user.email", "test@example.com") self.git(path, "config", "commit.gpgsign", "false") def test_sync_pushes_local_commit_to_empty_remote(self) -> None: remote = self.root / "remote.git" local = self.root / "local" remote.mkdir() self.git(remote, "init", "--bare") self.init_worktree(local) (local / "local.txt").write_text("local\n", encoding="utf-8") self.git(local, "add", "local.txt") self.git(local, "commit", "-m", "local") self.git(local, "remote", "add", "origin", str(remote)) os.chdir(local) self.assertTrue(self.app.sync_origin_repository()) self.assertEqual( self.git(remote, "rev-parse", "refs/heads/main"), self.git(local, "rev-parse", "HEAD"), ) def test_sync_populates_unborn_local_repository_from_remote(self) -> None: remote = self.root / "remote.git" seed = self.root / "seed" local = self.root / "local" remote.mkdir() self.git(remote, "init", "--bare") self.git(remote, "symbolic-ref", "HEAD", "refs/heads/main") self.init_worktree(seed) (seed / "remote.txt").write_text("remote\n", encoding="utf-8") self.git(seed, "add", "remote.txt") self.git(seed, "commit", "-m", "remote") self.git(seed, "remote", "add", "origin", str(remote)) self.git(seed, "push", "origin", "main") self.init_worktree(local) self.git(local, "remote", "add", "origin", str(remote)) os.chdir(local) self.assertTrue(self.app.sync_origin_repository()) self.assertEqual((local / "remote.txt").read_text(), "remote\n") self.assertEqual( self.git(local, "rev-parse", "HEAD"), self.git(remote, "rev-parse", "refs/heads/main"), ) def test_sync_merges_diverged_histories_and_pushes_result(self) -> None: remote = self.root / "remote.git" seed = self.root / "seed" local = self.root / "local" remote.mkdir() self.git(remote, "init", "--bare") self.git(remote, "symbolic-ref", "HEAD", "refs/heads/main") self.init_worktree(seed) (seed / "remote.txt").write_text("remote\n", encoding="utf-8") self.git(seed, "add", "remote.txt") self.git(seed, "commit", "-m", "remote") self.git(seed, "remote", "add", "origin", str(remote)) self.git(seed, "push", "origin", "main") self.init_worktree(local) (local / "local.txt").write_text("local\n", encoding="utf-8") self.git(local, "add", "local.txt") self.git(local, "commit", "-m", "local") self.git(local, "remote", "add", "origin", str(remote)) self.app.select_option_menu = lambda *_args: "merge" os.chdir(local) self.assertTrue(self.app.sync_origin_repository()) self.assertTrue((local / "local.txt").exists()) self.assertTrue((local / "remote.txt").exists()) self.assertEqual( self.git(local, "rev-parse", "HEAD"), self.git(remote, "rev-parse", "refs/heads/main"), ) class GiteaPublishTests(unittest.TestCase): def test_created_repository_can_keep_ssh_origin(self) -> None: app = git_tui.GitTuiApp.__new__(git_tui.GitTuiApp) app.status_message = "" prompt_values = iter(["owner/project", "https://git.example.com"]) app.prompt_input = lambda *_args: next(prompt_values) app.select_gitea_transport = lambda: "ssh" app.select_repository_visibility = lambda: False app.create_gitea_repository = lambda **_kwargs: ( True, { "clone_url": "https://git.example.com/owner/project.git", "ssh_url": "git@git.example.com:owner/project.git", "full_name": "owner/project", }, "", ) configured_urls = [] app.configure_origin_remote = lambda url: configured_urls.append(url) or True app.push_head_to_remote_branch = lambda *_args, **_kwargs: True app.current_local_branch = lambda: "main" app.show_output = lambda *_args: None with mock.patch.dict(os.environ, {"GITEA_TOKEN": "token"}, clear=False): self.assertTrue(app.publish_gitea_repository()) self.assertEqual( configured_urls, ["git@git.example.com:owner/project.git"], ) class MainMenuTests(unittest.TestCase): def test_remote_operations_and_disconnect_require_a_remote(self) -> None: app = git_tui.GitTuiApp.__new__(git_tui.GitTuiApp) app.has_any_remote = lambda: False action_names = [action for _, action in app.main_menu_items()] self.assertNotIn("push", action_names) self.assertNotIn("pull", action_names) self.assertNotIn("fetch", action_names) self.assertNotIn("disconnect_remote", action_names) def test_disconnect_is_available_when_a_remote_exists(self) -> None: app = git_tui.GitTuiApp.__new__(git_tui.GitTuiApp) app.has_any_remote = lambda: True action_names = [action for _, action in app.main_menu_items()] self.assertIn("push", action_names) self.assertIn("pull", action_names) self.assertIn("fetch", action_names) self.assertIn("disconnect_remote", action_names) if __name__ == "__main__": unittest.main()