Skip to main content
Glama
test_uml_renderer.py8.94 kB
"""UML渲染器单元测试""" import asyncio import os import tempfile import unittest from unittest.mock import Mock, patch, AsyncMock import base64 import pytest from src.uml_renderer import UMLRenderer from src.config import Config from src.exceptions import ( UMLRenderError, UMLTimeoutError, UMLValidationError, PlantUMLNotFoundError ) class TestUMLRenderer(unittest.TestCase): """UML渲染器测试类""" def setUp(self) -> None: """测试前置设置""" self.config = Config() self.renderer = UMLRenderer(self.config) # 测试用的UML代码 self.valid_uml = "@startuml\nAlice -> Bob: Hello\n@enduml" self.invalid_uml = "invalid uml code" self.complex_uml = """ @startuml class User { +username: String +email: String +login() +logout() } class Order { +id: Long +amount: BigDecimal +status: OrderStatus +create() +cancel() } class Product { +name: String +price: BigDecimal +description: String } User "1" -- "*" Order Order "*" -- "*" Product @enduml """ def tearDown(self) -> None: """测试后清理""" # 清理临时文件 if hasattr(self.renderer, 'temp_dir'): import shutil try: shutil.rmtree(self.renderer.temp_dir, ignore_errors=True) except: pass @patch('subprocess.run') def test_check_plantuml_availability_success(self, mock_run) -> None: """测试PlantUML可用性检查 - 成功""" mock_run.return_value.returncode = 0 mock_run.return_value.stdout = "PlantUML version 1.2024.0" result = self.renderer.check_plantuml_availability_sync() self.assertTrue(result) @patch('subprocess.run') def test_check_plantuml_availability_failure(self, mock_run) -> None: """测试PlantUML可用性检查 - 失败""" mock_run.side_effect = FileNotFoundError() with self.assertRaises(PlantUMLNotFoundError): self.renderer.check_plantuml_availability_sync() def test_validate_uml_syntax_valid(self) -> None: """测试UML语法验证 - 有效""" result = self.renderer.validate_uml_syntax(self.valid_uml) self.assertTrue(result) def test_validate_uml_syntax_invalid(self) -> None: """测试UML语法验证 - 无效""" with self.assertRaises(UMLValidationError): self.renderer.validate_uml_syntax(self.invalid_uml) def test_validate_uml_syntax_empty(self) -> None: """测试UML语法验证 - 空内容""" with self.assertRaises(UMLValidationError): self.renderer.validate_uml_syntax("") @patch('subprocess.run') async def test_render_uml_success_png(self, mock_run) -> None: """测试UML渲染 - PNG格式成功""" # 模拟成功的subprocess调用 mock_run.return_value.returncode = 0 # 创建模拟的PNG文件 with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file: # 写入一些测试数据 test_png_data = b'\x89PNG\r\n\x1a\n' + b'test_image_data' temp_file.write(test_png_data) temp_file.flush() # 模拟PlantUML生成文件 with patch('tempfile.NamedTemporaryFile') as mock_temp: mock_temp.return_value.__enter__.return_value.name = temp_file.name result = await self.renderer.render_uml(self.valid_uml, 'png') self.assertIn('image_base64', result) self.assertEqual(result['format'], 'png') self.assertIn('metadata', result) # 验证base64编码 decoded_data = base64.b64decode(result['image_base64']) self.assertEqual(decoded_data, test_png_data) # 清理临时文件 os.unlink(temp_file.name) @patch('subprocess.run') async def test_render_uml_success_svg(self, mock_run) -> None: """测试UML渲染 - SVG格式成功""" mock_run.return_value.returncode = 0 test_svg_data = b'<svg xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100"/></svg>' with tempfile.NamedTemporaryFile(suffix='.svg', delete=False) as temp_file: temp_file.write(test_svg_data) temp_file.flush() with patch('tempfile.NamedTemporaryFile') as mock_temp: mock_temp.return_value.__enter__.return_value.name = temp_file.name result = await self.renderer.render_uml(self.valid_uml, 'svg') self.assertIn('image_base64', result) self.assertEqual(result['format'], 'svg') decoded_data = base64.b64decode(result['image_base64']) self.assertEqual(decoded_data, test_svg_data) os.unlink(temp_file.name) @patch('subprocess.run') async def test_render_uml_plantuml_error(self, mock_run) -> None: """测试UML渲染 - PlantUML执行错误""" mock_run.return_value.returncode = 1 mock_run.return_value.stderr = "PlantUML syntax error" with self.assertRaises(UMLRenderError): await self.renderer.render_uml(self.valid_uml, 'png') @patch('subprocess.run') async def test_render_uml_timeout(self, mock_run) -> None: """测试UML渲染 - 超时""" import subprocess mock_run.side_effect = subprocess.TimeoutExpired('java', 30) with self.assertRaises(UMLTimeoutError): await self.renderer.render_uml(self.valid_uml, 'png') async def test_render_uml_invalid_format(self) -> None: """测试UML渲染 - 无效格式""" with self.assertRaises(UMLValidationError): await self.renderer.render_uml(self.valid_uml, 'invalid_format') @patch('src.uml_renderer.UMLRenderer.render_uml') async def test_concurrent_rendering(self, mock_render) -> None: """测试并发渲染""" # 模拟渲染结果 mock_render.return_value = { 'image_base64': 'dGVzdA==', 'format': 'png', 'metadata': {'size': 100} } # 创建多个并发任务 tasks = [] for i in range(5): task = asyncio.create_task( self.renderer.render_uml(f"{self.valid_uml}_{i}", 'png') ) tasks.append(task) # 等待所有任务完成 results = await asyncio.gather(*tasks) # 验证结果 self.assertEqual(len(results), 5) for result in results: self.assertIn('image_base64', result) self.assertEqual(result['format'], 'png') def test_get_metrics(self) -> None: """测试获取性能指标""" metrics = self.renderer.get_metrics() self.assertIn('total_renders', metrics) self.assertIn('successful_renders', metrics) self.assertIn('failed_renders', metrics) self.assertIn('cache_hits', metrics) self.assertIn('cache_misses', metrics) self.assertIn('average_render_time', metrics) async def test_cleanup_resources(self) -> None: """测试资源清理""" # 执行一些操作创建资源 await self.renderer.cleanup_resources() # 验证清理操作(这里主要测试不抛异常) self.assertTrue(True) class TestUMLRendererIntegration(unittest.TestCase): """UML渲染器集成测试""" def setUp(self) -> None: """测试前置设置""" self.config = Config() self.renderer = UMLRenderer(self.config) @unittest.skipIf(not os.getenv('RUN_INTEGRATION_TESTS'), "跳过集成测试") async def test_real_plantuml_rendering(self) -> None: """真实PlantUML渲染测试(需要实际环境)""" uml_code = "@startuml\nAlice -> Bob: Hello World\n@enduml" try: result = await self.renderer.render_uml(uml_code, 'png') self.assertIn('image_base64', result) self.assertEqual(result['format'], 'png') # 验证base64数据有效性 image_data = base64.b64decode(result['image_base64']) self.assertTrue(len(image_data) > 0) # PNG文件应该以特定字节开头 self.assertTrue(image_data.startswith(b'\x89PNG')) except PlantUMLNotFoundError: self.skipTest("PlantUML未安装,跳过真实渲染测试") if __name__ == '__main__': # 运行测试 unittest.main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/icatw/uml-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server