diff --git a/src/patch_modal.py b/src/patch_modal.py new file mode 100644 index 0000000..cb5eb2b --- /dev/null +++ b/src/patch_modal.py @@ -0,0 +1,73 @@ +from typing import Optional, Callable, List +from dataclasses import dataclass, field + +@dataclass +class PendingPatch: + patch_text: str + file_paths: List[str] + generated_by: str + timestamp: float + +class PatchModalManager: + def __init__(self): + self._pending_patch: Optional[PendingPatch] = None + self._show_modal: bool = False + self._on_apply_callback: Optional[Callable[[str], bool]] = None + self._on_reject_callback: Optional[Callable[[], None]] = None + + def request_patch_approval(self, patch_text: str, file_paths: List[str], generated_by: str = "Tier 4 QA") -> bool: + from time import time + self._pending_patch = PendingPatch( + patch_text=patch_text, + file_paths=file_paths, + generated_by=generated_by, + timestamp=time() + ) + self._show_modal = True + return True + + def get_pending_patch(self) -> Optional[PendingPatch]: + return self._pending_patch + + def is_modal_shown(self) -> bool: + return self._show_modal + + def set_apply_callback(self, callback: Callable[[str], bool]) -> None: + self._on_apply_callback = callback + + def set_reject_callback(self, callback: Callable[[], None]) -> None: + self._on_reject_callback = callback + + def apply_patch(self, patch_text: str) -> bool: + if self._on_apply_callback: + return self._on_apply_callback(patch_text) + return False + + def reject_patch(self) -> None: + self._pending_patch = None + self._show_modal = False + if self._on_reject_callback: + self._on_reject_callback() + + def close_modal(self) -> None: + self._show_modal = False + + def reset(self) -> None: + self._pending_patch = None + self._show_modal = False + self._on_apply_callback = None + self._on_reject_callback = None + +_patch_modal_manager: Optional[PatchModalManager] = None + +def get_patch_modal_manager() -> PatchModalManager: + global _patch_modal_manager + if _patch_modal_manager is None: + _patch_modal_manager = PatchModalManager() + return _patch_modal_manager + +def reset_patch_modal_manager() -> None: + global _patch_modal_manager + if _patch_modal_manager: + _patch_modal_manager.reset() + _patch_modal_manager = None \ No newline at end of file diff --git a/tests/test_patch_modal.py b/tests/test_patch_modal.py new file mode 100644 index 0000000..03737e0 --- /dev/null +++ b/tests/test_patch_modal.py @@ -0,0 +1,89 @@ +import pytest +from src.patch_modal import ( + PatchModalManager, PendingPatch, + get_patch_modal_manager, reset_patch_modal_manager +) + +def test_patch_modal_manager_init(): + manager = PatchModalManager() + assert manager.get_pending_patch() is None + assert manager.is_modal_shown() is False + +def test_request_patch_approval(): + manager = PatchModalManager() + patch_text = "--- a/test.py\n+++ b/test.py\n@@ -1 +1 @@\n-old\n+new" + file_paths = ["test.py"] + + result = manager.request_patch_approval(patch_text, file_paths) + assert result is True + assert manager.is_modal_shown() is True + + pending = manager.get_pending_patch() + assert pending is not None + assert pending.patch_text == patch_text + assert pending.file_paths == file_paths + +def test_reject_patch(): + manager = PatchModalManager() + manager.request_patch_approval("diff", ["file.py"]) + + manager.reject_patch() + assert manager.get_pending_patch() is None + assert manager.is_modal_shown() is False + +def test_close_modal(): + manager = PatchModalManager() + manager.request_patch_approval("diff", ["file.py"]) + + manager.close_modal() + assert manager.is_modal_shown() is False + +def test_apply_callback(): + manager = PatchModalManager() + called = [] + + def apply_fn(patch: str) -> bool: + called.append(patch) + return True + + manager.set_apply_callback(apply_fn) + + patch_text = "--- a/test.py\n+++ b/test.py\n" + result = manager.apply_patch(patch_text) + + assert result is True + assert len(called) == 1 + assert called[0] == patch_text + +def test_reject_callback(): + manager = PatchModalManager() + called = [] + + def reject_fn() -> None: + called.append(True) + + manager.set_reject_callback(reject_fn) + manager.reject_patch() + + assert len(called) == 1 + +def test_reset(): + manager = PatchModalManager() + manager.request_patch_approval("diff", ["file.py"]) + manager.set_apply_callback(lambda x: True) + manager.set_reject_callback(lambda: None) + + manager.reset() + + assert manager.get_pending_patch() is None + assert manager.is_modal_shown() is False + +def test_get_patch_modal_manager_singleton(): + reset_patch_modal_manager() + + mgr1 = get_patch_modal_manager() + mgr2 = get_patch_modal_manager() + + assert mgr1 is mgr2 + + reset_patch_modal_manager() \ No newline at end of file