test_copilot_context.py
1 """Tests for Copilot live /models context-window resolution.""" 2 3 from __future__ import annotations 4 5 import time 6 from unittest.mock import patch 7 8 import pytest 9 10 from hermes_cli.models import get_copilot_model_context 11 12 13 # Sample catalog items mimicking the Copilot /models API response 14 _SAMPLE_CATALOG = [ 15 { 16 "id": "claude-opus-4.6-1m", 17 "capabilities": { 18 "type": "chat", 19 "limits": {"max_prompt_tokens": 1000000, "max_output_tokens": 64000}, 20 }, 21 }, 22 { 23 "id": "gpt-4.1", 24 "capabilities": { 25 "type": "chat", 26 "limits": {"max_prompt_tokens": 128000, "max_output_tokens": 32768}, 27 }, 28 }, 29 { 30 "id": "claude-sonnet-4", 31 "capabilities": { 32 "type": "chat", 33 "limits": {"max_prompt_tokens": 200000, "max_output_tokens": 64000}, 34 }, 35 }, 36 { 37 "id": "model-without-limits", 38 "capabilities": {"type": "chat"}, 39 }, 40 { 41 "id": "model-zero-limit", 42 "capabilities": { 43 "type": "chat", 44 "limits": {"max_prompt_tokens": 0}, 45 }, 46 }, 47 ] 48 49 50 @pytest.fixture(autouse=True) 51 def _clear_cache(): 52 """Reset module-level cache before each test.""" 53 import hermes_cli.models as mod 54 55 mod._copilot_context_cache = {} 56 mod._copilot_context_cache_time = 0.0 57 yield 58 mod._copilot_context_cache = {} 59 mod._copilot_context_cache_time = 0.0 60 61 62 class TestGetCopilotModelContext: 63 """Tests for get_copilot_model_context().""" 64 65 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=_SAMPLE_CATALOG) 66 def test_returns_max_prompt_tokens(self, mock_fetch): 67 assert get_copilot_model_context("claude-opus-4.6-1m") == 1_000_000 68 assert get_copilot_model_context("gpt-4.1") == 128_000 69 70 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=_SAMPLE_CATALOG) 71 def test_returns_none_for_unknown_model(self, mock_fetch): 72 assert get_copilot_model_context("nonexistent-model") is None 73 74 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=_SAMPLE_CATALOG) 75 def test_skips_models_without_limits(self, mock_fetch): 76 assert get_copilot_model_context("model-without-limits") is None 77 78 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=_SAMPLE_CATALOG) 79 def test_skips_zero_limit(self, mock_fetch): 80 assert get_copilot_model_context("model-zero-limit") is None 81 82 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=_SAMPLE_CATALOG) 83 def test_caches_results(self, mock_fetch): 84 get_copilot_model_context("gpt-4.1") 85 get_copilot_model_context("claude-sonnet-4") 86 # Only one API call despite two lookups 87 assert mock_fetch.call_count == 1 88 89 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=_SAMPLE_CATALOG) 90 def test_cache_expires(self, mock_fetch): 91 import hermes_cli.models as mod 92 93 get_copilot_model_context("gpt-4.1") 94 assert mock_fetch.call_count == 1 95 96 # Expire the cache 97 mod._copilot_context_cache_time = time.time() - 7200 98 get_copilot_model_context("gpt-4.1") 99 assert mock_fetch.call_count == 2 100 101 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=None) 102 def test_returns_none_when_catalog_unavailable(self, mock_fetch): 103 assert get_copilot_model_context("gpt-4.1") is None 104 105 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=[]) 106 def test_returns_none_for_empty_catalog(self, mock_fetch): 107 assert get_copilot_model_context("gpt-4.1") is None 108 109 110 class TestModelMetadataCopilotIntegration: 111 """Test that get_model_context_length() uses Copilot live API for copilot provider.""" 112 113 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=_SAMPLE_CATALOG) 114 def test_copilot_provider_uses_live_api(self, mock_fetch): 115 from agent.model_metadata import get_model_context_length 116 117 ctx = get_model_context_length("claude-opus-4.6-1m", provider="copilot") 118 assert ctx == 1_000_000 119 120 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=_SAMPLE_CATALOG) 121 def test_copilot_acp_provider_uses_live_api(self, mock_fetch): 122 from agent.model_metadata import get_model_context_length 123 124 ctx = get_model_context_length("claude-sonnet-4", provider="copilot-acp") 125 assert ctx == 200_000 126 127 @patch("hermes_cli.models.fetch_github_model_catalog", return_value=None) 128 def test_falls_through_when_catalog_unavailable(self, mock_fetch): 129 from agent.model_metadata import get_model_context_length 130 131 # Should not raise, should fall through to models.dev or defaults 132 ctx = get_model_context_length("gpt-4.1", provider="copilot") 133 assert isinstance(ctx, int) 134 assert ctx > 0