Files
markitect-main/tests/test_issue_144_auto_discovery_workspace.py
tegwick c55a10170f feat: complete Issue #144 - Phase 3: Advanced Features and Performance
Implements comprehensive advanced asset management features using TDD8 methodology,
building upon the solid foundation from Issues #142 and #143.

🚀 **Complete TDD8 Implementation:**
-  ISSUE: Clear requirements defined for advanced features
-  TEST: 36+ comprehensive tests across 5 test categories
-  RED: All tests failed appropriately guiding implementation
-  GREEN: Complete implementation passing all tests
-  REFACTOR: 350+ lines of reusable utilities extracted
-  DOCUMENT: Comprehensive docstrings and API documentation
-  REFINE: Integration testing with zero regressions
-  PUBLISH: Production-ready advanced asset management

🎯 **Advanced Features Delivered:**

**Batch Processing (BatchAssetProcessor):**
- Multi-file import with progress reporting and conflict resolution
- Recursive directory scanning with file filtering
- Parallel processing support for large operations
- Comprehensive error handling and recovery

**Asset Discovery (AssetDiscoveryEngine):**
- Automatic asset discovery in markdown documents
- Reference tracking and dependency analysis
- Cross-document asset relationship mapping
- Smart asset scanning with pattern recognition

**Performance Monitoring (PerformanceMonitor):**
- Real-time operation tracking with detailed metrics
- Query optimization and performance analysis
- Slowest operation identification and reporting
- Context-aware performance measurement

**Database Enhancements (AssetDatabase):**
- Enhanced metadata storage with migration support
- Performance optimizations for large asset libraries
- Advanced querying capabilities with indexing
- Schema evolution and backward compatibility

**Caching System (AssetCache):**
- Multi-strategy caching (LRU, TTL, size-based)
- Configurable cache policies and expiration
- Memory-efficient asset metadata caching
- Performance boost for repeated operations

**Content Analysis (ContentAnalyzer):**
- Asset similarity detection and duplicate identification
- Content-based analysis and classification
- Metadata extraction and enhancement
- Smart asset organization suggestions

**Optimization Engine (AssetOptimizer):**
- Asset optimization with multiple profiles
- Image compression and format conversion
- File size reduction with quality preservation
- Batch optimization workflows

**Analytics & Reporting (AssetAnalytics):**
- Usage analytics and reporting
- Storage efficiency analysis
- Asset utilization tracking
- Performance trend analysis

🛠️ **Technical Excellence:**
- **9 new core modules** with comprehensive functionality
- **350+ lines of utilities** for code reuse and maintainability
- **Backward compatibility** with enhanced AssetManager
- **Performance optimized** for sub-second operations
- **Production-ready** error handling and logging

🧪 **Quality Metrics:**
- **36+ tests passing** across all advanced features
- **Zero regressions** in existing asset management functionality
- **Comprehensive integration** with Issues #142-143 foundation
- **Professional documentation** with usage examples

**CLI Integration:**
- Seamless integration with existing asset CLI commands
- Advanced features accessible through enhanced AssetManager API
- Performance monitoring available for all operations
- Batch processing ready for CLI workflow integration

This implementation transforms MarkiTect's asset management from basic functionality
into a comprehensive, enterprise-ready system with advanced performance, analytics,
and optimization capabilities.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 17:53:47 +02:00

450 lines
16 KiB
Python

"""
Test scenario for Issue #144: Auto-Discovery and Workspace Management
This test covers markdown scanning for asset references, automatic asset
registration, workspace templates, and advanced workspace management features.
Issue #144: Phase 3 - Advanced Features and Performance
"""
import pytest
import tempfile
import shutil
from pathlib import Path
from unittest.mock import Mock, patch, MagicMock
import json
import yaml
from markitect.assets import AssetManager
from markitect.assets.discovery import AssetDiscoveryEngine, MarkdownScanner, AssetReference
from markitect.workspace import WorkspaceManager, WorkspaceTemplate
from markitect.assets.analytics import AssetAnalytics, UsageReport
class TestAutoDiscoveryAndWorkspace:
"""Test auto-discovery and workspace management features for Issue #144."""
def setup_method(self):
"""Set up test environment with sample markdown files and workspace."""
self.temp_dir = tempfile.mkdtemp()
self.project_dir = Path(self.temp_dir) / "test_project"
self.assets_dir = self.project_dir / "assets"
self.docs_dir = self.project_dir / "docs"
self.project_dir.mkdir()
self.assets_dir.mkdir()
self.docs_dir.mkdir()
self.create_test_markdown_files()
self.create_test_assets()
self.asset_manager = AssetManager(storage_path=self.assets_dir)
def teardown_method(self):
"""Clean up temporary directories."""
shutil.rmtree(self.temp_dir)
def create_test_markdown_files(self):
"""Create test markdown files with various asset references."""
# Main document with multiple asset types
main_doc = """
# Project Documentation
Here's our project logo:
![Project Logo](./assets/logo.png "Company Logo")
## Architecture Diagram
The system architecture is shown below:
![Architecture](../diagrams/system_arch.svg)
## Screenshots
Here are some screenshots:
![Screenshot 1](./screenshots/app_home.png)
![Screenshot 2](./screenshots/app_settings.png)
## Documents
See the [user manual](./docs/manual.pdf) for details.
## Broken Links
This image doesn't exist: ![Missing](./missing/not_found.png)
"""
(self.docs_dir / "main.md").write_text(main_doc)
# Nested document
nested_doc = """
# Nested Documentation
![Nested Image](../../assets/nested_image.jpg)
[Download Guide](../downloads/guide.pdf)
"""
nested_dir = self.docs_dir / "nested"
nested_dir.mkdir()
(nested_dir / "nested.md").write_text(nested_doc)
# Document with unusual references
complex_doc = """
# Complex References
![With Spaces](./assets/image with spaces.png)
![With URL](https://example.com/image.png)
![Base64](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==)
Reference style:
[image-ref]: ./assets/reference_image.png
![Reference Image][image-ref]
"""
(self.docs_dir / "complex.md").write_text(complex_doc)
def create_test_assets(self):
"""Create some test asset files."""
test_assets = [
"logo.png",
"nested_image.jpg",
"image with spaces.png",
"reference_image.png"
]
for asset in test_assets:
(self.assets_dir / asset).write_bytes(b"mock asset content")
# Create additional directories
(self.project_dir / "diagrams").mkdir()
(self.project_dir / "diagrams" / "system_arch.svg").write_text("<svg></svg>")
(self.project_dir / "screenshots").mkdir()
(self.project_dir / "screenshots" / "app_home.png").write_bytes(b"screenshot")
def test_markdown_scanner_initialization(self):
"""Test MarkdownScanner initialization and configuration."""
scanner = MarkdownScanner(
scan_patterns=["*.md", "*.mdx"],
ignore_patterns=["**/node_modules/**", "**/.git/**"]
)
assert scanner.scan_patterns == ["*.md", "*.mdx"]
assert "**/node_modules/**" in scanner.ignore_patterns
def test_asset_reference_detection(self):
"""Test detection of asset references in markdown files."""
scanner = MarkdownScanner()
main_doc_path = self.docs_dir / "main.md"
references = scanner.scan_file(main_doc_path)
# Should find multiple references
assert len(references) >= 5
# Check specific references
reference_paths = [ref.asset_path for ref in references]
assert "./assets/logo.png" in reference_paths
assert "../diagrams/system_arch.svg" in reference_paths
assert "./screenshots/app_home.png" in reference_paths
# Check reference types
image_refs = [ref for ref in references if ref.reference_type == "image"]
link_refs = [ref for ref in references if ref.reference_type == "link"]
assert len(image_refs) >= 4
assert len(link_refs) >= 1
def test_recursive_directory_scanning(self):
"""Test recursive scanning of directory structure."""
discovery_engine = AssetDiscoveryEngine(self.asset_manager)
scan_result = discovery_engine.scan_directory(
self.project_dir,
recursive=True,
file_patterns=["*.md"]
)
# Should find all markdown files
assert len(scan_result.scanned_files) >= 3
assert len(scan_result.asset_references) >= 6
# Check that nested files were found
scanned_paths = [str(f) for f in scan_result.scanned_files]
assert any("nested.md" in path for path in scanned_paths)
def test_broken_link_detection(self):
"""Test detection and reporting of broken asset links."""
discovery_engine = AssetDiscoveryEngine(self.asset_manager)
scan_result = discovery_engine.scan_directory(
self.project_dir,
recursive=True
)
broken_links = scan_result.get_broken_links()
# Should find the missing image reference
assert len(broken_links) >= 1
broken_paths = [link.asset_path for link in broken_links]
assert "./missing/not_found.png" in broken_paths
assert "./screenshots/app_settings.png" in broken_paths # File doesn't exist
def test_automatic_asset_registration(self):
"""Test automatic registration of discovered assets."""
discovery_engine = AssetDiscoveryEngine(self.asset_manager)
# Scan and auto-register
registration_result = discovery_engine.auto_register_assets(
self.project_dir,
register_existing=True,
skip_broken=True
)
assert registration_result.registered_count > 0
assert registration_result.skipped_broken > 0
# Verify assets were registered
registry = self.asset_manager.registry
registered_assets = registry.list_assets()
assert len(registered_assets) >= 3
# Check specific assets
asset_filenames = [asset.filename for asset in registered_assets]
assert "logo.png" in asset_filenames
def test_unused_asset_identification(self):
"""Test identification of unused assets and cleanup suggestions."""
discovery_engine = AssetDiscoveryEngine(self.asset_manager)
# Add some assets that aren't referenced
unused_asset1 = self.assets_dir / "unused1.png"
unused_asset2 = self.assets_dir / "unused2.jpg"
unused_asset1.write_bytes(b"unused content 1")
unused_asset2.write_bytes(b"unused content 2")
# Register all assets
self.asset_manager.add_asset(self.assets_dir / "logo.png")
self.asset_manager.add_asset(unused_asset1)
self.asset_manager.add_asset(unused_asset2)
# Scan for usage
usage_analysis = discovery_engine.analyze_asset_usage(self.project_dir)
# Should identify unused assets
unused_assets = usage_analysis.get_unused_assets()
assert len(unused_assets) >= 2
unused_filenames = [asset.filename for asset in unused_assets]
assert "unused1.png" in unused_filenames
assert "unused2.jpg" in unused_filenames
def test_asset_analytics_and_reporting(self):
"""Test asset usage analytics and reporting."""
analytics = AssetAnalytics(self.asset_manager)
# Add some assets and simulate usage
logo_result = self.asset_manager.add_asset(self.assets_dir / "logo.png")
analytics.record_usage(logo_result.content_hash, self.docs_dir / "main.md")
# Generate usage report
report = analytics.generate_usage_report(
start_date=None, # All time
include_unused=True
)
assert isinstance(report, UsageReport)
assert report.total_assets >= 1
assert report.used_assets >= 1
# Check specific metrics
assert hasattr(report, 'usage_frequency')
assert hasattr(report, 'popular_assets')
assert hasattr(report, 'unused_assets')
def test_workspace_template_creation(self):
"""Test creation and management of workspace templates."""
template_manager = WorkspaceManager()
# Create a template from current workspace
template_result = template_manager.create_template(
name="documentation_project",
source_path=self.project_dir,
description="Standard documentation project template",
include_assets=True
)
assert template_result.success is True
assert template_result.template_path.exists()
# Verify template metadata
template_metadata = template_manager.get_template_metadata("documentation_project")
assert template_metadata.name == "documentation_project"
assert template_metadata.asset_count > 0
def test_workspace_creation_from_template(self):
"""Test creating new workspace from template."""
template_manager = WorkspaceManager()
# First create a template
template_manager.create_template(
name="test_template",
source_path=self.project_dir,
include_assets=True
)
# Create new workspace from template
new_workspace = Path(self.temp_dir) / "new_project"
creation_result = template_manager.create_workspace_from_template(
template_name="test_template",
target_path=new_workspace,
project_name="New Project"
)
assert creation_result.success is True
assert new_workspace.exists()
# Verify structure was copied
assert (new_workspace / "docs").exists()
assert (new_workspace / "assets").exists()
assert (new_workspace / "docs" / "main.md").exists()
def test_multi_project_workspace_support(self):
"""Test multi-project workspace management."""
workspace_manager = WorkspaceManager()
# Initialize multi-project workspace
workspace_root = Path(self.temp_dir) / "multi_workspace"
workspace_manager.initialize_multi_project_workspace(workspace_root)
# Add projects
project1_result = workspace_manager.add_project(
workspace_root=workspace_root,
project_name="project1",
template="documentation_project"
)
project2_result = workspace_manager.add_project(
workspace_root=workspace_root,
project_name="project2",
template="documentation_project"
)
assert project1_result.success is True
assert project2_result.success is True
# Verify project isolation
assert (workspace_root / "project1" / "assets").exists()
assert (workspace_root / "project2" / "assets").exists()
# Test shared asset library
shared_assets = workspace_manager.get_shared_asset_library(workspace_root)
assert shared_assets is not None
def test_workspace_asset_synchronization(self):
"""Test asset library synchronization between workspaces."""
workspace_manager = WorkspaceManager()
# Create two workspaces
workspace1 = Path(self.temp_dir) / "ws1"
workspace2 = Path(self.temp_dir) / "ws2"
workspace_manager.initialize_workspace(workspace1)
workspace_manager.initialize_workspace(workspace2)
# Add assets to first workspace
ws1_asset_manager = AssetManager(storage_path=workspace1 / "assets")
asset_result = ws1_asset_manager.add_asset(self.assets_dir / "logo.png")
# Synchronize to second workspace
sync_result = workspace_manager.synchronize_assets(
source_workspace=workspace1,
target_workspace=workspace2,
sync_mode="incremental"
)
assert sync_result.synchronized_count > 0
# Verify asset exists in second workspace
ws2_asset_manager = AssetManager(storage_path=workspace2 / "assets")
ws2_assets = ws2_asset_manager.registry.list_assets()
assert len(ws2_assets) > 0
assert any(asset.filename == "logo.png" for asset in ws2_assets)
def test_workspace_backup_and_restore(self):
"""Test workspace backup and restore functionality."""
workspace_manager = WorkspaceManager()
# Create backup
backup_path = Path(self.temp_dir) / "workspace_backup.zip"
backup_result = workspace_manager.create_backup(
workspace_path=self.project_dir,
backup_path=backup_path,
include_assets=True,
compression_level=6
)
assert backup_result.success is True
assert backup_path.exists()
# Test restore
restore_path = Path(self.temp_dir) / "restored_workspace"
restore_result = workspace_manager.restore_from_backup(
backup_path=backup_path,
target_path=restore_path
)
assert restore_result.success is True
assert restore_path.exists()
# Verify structure was restored
assert (restore_path / "docs" / "main.md").exists()
assert (restore_path / "assets" / "logo.png").exists()
def test_collaborative_workspace_features(self):
"""Test collaborative workspace features and conflict resolution."""
workspace_manager = WorkspaceManager()
# Simulate concurrent modifications
workspace_path = self.project_dir
# Create workspace state snapshot
state1 = workspace_manager.capture_workspace_state(workspace_path)
# Simulate changes from user 1
(workspace_path / "docs" / "user1_doc.md").write_text("User 1 content")
# Simulate changes from user 2
(workspace_path / "docs" / "user2_doc.md").write_text("User 2 content")
# Both users modify same file
main_doc_path = workspace_path / "docs" / "main.md"
original_content = main_doc_path.read_text()
# User 1 change
user1_content = original_content + "\n\n## User 1 Addition"
main_doc_path.write_text(user1_content)
state2 = workspace_manager.capture_workspace_state(workspace_path)
# User 2 change (conflict)
user2_content = original_content + "\n\n## User 2 Addition"
main_doc_path.write_text(user2_content)
state3 = workspace_manager.capture_workspace_state(workspace_path)
# Detect conflicts
conflicts = workspace_manager.detect_conflicts(state2, state3)
assert len(conflicts) > 0
# Test merge resolution
merge_result = workspace_manager.resolve_conflicts(
conflicts,
resolution_strategy="manual" # Would integrate with conflict resolution UI
)
assert hasattr(merge_result, 'resolved_conflicts')
assert hasattr(merge_result, 'unresolved_conflicts')