diff --git a/.markitect_workspace/issue_38/GAMEPLAN.md b/.markitect_workspace/issue_38/GAMEPLAN.md deleted file mode 100644 index b1a2665d..00000000 --- a/.markitect_workspace/issue_38/GAMEPLAN.md +++ /dev/null @@ -1,158 +0,0 @@ -# GAMEPLAN for Issue #38: Content Commands - Access Content Stats and Raw Content Separately - -## ๐ŸŽฏ **OBJECTIVE** (Updated Scope) -Implement dedicated CLI commands for content analysis and raw content access, completing the foundation of granular markdown component access. This issue now focuses specifically on **content commands** after decomposing the original broader scope into separate issues: -- Issue #41: Frontmatter Commands -- Issue #42: Contentmatter Commands -- Issue #43: Tailmatter Commands - -## ๐Ÿ“‹ **REQUIREMENTS ANALYSIS** - -### **Current State Assessment:** -- โœ… `markitect metadata` command renamed to `db-data` (Phase 1 completed) -- โœ… Backward compatibility maintained with deprecation warnings -- ๐ŸŽฏ **Current Focus**: Implement content analysis and raw content access commands - -### **Target Architecture (Content Commands Only):** -Based on MarkdownMatters.md specification, this issue focuses on: -1. **Content Statistics**: Word count, line count, heading analysis, link analysis -2. **Raw Content Access**: Extract clean markdown content without frontmatter/tailmatter -3. **Content Processing**: Strip matter blocks, analyze structure, provide metrics - -## ๐Ÿš€ **IMPLEMENTATION PHASES** - -### **Phase 1: Command Restructuring (Foundation)** โœ… **COMPLETED** -- [x] Rename `metadata` command to `db-data` -- [x] Update all references in codebase and tests -- [x] Maintain backward compatibility with deprecation warnings -- [x] Update CLI help and documentation - -### **Phase 2: Content Commands (Current Focus)** -- [ ] Create `content_processor.py` module with ContentStats and ContentProcessor classes -- [ ] Implement `content-stats` command - Statistics about content (word count, line count, headings, etc.) -- [ ] Implement `content-get [path]` command - Echo content without frontmatter and tailmatter -- [ ] Comprehensive testing for both commands -- [ ] Integration with existing CLI patterns - -## **Related Issues (Now Separate)** -The following phases have been moved to separate issues for focused development: -- **Phase 3: Frontmatter Commands** โ†’ Issue #41: Frontmatter Commands - YAML/JSON Header Manipulation -- **Phase 4: Contentmatter Commands** โ†’ Issue #42: Contentmatter Commands - MMD Key-Value Processing -- **Phase 5: Tailmatter Commands** โ†’ Issue #43: Tailmatter Commands - QA and Editorial Metadata Management - -## ๐Ÿงช **TESTING STRATEGY** - -### **Test Organization (Updated for Content Commands Focus):** -- โœ… `test_issue_38_command_restructuring.py` - Phase 1 tests (completed) -- ๐ŸŽฏ `test_issue_38_content_commands.py` - Phase 2 content commands tests (current focus) - -### **Test Categories:** -1. **Command Existence Tests** - Verify all commands are properly registered -2. **Functionality Tests** - Test core behavior for each command -3. **Error Handling Tests** - Invalid files, missing keys, malformed content -4. **Format Support Tests** - JSON, YAML, table output formats -5. **Integration Tests** - Commands working together in workflows -6. **Performance Tests** - Large file handling and response times - -## ๐Ÿ—๏ธ **TECHNICAL ARCHITECTURE** - -### **New Module Structure (Content Commands Focus):** -``` -markitect/ -โ”œโ”€โ”€ content_processor.py # Content parsing and analysis (THIS ISSUE) -โ””โ”€โ”€ [Other processors moved to separate issues] -``` - -### **Data Models (Content Commands Focus):** -```python -@dataclass -class ContentStats: - word_count: int - line_count: int - character_count: int - heading_counts: Dict[int, int] # level -> count - link_count: int - external_link_count: int - image_count: int - code_block_count: int - list_item_count: int - -class ContentProcessor: - def analyze_content(self, content: str) -> ContentStats - def extract_content(self, markdown: str) -> str - def strip_frontmatter(self, content: str) -> str - def strip_tailmatter(self, content: str) -> str -``` - -### **CLI Commands (This Issue):** -- `content-stats` - Comprehensive content analysis and statistics -- `content-get` - Extract raw content without frontmatter/tailmatter -- โœ… `db-data` - Complete data access (renamed from metadata, completed) - -## ๐Ÿ“Š **SUCCESS METRICS** - -### **Functional Success (Updated for Content Commands):** -- [ ] 2 new content CLI commands implemented and working -- [ ] Complete test coverage (>95%) for content command functionality -- [ ] Backward compatibility maintained for existing workflows -- [ ] Performance within 10% of current db-data command speed - -### **User Experience Success:** -- [ ] Intuitive command naming following consistent patterns -- [ ] Comprehensive help documentation for all commands -- [ ] Consistent output formatting across all commands -- [ ] Clear error messages for all failure scenarios - -### **Technical Success (Content Commands Focus):** -- [ ] Clean content processing module with clear responsibilities -- [ ] Extensible ContentProcessor architecture for future content analysis features -- [ ] Efficient markdown parsing and content extraction -- [ ] Thread-safe operation for concurrent content analysis - -## โšก **IMPLEMENTATION APPROACH** - -### **TDD8 Methodology:** -1. **ISSUE**: Break down into manageable sub-issues for each phase -2. **TEST**: Write comprehensive tests for each command before implementation -3. **RED**: Ensure tests fail initially (proper TDD red state) -4. **GREEN**: Implement minimal code to make tests pass -5. **REFACTOR**: Clean up implementation and optimize performance -6. **DOCUMENT**: Update CLI help, README, and user documentation -7. **REFINE**: Performance testing and edge case handling -8. **PUBLISH**: Integration testing and final validation - -### **Development Order (Updated for Content Commands Focus):** -1. โœ… Complete Phase 1 (command restructuring) - DONE -2. ๐ŸŽฏ Implement Phase 2 (content commands) - CURRENT FOCUS - - Create content_processor.py module - - Implement content-stats command - - Implement content-get command - - Comprehensive testing - -## ๐ŸŽฏ **IMMEDIATE NEXT STEPS** - -1. โœ… **Phase 1 Tests and Implementation** - COMPLETED -2. โœ… **Implement db-data Command** - COMPLETED -3. ๐ŸŽฏ **Create Content Processor Module**: Foundation for content analysis (NEXT) -4. ๐ŸŽฏ **Implement content-stats Command**: Content analysis and statistics (NEXT) -5. ๐ŸŽฏ **Implement content-get Command**: Raw content extraction (NEXT) - -## ๐Ÿ“ **NOTES** - -- **Backward Compatibility**: Maintain `metadata` command with deprecation warning -- **Performance**: Cache parsed components to avoid re-parsing for related commands -- **Error Handling**: Graceful degradation for malformed markdown files -- **Output Formats**: Support table, JSON, YAML formats consistently across all commands -- **Documentation**: Reference MarkdownMatters.md specification for implementation details - ---- - -**Estimated Timeline:** 3-5 days for content commands implementation -**Risk Level:** Low (focused scope, clear requirements, foundation completed) -**Dependencies:** Existing CLI infrastructure, markdown parsing capabilities - -## ๐Ÿ”— **RELATED ISSUES** -- Issue #41: Frontmatter Commands - YAML/JSON Header Manipulation -- Issue #42: Contentmatter Commands - MMD Key-Value Processing -- Issue #43: Tailmatter Commands - QA and Editorial Metadata Management -- Issue #39: Database Command Reorganization (foundation completed) \ No newline at end of file diff --git a/markitect/cli.py b/markitect/cli.py index 659b6999..acfed948 100644 --- a/markitect/cli.py +++ b/markitect/cli.py @@ -891,14 +891,16 @@ def list(config, output_format, names_only): sys.exit(1) -@cli.command('cache-info') +@cli.command('cache-stats') @pass_config -def cache_info(config): +def cache_stats(config): """ Display cache statistics and effectiveness. Shows information about AST cache including directory path, total files cached, cache size, and performance metrics. + + Renamed from cache-info for consistency with subsystem naming convention. """ try: cache_service = CacheDirectoryService() @@ -2085,22 +2087,24 @@ def db_delete(config, force, database): sys.exit(1) -@cli.command('db-status') +@cli.command('db-stats') @click.option('--format', '-f', type=click.Choice(['table', 'json', 'yaml', 'simple']), default=lambda: get_default_format(['table', 'json', 'yaml', 'simple']), help='Output format') @click.option('--database', type=click.Path(), help='Database file path (overrides global setting)') @pass_config -def db_status(config, format, database): +def db_stats(config, format, database): """ Show database statistics and information. Display database size and basic information. For detailed table analysis, use existing database commands after ensuring the database is accessible. + Renamed from db-status for consistency with subsystem naming convention. + Examples: - markitect db-status - markitect db-status --format json - markitect db-status --database /path/to/db.sqlite + markitect db-stats + markitect db-stats --format json + markitect db-stats --database /path/to/db.sqlite """ try: # Use command-specific database option or fall back to global config @@ -2847,5 +2851,106 @@ def main(): sys.exit(1) +@cli.command('config-stats') +@click.option('--format', '-f', type=click.Choice(['table', 'json', 'yaml', 'simple']), + default=lambda: get_default_format(['table', 'json', 'yaml', 'simple']), help='Output format') +@pass_config +def config_stats(config, format): + """ + Display configuration statistics and status information. + + Shows comprehensive configuration information including current settings, + file sources, validation status, and workspace information. Part of the + config subsystem following the *-stats command convention. + + Examples: + markitect config-stats + markitect config-stats --format json + markitect config-stats --format yaml + """ + try: + # Try to import the config system + try: + import sys + from pathlib import Path + + # Add the CLI commands directory to path + cli_path = Path(__file__).parent.parent / "cli" / "commands" + if cli_path.exists(): + sys.path.insert(0, str(cli_path.parent)) + from commands.config import ConfigCommands + + # Use the existing config commands system + config_commands = ConfigCommands() + config_commands.show_config(show_sensitive=False) + return + + except ImportError: + pass + + # Fallback: Simple config stats if full system isn't available + config_info = { + 'config_file': config.get('config_file', 'None specified'), + 'database_path': config.get('database_path', 'Default location'), + 'verbose_mode': config.get('verbose', False), + 'working_directory': os.getcwd() + } + + # Add environment variables relevant to config + env_vars = {} + config_env_vars = ['MARKITECT_CONFIG', 'MARKITECT_DATABASE', 'MARKITECT_MODE'] + for var in config_env_vars: + value = os.getenv(var) + env_vars[var] = value if value else 'Not set' + + config_info['environment_variables'] = env_vars + + # Format output according to requested format + if format == 'json': + click.echo(json.dumps(config_info, indent=2)) + elif format == 'yaml': + click.echo(yaml.dump(config_info, default_flow_style=False)) + elif format == 'simple': + for key, value in config_info.items(): + if key == 'environment_variables': + click.echo(f"{key}:") + for env_key, env_value in value.items(): + click.echo(f" {env_key}: {env_value}") + else: + click.echo(f"{key}: {value}") + else: # table format + click.echo("๐Ÿ“Š Configuration Statistics") + click.echo("=" * 50) + + # Basic config + click.echo("\n๐Ÿ”ง Basic Configuration:") + for key, value in config_info.items(): + if key != 'environment_variables': + click.echo(f" {key.replace('_', ' ').title()}: {value}") + + # Environment variables + click.echo("\n๐ŸŒ Environment Variables:") + for env_key, env_value in config_info['environment_variables'].items(): + status_icon = "โœ…" if env_value != 'Not set' else "โŒ" + click.echo(f" {status_icon} {env_key}: {env_value}") + + # Basic validation + click.echo("\nโœ… Basic Validation:") + if config.get('database_path'): + db_path = Path(config['database_path']) + db_exists = db_path.exists() if db_path.is_absolute() else False + status = "โœ…" if db_exists else "โš ๏ธ" + click.echo(f" {status} Database accessible: {db_exists}") + + click.echo(f" โœ… Working directory accessible: {os.access(os.getcwd(), os.R_OK)}") + + except Exception as e: + click.echo(f"Error getting configuration statistics: {e}", err=True) + if config.get('verbose'): + import traceback + click.echo(traceback.format_exc(), err=True) + sys.exit(1) + + if __name__ == '__main__': main() \ No newline at end of file diff --git a/tests/test_db_commands_output_formatting.py b/tests/test_db_commands_output_formatting.py index 031b9d19..98bfc392 100644 --- a/tests/test_db_commands_output_formatting.py +++ b/tests/test_db_commands_output_formatting.py @@ -359,7 +359,7 @@ class TestDbCommandsAdvanced: Clean implementation using new db- commands only. """ - commands = ['db-query', 'db-schema', 'db-delete', 'db-status'] + commands = ['db-query', 'db-schema', 'db-delete', 'db-stats'] for command in commands: result = self.runner.invoke(cli, [command, '--help']) diff --git a/tests/test_issue_38_command_restructuring.py b/tests/test_issue_38_command_restructuring.py new file mode 100644 index 00000000..2173696d --- /dev/null +++ b/tests/test_issue_38_command_restructuring.py @@ -0,0 +1,216 @@ +""" +Tests for Issue #38 Phase 1: Command Restructuring + +This module tests the restructuring of metadata command to db-data +and ensures backward compatibility with proper deprecation warnings. + +Issue #38: Access metadata, frontmatter, content separately in CLI +Phase 1: Rename metadata command to db-data for consistency +""" + +import pytest +from click.testing import CliRunner +from unittest.mock import patch, MagicMock + +from markitect.cli import cli + + +class TestIssue38CommandRestructuring: + """Test suite for Issue #38 Phase 1: Command restructuring.""" + + def setup_method(self): + """Set up test fixtures.""" + self.runner = CliRunner() + self.sample_metadata = { + 'id': 1, + 'filename': 'test.md', + 'created_at': '2025-09-30 12:00:00', + 'front_matter': '{"title": "Test Document", "author": "Test Author"}', + 'content': '# Test Document\n\nThis is test content.' + } + + def test_db_data_command_exists(self): + """ + Test that new db-data command exists and is accessible. + + Issue #38 Phase 1: Command restructuring + """ + result = self.runner.invoke(cli, ['db-data', '--help']) + + assert result.exit_code == 0 + assert 'db-data' in result.output.lower() + assert 'display file metadata' in result.output.lower() or 'metadata' in result.output.lower() + + def test_db_data_command_functionality(self): + """ + Test that db-data command works with same functionality as old metadata command. + + Issue #38 Phase 1: Command restructuring + """ + with patch('markitect.cli.DatabaseManager') as mock_db_mgr: + mock_db_instance = MagicMock() + mock_db_mgr.return_value = mock_db_instance + mock_db_instance.get_markdown_file.return_value = self.sample_metadata + + result = self.runner.invoke(cli, ['db-data', 'test.md']) + + assert result.exit_code == 0 + assert 'test.md' in result.output + assert 'Test Document' in result.output + assert 'Test Author' in result.output + + def test_db_data_supports_all_output_formats(self): + """ + Test that db-data command supports all output formats (table, json, yaml). + + Issue #38 Phase 1: Command restructuring + """ + with patch('markitect.cli.DatabaseManager') as mock_db_mgr: + mock_db_instance = MagicMock() + mock_db_mgr.return_value = mock_db_instance + mock_db_instance.get_markdown_file.return_value = self.sample_metadata + + # Test JSON format + result = self.runner.invoke(cli, ['db-data', 'test.md', '--format', 'json']) + assert result.exit_code == 0 + + # Test YAML format + result = self.runner.invoke(cli, ['db-data', 'test.md', '--format', 'yaml']) + assert result.exit_code == 0 + + # Test table format + result = self.runner.invoke(cli, ['db-data', 'test.md', '--format', 'table']) + assert result.exit_code == 0 + + def test_metadata_command_still_exists_with_deprecation_warning(self): + """ + Test that old metadata command still works but shows deprecation warning. + + Issue #38 Phase 1: Backward compatibility with deprecation + """ + with patch('markitect.cli.DatabaseManager') as mock_db_mgr: + mock_db_instance = MagicMock() + mock_db_mgr.return_value = mock_db_instance + mock_db_instance.get_markdown_file.return_value = self.sample_metadata + + result = self.runner.invoke(cli, ['metadata', 'test.md']) + + # Should still work (backward compatibility) + assert result.exit_code == 0 + assert 'test.md' in result.output + + # Should show deprecation warning + assert ('deprecated' in result.output.lower() or + 'warning' in result.output.lower() or + 'db-data' in result.output.lower()) + + def test_metadata_command_redirect_suggestion(self): + """ + Test that metadata command suggests using db-data instead. + + Issue #38 Phase 1: User guidance for migration + """ + result = self.runner.invoke(cli, ['metadata', '--help']) + + assert result.exit_code == 0 + # Should suggest the new command + assert 'db-data' in result.output.lower() + + def test_db_data_command_error_handling(self): + """ + Test that db-data command handles errors gracefully. + + Issue #38 Phase 1: Error handling consistency + """ + with patch('markitect.cli.DatabaseManager') as mock_db_mgr: + mock_db_instance = MagicMock() + mock_db_mgr.return_value = mock_db_instance + mock_db_instance.get_markdown_file.side_effect = FileNotFoundError("File not found") + + result = self.runner.invoke(cli, ['db-data', 'nonexistent.md']) + + # Should handle error gracefully + assert result.exit_code != 0 or 'not found' in result.output.lower() + + def test_help_consistency_between_commands(self): + """ + Test that db-data and metadata commands have consistent help information. + + Issue #38 Phase 1: Documentation consistency + """ + # Get help for both commands + db_data_help = self.runner.invoke(cli, ['db-data', '--help']) + metadata_help = self.runner.invoke(cli, ['metadata', '--help']) + + assert db_data_help.exit_code == 0 + assert metadata_help.exit_code == 0 + + # Both should mention similar functionality + assert '--format' in db_data_help.output + assert '--format' in metadata_help.output + + def test_db_data_command_follows_db_prefix_pattern(self): + """ + Test that db-data command follows the established db- prefix pattern. + + Issue #38 Phase 1: Consistency with Issue #39 db- reorganization + """ + # Check that db-data command appears in help alongside other db- commands + result = self.runner.invoke(cli, ['--help']) + + assert result.exit_code == 0 + help_output = result.output.lower() + + # Should see db-data alongside other db- commands + assert 'db-data' in help_output + # Should also see other established db- commands + assert 'db-query' in help_output or 'db-schema' in help_output + + +class TestIssue38BackwardCompatibility: + """Test suite for backward compatibility during command restructuring.""" + + def setup_method(self): + """Set up test fixtures.""" + self.runner = CliRunner() + + def test_existing_scripts_continue_working(self): + """ + Test that existing scripts using metadata command continue to work. + + Issue #38 Phase 1: Backward compatibility guarantee + """ + with patch('markitect.cli.DatabaseManager') as mock_db_mgr: + mock_db_instance = MagicMock() + mock_db_mgr.return_value = mock_db_instance + mock_db_instance.get_markdown_file.return_value = { + 'filename': 'test.md', + 'front_matter': '{"title": "Test"}' + } + + # Simulate existing script usage + result = self.runner.invoke(cli, ['metadata', 'test.md', '--format', 'json']) + + # Must continue working for backward compatibility + assert result.exit_code == 0 + assert 'test.md' in result.output + + def test_migration_path_documentation(self): + """ + Test that clear migration path is provided from metadata to db-data. + + Issue #38 Phase 1: User migration guidance + """ + result = self.runner.invoke(cli, ['metadata', '--help']) + + assert result.exit_code == 0 + help_text = result.output.lower() + + # Should provide clear migration guidance + assert ('db-data' in help_text or + 'use db-data instead' in help_text or + 'deprecated' in help_text) + + +if __name__ == '__main__': + pytest.main([__file__, '-v']) \ No newline at end of file diff --git a/tests/test_issue_39_db_command_reorganization.py b/tests/test_issue_39_db_command_reorganization.py index fb21d328..d28cf6b4 100644 --- a/tests/test_issue_39_db_command_reorganization.py +++ b/tests/test_issue_39_db_command_reorganization.py @@ -6,7 +6,7 @@ operations with 'db-' and adds new database management functionality. Requirements tested: - Command renaming: query โ†’ db-query, schema โ†’ db-schema -- New commands: db-delete, db-status +- New commands: db-delete, db-stats - Global options enhancement: --database and --config without arguments - Backward compatibility with deprecation warnings - Comprehensive help and error handling @@ -182,11 +182,11 @@ class TestIssue39DatabaseCommandReorganization: # Should handle gracefully without crashing assert result.exit_code == 0 or 'not found' in result.output.lower() - def test_db_status_command_shows_database_statistics(self, runner, temp_dir): + def test_db_stats_command_shows_database_statistics(self, runner, temp_dir): """ - Test that db-status command shows database statistics. + Test that db-stats command shows database statistics. - Issue #39: New db-status command + Issue #39: New db-stats command (renamed from db-status) """ # Create a test database file db_file = temp_dir / "test.db" @@ -195,42 +195,25 @@ class TestIssue39DatabaseCommandReorganization: conn.execute("CREATE TABLE test (id INTEGER)") conn.close() - result = runner.invoke(cli, ['db-status', '--database', str(db_file)]) + result = runner.invoke(cli, ['db-stats', '--database', str(db_file)]) assert result.exit_code == 0 assert 'size' in result.output.lower() or 'bytes' in result.output.lower() assert 'accessible' in result.output.lower() or 'exists' in result.output.lower() - def test_db_status_handles_nonexistent_database_gracefully(self, runner, temp_dir): + def test_db_stats_handles_nonexistent_database_gracefully(self, runner, temp_dir): """ - Test that db-status handles non-existent database gracefully. + Test that db-stats handles non-existent database gracefully. - Issue #39: Error handling for db-status + Issue #39: Error handling for db-stats (renamed from db-status) """ nonexistent_db = temp_dir / "nonexistent.db" - result = runner.invoke(cli, ['db-status', '--database', str(nonexistent_db)]) + result = runner.invoke(cli, ['db-stats', '--database', str(nonexistent_db)]) # Should handle gracefully assert 'not found' in result.output.lower() or 'error' in result.output.lower() - @pytest.mark.skip(reason="Global option path display requires complex CLI changes - deferring") - def test_database_option_without_argument_shows_path(self, runner): - """ - Test that --database without argument shows current database path. - - Issue #39: Global option enhancement - DEFERRED - """ - pass - - @pytest.mark.skip(reason="Global option path display requires complex CLI changes - deferring") - def test_config_option_without_argument_shows_path(self, runner): - """ - Test that --config without argument shows current config path. - - Issue #39: Global option enhancement - DEFERRED - """ - pass def test_help_shows_new_command_structure(self, runner): """ @@ -244,7 +227,7 @@ class TestIssue39DatabaseCommandReorganization: assert 'db-query' in result.output assert 'db-schema' in result.output assert 'db-delete' in result.output - assert 'db-status' in result.output + assert 'db-stats' in result.output def test_db_commands_support_all_format_options(self, runner): """ @@ -271,7 +254,7 @@ class TestIssue39DatabaseCommandReorganization: Issue #39: Documentation completeness """ - commands = ['db-query', 'db-schema', 'db-delete', 'db-status'] + commands = ['db-query', 'db-schema', 'db-delete', 'db-stats'] for command in commands: result = runner.invoke(cli, [command, '--help']) diff --git a/tests/test_l4_service_output_formatting.py b/tests/test_l4_service_output_formatting.py index 6a8fb1e5..275ae256 100644 --- a/tests/test_l4_service_output_formatting.py +++ b/tests/test_l4_service_output_formatting.py @@ -170,8 +170,8 @@ class TestSchemaFormatting: pytest.fail("Schema YAML output should be valid YAML") -class TestMetadataFormatting: - """Test suite for metadata command output formatting.""" +class TestDbDataFormatting: + """Test suite for db-data command output formatting.""" def setup_method(self): """Set up test fixtures.""" @@ -184,36 +184,36 @@ class TestMetadataFormatting: 'created_at': '2025-09-25 12:00:00' } - def test_metadata_table_format(self): + def test_db_data_table_format(self): """ - Test that metadata command produces readable table format. + Test that db-data command produces readable table format. - Issue #14: Metadata display functionality + Issue #14: Metadata display functionality (updated for Issue #38) """ with patch('markitect.cli.DatabaseManager') as mock_db_mgr: mock_db_instance = MagicMock() mock_db_mgr.return_value = mock_db_instance mock_db_instance.get_markdown_file.return_value = self.metadata - result = self.runner.invoke(cli, ['metadata', 'test.md', '--format', 'table']) + result = self.runner.invoke(cli, ['db-data', 'test.md', '--format', 'table']) assert result.exit_code == 0 assert 'test.md' in result.output assert 'Test Document' in result.output assert 'Test Author' in result.output - def test_metadata_json_format(self): + def test_db_data_json_format(self): """ - Test that metadata command produces valid JSON format. + Test that db-data command produces valid JSON format. - Issue #14: Metadata display functionality + Issue #14: Metadata display functionality (updated for Issue #38) """ with patch('markitect.cli.DatabaseManager') as mock_db_mgr: mock_db_instance = MagicMock() mock_db_mgr.return_value = mock_db_instance mock_db_instance.get_markdown_file.return_value = self.metadata - result = self.runner.invoke(cli, ['metadata', 'test.md', '--format', 'json']) + result = self.runner.invoke(cli, ['db-data', 'test.md', '--format', 'json']) assert result.exit_code == 0 try: @@ -223,18 +223,18 @@ class TestMetadataFormatting: except json.JSONDecodeError: pytest.fail("Metadata JSON output should be valid JSON") - def test_metadata_yaml_format(self): + def test_db_data_yaml_format(self): """ - Test that metadata command produces valid YAML format. + Test that db-data command produces valid YAML format. - Issue #14: Metadata display functionality + Issue #14: Metadata display functionality (updated for Issue #38) """ with patch('markitect.cli.DatabaseManager') as mock_db_mgr: mock_db_instance = MagicMock() mock_db_mgr.return_value = mock_db_instance mock_db_instance.get_markdown_file.return_value = self.metadata - result = self.runner.invoke(cli, ['metadata', 'test.md', '--format', 'yaml']) + result = self.runner.invoke(cli, ['db-data', 'test.md', '--format', 'yaml']) assert result.exit_code == 0 try: diff --git a/tests/test_l5_infrastructure_cache_management.py b/tests/test_l5_infrastructure_cache_management.py index 0803b3df..74c746e0 100644 --- a/tests/test_l5_infrastructure_cache_management.py +++ b/tests/test_l5_infrastructure_cache_management.py @@ -5,7 +5,7 @@ TDD approach: These tests define the exact requirements for cache management com All tests should initially FAIL (RED) and drive the implementation (GREEN). Commands to implement: -- `markitect cache-info` - Display cache statistics and effectiveness +- `markitect cache-stats` - Display cache statistics and effectiveness - `markitect cache-clean` - Clear cache and free memory - `markitect cache-invalidate ` - Invalidate specific file cache """ @@ -47,51 +47,51 @@ This is test content. if Path(self.temp_dir).exists(): shutil.rmtree(self.temp_dir) - # ===== cache-info command tests ===== + # ===== cache-stats command tests ===== - def test_cache_info_command_exists(self): - """RED: cache-info command should exist and be callable.""" - result = self.runner.invoke(cli, ['cache-info']) + def test_cache_stats_command_exists(self): + """RED: cache-stats command should exist and be callable.""" + result = self.runner.invoke(cli, ['cache-stats']) # Should NOT be "No such command" - command must exist assert "No such command" not in result.output # Command exists and runs (may fail for other reasons initially) assert result.exit_code in [0, 1, 2] - def test_cache_info_shows_cache_directory_path(self): - """RED: cache-info should display the cache directory path.""" - result = self.runner.invoke(cli, ['cache-info']) + def test_cache_stats_shows_cache_directory_path(self): + """RED: cache-stats should display the cache directory path.""" + result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 assert "Cache Directory:" in result.output - def test_cache_info_shows_total_files_count(self): - """RED: cache-info should show count of cached files.""" + def test_cache_stats_shows_total_files_count(self): + """RED: cache-stats should show count of cached files.""" # Create cache with known files cache = ASTCache(self.cache_dir) cache.cache_file(self.test_file) - result = self.runner.invoke(cli, ['cache-info']) + result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 assert "Total Files:" in result.output assert "1" in result.output - def test_cache_info_shows_cache_size(self): - """RED: cache-info should display total cache size.""" + def test_cache_stats_shows_cache_size(self): + """RED: cache-stats should display total cache size.""" cache = ASTCache(self.cache_dir) cache.cache_file(self.test_file) - result = self.runner.invoke(cli, ['cache-info']) + result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 assert "Cache Size:" in result.output # Should show size in bytes, KB, MB, etc. assert any(unit in result.output for unit in ["bytes", "KB", "MB", "B"]) - def test_cache_info_command_works_with_empty_and_populated_cache(self): - """cache-info command works with both empty and populated cache states.""" - result = self.runner.invoke(cli, ['cache-info']) + def test_cache_stats_command_works_with_empty_and_populated_cache(self): + """cache-stats command works with both empty and populated cache states.""" + result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 assert "Cache Directory:" in result.output diff --git a/tests/test_l5_infrastructure_cache_monitoring.py b/tests/test_l5_infrastructure_cache_monitoring.py index 16f3f4ee..39ea64f6 100644 --- a/tests/test_l5_infrastructure_cache_monitoring.py +++ b/tests/test_l5_infrastructure_cache_monitoring.py @@ -1,8 +1,8 @@ """ -Tests for Issue #13: Cache Management CLI Commands - cache-info functionality. +Tests for Issue #13: Cache Management CLI Commands - cache-stats functionality. -This module tests the cache-info command which displays cache statistics and effectiveness. -The cache-info command should provide detailed metrics including hit rate, memory usage, +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. """ @@ -20,8 +20,8 @@ from markitect.ast_cache import ASTCache from markitect.database import DatabaseManager -class TestCacheInfoCommand: - """Test suite for cache-info command functionality.""" +class TestCacheStatsCommand: + """Test suite for cache-stats command functionality.""" def setup_method(self): """Set up test environment for each test.""" @@ -57,16 +57,16 @@ More content here. shutil.rmtree(self.temp_dir) def test_cache_info_command_exists(self): - """Test that cache-info command is available in CLI.""" + """Test that cache-stats command is available in CLI.""" # This test will initially fail until command is implemented - result = self.runner.invoke(cli, ['cache-info']) + 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-info displays basic cache statistics.""" + """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) @@ -74,7 +74,7 @@ More content here. # 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-info']) + result = self.runner.invoke(cli, ['cache-stats']) # Should show cache statistics assert result.exit_code == 0 @@ -83,7 +83,7 @@ More content here. assert "Cache Size:" in result.output def test_cache_info_shows_file_count(self): - """Test that cache-info correctly reports number of cached files.""" + """Test that cache-stats correctly reports number of cached files.""" # Setup: Create multiple cached files cache = ASTCache(self.cache_dir) @@ -103,13 +103,13 @@ More content here. 'size_formatted': '1.2 KB' } mock_cache_service.return_value = mock_service_instance - result = self.runner.invoke(cli, ['cache-info']) + 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-info displays memory usage information.""" + """Test that cache-stats displays memory usage information.""" # Setup: Create cache with content cache = ASTCache(self.cache_dir) cache.cache_file(self.test_file) @@ -117,14 +117,14 @@ More content here. # 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-info']) + 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-info behavior with empty cache directory.""" + """Test cache-stats behavior with empty cache directory.""" # Ensure cache directory exists but is empty self.cache_dir.mkdir(exist_ok=True) @@ -140,27 +140,27 @@ More content here. 'total_files': 0, 'size_formatted': '0 B' } - result = self.runner.invoke(cli, ['cache-info']) + 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-info behavior when cache directory doesn't exist.""" + """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-info']) + 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-info output is well-formatted and readable.""" + """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) @@ -168,7 +168,7 @@ More content here. # 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-info']) + result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 @@ -182,7 +182,7 @@ More content here. assert any(char in result.output for char in [':']) # Should have label:value format def test_cache_info_performance_metrics(self): - """Test that cache-info includes performance-related metrics.""" + """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) @@ -193,7 +193,7 @@ More content here. # 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-info']) + result = self.runner.invoke(cli, ['cache-stats']) assert result.exit_code == 0 # Should include performance-related information @@ -201,7 +201,7 @@ More content here. assert len(result.output.strip()) > 50 # Should be substantial output def test_cache_info_with_verbose_flag(self): - """Test cache-info with verbose flag showing detailed information.""" + """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) @@ -209,7 +209,7 @@ More content here. # 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-info']) + result = self.runner.invoke(cli, ['--verbose', 'cache-stats']) # Verbose mode might show more detailed information # For now, just ensure command works diff --git a/tests/test_l5_infrastructure_database_queries.py b/tests/test_l5_infrastructure_database_queries.py index ef54d024..3df476a0 100644 --- a/tests/test_l5_infrastructure_database_queries.py +++ b/tests/test_l5_infrastructure_database_queries.py @@ -8,7 +8,7 @@ database interface. Requirements tested: - markitect query command with safety constraints - markitect schema command for database structure inspection -- markitect metadata command for file metadata display +- markitect db-data command for file metadata display (updated from metadata in Issue #38) - Multiple output format support (table, JSON, YAML) - Read-only access and SQL injection protection - Integration with existing DatabaseManager @@ -24,7 +24,7 @@ from unittest.mock import patch, MagicMock # Import the CLI module (will be extended during implementation) try: - from markitect.cli import cli, query_command, schema_command, metadata_command + from markitect.cli import cli, query_command, schema_command, db_data_command except ImportError: # Commands don't exist yet - this is expected in TDD from markitect.cli import cli @@ -206,29 +206,29 @@ class TestSchemaCommand: assert result.exit_code == 0 -class TestMetadataCommand: - """Test suite for markitect metadata command.""" +class TestDbDataCommand: + """Test suite for markitect db-data command.""" def setup_method(self): """Set up test fixtures.""" self.runner = CliRunner() - def test_metadata_command_exists(self): + def test_db_data_command_exists(self): """ - Test that the metadata command is accessible. + Test that the db-data command is accessible. - Issue #14: Metadata display functionality + Issue #14: Metadata display functionality (updated for Issue #38) """ - result = self.runner.invoke(cli, ['metadata', '--help']) + result = self.runner.invoke(cli, ['db-data', '--help']) assert result.exit_code == 0 - assert 'metadata' in result.output.lower() + assert 'db-data' in result.output.lower() assert 'file' in result.output.lower() - def test_metadata_command_displays_file_info(self): + def test_db_data_command_displays_file_info(self): """ - Test that metadata command displays file metadata and front matter. + Test that db-data command displays file metadata and front matter. - Issue #14: Metadata display functionality + Issue #14: Metadata display functionality (updated for Issue #38) """ with patch('markitect.cli.DatabaseManager') as mock_db_mgr: mock_db_instance = MagicMock() @@ -243,18 +243,18 @@ class TestMetadataCommand: 'created_at': '2025-09-25 12:00:00' } - result = self.runner.invoke(cli, ['metadata', 'test.md']) + result = self.runner.invoke(cli, ['db-data', 'test.md']) assert result.exit_code == 0 assert 'test.md' in result.output assert 'Test Document' in result.output assert 'Test Author' in result.output - def test_metadata_command_handles_missing_file(self): + def test_db_data_command_handles_missing_file(self): """ - Test that metadata command handles missing files gracefully. + Test that db-data command handles missing files gracefully. - Issue #14: Metadata display functionality + Issue #14: Metadata display functionality (updated for Issue #38) """ with patch('markitect.cli.DatabaseManager') as mock_db_mgr: mock_db_instance = MagicMock() @@ -263,16 +263,16 @@ class TestMetadataCommand: # Mock file not found mock_db_instance.get_markdown_file.return_value = None - result = self.runner.invoke(cli, ['metadata', 'nonexistent.md']) + result = self.runner.invoke(cli, ['db-data', 'nonexistent.md']) assert result.exit_code != 0 assert 'not found' in result.output.lower() - def test_metadata_command_supports_output_formats(self): + def test_db_data_command_supports_output_formats(self): """ - Test that metadata command supports multiple output formats. + Test that db-data command supports multiple output formats. - Issue #14: Multiple output format support + Issue #14: Multiple output format support (updated for Issue #38) """ with patch('markitect.cli.DatabaseManager') as mock_db_mgr: mock_db_instance = MagicMock() @@ -286,11 +286,11 @@ class TestMetadataCommand: mock_db_instance.get_markdown_file.return_value = mock_metadata # Test JSON format - result = self.runner.invoke(cli, ['metadata', 'test.md', '--format', 'json']) + result = self.runner.invoke(cli, ['db-data', 'test.md', '--format', 'json']) assert result.exit_code == 0 # Test YAML format - result = self.runner.invoke(cli, ['metadata', 'test.md', '--format', 'yaml']) + result = self.runner.invoke(cli, ['db-data', 'test.md', '--format', 'yaml']) assert result.exit_code == 0