quantum-framework 0.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/admin.py +131 -0
  4. quantum/cli/check.py +223 -0
  5. quantum/cli/commands/__init__.py +15 -0
  6. quantum/cli/commands/build.py +414 -0
  7. quantum/cli/commands/dev.py +185 -0
  8. quantum/cli/commands/docs.py +329 -0
  9. quantum/cli/commands/lint.py +523 -0
  10. quantum/cli/commands/migrate.py +548 -0
  11. quantum/cli/commands/new.py +622 -0
  12. quantum/cli/commands/serve.py +190 -0
  13. quantum/cli/commands/test.py +193 -0
  14. quantum/cli/deploy.py +810 -0
  15. quantum/cli/hot_reload.py +951 -0
  16. quantum/cli/jobs.py +356 -0
  17. quantum/cli/mq.py +582 -0
  18. quantum/cli/pkg.py +390 -0
  19. quantum/cli/runner.py +565 -0
  20. quantum/cli/server_process.py +159 -0
  21. quantum/cli/utils.py +334 -0
  22. quantum/compiler/__init__.py +30 -0
  23. quantum/compiler/base_generator.py +367 -0
  24. quantum/compiler/cli.py +295 -0
  25. quantum/compiler/expression_transformer.py +444 -0
  26. quantum/compiler/javascript/__init__.py +10 -0
  27. quantum/compiler/javascript/generator.py +659 -0
  28. quantum/compiler/optimizer.py +270 -0
  29. quantum/compiler/python/__init__.py +10 -0
  30. quantum/compiler/python/generator.py +883 -0
  31. quantum/compiler/python/runtime.py +863 -0
  32. quantum/compiler/transpiler.py +330 -0
  33. quantum/core/__init__.py +3 -0
  34. quantum/core/ast_nodes.py +2619 -0
  35. quantum/core/config_env.py +54 -0
  36. quantum/core/docs_links.py +8 -0
  37. quantum/core/expression_diagnostics.py +87 -0
  38. quantum/core/expression_stdlib.py +208 -0
  39. quantum/core/expressions.py +677 -0
  40. quantum/core/features/agents/src/__init__.py +30 -0
  41. quantum/core/features/agents/src/ast_node.py +540 -0
  42. quantum/core/features/conditionals/src/__init__.py +8 -0
  43. quantum/core/features/conditionals/src/ast_node.py +68 -0
  44. quantum/core/features/data_import/src/__init__.py +0 -0
  45. quantum/core/features/data_import/src/ast_node.py +292 -0
  46. quantum/core/features/data_import/src/runtime.py +533 -0
  47. quantum/core/features/dump/src/__init__.py +12 -0
  48. quantum/core/features/dump/src/ast_node.py +106 -0
  49. quantum/core/features/dump/src/parser.py +61 -0
  50. quantum/core/features/dump/src/runtime.py +246 -0
  51. quantum/core/features/functions/src/__init__.py +8 -0
  52. quantum/core/features/functions/src/ast_node.py +149 -0
  53. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  54. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  55. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  56. quantum/core/features/invocation/src/__init__.py +0 -0
  57. quantum/core/features/invocation/src/ast_node.py +147 -0
  58. quantum/core/features/invocation/src/runtime.py +332 -0
  59. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  60. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  61. quantum/core/features/knowledge_base/src/parser.py +82 -0
  62. quantum/core/features/logging/src/__init__.py +12 -0
  63. quantum/core/features/logging/src/ast_node.py +111 -0
  64. quantum/core/features/logging/src/parser.py +50 -0
  65. quantum/core/features/logging/src/runtime.py +190 -0
  66. quantum/core/features/loops/src/__init__.py +8 -0
  67. quantum/core/features/loops/src/ast_node.py +60 -0
  68. quantum/core/features/query/src/__init__.py +0 -0
  69. quantum/core/features/query/src/query_validators.py +20 -0
  70. quantum/core/features/state_management/src/__init__.py +11 -0
  71. quantum/core/features/state_management/src/ast_node.py +121 -0
  72. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  73. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  74. quantum/core/features/terminal_engine/src/parser.py +361 -0
  75. quantum/core/features/testing_engine/src/__init__.py +41 -0
  76. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  77. quantum/core/features/testing_engine/src/parser.py +604 -0
  78. quantum/core/features/theming/src/__init__.py +48 -0
  79. quantum/core/features/theming/src/ast_node.py +137 -0
  80. quantum/core/features/theming/src/presets.py +405 -0
  81. quantum/core/features/ui_engine/src/__init__.py +20 -0
  82. quantum/core/features/ui_engine/src/ast_nodes.py +1913 -0
  83. quantum/core/features/ui_engine/src/parser.py +1192 -0
  84. quantum/core/features/websocket/src/__init__.py +24 -0
  85. quantum/core/features/websocket/src/ast_node.py +247 -0
  86. quantum/core/guards.py +53 -0
  87. quantum/core/html_compat.py +299 -0
  88. quantum/core/parser.py +1551 -0
  89. quantum/core/parser_registry.py +213 -0
  90. quantum/core/parsers/__init__.py +76 -0
  91. quantum/core/parsers/ai/__init__.py +12 -0
  92. quantum/core/parsers/ai/agent_parser.py +106 -0
  93. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  94. quantum/core/parsers/ai/llm_parser.py +78 -0
  95. quantum/core/parsers/ai/team_parser.py +88 -0
  96. quantum/core/parsers/base.py +322 -0
  97. quantum/core/parsers/composition/__init__.py +10 -0
  98. quantum/core/parsers/composition/import_parser.py +50 -0
  99. quantum/core/parsers/composition/slot_parser.py +48 -0
  100. quantum/core/parsers/control_flow/__init__.py +11 -0
  101. quantum/core/parsers/control_flow/if_parser.py +71 -0
  102. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  103. quantum/core/parsers/control_flow/set_parser.py +93 -0
  104. quantum/core/parsers/data/__init__.py +12 -0
  105. quantum/core/parsers/data/data_parser.py +229 -0
  106. quantum/core/parsers/data/invoke_parser.py +133 -0
  107. quantum/core/parsers/data/query_parser.py +213 -0
  108. quantum/core/parsers/data/transaction_parser.py +86 -0
  109. quantum/core/parsers/events/__init__.py +9 -0
  110. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  111. quantum/core/parsers/forms/__init__.py +11 -0
  112. quantum/core/parsers/forms/action_parser.py +79 -0
  113. quantum/core/parsers/forms/flash_parser.py +34 -0
  114. quantum/core/parsers/forms/redirect_parser.py +33 -0
  115. quantum/core/parsers/functions/__init__.py +11 -0
  116. quantum/core/parsers/functions/function_parser.py +98 -0
  117. quantum/core/parsers/functions/param_parser.py +73 -0
  118. quantum/core/parsers/functions/return_parser.py +31 -0
  119. quantum/core/parsers/html/__init__.py +10 -0
  120. quantum/core/parsers/html/component_call_parser.py +130 -0
  121. quantum/core/parsers/html/html_parser.py +115 -0
  122. quantum/core/parsers/jobs/__init__.py +11 -0
  123. quantum/core/parsers/jobs/job_parser.py +71 -0
  124. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  125. quantum/core/parsers/jobs/thread_parser.py +57 -0
  126. quantum/core/parsers/messaging/__init__.py +17 -0
  127. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  128. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_parser.py +114 -0
  130. quantum/core/parsers/messaging/queue_parser.py +61 -0
  131. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  132. quantum/core/parsers/routing/__init__.py +9 -0
  133. quantum/core/parsers/routing/route_parser.py +41 -0
  134. quantum/core/parsers/scripting/__init__.py +12 -0
  135. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  136. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  137. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  138. quantum/core/parsers/scripting/python_parser.py +49 -0
  139. quantum/core/parsers/services/__init__.py +12 -0
  140. quantum/core/parsers/services/dump_parser.py +52 -0
  141. quantum/core/parsers/services/file_parser.py +48 -0
  142. quantum/core/parsers/services/log_parser.py +46 -0
  143. quantum/core/parsers/services/mail_parser.py +65 -0
  144. quantum/core/tiers.py +164 -0
  145. quantum/core/xml_lines.py +81 -0
  146. quantum/packages/__init__.py +28 -0
  147. quantum/packages/manager.py +413 -0
  148. quantum/packages/manifest.py +351 -0
  149. quantum/packages/registry.py +399 -0
  150. quantum/packages/resolver.py +336 -0
  151. quantum/plugins/__init__.py +33 -0
  152. quantum/plugins/hooks.py +329 -0
  153. quantum/plugins/loader.py +479 -0
  154. quantum/plugins/manifest.py +336 -0
  155. quantum/plugins/registry.py +371 -0
  156. quantum/runtime/__init__.py +25 -0
  157. quantum/runtime/action_handler.py +532 -0
  158. quantum/runtime/adapters/__init__.py +88 -0
  159. quantum/runtime/adapters/memory_adapter.py +690 -0
  160. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  161. quantum/runtime/adapters/redis_adapter.py +582 -0
  162. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  163. quantum/runtime/agent_service.py +1133 -0
  164. quantum/runtime/ast_cache.py +506 -0
  165. quantum/runtime/auth_service.py +267 -0
  166. quantum/runtime/component.py +1048 -0
  167. quantum/runtime/component_composer.py +133 -0
  168. quantum/runtime/component_resolver.py +174 -0
  169. quantum/runtime/conditions.py +54 -0
  170. quantum/runtime/database_service.py +595 -0
  171. quantum/runtime/db_schema.py +100 -0
  172. quantum/runtime/dev_panel.py +178 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +283 -0
  176. quantum/runtime/executor_registry.py +178 -0
  177. quantum/runtime/executors/__init__.py +71 -0
  178. quantum/runtime/executors/ai/__init__.py +12 -0
  179. quantum/runtime/executors/ai/agent_executor.py +217 -0
  180. quantum/runtime/executors/ai/knowledge_executor.py +114 -0
  181. quantum/runtime/executors/ai/llm_executor.py +153 -0
  182. quantum/runtime/executors/ai/team_executor.py +171 -0
  183. quantum/runtime/executors/base.py +264 -0
  184. quantum/runtime/executors/control_flow/__init__.py +12 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +98 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +308 -0
  187. quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
  188. quantum/runtime/executors/control_flow/set_executor.py +464 -0
  189. quantum/runtime/executors/data/__init__.py +12 -0
  190. quantum/runtime/executors/data/data_executor.py +156 -0
  191. quantum/runtime/executors/data/invoke_executor.py +228 -0
  192. quantum/runtime/executors/data/query_executor.py +290 -0
  193. quantum/runtime/executors/data/transaction_executor.py +91 -0
  194. quantum/runtime/executors/jobs/__init__.py +11 -0
  195. quantum/runtime/executors/jobs/job_executor.py +190 -0
  196. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  197. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  198. quantum/runtime/executors/messaging/__init__.py +17 -0
  199. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  200. quantum/runtime/executors/messaging/message_executor.py +174 -0
  201. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  202. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  203. quantum/runtime/executors/scripting/__init__.py +11 -0
  204. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  205. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  206. quantum/runtime/executors/scripting/python_executor.py +249 -0
  207. quantum/runtime/executors/services/__init__.py +12 -0
  208. quantum/runtime/executors/services/dump_executor.py +72 -0
  209. quantum/runtime/executors/services/file_executor.py +89 -0
  210. quantum/runtime/executors/services/log_executor.py +77 -0
  211. quantum/runtime/executors/services/mail_executor.py +81 -0
  212. quantum/runtime/expression_cache.py +498 -0
  213. quantum/runtime/file_upload_service.py +326 -0
  214. quantum/runtime/function_registry.py +118 -0
  215. quantum/runtime/game_builder.py +166 -0
  216. quantum/runtime/game_code_generator.py +2371 -0
  217. quantum/runtime/game_templates.py +2006 -0
  218. quantum/runtime/godot_code_generator.py +4681 -0
  219. quantum/runtime/godot_templates.py +1449 -0
  220. quantum/runtime/job_executor.py +1599 -0
  221. quantum/runtime/knowledge_service.py +546 -0
  222. quantum/runtime/llm_cache.py +100 -0
  223. quantum/runtime/llm_providers.py +704 -0
  224. quantum/runtime/llm_service.py +287 -0
  225. quantum/runtime/local_server.py +28 -0
  226. quantum/runtime/logging_setup.py +141 -0
  227. quantum/runtime/message_broker.py +364 -0
  228. quantum/runtime/message_queue_service.py +571 -0
  229. quantum/runtime/param_validation.py +218 -0
  230. quantum/runtime/pypy_compat.py +317 -0
  231. quantum/runtime/python_bridge.py +698 -0
  232. quantum/runtime/query_validators.py +304 -0
  233. quantum/runtime/renderer.py +734 -0
  234. quantum/runtime/service_container.py +470 -0
  235. quantum/runtime/table_params.py +163 -0
  236. quantum/runtime/terminal_builder.py +76 -0
  237. quantum/runtime/terminal_code_generator.py +607 -0
  238. quantum/runtime/terminal_templates.py +243 -0
  239. quantum/runtime/testing_builder.py +77 -0
  240. quantum/runtime/testing_code_generator.py +833 -0
  241. quantum/runtime/testing_templates.py +85 -0
  242. quantum/runtime/ui_builder.py +222 -0
  243. quantum/runtime/ui_console.py +405 -0
  244. quantum/runtime/ui_desktop.py +56 -0
  245. quantum/runtime/ui_form_rules.py +193 -0
  246. quantum/runtime/ui_html_adapter.py +2567 -0
  247. quantum/runtime/ui_html_templates.py +2106 -0
  248. quantum/runtime/ui_mobile_adapter.py +1832 -0
  249. quantum/runtime/ui_mobile_templates.py +1003 -0
  250. quantum/runtime/ui_page_renderer.py +392 -0
  251. quantum/runtime/ui_pager.py +73 -0
  252. quantum/runtime/ui_table_edit.py +139 -0
  253. quantum/runtime/ui_textual_adapter.py +1866 -0
  254. quantum/runtime/ui_textual_templates.py +45 -0
  255. quantum/runtime/ui_tokens.py +465 -0
  256. quantum/runtime/ui_validator.py +365 -0
  257. quantum/runtime/ui_view.py +221 -0
  258. quantum/runtime/validators.py +256 -0
  259. quantum/runtime/web_server.py +2131 -0
  260. quantum/runtime/websocket_adapter.py +501 -0
  261. quantum/runtime/websocket_service.py +585 -0
  262. quantum/runtime/websocket_transport.py +289 -0
  263. quantum/runtime/wsgi.py +101 -0
  264. quantum/services.py +109 -0
  265. quantum/utils/__init__.py +1 -0
  266. quantum_admin/__init__.py +4 -0
  267. quantum_admin/assets/quantum-admin-connectors.js +123 -0
  268. quantum_admin/assets/quantum-admin.css +2058 -0
  269. quantum_admin/assets/quantum-source-highlight.js +106 -0
  270. quantum_admin/backend/__init__.py +4 -0
  271. quantum_admin/backend/audit_service.py +346 -0
  272. quantum_admin/backend/auth_service.py +394 -0
  273. quantum_admin/backend/component_discovery.py +766 -0
  274. quantum_admin/backend/config_generator.py +586 -0
  275. quantum_admin/backend/connector_service.py +1016 -0
  276. quantum_admin/backend/crud.py +557 -0
  277. quantum_admin/backend/database.py +226 -0
  278. quantum_admin/backend/db_setup_service.py +193 -0
  279. quantum_admin/backend/deploy_service.py +698 -0
  280. quantum_admin/backend/docker_service.py +422 -0
  281. quantum_admin/backend/environment_service.py +418 -0
  282. quantum_admin/backend/git_service.py +370 -0
  283. quantum_admin/backend/health_service.py +390 -0
  284. quantum_admin/backend/job_service.py +347 -0
  285. quantum_admin/backend/log_service.py +331 -0
  286. quantum_admin/backend/main.py +14426 -0
  287. quantum_admin/backend/models.py +986 -0
  288. quantum_admin/backend/pipeline_service.py +812 -0
  289. quantum_admin/backend/resource_manager.py +1118 -0
  290. quantum_admin/backend/schemas.py +419 -0
  291. quantum_admin/backend/secret_manager.py +277 -0
  292. quantum_admin/backend/settings_service.py +488 -0
  293. quantum_admin/backend/template_service.py +710 -0
  294. quantum_admin/backend/test_execution_service.py +364 -0
  295. quantum_admin/backend/test_generator.py +782 -0
  296. quantum_admin/backend/webhook_service.py +454 -0
  297. quantum_admin/backend/websocket_manager.py +360 -0
  298. quantum_admin/components/admin/_layout/AdminShell.q +121 -0
  299. quantum_admin/components/admin/agents.q +107 -0
  300. quantum_admin/components/admin/app/[name].q +558 -0
  301. quantum_admin/components/admin/applications.q +160 -0
  302. quantum_admin/components/admin/component/[...path].q +205 -0
  303. quantum_admin/components/admin/components.q +59 -0
  304. quantum_admin/components/admin/connectors.q +283 -0
  305. quantum_admin/components/admin/dashboard.q +86 -0
  306. quantum_admin/components/admin/database.q +95 -0
  307. quantum_admin/components/admin/features.q +69 -0
  308. quantum_admin/components/admin/index.q +12 -0
  309. quantum_admin/components/admin/jobs.q +75 -0
  310. quantum_admin/components/admin/login.q +65 -0
  311. quantum_admin/components/admin/logout.q +11 -0
  312. quantum_admin/components/admin/settings.q +174 -0
  313. quantum_admin/components/admin/source.q +45 -0
  314. quantum_admin/components/admin/tests.q +73 -0
  315. quantum_admin/run.py +13 -0
  316. quantum_admin/services/__init__.py +16 -0
  317. quantum_admin/services/_base.py +84 -0
  318. quantum_admin/services/_component_test_generator.py +205 -0
  319. quantum_admin/services/_yaml_editor.py +82 -0
  320. quantum_admin/services/apps.py +330 -0
  321. quantum_admin/services/auth.py +61 -0
  322. quantum_admin/services/components.py +210 -0
  323. quantum_admin/services/connectors.py +164 -0
  324. quantum_admin/services/projects.py +189 -0
  325. quantum_admin/services/repository.py +223 -0
  326. quantum_admin/services/settings.py +150 -0
  327. quantum_admin/services/yaml_import.py +118 -0
  328. quantum_framework-0.18.0.dist-info/METADATA +281 -0
  329. quantum_framework-0.18.0.dist-info/RECORD +333 -0
  330. quantum_framework-0.18.0.dist-info/WHEEL +5 -0
  331. quantum_framework-0.18.0.dist-info/entry_points.txt +2 -0
  332. quantum_framework-0.18.0.dist-info/licenses/LICENSE +21 -0
  333. quantum_framework-0.18.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,782 @@
1
+ """
2
+ Test Generator Service - Automatically generate test cases for Quantum components
3
+
4
+ Features:
5
+ - Analyze component metadata to generate test cases
6
+ - Generate test files for functions, endpoints, and queries
7
+ - Support pytest test format
8
+ - Generate test stubs with assertions
9
+ - Template-based test generation
10
+ """
11
+
12
+ import os
13
+ import re
14
+ import logging
15
+ from pathlib import Path
16
+ from typing import Dict, List, Optional, Any, Tuple
17
+ from datetime import datetime
18
+ from dataclasses import dataclass, field
19
+ from sqlalchemy.orm import Session
20
+
21
+ try:
22
+ from .models import Project, Component, Endpoint, TestRun, TestResult, ComponentTest
23
+ from .component_discovery import get_discovery_service, ComponentMetadata, DiscoveredFunction, DiscoveredEndpoint, DiscoveredQuery
24
+ except ImportError:
25
+ from models import Project, Component, Endpoint, TestRun, TestResult, ComponentTest
26
+ from component_discovery import get_discovery_service, ComponentMetadata, DiscoveredFunction, DiscoveredEndpoint, DiscoveredQuery
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ @dataclass
32
+ class GeneratedTest:
33
+ """Represents a generated test case"""
34
+ name: str
35
+ description: str
36
+ test_type: str # unit, integration, api, e2e
37
+ file_path: str
38
+ content: str
39
+ component_name: Optional[str] = None
40
+ target_function: Optional[str] = None
41
+ target_endpoint: Optional[str] = None
42
+ assertions: List[str] = field(default_factory=list)
43
+
44
+
45
+ @dataclass
46
+ class TestSuite:
47
+ """Collection of generated tests"""
48
+ name: str
49
+ component_name: str
50
+ tests: List[GeneratedTest] = field(default_factory=list)
51
+ setup_code: str = ""
52
+ teardown_code: str = ""
53
+
54
+
55
+ class TestTemplates:
56
+ """Test code templates for different test types"""
57
+
58
+ # File header template
59
+ FILE_HEADER = '''"""
60
+ Auto-generated tests for {component_name}
61
+ Generated by Quantum Admin Test Generator
62
+ Date: {date}
63
+ """
64
+
65
+ import pytest
66
+ from pathlib import Path
67
+ import sys
68
+
69
+ # Add project root to path
70
+ PROJECT_ROOT = Path(__file__).parent.parent
71
+ sys.path.insert(0, str(PROJECT_ROOT))
72
+
73
+
74
+ '''
75
+
76
+ # Function test template
77
+ FUNCTION_TEST = '''
78
+ def test_{function_name}_basic():
79
+ """Test {function_name} with basic inputs"""
80
+ # Arrange
81
+ {setup_code}
82
+
83
+ # Act
84
+ result = {function_call}
85
+
86
+ # Assert
87
+ assert result is not None
88
+ {assertions}
89
+
90
+
91
+ '''
92
+
93
+ # Function with params test template
94
+ FUNCTION_PARAMS_TEST = '''
95
+ @pytest.mark.parametrize("input_value,expected", [
96
+ {param_cases}
97
+ ])
98
+ def test_{function_name}_params(input_value, expected):
99
+ """Test {function_name} with various inputs"""
100
+ # Arrange
101
+ {setup_code}
102
+
103
+ # Act
104
+ result = {function_call}
105
+
106
+ # Assert
107
+ assert result == expected
108
+
109
+
110
+ '''
111
+
112
+ # Endpoint test template
113
+ ENDPOINT_TEST = '''
114
+ def test_{endpoint_name}_{method}_basic(client):
115
+ """Test {method} {path}"""
116
+ # Arrange
117
+ {setup_code}
118
+
119
+ # Act
120
+ response = client.{method_lower}("{path}")
121
+
122
+ # Assert
123
+ assert response.status_code == 200
124
+ {assertions}
125
+
126
+
127
+ '''
128
+
129
+ # Endpoint with body template
130
+ ENDPOINT_POST_TEST = '''
131
+ def test_{endpoint_name}_{method}_with_data(client):
132
+ """Test {method} {path} with request body"""
133
+ # Arrange
134
+ data = {{
135
+ {request_body}
136
+ }}
137
+
138
+ # Act
139
+ response = client.{method_lower}("{path}", json=data)
140
+
141
+ # Assert
142
+ assert response.status_code in [200, 201]
143
+ {assertions}
144
+
145
+
146
+ '''
147
+
148
+ # Query test template
149
+ QUERY_TEST = '''
150
+ def test_{query_name}_query(db_session):
151
+ """Test {query_name} database query"""
152
+ # Arrange
153
+ {setup_code}
154
+
155
+ # Act
156
+ result = execute_query("{query_name}", db_session)
157
+
158
+ # Assert
159
+ assert result is not None
160
+ assert len(result) >= 0
161
+ {assertions}
162
+
163
+
164
+ '''
165
+
166
+ # Fixtures template
167
+ FIXTURES = '''
168
+ @pytest.fixture
169
+ def client():
170
+ """Create test client"""
171
+ from quantum.runtime.web_server import create_app
172
+ app = create_app()
173
+ app.config['TESTING'] = True
174
+ with app.test_client() as client:
175
+ yield client
176
+
177
+
178
+ @pytest.fixture
179
+ def db_session():
180
+ """Create database session for testing"""
181
+ from quantum.runtime.database_service import get_db_session
182
+ session = get_db_session()
183
+ yield session
184
+ session.rollback()
185
+ session.close()
186
+
187
+
188
+ '''
189
+
190
+ # Conftest template
191
+ CONFTEST = '''"""
192
+ Pytest configuration and fixtures for {project_name}
193
+ Auto-generated by Quantum Admin Test Generator
194
+ """
195
+
196
+ import pytest
197
+ import sys
198
+ from pathlib import Path
199
+
200
+ # Add project root to path
201
+ PROJECT_ROOT = Path(__file__).parent.parent
202
+ sys.path.insert(0, str(PROJECT_ROOT))
203
+
204
+
205
+ @pytest.fixture(scope="session")
206
+ def project_root():
207
+ """Return project root path"""
208
+ return PROJECT_ROOT
209
+
210
+
211
+ @pytest.fixture
212
+ def sample_data():
213
+ """Provide sample test data"""
214
+ return {{
215
+ "string": "test_value",
216
+ "number": 42,
217
+ "boolean": True,
218
+ "list": [1, 2, 3],
219
+ "dict": {{"key": "value"}}
220
+ }}
221
+
222
+
223
+ '''
224
+
225
+
226
+ class TestGeneratorService:
227
+ """Service for generating tests for Quantum components"""
228
+
229
+ def __init__(self, db: Session):
230
+ self.db = db
231
+ self.discovery = get_discovery_service(db)
232
+ self.templates = TestTemplates()
233
+
234
+ def generate_tests_for_component(
235
+ self,
236
+ project_id: int,
237
+ component_id: int,
238
+ output_dir: Optional[str] = None,
239
+ test_types: Optional[List[str]] = None,
240
+ save_to_db: bool = True
241
+ ) -> TestSuite:
242
+ """
243
+ Generate tests for a specific component
244
+
245
+ Args:
246
+ project_id: Project ID
247
+ component_id: Component ID
248
+ output_dir: Directory to write test files
249
+ test_types: List of test types to generate (unit, integration, api)
250
+ save_to_db: Whether to save test records to database
251
+
252
+ Returns:
253
+ TestSuite with generated tests
254
+ """
255
+ # Get component metadata
256
+ metadata = self.discovery.get_component_details(project_id, component_id)
257
+ if not metadata:
258
+ raise ValueError(f"Component {component_id} not found")
259
+
260
+ if test_types is None:
261
+ test_types = ["unit", "integration"]
262
+
263
+ suite = TestSuite(
264
+ name=f"test_{metadata.name.lower()}",
265
+ component_name=metadata.name
266
+ )
267
+
268
+ # Generate function tests
269
+ if "unit" in test_types:
270
+ for func in metadata.functions:
271
+ test = self._generate_function_test(metadata, func)
272
+ suite.tests.append(test)
273
+
274
+ # Generate endpoint tests
275
+ if "api" in test_types or "integration" in test_types:
276
+ for endpoint in metadata.endpoints:
277
+ test = self._generate_endpoint_test(metadata, endpoint)
278
+ suite.tests.append(test)
279
+
280
+ # Generate query tests
281
+ if "integration" in test_types:
282
+ for query in metadata.queries:
283
+ test = self._generate_query_test(metadata, query)
284
+ suite.tests.append(test)
285
+
286
+ # Write to files if output_dir specified
287
+ if output_dir:
288
+ self._write_test_files(suite, output_dir)
289
+
290
+ # Save test records to database
291
+ if save_to_db and suite.tests:
292
+ self._save_component_tests(component_id, suite, output_dir)
293
+
294
+ return suite
295
+
296
+ def _save_component_tests(
297
+ self,
298
+ component_id: int,
299
+ suite: TestSuite,
300
+ output_dir: Optional[str] = None
301
+ ):
302
+ """Save generated tests to database"""
303
+ # Get the component
304
+ component = self.db.query(Component).filter(
305
+ Component.id == component_id
306
+ ).first()
307
+
308
+ if not component:
309
+ return
310
+
311
+ # Get existing tests for this component
312
+ existing = self.db.query(ComponentTest).filter(
313
+ ComponentTest.component_id == component_id
314
+ ).all()
315
+ existing_names = {t.test_name for t in existing}
316
+
317
+ tests_created = 0
318
+ for test in suite.tests:
319
+ if test.name not in existing_names:
320
+ # Build the full file path
321
+ file_path = test.file_path
322
+ if output_dir:
323
+ file_path = str(Path(output_dir) / test.file_path)
324
+
325
+ component_test = ComponentTest(
326
+ component_id=component_id,
327
+ test_file=file_path,
328
+ test_name=test.name,
329
+ test_type=test.test_type,
330
+ generated_at=datetime.now(),
331
+ generated_by="auto",
332
+ last_status="pending"
333
+ )
334
+ self.db.add(component_test)
335
+ tests_created += 1
336
+
337
+ # Update component test count
338
+ component.test_count = len(existing) + tests_created
339
+
340
+ self.db.commit()
341
+ logger.info(f"Created {tests_created} test records for component {component.name}")
342
+
343
+ def generate_tests_for_project(
344
+ self,
345
+ project_id: int,
346
+ base_path: Optional[str] = None,
347
+ output_dir: Optional[str] = None,
348
+ test_types: Optional[List[str]] = None
349
+ ) -> List[TestSuite]:
350
+ """
351
+ Generate tests for all components in a project
352
+
353
+ Args:
354
+ project_id: Project ID
355
+ base_path: Base path to scan for components
356
+ output_dir: Directory to write test files
357
+ test_types: List of test types to generate
358
+
359
+ Returns:
360
+ List of TestSuites for each component
361
+ """
362
+ # Discover components
363
+ components = self.discovery.discover_components(project_id, base_path)
364
+
365
+ suites = []
366
+ for metadata in components:
367
+ if not metadata.is_valid:
368
+ logger.warning(f"Skipping invalid component: {metadata.name}")
369
+ continue
370
+
371
+ suite = self._generate_suite_from_metadata(metadata, test_types)
372
+ if suite.tests:
373
+ suites.append(suite)
374
+
375
+ # Write to files if output_dir specified
376
+ if output_dir:
377
+ self._write_project_tests(suites, output_dir)
378
+
379
+ return suites
380
+
381
+ def generate_conftest(
382
+ self,
383
+ project_id: int,
384
+ output_dir: Optional[str] = None
385
+ ) -> str:
386
+ """Generate conftest.py with common fixtures"""
387
+ project = self.db.query(Project).filter(Project.id == project_id).first()
388
+ if not project:
389
+ raise ValueError(f"Project {project_id} not found")
390
+
391
+ content = self.templates.CONFTEST.format(
392
+ project_name=project.name
393
+ )
394
+
395
+ if output_dir:
396
+ path = Path(output_dir) / "conftest.py"
397
+ path.parent.mkdir(parents=True, exist_ok=True)
398
+ path.write_text(content, encoding='utf-8')
399
+
400
+ return content
401
+
402
+ def _generate_suite_from_metadata(
403
+ self,
404
+ metadata: ComponentMetadata,
405
+ test_types: Optional[List[str]] = None
406
+ ) -> TestSuite:
407
+ """Generate test suite from component metadata"""
408
+ if test_types is None:
409
+ test_types = ["unit", "integration"]
410
+
411
+ suite = TestSuite(
412
+ name=f"test_{self._sanitize_name(metadata.name)}",
413
+ component_name=metadata.name
414
+ )
415
+
416
+ # Generate function tests
417
+ if "unit" in test_types:
418
+ for func in metadata.functions:
419
+ test = self._generate_function_test(metadata, func)
420
+ suite.tests.append(test)
421
+
422
+ # Generate endpoint tests
423
+ if "api" in test_types or "integration" in test_types:
424
+ for endpoint in metadata.endpoints:
425
+ test = self._generate_endpoint_test(metadata, endpoint)
426
+ suite.tests.append(test)
427
+
428
+ # Generate query tests
429
+ if "integration" in test_types:
430
+ for query in metadata.queries:
431
+ test = self._generate_query_test(metadata, query)
432
+ suite.tests.append(test)
433
+
434
+ return suite
435
+
436
+ def _generate_function_test(
437
+ self,
438
+ metadata: ComponentMetadata,
439
+ func: DiscoveredFunction
440
+ ) -> GeneratedTest:
441
+ """Generate test for a function"""
442
+ func_name = self._sanitize_name(func.name)
443
+
444
+ # Build function call
445
+ if func.params:
446
+ param_values = ", ".join([f"{p}=sample_value" for p in func.params])
447
+ function_call = f"{func.name}({param_values})"
448
+ else:
449
+ function_call = f"{func.name}()"
450
+
451
+ # Generate assertions based on return type
452
+ assertions = []
453
+ if func.return_type:
454
+ if func.return_type.lower() in ['string', 'str']:
455
+ assertions.append("assert isinstance(result, str)")
456
+ elif func.return_type.lower() in ['int', 'integer', 'number']:
457
+ assertions.append("assert isinstance(result, (int, float))")
458
+ elif func.return_type.lower() in ['list', 'array']:
459
+ assertions.append("assert isinstance(result, list)")
460
+ elif func.return_type.lower() in ['dict', 'object']:
461
+ assertions.append("assert isinstance(result, dict)")
462
+ elif func.return_type.lower() in ['bool', 'boolean']:
463
+ assertions.append("assert isinstance(result, bool)")
464
+
465
+ content = self.templates.FUNCTION_TEST.format(
466
+ function_name=func_name,
467
+ setup_code="sample_value = 'test'",
468
+ function_call=function_call,
469
+ assertions="\n ".join(assertions) if assertions else "# Add specific assertions"
470
+ )
471
+
472
+ return GeneratedTest(
473
+ name=f"test_{func_name}_basic",
474
+ description=f"Test {func.name} function",
475
+ test_type="unit",
476
+ file_path=f"test_{self._sanitize_name(metadata.name)}.py",
477
+ content=content,
478
+ component_name=metadata.name,
479
+ target_function=func.name,
480
+ assertions=assertions
481
+ )
482
+
483
+ def _generate_endpoint_test(
484
+ self,
485
+ metadata: ComponentMetadata,
486
+ endpoint: DiscoveredEndpoint
487
+ ) -> GeneratedTest:
488
+ """Generate test for an endpoint"""
489
+ # Sanitize endpoint name for test function
490
+ endpoint_name = self._sanitize_name(endpoint.path.replace('/', '_'))
491
+ method = endpoint.method.upper()
492
+ method_lower = method.lower()
493
+
494
+ # Choose template based on method
495
+ if method in ['POST', 'PUT', 'PATCH']:
496
+ content = self.templates.ENDPOINT_POST_TEST.format(
497
+ endpoint_name=endpoint_name,
498
+ method=method,
499
+ method_lower=method_lower,
500
+ path=endpoint.path,
501
+ setup_code="",
502
+ request_body='"key": "value"',
503
+ assertions=""
504
+ )
505
+ else:
506
+ content = self.templates.ENDPOINT_TEST.format(
507
+ endpoint_name=endpoint_name,
508
+ method=method,
509
+ method_lower=method_lower,
510
+ path=endpoint.path,
511
+ setup_code="",
512
+ assertions=""
513
+ )
514
+
515
+ return GeneratedTest(
516
+ name=f"test_{endpoint_name}_{method_lower}",
517
+ description=f"Test {method} {endpoint.path}",
518
+ test_type="api",
519
+ file_path=f"test_{self._sanitize_name(metadata.name)}.py",
520
+ content=content,
521
+ component_name=metadata.name,
522
+ target_endpoint=endpoint.path,
523
+ assertions=[]
524
+ )
525
+
526
+ def _generate_query_test(
527
+ self,
528
+ metadata: ComponentMetadata,
529
+ query: DiscoveredQuery
530
+ ) -> GeneratedTest:
531
+ """Generate test for a database query"""
532
+ query_name = self._sanitize_name(query.name)
533
+
534
+ content = self.templates.QUERY_TEST.format(
535
+ query_name=query_name,
536
+ setup_code="# Set up test data" if query.query_type != "select" else "",
537
+ assertions=""
538
+ )
539
+
540
+ return GeneratedTest(
541
+ name=f"test_{query_name}_query",
542
+ description=f"Test {query.name} query",
543
+ test_type="integration",
544
+ file_path=f"test_{self._sanitize_name(metadata.name)}.py",
545
+ content=content,
546
+ component_name=metadata.name,
547
+ assertions=[]
548
+ )
549
+
550
+ def _write_test_files(self, suite: TestSuite, output_dir: str):
551
+ """Write test suite to files"""
552
+ output_path = Path(output_dir)
553
+ output_path.mkdir(parents=True, exist_ok=True)
554
+
555
+ # Group tests by file
556
+ file_tests: Dict[str, List[GeneratedTest]] = {}
557
+ for test in suite.tests:
558
+ if test.file_path not in file_tests:
559
+ file_tests[test.file_path] = []
560
+ file_tests[test.file_path].append(test)
561
+
562
+ # Write each file
563
+ for file_path, tests in file_tests.items():
564
+ full_path = output_path / file_path
565
+ content = self.templates.FILE_HEADER.format(
566
+ component_name=suite.component_name,
567
+ date=datetime.now().isoformat()
568
+ )
569
+ content += self.templates.FIXTURES
570
+
571
+ for test in tests:
572
+ content += test.content
573
+
574
+ full_path.write_text(content, encoding='utf-8')
575
+ logger.info(f"Generated test file: {full_path}")
576
+
577
+ def _write_project_tests(self, suites: List[TestSuite], output_dir: str):
578
+ """Write all test suites for a project"""
579
+ output_path = Path(output_dir)
580
+ output_path.mkdir(parents=True, exist_ok=True)
581
+
582
+ for suite in suites:
583
+ suite_path = output_path / f"test_{self._sanitize_name(suite.component_name)}.py"
584
+
585
+ content = self.templates.FILE_HEADER.format(
586
+ component_name=suite.component_name,
587
+ date=datetime.now().isoformat()
588
+ )
589
+ content += self.templates.FIXTURES
590
+
591
+ for test in suite.tests:
592
+ content += test.content
593
+
594
+ suite_path.write_text(content, encoding='utf-8')
595
+ logger.info(f"Generated test file: {suite_path}")
596
+
597
+ def _sanitize_name(self, name: str) -> str:
598
+ """Sanitize a name for use in test function names"""
599
+ # Remove invalid characters
600
+ sanitized = re.sub(r'[^a-zA-Z0-9_]', '_', name)
601
+ # Remove leading digits
602
+ sanitized = re.sub(r'^[0-9]+', '', sanitized)
603
+ # Convert to lowercase
604
+ sanitized = sanitized.lower()
605
+ # Remove consecutive underscores
606
+ sanitized = re.sub(r'_+', '_', sanitized)
607
+ # Remove trailing underscores
608
+ sanitized = sanitized.strip('_')
609
+ return sanitized or 'unnamed'
610
+
611
+ def get_test_coverage_estimate(
612
+ self,
613
+ project_id: int,
614
+ base_path: Optional[str] = None
615
+ ) -> Dict[str, Any]:
616
+ """
617
+ Estimate test coverage based on component analysis
618
+
619
+ Returns:
620
+ Dict with coverage estimates
621
+ """
622
+ components = self.discovery.discover_components(project_id, base_path)
623
+
624
+ total_functions = 0
625
+ total_endpoints = 0
626
+ total_queries = 0
627
+ total_components = 0
628
+
629
+ for metadata in components:
630
+ if metadata.is_valid:
631
+ total_components += 1
632
+ total_functions += len(metadata.functions)
633
+ total_endpoints += len(metadata.endpoints)
634
+ total_queries += len(metadata.queries)
635
+
636
+ # Calculate potential test counts
637
+ potential_function_tests = total_functions
638
+ potential_endpoint_tests = total_endpoints
639
+ potential_query_tests = total_queries
640
+ potential_total = potential_function_tests + potential_endpoint_tests + potential_query_tests
641
+
642
+ return {
643
+ "total_components": total_components,
644
+ "total_functions": total_functions,
645
+ "total_endpoints": total_endpoints,
646
+ "total_queries": total_queries,
647
+ "potential_tests": {
648
+ "function_tests": potential_function_tests,
649
+ "endpoint_tests": potential_endpoint_tests,
650
+ "query_tests": potential_query_tests,
651
+ "total": potential_total
652
+ },
653
+ "recommendation": self._get_test_recommendation(
654
+ total_functions, total_endpoints, total_queries
655
+ )
656
+ }
657
+
658
+ def _get_test_recommendation(
659
+ self,
660
+ functions: int,
661
+ endpoints: int,
662
+ queries: int
663
+ ) -> str:
664
+ """Get testing recommendation based on component analysis"""
665
+ if endpoints > 0:
666
+ return "Focus on API/integration tests for endpoints first"
667
+ elif functions > 0:
668
+ return "Focus on unit tests for functions"
669
+ elif queries > 0:
670
+ return "Focus on database integration tests"
671
+ else:
672
+ return "Add q:function or q:endpoint tags to components for better test coverage"
673
+
674
+ def update_test_results(
675
+ self,
676
+ component_id: int,
677
+ test_run_id: int,
678
+ test_results: List[Dict[str, Any]]
679
+ ):
680
+ """
681
+ Update component test records with results from a test run
682
+
683
+ Args:
684
+ component_id: Component ID
685
+ test_run_id: TestRun ID
686
+ test_results: List of test result dicts with name, status, duration, error
687
+ """
688
+ # Get the component
689
+ component = self.db.query(Component).filter(
690
+ Component.id == component_id
691
+ ).first()
692
+
693
+ if not component:
694
+ return
695
+
696
+ # Get all tests for this component
697
+ tests = self.db.query(ComponentTest).filter(
698
+ ComponentTest.component_id == component_id
699
+ ).all()
700
+
701
+ tests_map = {t.test_name: t for t in tests}
702
+
703
+ passed = 0
704
+ failed = 0
705
+
706
+ for result in test_results:
707
+ test_name = result.get("name")
708
+ if test_name and test_name in tests_map:
709
+ test = tests_map[test_name]
710
+ test.last_run_id = test_run_id
711
+ test.last_status = result.get("status", "unknown")
712
+ test.last_run_at = datetime.now()
713
+ test.last_duration_seconds = result.get("duration")
714
+ test.last_error_message = result.get("error")
715
+
716
+ if test.last_status == "passed":
717
+ passed += 1
718
+ elif test.last_status in ["failed", "error"]:
719
+ failed += 1
720
+
721
+ # Update component counters
722
+ component.tests_passing = passed
723
+ component.tests_failing = failed
724
+ component.last_test_run_id = test_run_id
725
+ component.last_test_run_at = datetime.now()
726
+
727
+ self.db.commit()
728
+ logger.info(f"Updated test results for component {component.name}: {passed} passed, {failed} failed")
729
+
730
+ def get_component_test_summary(
731
+ self,
732
+ component_id: int
733
+ ) -> Dict[str, Any]:
734
+ """Get test summary for a component"""
735
+ component = self.db.query(Component).filter(
736
+ Component.id == component_id
737
+ ).first()
738
+
739
+ if not component:
740
+ return {}
741
+
742
+ tests = self.db.query(ComponentTest).filter(
743
+ ComponentTest.component_id == component_id
744
+ ).all()
745
+
746
+ by_status = {"passed": 0, "failed": 0, "pending": 0, "skipped": 0, "error": 0}
747
+ by_type = {"unit": 0, "api": 0, "integration": 0, "e2e": 0}
748
+
749
+ for test in tests:
750
+ status = test.last_status or "pending"
751
+ if status in by_status:
752
+ by_status[status] += 1
753
+ if test.test_type in by_type:
754
+ by_type[test.test_type] += 1
755
+
756
+ return {
757
+ "component_id": component_id,
758
+ "component_name": component.name,
759
+ "total_tests": len(tests),
760
+ "tests_passing": component.tests_passing,
761
+ "tests_failing": component.tests_failing,
762
+ "by_status": by_status,
763
+ "by_type": by_type,
764
+ "last_run_at": component.last_test_run_at.isoformat() if component.last_test_run_at else None,
765
+ "coverage": component.test_coverage if hasattr(component, 'test_coverage') else 0
766
+ }
767
+
768
+
769
+ # Singleton instance
770
+ _test_generator = None
771
+
772
+
773
+ def get_test_generator(db: Session = None) -> TestGeneratorService:
774
+ """Get test generator service instance"""
775
+ if db:
776
+ return TestGeneratorService(db)
777
+
778
+ global _test_generator
779
+ if _test_generator is None:
780
+ from .database import SessionLocal
781
+ _test_generator = TestGeneratorService(SessionLocal())
782
+ return _test_generator