282 lines
9.7 KiB
Python
282 lines
9.7 KiB
Python
#!/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)
|