""" Tests for Issue #13: Cache Management CLI Commands - cache-stats functionality. This module tests the cache-stats command which displays cache statistics and effectiveness. The cache-stats command should provide detailed metrics including hit rate, memory usage, file count, and performance monitoring data. """ import json import os import tempfile from pathlib import Path from unittest.mock import patch, MagicMock import pytest from click.testing import CliRunner from markitect.cli import cli from markitect.ast_cache import ASTCache from markitect.database import DatabaseManager class TestCacheStatsCommand: """Test suite for cache-stats command functionality.""" def setup_method(self): """Set up test environment for each test.""" self.runner = CliRunner() self.temp_dir = tempfile.mkdtemp() self.cache_dir = Path(self.temp_dir) / ".ast_cache" self.db_path = Path(self.temp_dir) / "test.db" # Create test markdown file self.test_file = Path(self.temp_dir) / "test.md" self.test_file.write_text("""--- title: Test Document author: Test Author --- # Test Heading This is a test document with some content. ## Section 1 Content for section 1. ## Section 2 More content here. """) def teardown_method(self): """Clean up after each test.""" import shutil if Path(self.temp_dir).exists(): shutil.rmtree(self.temp_dir) def test_cache_info_command_exists(self): """Test that cache-stats command is available in CLI.""" # This test will initially fail until command is implemented result = self.runner.invoke(cli, ['cache-stats']) # Should not return "No such command" error assert "No such command" not in result.output assert result.exit_code in [0, 1] # 0 for success, 1 for expected errors def test_cache_info_displays_basic_statistics(self): """Test that cache-stats displays basic cache statistics.""" # Setup: Create cache with some files cache = ASTCache(self.cache_dir) cache.cache_file(self.test_file) # Execute command - patch the cache service instead of global Path with patch('markitect.cache_service.CacheDirectoryService.get_cache_directory') as mock_cache_dir: mock_cache_dir.return_value = self.cache_dir result = self.runner.invoke(cli, ['cache-stats']) # Should show cache statistics assert result.exit_code == 0 assert "Cache Directory:" in result.output assert "Total Files:" in result.output assert "Cache Size:" in result.output def test_cache_info_shows_file_count(self): """Test that cache-stats correctly reports number of cached files.""" # Setup: Create multiple cached files cache = ASTCache(self.cache_dir) # Create additional test files test_file2 = Path(self.temp_dir) / "test2.md" test_file2.write_text("# Another Test\n\nContent here.") cache.cache_file(self.test_file) cache.cache_file(test_file2) # Execute command - mock CacheDirectoryService to return our test cache stats with patch('markitect.cli.CacheDirectoryService') as mock_cache_service: mock_service_instance = MagicMock() mock_service_instance.get_cache_stats.return_value = { 'directory': str(self.cache_dir), 'total_files': 2, 'size_formatted': '1.2 KB' } mock_cache_service.return_value = mock_service_instance result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 assert "Total Files: 2" in result.output def test_cache_info_shows_memory_usage(self): """Test that cache-stats displays memory usage information.""" # Setup: Create cache with content cache = ASTCache(self.cache_dir) cache.cache_file(self.test_file) # Execute command - patch the cache service instead of global Path with patch('markitect.cache_service.CacheDirectoryService.get_cache_directory') as mock_cache_dir: mock_cache_dir.return_value = self.cache_dir result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 # Should show memory/size information assert any(keyword in result.output.lower() for keyword in ["size", "memory", "bytes", "kb", "mb"]) def test_cache_info_with_empty_cache(self): """Test cache-stats behavior with empty cache directory.""" # Ensure cache directory exists but is empty self.cache_dir.mkdir(exist_ok=True) # Clear any existing files to ensure it's actually empty for file in self.cache_dir.iterdir(): if file.is_file(): file.unlink() # Execute command - patch the cache stats directly for this test with patch('markitect.cache_service.CacheDirectoryService.get_cache_stats') as mock_stats: mock_stats.return_value = { 'directory': str(self.cache_dir), 'total_files': 0, 'size_formatted': '0 B' } result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 assert "Total Files: 0" in result.output or "empty" in result.output.lower() def test_cache_info_with_nonexistent_cache(self): """Test cache-stats behavior when cache directory doesn't exist.""" # Use non-existent cache directory nonexistent_dir = Path(self.temp_dir) / "nonexistent_cache" # Execute command - patch the cache service instead of global Path with patch('markitect.cache_service.CacheDirectoryService.get_cache_directory') as mock_cache_dir: mock_cache_dir.return_value = nonexistent_dir result = self.runner.invoke(cli, ['cache-stats']) # Should handle gracefully, either create directory or show appropriate message assert result.exit_code in [0, 1] assert "error" in result.output.lower() or "not found" in result.output.lower() or "0" in result.output def test_cache_info_output_format(self): """Test that cache-stats output is well-formatted and readable.""" # Setup: Create cache with content cache = ASTCache(self.cache_dir) cache.cache_file(self.test_file) # Execute command - patch the cache service instead of global Path with patch('markitect.cache_service.CacheDirectoryService.get_cache_directory') as mock_cache_dir: mock_cache_dir.return_value = self.cache_dir result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 # Should have structured output with clear labels lines = result.output.strip().split('\n') assert len(lines) >= 3 # Should have multiple lines of info # Should contain key information output_lower = result.output.lower() assert "cache" in output_lower assert any(char in result.output for char in [':']) # Should have label:value format def test_cache_info_performance_metrics(self): """Test that cache-stats includes performance-related metrics.""" # Setup: Create cache and simulate usage cache = ASTCache(self.cache_dir) cache.cache_file(self.test_file) # Load cached AST to simulate cache hit cache.load_cached_ast(self.test_file) # Execute command - patch the cache service instead of global Path with patch('markitect.cache_service.CacheDirectoryService.get_cache_directory') as mock_cache_dir: mock_cache_dir.return_value = self.cache_dir result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 # Should include performance-related information # This might include cache effectiveness, file ages, etc. assert len(result.output.strip()) > 50 # Should be substantial output def test_cache_info_with_verbose_flag(self): """Test cache-stats with verbose flag showing detailed information.""" # Setup: Create cache with content cache = ASTCache(self.cache_dir) cache.cache_file(self.test_file) # Execute command with verbose flag - patch the cache service instead of global Path with patch('markitect.cache_service.CacheDirectoryService.get_cache_directory') as mock_cache_dir: mock_cache_dir.return_value = self.cache_dir result = self.runner.invoke(cli, ['--verbose', 'cache-stats']) # Verbose mode might show more detailed information # For now, just ensure command works assert result.exit_code in [0, 1]