fix(cli): extend agents diagnostics to check all 9 supported providers #3469
@@ -0,0 +1,101 @@
|
||||
@diagnostics
|
||||
Feature: Diagnostics provider coverage — all 9 providers checked
|
||||
As a developer ensuring thorough diagnostic coverage
|
||||
I want _check_providers() to report status for all 9 supported providers
|
||||
So that users of Groq, Together AI, Cohere, Azure, and Gemini see diagnostic feedback
|
||||
|
||||
# ===========================================================================
|
||||
# _check_providers — all 9 providers present
|
||||
# ===========================================================================
|
||||
|
||||
Scenario: check_providers returns results for all 9 supported providers
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with no providers configured
|
||||
Then the provider check results should contain exactly 9 entries
|
||||
And the provider check results should include provider "openai"
|
||||
And the provider check results should include provider "anthropic"
|
||||
And the provider check results should include provider "google"
|
||||
And the provider check results should include provider "azure"
|
||||
And the provider check results should include provider "openrouter"
|
||||
And the provider check results should include provider "gemini"
|
||||
And the provider check results should include provider "cohere"
|
||||
And the provider check results should include provider "groq"
|
||||
And the provider check results should include provider "together"
|
||||
|
||||
# ===========================================================================
|
||||
# _check_providers — configured provider shows OK
|
||||
# ===========================================================================
|
||||
|
||||
Scenario: check_providers shows OK status for a configured provider
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with "groq" configured
|
||||
Then the provider check result for "groq" should have status "ok"
|
||||
And the provider check result for "groq" should have details "configured"
|
||||
And the provider check result for "groq" should have no recommendation
|
||||
|
||||
Scenario: check_providers shows OK status for azure when configured
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with "azure" configured
|
||||
Then the provider check result for "azure" should have status "ok"
|
||||
And the provider check result for "azure" should have details "configured"
|
||||
And the provider check result for "azure" should have no recommendation
|
||||
|
||||
Scenario: check_providers shows OK status for together when configured
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with "together" configured
|
||||
Then the provider check result for "together" should have status "ok"
|
||||
And the provider check result for "together" should have details "configured"
|
||||
And the provider check result for "together" should have no recommendation
|
||||
|
||||
Scenario: check_providers shows OK status for cohere when configured
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with "cohere" configured
|
||||
Then the provider check result for "cohere" should have status "ok"
|
||||
And the provider check result for "cohere" should have details "configured"
|
||||
And the provider check result for "cohere" should have no recommendation
|
||||
|
||||
Scenario: check_providers shows OK status for gemini when configured
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with "gemini" configured
|
||||
Then the provider check result for "gemini" should have status "ok"
|
||||
And the provider check result for "gemini" should have details "configured"
|
||||
And the provider check result for "gemini" should have no recommendation
|
||||
|
||||
# ===========================================================================
|
||||
# _check_providers — unconfigured provider shows WARN with recommendation
|
||||
# ===========================================================================
|
||||
|
||||
Scenario: check_providers shows WARN for groq when not configured
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with no providers configured
|
||||
Then the provider check result for "groq" should have status "warn"
|
||||
And the provider check result for "groq" should have details "missing"
|
||||
And the provider check result for "groq" should have a recommendation mentioning "GROQ_API_KEY"
|
||||
|
||||
Scenario: check_providers shows WARN for together when not configured
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with no providers configured
|
||||
Then the provider check result for "together" should have status "warn"
|
||||
And the provider check result for "together" should have details "missing"
|
||||
And the provider check result for "together" should have a recommendation mentioning "TOGETHER_API_KEY"
|
||||
|
||||
Scenario: check_providers shows WARN for cohere when not configured
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with no providers configured
|
||||
Then the provider check result for "cohere" should have status "warn"
|
||||
And the provider check result for "cohere" should have details "missing"
|
||||
And the provider check result for "cohere" should have a recommendation mentioning "COHERE_API_KEY"
|
||||
|
||||
Scenario: check_providers shows WARN for azure when not configured
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with no providers configured
|
||||
Then the provider check result for "azure" should have status "warn"
|
||||
And the provider check result for "azure" should have details "missing"
|
||||
And the provider check result for "azure" should have a recommendation mentioning "AZURE_OPENAI_API_KEY"
|
||||
|
||||
Scenario: check_providers shows WARN for gemini when not configured
|
||||
Given the diagnostics provider check module is loaded
|
||||
When I call check_providers with no providers configured
|
||||
Then the provider check result for "gemini" should have status "warn"
|
||||
And the provider check result for "gemini" should have details "missing"
|
||||
And the provider check result for "gemini" should have a recommendation mentioning "GEMINI_API_KEY"
|
||||
@@ -0,0 +1,38 @@
|
||||
"""Mock Settings object for testing diagnostics and provider configuration.
|
||||
|
||||
This mock is used in Behave step definitions to simulate the Settings object
|
||||
returned by ``cleveragents.config.settings.get_settings()``.
|
||||
|
||||
Following the mock placement rule (ADR-022), all mocking code must exist only
|
||||
within the ``features/mocks/`` directory and never in step definition files.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
def make_settings_mock(configured_providers: set[str] | None = None) -> MagicMock:
|
||||
"""Build a mock Settings object for use in Behave step definitions.
|
||||
|
||||
Args:
|
||||
configured_providers: Set of provider names that should appear configured.
|
||||
If None, no providers are configured.
|
||||
|
||||
Returns:
|
||||
A MagicMock that mimics the Settings interface, with
|
||||
``has_provider_configured()`` returning True only for providers in
|
||||
``configured_providers``.
|
||||
"""
|
||||
if configured_providers is None:
|
||||
configured_providers = set()
|
||||
|
||||
s = MagicMock()
|
||||
|
||||
def has_provider_configured(provider: str | None = None) -> bool:
|
||||
if provider is None:
|
||||
return bool(configured_providers)
|
||||
return provider in configured_providers
|
||||
|
||||
s.has_provider_configured = MagicMock(side_effect=has_provider_configured)
|
||||
return s
|
||||
@@ -0,0 +1,170 @@
|
||||
"""Step definitions for diagnostics_provider_coverage.feature.
|
||||
|
||||
Tests that _check_providers() in system.py reports diagnostic status for
|
||||
all 9 supported providers: openai, anthropic, google, azure, openrouter,
|
||||
gemini, cohere, groq, together.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from behave import given, then, when
|
||||
from behave.runner import Context
|
||||
|
||||
from features.mocks.settings_mock import make_settings_mock
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Given
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@given("the diagnostics provider check module is loaded")
|
||||
def step_provider_check_module_loaded(context: Context) -> None:
|
||||
context.provider_results = None
|
||||
context.configured_providers: set[str] = set()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# When
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@when("I call check_providers with no providers configured")
|
||||
def step_check_providers_none_configured(context: Context) -> None:
|
||||
from cleveragents.cli.commands.system import _check_providers
|
||||
|
||||
ms = make_settings_mock(configured_providers=set())
|
||||
with patch("cleveragents.config.settings.get_settings", return_value=ms):
|
||||
context.provider_results = _check_providers()
|
||||
|
||||
|
||||
@when('I call check_providers with "{provider}" configured')
|
||||
def step_check_providers_one_configured(context: Context, provider: str) -> None:
|
||||
from cleveragents.cli.commands.system import _check_providers
|
||||
|
||||
ms = make_settings_mock(configured_providers={provider})
|
||||
with patch("cleveragents.config.settings.get_settings", return_value=ms):
|
||||
context.provider_results = _check_providers()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Then — count assertions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@then("the provider check results should contain exactly {count:d} entries")
|
||||
def step_assert_result_count(context: Context, count: int) -> None:
|
||||
results = context.provider_results
|
||||
assert results is not None, "provider_results is None"
|
||||
actual = len(results)
|
||||
assert actual == count, (
|
||||
f"Expected exactly {count} provider check entries, got {actual}. "
|
||||
f"Entries: {[r['name'] for r in results]}"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Then — provider presence assertions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@then('the provider check results should include provider "{provider}"')
|
||||
def step_assert_provider_present(context: Context, provider: str) -> None:
|
||||
results = context.provider_results
|
||||
assert results is not None, "provider_results is None"
|
||||
names_lower = [r["name"].lower() for r in results]
|
||||
assert any(provider.lower() in name for name in names_lower), (
|
||||
f"Expected provider '{provider}' in results, but got: "
|
||||
f"{[r['name'] for r in results]}"
|
||||
)
|
||||
|
||||
|
||||
def _find_provider_result(results: list[dict], provider: str) -> dict | None:
|
||||
"""Find the result entry for a given provider name."""
|
||||
for r in results:
|
||||
if provider.lower() in r["name"].lower():
|
||||
return r
|
||||
return None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Then — per-provider status/details/recommendation assertions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@then(
|
||||
'the provider check result for "{provider}" should have status "{expected_status}"'
|
||||
)
|
||||
def step_assert_provider_status(
|
||||
context: Context, provider: str, expected_status: str
|
||||
) -> None:
|
||||
results = context.provider_results
|
||||
assert results is not None, "provider_results is None"
|
||||
entry = _find_provider_result(results, provider)
|
||||
assert entry is not None, (
|
||||
f"No result found for provider '{provider}'. "
|
||||
f"Available: {[r['name'] for r in results]}"
|
||||
)
|
||||
actual = str(entry["status"])
|
||||
assert actual == expected_status, (
|
||||
f"Provider '{provider}': expected status '{expected_status}', got '{actual}'"
|
||||
)
|
||||
|
||||
|
||||
@then(
|
||||
'the provider check result for "{provider}" should have details "{expected_details}"'
|
||||
)
|
||||
def step_assert_provider_details(
|
||||
context: Context, provider: str, expected_details: str
|
||||
) -> None:
|
||||
results = context.provider_results
|
||||
assert results is not None, "provider_results is None"
|
||||
entry = _find_provider_result(results, provider)
|
||||
assert entry is not None, (
|
||||
f"No result found for provider '{provider}'. "
|
||||
f"Available: {[r['name'] for r in results]}"
|
||||
)
|
||||
actual = entry["details"]
|
||||
assert actual == expected_details, (
|
||||
f"Provider '{provider}': expected details '{expected_details}', got '{actual}'"
|
||||
)
|
||||
|
||||
|
||||
@then('the provider check result for "{provider}" should have no recommendation')
|
||||
def step_assert_provider_no_recommendation(context: Context, provider: str) -> None:
|
||||
results = context.provider_results
|
||||
assert results is not None, "provider_results is None"
|
||||
entry = _find_provider_result(results, provider)
|
||||
assert entry is not None, (
|
||||
f"No result found for provider '{provider}'. "
|
||||
f"Available: {[r['name'] for r in results]}"
|
||||
)
|
||||
rec = entry.get("recommendation")
|
||||
assert rec is None, (
|
||||
f"Provider '{provider}': expected no recommendation, got '{rec}'"
|
||||
)
|
||||
|
||||
|
||||
@then(
|
||||
'the provider check result for "{provider}" should have a recommendation mentioning "{env_var}"'
|
||||
)
|
||||
def step_assert_provider_recommendation_mentions(
|
||||
context: Context, provider: str, env_var: str
|
||||
) -> None:
|
||||
results = context.provider_results
|
||||
assert results is not None, "provider_results is None"
|
||||
entry = _find_provider_result(results, provider)
|
||||
assert entry is not None, (
|
||||
f"No result found for provider '{provider}'. "
|
||||
f"Available: {[r['name'] for r in results]}"
|
||||
)
|
||||
rec = entry.get("recommendation")
|
||||
assert rec is not None, (
|
||||
f"Provider '{provider}': expected a recommendation mentioning '{env_var}', "
|
||||
f"but recommendation is None"
|
||||
)
|
||||
assert env_var in rec, (
|
||||
f"Provider '{provider}': expected recommendation to mention '{env_var}', "
|
||||
f"got '{rec}'"
|
||||
)
|
||||
@@ -250,7 +250,12 @@ def _check_providers() -> list[dict[str, Any]]:
|
||||
("openai", "OPENAI_API_KEY"),
|
||||
("anthropic", "ANTHROPIC_API_KEY"),
|
||||
("google", "GOOGLE_API_KEY"),
|
||||
("azure", "AZURE_OPENAI_API_KEY"),
|
||||
("openrouter", "OPENROUTER_API_KEY"),
|
||||
("gemini", "GEMINI_API_KEY"),
|
||||
("cohere", "COHERE_API_KEY"),
|
||||
("groq", "GROQ_API_KEY"),
|
||||
("together", "TOGETHER_API_KEY"),
|
||||
]
|
||||
|
||||
for provider_name, env_var in provider_checks:
|
||||
|
||||
Reference in New Issue
Block a user