"""Tests du moteur : capture -> IA -> dactylographie (tout mocké).""" from types import SimpleNamespace from ai_typewriter.config import ConfigStore from ai_typewriter.engine import AITypewriterEngine def _make_store(tmp_path) -> ConfigStore: store = ConfigStore(tmp_path / "config.json") store.ensure_defaults() store.load() return store def _fake_keyboard(monkeypatch, typed, sent): hook = {} monkeypatch.setattr( "ai_typewriter.key_stepper.keyboard.write", lambda char, delay=0, exact=True: typed.append(char), ) monkeypatch.setattr("ai_typewriter.key_stepper.keyboard.send", lambda v: sent.append(v)) monkeypatch.setattr( "ai_typewriter.key_stepper.keyboard.hook", lambda callback, suppress=True: hook.update({"callback": callback}) or "hook", ) monkeypatch.setattr("ai_typewriter.key_stepper.keyboard.unhook", lambda v: None) return hook def test_capture_ask_and_step_types_response(monkeypatch, tmp_path): store = _make_store(tmp_path) engine = AITypewriterEngine(store) typed, sent = [], [] hook = _fake_keyboard(monkeypatch, typed, sent) monkeypatch.setattr( "ai_typewriter.engine.capture_clipboard", lambda: "Question de test" ) monkeypatch.setattr( "ai_typewriter.engine.ask_ai", lambda prompt, profile, store: "Réponse IA" ) engine.capture_ask_and_step() assert engine._stepper is not None hook["callback"](SimpleNamespace(event_type="down")) hook["callback"](SimpleNamespace(event_type="down")) assert typed == ["R", "é"] def test_capture_ask_and_step_with_equations(monkeypatch, tmp_path): store = _make_store(tmp_path) store.set_active("Mathématiques (LaTeX)") engine = AITypewriterEngine(store) typed, sent = [], [] hook = _fake_keyboard(monkeypatch, typed, sent) monkeypatch.setattr( "ai_typewriter.engine.capture_clipboard", lambda: "Calcule" ) monkeypatch.setattr( "ai_typewriter.engine.ask_ai", lambda prompt, profile, store: r"[EQ]a=x[/EQ]" ) engine.capture_ask_and_step() # 1:[EQ]->alt+= ; a: char ; =: char ; x: char ; [/EQ]->right => 5 actions for _ in range(5): hook["callback"](SimpleNamespace(event_type="down")) assert sent == ["alt+=", "right"] assert typed == ["a", "=", "x"] def test_empty_clipboard_does_not_ask(monkeypatch, tmp_path): store = _make_store(tmp_path) engine = AITypewriterEngine(store) called = [] monkeypatch.setattr("ai_typewriter.engine.capture_clipboard", lambda: " ") monkeypatch.setattr( "ai_typewriter.engine.ask_ai", lambda prompt, profile, store: called.append(prompt) or "x", ) engine.capture_ask_and_step() assert called == [] def test_ask_only_returns_answer(monkeypatch, tmp_path): store = _make_store(tmp_path) engine = AITypewriterEngine(store) monkeypatch.setattr( "ai_typewriter.engine.ask_ai", lambda prompt, profile, store: "reponse-brute", ) assert engine.ask_only("bonjour") == "reponse-brute" def test_concurrent_hotkey_ignored_while_busy(monkeypatch, tmp_path): store = _make_store(tmp_path) engine = AITypewriterEngine(store) # Occupe le verrou assert engine._busy.acquire(blocking=False) is True calls = [] engine.handle_hotkey() # doit être ignoré (busy) assert calls == [] engine._busy.release()