116 lines
4.0 KiB
Python
116 lines
4.0 KiB
Python
"""Tests for context & token visualization (Track: context_token_viz_20260301)."""
|
|
from typing import Generator
|
|
from unittest.mock import patch
|
|
import pytest
|
|
|
|
import ai_client
|
|
from ai_client import _add_bleed_derived, get_history_bleed_stats
|
|
from gui_2 import App
|
|
|
|
|
|
@pytest.fixture
|
|
def app_instance() -> Generator[App, None, None]:
|
|
with (
|
|
patch('gui_2.load_config', return_value={'gui': {'show_windows': {}}}),
|
|
patch('gui_2.save_config'),
|
|
patch('gui_2.project_manager'),
|
|
patch('gui_2.session_logger'),
|
|
patch('gui_2.immapp.run'),
|
|
patch.object(App, '_load_active_project'),
|
|
patch.object(App, '_fetch_models'),
|
|
patch.object(App, '_load_fonts'),
|
|
patch.object(App, '_post_init')
|
|
):
|
|
yield App()
|
|
|
|
|
|
# --- _add_bleed_derived unit tests ---
|
|
|
|
def test_add_bleed_derived_aliases() -> None:
|
|
base = {"provider": "test", "limit": 1000, "current": 400, "percentage": 40.0}
|
|
result = _add_bleed_derived(base)
|
|
assert result["estimated_prompt_tokens"] == 400
|
|
assert result["max_prompt_tokens"] == 1000
|
|
assert result["utilization_pct"] == 40.0
|
|
|
|
|
|
def test_add_bleed_derived_headroom() -> None:
|
|
base = {"provider": "test", "limit": 1000, "current": 400, "percentage": 40.0}
|
|
result = _add_bleed_derived(base)
|
|
assert result["headroom_tokens"] == 600
|
|
|
|
|
|
def test_add_bleed_derived_would_trim_false() -> None:
|
|
base = {"provider": "test", "limit": 100000, "current": 10000, "percentage": 10.0}
|
|
result = _add_bleed_derived(base)
|
|
assert result["would_trim"] is False
|
|
|
|
|
|
def test_add_bleed_derived_would_trim_true() -> None:
|
|
base = {"provider": "test", "limit": 100000, "current": 90000, "percentage": 90.0}
|
|
result = _add_bleed_derived(base)
|
|
assert result["would_trim"] is True # headroom = 10000 < 20000
|
|
|
|
|
|
def test_add_bleed_derived_breakdown() -> None:
|
|
base = {"provider": "test", "limit": 10000, "current": 5000, "percentage": 50.0}
|
|
result = _add_bleed_derived(base, sys_tok=500, tool_tok=2500)
|
|
assert result["system_tokens"] == 500
|
|
assert result["tools_tokens"] == 2500
|
|
assert result["history_tokens"] == 2000 # 5000 - 500 - 2500
|
|
|
|
|
|
def test_add_bleed_derived_history_clamped_to_zero() -> None:
|
|
"""history_tokens should not go negative when sys+tool > current."""
|
|
base = {"provider": "test", "limit": 1000, "current": 100, "percentage": 10.0}
|
|
result = _add_bleed_derived(base, sys_tok=200, tool_tok=2500)
|
|
assert result["history_tokens"] == 0
|
|
|
|
|
|
def test_add_bleed_derived_headroom_clamped_to_zero() -> None:
|
|
base = {"provider": "test", "limit": 1000, "current": 1100, "percentage": 110.0}
|
|
result = _add_bleed_derived(base)
|
|
assert result["headroom_tokens"] == 0
|
|
|
|
|
|
# --- get_history_bleed_stats returns all required keys ---
|
|
|
|
REQUIRED_KEYS = [
|
|
"provider", "limit", "current", "percentage",
|
|
"estimated_prompt_tokens", "max_prompt_tokens", "utilization_pct",
|
|
"headroom_tokens", "would_trim", "system_tokens", "tools_tokens", "history_tokens",
|
|
]
|
|
|
|
def test_get_history_bleed_stats_returns_all_keys_unknown_provider() -> None:
|
|
"""Fallback path (unknown provider) must still return all derived keys."""
|
|
original = ai_client._provider
|
|
try:
|
|
ai_client._provider = "unknown_test_provider"
|
|
stats = get_history_bleed_stats()
|
|
for key in REQUIRED_KEYS:
|
|
assert key in stats, f"Missing key: {key}"
|
|
finally:
|
|
ai_client._provider = original
|
|
|
|
|
|
# --- App initialization ---
|
|
|
|
def test_app_token_stats_initialized_empty(app_instance: App) -> None:
|
|
assert app_instance._token_stats == {}
|
|
|
|
|
|
def test_app_last_stable_md_initialized_empty(app_instance: App) -> None:
|
|
assert app_instance._last_stable_md == ""
|
|
|
|
|
|
def test_app_has_render_token_budget_panel(app_instance: App) -> None:
|
|
assert callable(getattr(app_instance, "_render_token_budget_panel", None))
|
|
|
|
|
|
def test_render_token_budget_panel_empty_stats_no_crash(app_instance: App) -> None:
|
|
"""With empty _token_stats, _render_token_budget_panel must not raise."""
|
|
app_instance._token_stats = {}
|
|
# We can't render ImGui in tests, so just verify the guard condition logic
|
|
# by checking the method exists and _token_stats is empty (early-return path)
|
|
assert not app_instance._token_stats # falsy — method would return early
|