Initial commit: hqt — HQ Terminal TUI
TUI for orchestrating AI coding harness sessions (Claude Code, Codex, Kiro, etc.) via tmux. Click CLI bootstraps a Textual TUI over ProjectService/SessionService backed by SQLite, spawning harness sessions as tmux windows through TmuxManager. Includes recent fixes: - Visible Tab focus highlight on dialog OK/Cancel buttons - Auto-select first project on launch - Auto-select first session + per-project session-selection memory - tmux new-window targets an explicit free index, fixing "index N in use" failures (broken spawn/attach in attached sessions) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import hqt.logging as hqt_logging
|
||||
from hqt.config import Settings
|
||||
|
||||
|
||||
def test_setup_logging_does_not_write_to_terminal(tmp_path, monkeypatch):
|
||||
"""setup_logging must NOT attach a StreamHandler that writes to the real
|
||||
terminal stderr/stdout.
|
||||
|
||||
The TUI is a Textual app that paints to sys.__stderr__. A root-logger
|
||||
StreamHandler bound to that same fd writes raw log lines straight onto the
|
||||
pane Textual owns, corrupting tmux's grid — visible as rendering artifacts
|
||||
when switching back to the hqt window. Logs must go to file only.
|
||||
"""
|
||||
settings = Settings(db_path=tmp_path / "hqt.db")
|
||||
monkeypatch.setattr(hqt_logging, "get_settings", lambda: settings)
|
||||
|
||||
root = logging.getLogger()
|
||||
saved_handlers = root.handlers[:]
|
||||
saved_level = root.level
|
||||
try:
|
||||
root.handlers.clear()
|
||||
hqt_logging.setup_logging()
|
||||
|
||||
terminal_streams = {sys.stderr, sys.__stderr__, sys.stdout, sys.__stdout__}
|
||||
offending = [
|
||||
h
|
||||
for h in root.handlers
|
||||
# FileHandler subclasses StreamHandler but writes to a file, not the terminal.
|
||||
if type(h) is logging.StreamHandler
|
||||
and getattr(h, "stream", None) in terminal_streams
|
||||
]
|
||||
assert not offending, (
|
||||
f"setup_logging attached a terminal StreamHandler: {offending}"
|
||||
)
|
||||
# Logs are still captured to a file.
|
||||
assert any(isinstance(h, logging.FileHandler) for h in root.handlers)
|
||||
finally:
|
||||
root.handlers[:] = saved_handlers
|
||||
root.setLevel(saved_level)
|
||||
Reference in New Issue
Block a user