"""Palette Quick Connect (Ctrl+K) : collecte des resultats (sessions, hosts OpenSSH, workspaces, snippets, actions), filtrage flou, navigation clavier et activation. SSHConnection.start()/LocalTerminalConnection.start() sont neutralisees, et ~/.ssh/config est redirige vers un fichier absent (pas de dependance au poste qui execute le test).""" import sys, pathlib sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent)) import tempfile from pathlib import Path from unittest.mock import patch from PySide6.QtCore import Qt, QEvent from PySide6.QtGui import QKeyEvent from PySide6.QtWidgets import QApplication, QWidget from app.session_manager import Session, SessionStore from app.key_manager import KeyManager from app.ssh_connection import SSHConnection from app.local_terminal import LocalTerminalConnection from app.controllers.broadcast_manager import BroadcastManager from app.controllers.connection_controller import ConnectionController from app.controllers.session_controller import SessionController from app.controllers.workspace_manager import WorkspaceManager from app.workspace import WorkspaceStore from app.snippets import Snippet, SnippetStore from app.dialogs.quick_connect_dialog import QuickConnectDialog import app.app_settings as app_settings app = QApplication.instance() or QApplication([]) tmp = Path(tempfile.mkdtemp()) absent_ssh_config = tmp / "no-such-ssh-config" no_ssh_thread = patch.object(SSHConnection, "start", lambda self: None) no_local_thread = patch.object(LocalTerminalConnection, "start", lambda self: None) settings_redirect = patch.object(app_settings, "SETTINGS_FILE", tmp / "settings.json") no_ssh_thread.start() no_local_thread.start() settings_redirect.start() def make_palette(): session_store = SessionStore(path=tmp / "sessions.json") key_manager = KeyManager(registry_path=tmp / "keys.json") workspace_store = WorkspaceStore(path=tmp / "workspaces.json") snippet_store = SnippetStore(path=tmp / "snippets.json") snippet_store.snippets = [Snippet(name="Disk Usage", category="Linux", command="df -h")] host_widget = QWidget() broadcast = BroadcastManager(lambda: [host_widget]) cc = ConnectionController(session_store, key_manager, broadcast, snippet_store=snippet_store, parent_widget=host_widget) sc = SessionController(session_store, key_manager, broadcast.button, parent_widget=host_widget, openssh_config_path=absent_ssh_config) wm = WorkspaceManager(workspace_store, session_store, cc, parent_widget=host_widget) dialog = QuickConnectDialog(session_store, cc, wm, snippet_store, sc, key_manager, parent=host_widget) return session_store, cc, wm, snippet_store, dialog print("=== 1. Collecte : sessions, workspaces, snippets et actions presents ===") session_store, cc, wm, snippet_store, dialog = make_palette() session = Session(name="prod-web", host="10.0.0.5", username="root") session_store.add(session) cc.open_local_terminal() workspace = wm.save_as_new("Homelab") dialog._all_results = dialog._collect_results() # recollecte apres l'ajout de la session/workspace kinds = {r.kind for r in dialog._all_results} print(" types de resultats collectes:", kinds) assert "session" in kinds assert "workspace" in kinds assert "snippet" in kinds assert "action" in kinds assert any(r.label == "prod-web" for r in dialog._all_results) assert any(r.label == "Homelab" for r in dialog._all_results) assert any(r.label == "Disk Usage" for r in dialog._all_results) print(" OK") print() print("=== 2. Filtrage : la recherche restreint et classe les resultats affiches ===") dialog.search_edit.setText("prod") print(f" {dialog.list.count()} resultat(s) pour 'prod'") assert dialog.list.count() >= 1 top_label = dialog.list.item(0).data(Qt.UserRole).label print(" meilleur resultat:", top_label) assert "prod" in top_label.lower() print(" OK") print() print("=== 3. Chaine vide -> tous les resultats affiches ===") dialog.search_edit.setText("") assert dialog.list.count() == len(dialog._all_results) print(" OK") print() print("=== 4. Navigation clavier : bas/haut deplacent la selection ===") dialog.search_edit.setText("") dialog.list.setCurrentRow(0) down = QKeyEvent(QEvent.KeyPress, Qt.Key_Down, Qt.NoModifier) dialog.eventFilter(dialog.search_edit, down) assert dialog.list.currentRow() == 1 up = QKeyEvent(QEvent.KeyPress, Qt.Key_Up, Qt.NoModifier) dialog.eventFilter(dialog.search_edit, up) assert dialog.list.currentRow() == 0 print(" OK") print() print("=== 5. Activation d'une session ouvre bien un onglet SSH ===") session_store2, cc2, wm2, snippet_store2, dialog2 = make_palette() session2 = Session(name="cible", host="10.0.0.9", username="root") session_store2.add(session2) dialog2._all_results = dialog2._collect_results() dialog2.search_edit.setText("cible") assert dialog2.list.count() == 1 before = cc2.tabs.count() dialog2._activate_current() print(f" onglets avant/apres activation: {before}/{cc2.tabs.count()}") assert cc2.tabs.count() == before + 1 from app.ui.tabs import SessionTabWidget assert isinstance(cc2.tabs.widget(cc2.tabs.count() - 1), SessionTabWidget) print(" OK") print() print("=== 6. Activation d'un snippet insere dans l'onglet courant ===") session_store3, cc3, wm3, snippet_store3, dialog3 = make_palette() cc3.open_local_terminal() dialog3._all_results = dialog3._collect_results() dialog3.search_edit.setText("Disk Usage") assert dialog3.list.count() == 1 dialog3._activate_current() current_tab = cc3.tabs.currentWidget() sent = current_tab.connection._out_queue.get_nowait() print(" envoye dans l'onglet courant:", sent) assert sent == b"df -h" print(" OK") no_ssh_thread.stop() no_local_thread.stop() settings_redirect.stop() print() print("QUICK CONNECT : TOUS LES TESTS OK")