Initial project setup: Obsidian intelligent journal organizer
- Add core agent architecture with Command + Skill pattern - Implement Claude API integration for content analysis - Add Obsidian REST API integration for vault operations - Create conversational interface (v2.0) with natural language processing - Add comprehensive configuration management and validation - Include project documentation and developer guides - Set up testing framework with unit, integration, and property tests - Add Kiro specs for Claude API configuration and code quality improvements - Configure project steering files for development guidelines
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple application startup validation test.
|
||||
Tests the application by running it as a module to validate basic functionality.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, List, Tuple
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger("ApplicationStartup")
|
||||
|
||||
|
||||
class ApplicationStartupTest:
|
||||
"""Test application startup and basic functionality"""
|
||||
|
||||
def __init__(self):
|
||||
self.test_results: List[Tuple[str, bool, str]] = []
|
||||
self.temp_config_path: Path = None
|
||||
|
||||
def create_test_config(self) -> Path:
|
||||
"""Create a temporary test configuration file"""
|
||||
config_content = {
|
||||
'obsidian': {
|
||||
'vault_path': '/tmp/test_vault',
|
||||
'rest_api': {
|
||||
'url': 'https://localhost:27123',
|
||||
'api_key': 'test-api-key',
|
||||
'verify_ssl': False
|
||||
}
|
||||
},
|
||||
'claude': {
|
||||
'api_key': 'test-api-key-placeholder',
|
||||
'model': 'claude-3-5-sonnet-20241022',
|
||||
'max_tokens': 4096,
|
||||
'temperature': 0.7
|
||||
},
|
||||
'journal': {
|
||||
'daily_notes_folder': 'Daily',
|
||||
'date_format': 'YYYY-MM-DD',
|
||||
'file_extension': '.md'
|
||||
},
|
||||
'output': {
|
||||
'experiences_folder': 'Knowledge/Experiences',
|
||||
'lessons_folder': 'Knowledge/Lessons',
|
||||
'tasks_folder': 'Tasks/Daily',
|
||||
'problems_folder': 'Knowledge/Problems',
|
||||
'achievements_folder': 'Knowledge/Achievements',
|
||||
'improvements_folder': 'Knowledge/Improvements'
|
||||
},
|
||||
'logging': {
|
||||
'level': 'INFO',
|
||||
'file': 'logs/journal_organizer.log'
|
||||
}
|
||||
}
|
||||
|
||||
# Create temporary config file
|
||||
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False)
|
||||
json.dump(config_content, temp_file, indent=2)
|
||||
temp_file.close()
|
||||
|
||||
self.temp_config_path = Path(temp_file.name)
|
||||
return self.temp_config_path
|
||||
|
||||
def cleanup(self):
|
||||
"""Clean up temporary files"""
|
||||
if self.temp_config_path and self.temp_config_path.exists():
|
||||
self.temp_config_path.unlink()
|
||||
|
||||
def run_command(self, cmd: List[str], timeout: int = 30) -> Tuple[bool, str, str]:
|
||||
"""Run a command and return success, stdout, stderr"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
cwd=Path.cwd()
|
||||
)
|
||||
return result.returncode == 0, result.stdout, result.stderr
|
||||
except subprocess.TimeoutExpired:
|
||||
return False, "", "Command timed out"
|
||||
except Exception as e:
|
||||
return False, "", str(e)
|
||||
|
||||
def test_module_import(self) -> Tuple[str, bool, str]:
|
||||
"""Test if the module can be imported"""
|
||||
test_name = "Module Import Test"
|
||||
|
||||
cmd = [sys.executable, "-c", "import sys; sys.path.insert(0, '.'); import main; print('SUCCESS: Module imported')"]
|
||||
success, stdout, stderr = self.run_command(cmd, timeout=10)
|
||||
|
||||
if success and "SUCCESS" in stdout:
|
||||
return test_name, True, "Module imported successfully"
|
||||
else:
|
||||
return test_name, False, f"Import failed: {stderr}"
|
||||
|
||||
def test_help_command(self) -> Tuple[str, bool, str]:
|
||||
"""Test the help command"""
|
||||
test_name = "Help Command Test"
|
||||
|
||||
cmd = [sys.executable, "-m", "__main__", "--help"]
|
||||
success, stdout, stderr = self.run_command(cmd, timeout=10)
|
||||
|
||||
if success and ("usage:" in stdout.lower() or "help" in stdout.lower()):
|
||||
return test_name, True, "Help command works"
|
||||
else:
|
||||
return test_name, False, f"Help command failed: {stderr}"
|
||||
|
||||
def test_list_command(self) -> Tuple[str, bool, str]:
|
||||
"""Test the list command"""
|
||||
test_name = "List Command Test"
|
||||
|
||||
config_path = self.create_test_config()
|
||||
cmd = [sys.executable, "-m", "__main__", "--config", str(config_path), "list"]
|
||||
success, stdout, stderr = self.run_command(cmd, timeout=15)
|
||||
|
||||
if success and ("organize" in stdout.lower() or "可用命令" in stdout):
|
||||
return test_name, True, "List command works"
|
||||
else:
|
||||
return test_name, False, f"List command failed: {stderr}"
|
||||
|
||||
def test_check_deps_command(self) -> Tuple[str, bool, str]:
|
||||
"""Test the check-deps command"""
|
||||
test_name = "Check Dependencies Command Test"
|
||||
|
||||
cmd = [sys.executable, "-m", "__main__", "check-deps"]
|
||||
success, stdout, stderr = self.run_command(cmd, timeout=15)
|
||||
|
||||
# This command should run regardless of missing dependencies
|
||||
if "检查依赖项状态" in stdout or "dependency" in stdout.lower() or success:
|
||||
return test_name, True, "Check-deps command works"
|
||||
else:
|
||||
return test_name, False, f"Check-deps command failed: {stderr}"
|
||||
|
||||
def test_info_command(self) -> Tuple[str, bool, str]:
|
||||
"""Test the info command"""
|
||||
test_name = "Info Command Test"
|
||||
|
||||
config_path = self.create_test_config()
|
||||
cmd = [sys.executable, "-m", "__main__", "--config", str(config_path), "info"]
|
||||
success, stdout, stderr = self.run_command(cmd, timeout=15)
|
||||
|
||||
if success and ("{" in stdout or "info" in stdout.lower()):
|
||||
return test_name, True, "Info command works"
|
||||
else:
|
||||
return test_name, False, f"Info command failed: {stderr}"
|
||||
|
||||
def test_chat_help(self) -> Tuple[str, bool, str]:
|
||||
"""Test the chat interface help"""
|
||||
test_name = "Chat Interface Help Test"
|
||||
|
||||
cmd = [sys.executable, "-c", "import sys; sys.path.insert(0, '.'); import chat_main; print('SUCCESS: Chat module imported')"]
|
||||
success, stdout, stderr = self.run_command(cmd, timeout=10)
|
||||
|
||||
if success and "SUCCESS" in stdout:
|
||||
return test_name, True, "Chat module can be imported"
|
||||
else:
|
||||
return test_name, False, f"Chat module import failed: {stderr}"
|
||||
|
||||
def test_configuration_validation(self) -> Tuple[str, bool, str]:
|
||||
"""Test configuration validation"""
|
||||
test_name = "Configuration Validation Test"
|
||||
|
||||
# Test with valid config
|
||||
config_path = self.create_test_config()
|
||||
cmd = [sys.executable, "-c", f"""
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# Test config loading
|
||||
config_path = Path('{config_path}')
|
||||
with config_path.open('r') as f:
|
||||
config = json.load(f)
|
||||
|
||||
print(f'SUCCESS: Config loaded with {{len(config)}} sections')
|
||||
"""]
|
||||
|
||||
success, stdout, stderr = self.run_command(cmd, timeout=10)
|
||||
|
||||
if success and "SUCCESS" in stdout:
|
||||
return test_name, True, "Configuration validation works"
|
||||
else:
|
||||
return test_name, False, f"Configuration validation failed: {stderr}"
|
||||
|
||||
def run_all_tests(self) -> List[Tuple[str, bool, str]]:
|
||||
"""Run all startup tests"""
|
||||
logger.info("🚀 Starting application startup tests...")
|
||||
|
||||
test_methods = [
|
||||
self.test_module_import,
|
||||
self.test_help_command,
|
||||
self.test_list_command,
|
||||
self.test_check_deps_command,
|
||||
self.test_info_command,
|
||||
self.test_chat_help,
|
||||
self.test_configuration_validation
|
||||
]
|
||||
|
||||
for test_method in test_methods:
|
||||
try:
|
||||
logger.info(f"Running {test_method.__name__}...")
|
||||
result = test_method()
|
||||
self.test_results.append(result)
|
||||
|
||||
status = "✅ PASS" if result[1] else "❌ FAIL"
|
||||
logger.info(f"{status} {result[0]}: {result[2]}")
|
||||
|
||||
except Exception as e:
|
||||
error_result = (test_method.__name__, False, f"Test execution failed: {str(e)}")
|
||||
self.test_results.append(error_result)
|
||||
logger.error(f"❌ FAIL {error_result[0]}: {error_result[2]}")
|
||||
|
||||
return self.test_results
|
||||
|
||||
def generate_report(self) -> str:
|
||||
"""Generate test report"""
|
||||
total_tests = len(self.test_results)
|
||||
passed_tests = sum(1 for _, success, _ in self.test_results if success)
|
||||
failed_tests = total_tests - passed_tests
|
||||
|
||||
report = f"""
|
||||
{'='*60}
|
||||
APPLICATION STARTUP TEST REPORT
|
||||
{'='*60}
|
||||
|
||||
Summary:
|
||||
Total Tests: {total_tests}
|
||||
Passed: {passed_tests}
|
||||
Failed: {failed_tests}
|
||||
Success Rate: {(passed_tests/total_tests*100):.1f}%
|
||||
|
||||
Test Results:
|
||||
"""
|
||||
|
||||
for test_name, success, message in self.test_results:
|
||||
status = "✅ PASS" if success else "❌ FAIL"
|
||||
report += f"\n{status} {test_name}: {message}"
|
||||
|
||||
report += f"\n\n{'='*60}\n"
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
"""Main test function"""
|
||||
tester = ApplicationStartupTest()
|
||||
|
||||
try:
|
||||
results = tester.run_all_tests()
|
||||
report = tester.generate_report()
|
||||
|
||||
print(report)
|
||||
|
||||
# Return appropriate exit code
|
||||
failed_count = sum(1 for _, success, _ in results if not success)
|
||||
return 0 if failed_count == 0 else 1
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Test execution failed: {str(e)}")
|
||||
return 1
|
||||
|
||||
finally:
|
||||
tester.cleanup()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = main()
|
||||
sys.exit(exit_code)
|
||||
Reference in New Issue
Block a user