""" Tests for the system commands in the CLI. - health - settings - status x serve x image-exists x docker-down x generate-report x update - version """ import json from importlib.metadata import version as get_version import pytest from click.testing import CliRunner from cli.commands.system import health, settings, status, version from r2r import R2RAsyncClient from tests.cli.async_invoke import async_invoke @pytest.mark.asyncio async def test_health_against_server(): """Test health check against a real server.""" # Create real client client = R2RAsyncClient(base_url="http://localhost:7272") # Run command runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, health, obj=client) # Extract just the JSON part (everything after the "Time taken" line) output = result.stdout_bytes.decode() json_str = output.split("\n", 1)[1] # Basic validation response_data = json.loads(json_str) assert "results" in response_data assert "message" in response_data["results"] assert response_data["results"]["message"] == "ok" assert result.exit_code == 0 @pytest.mark.asyncio async def test_health_server_down(): """Test health check when server is unreachable.""" client = R2RAsyncClient(base_url="http://localhost:54321") # Invalid port runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, health, obj=client) assert result.exit_code != 0 assert ( "Request failed: All connection attempts failed" in result.stderr_bytes.decode() ) @pytest.mark.asyncio async def test_health_invalid_url(): """Test health check with invalid URL.""" client = R2RAsyncClient(base_url="http://invalid.localhost") runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, health, obj=client) assert result.exit_code != 0 assert "Request failed" in result.stderr_bytes.decode() @pytest.mark.asyncio async def test_settings_against_server(): """Test settings retrieval against a real server.""" client = R2RAsyncClient(base_url="http://localhost:7272") runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, settings, obj=client) # Extract JSON part after "Time taken" line output = result.stdout_bytes.decode() json_str = output.split("\n", 1)[1] # Validate response structure response_data = json.loads(json_str) assert "results" in response_data assert "config" in response_data["results"] assert "prompts" in response_data["results"] # Validate key configuration sections config = response_data["results"]["config"] assert "completion" in config assert "database" in config assert "embedding" in config assert "ingestion" in config assert result.exit_code == 0 @pytest.mark.asyncio async def test_settings_server_down(): """Test settings retrieval when server is unreachable.""" client = R2RAsyncClient(base_url="http://localhost:54321") # Invalid port runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, settings, obj=client) assert result.exit_code != 0 assert ( "Request failed: All connection attempts failed" in result.stderr_bytes.decode() ) @pytest.mark.asyncio async def test_settings_invalid_url(): """Test settings retrieval with invalid URL.""" client = R2RAsyncClient(base_url="http://invalid.localhost") runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, settings, obj=client) assert result.exit_code != 0 assert "Request failed" in result.stderr_bytes.decode() @pytest.mark.asyncio async def test_settings_response_structure(): """Test detailed structure of settings response.""" client = R2RAsyncClient(base_url="http://localhost:7272") runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, settings, obj=client) output = result.stdout_bytes.decode() json_str = output.split("\n", 1)[1] response_data = json.loads(json_str) # Validate prompts structure prompts = response_data["results"]["prompts"] assert "results" in prompts assert "total_entries" in prompts assert isinstance(prompts["results"], list) # Validate prompt entries for prompt in prompts["results"]: assert "name" in prompt assert "id" in prompt assert "template" in prompt assert "input_types" in prompt assert "created_at" in prompt assert "updated_at" in prompt assert result.exit_code == 0 @pytest.mark.asyncio async def test_settings_config_validation(): """Test specific configuration values in settings response.""" client = R2RAsyncClient(base_url="http://localhost:7272") runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, settings, obj=client) output = result.stdout_bytes.decode() json_str = output.split("\n", 1)[1] response_data = json.loads(json_str) config = response_data["results"]["config"] # Validate completion config completion = config["completion"] assert "provider" in completion assert "concurrent_request_limit" in completion assert "generation_config" in completion # Validate database config database = config["database"] assert "provider" in database assert "default_collection_name" in database assert "limits" in database assert result.exit_code == 0 @pytest.mark.asyncio async def test_status_against_server(): """Test status check against a real server.""" client = R2RAsyncClient(base_url="http://localhost:7272") runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, status, obj=client) # Extract JSON part after "Time taken" line output = result.stdout_bytes.decode() json_str = output.split("\n", 1)[1] # Validate response structure response_data = json.loads(json_str) assert "results" in response_data # Validate specific fields results = response_data["results"] assert "start_time" in results assert "uptime_seconds" in results assert "cpu_usage" in results assert "memory_usage" in results # Validate data types assert isinstance(results["uptime_seconds"], (int, float)) assert isinstance(results["cpu_usage"], (int, float)) assert isinstance(results["memory_usage"], (int, float)) assert result.exit_code == 0 @pytest.mark.asyncio async def test_status_server_down(): """Test status check when server is unreachable.""" client = R2RAsyncClient(base_url="http://localhost:54321") # Invalid port runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, status, obj=client) assert result.exit_code != 0 assert ( "Request failed: All connection attempts failed" in result.stderr_bytes.decode() ) @pytest.mark.asyncio async def test_status_invalid_url(): """Test status check with invalid URL.""" client = R2RAsyncClient(base_url="http://invalid.localhost") runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, status, obj=client) assert result.exit_code != 0 assert "Request failed" in result.stderr_bytes.decode() @pytest.mark.asyncio async def test_status_value_ranges(): """Test that status values are within expected ranges.""" client = R2RAsyncClient(base_url="http://localhost:7272") runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, status, obj=client) output = result.stdout_bytes.decode() json_str = output.split("\n", 1)[1] response_data = json.loads(json_str) results = response_data["results"] # CPU usage should be between 0 and 100 assert 0 <= results["cpu_usage"] <= 100 # Memory usage should be between 0 and 100 assert 0 <= results["memory_usage"] <= 100 # Uptime should be positive assert results["uptime_seconds"] > 0 assert result.exit_code == 0 @pytest.mark.asyncio async def test_status_start_time_format(): """Test that start_time is in correct ISO format.""" client = R2RAsyncClient(base_url="http://localhost:7272") runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, status, obj=client) output = result.stdout_bytes.decode() json_str = output.split("\n", 1)[1] response_data = json.loads(json_str) from datetime import datetime # Verify start_time is valid ISO format start_time = response_data["results"]["start_time"] try: datetime.fromisoformat(start_time.replace("Z", "+00:00")) except ValueError: pytest.fail("start_time is not in valid ISO format") assert result.exit_code == 0 @pytest.mark.asyncio async def test_version_command(): """Test basic version command functionality.""" runner = CliRunner() result = await async_invoke(runner, version) # Verify command succeeded assert result.exit_code == 0 # Verify output is valid JSON and matches actual package version expected_version = get_version("r2r") actual_version = json.loads(result.stdout_bytes.decode()) assert actual_version == expected_version @pytest.mark.asyncio async def test_version_output_format(): """Test that version output is properly formatted JSON.""" runner = CliRunner() result = await async_invoke(runner, version) # Verify output is valid JSON try: output = result.stdout_bytes.decode() parsed = json.loads(output) assert isinstance(parsed, str) # Version should be a string except json.JSONDecodeError: pytest.fail("Version output is not valid JSON") # Should be non-empty output ending with newline assert output.strip() assert output.endswith("\n") @pytest.mark.asyncio async def test_version_error_handling(monkeypatch): """Test error handling when version import fails.""" def mock_version(_): raise ImportError("Package not found") # Mock the version function to raise an error monkeypatch.setattr("importlib.metadata.version", mock_version) runner = CliRunner(mix_stderr=False) result = await async_invoke(runner, version) # Verify command failed with exception assert result.exit_code == 1 error_output = result.stderr_bytes.decode() assert "An unexpected error occurred" in error_output assert "Package not found" in error_output