aegis-stack 0.2.0rc2__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 (392) hide show
  1. aegis/__init__.py +5 -0
  2. aegis/__main__.py +51 -0
  3. aegis/cli/__init__.py +6 -0
  4. aegis/cli/callbacks.py +114 -0
  5. aegis/cli/interactive.py +611 -0
  6. aegis/cli/utils.py +70 -0
  7. aegis/cli/validators.py +34 -0
  8. aegis/commands/__init__.py +6 -0
  9. aegis/commands/add.py +353 -0
  10. aegis/commands/add_service.py +332 -0
  11. aegis/commands/components.py +35 -0
  12. aegis/commands/init.py +370 -0
  13. aegis/commands/remove.py +227 -0
  14. aegis/commands/services.py +52 -0
  15. aegis/commands/update.py +252 -0
  16. aegis/commands/version.py +12 -0
  17. aegis/config/__init__.py +1 -0
  18. aegis/config/shared_files.py +136 -0
  19. aegis/core/CLAUDE.md +377 -0
  20. aegis/core/__init__.py +6 -0
  21. aegis/core/component_files.py +228 -0
  22. aegis/core/component_utils.py +220 -0
  23. aegis/core/components.py +127 -0
  24. aegis/core/copier_manager.py +315 -0
  25. aegis/core/copier_updater.py +475 -0
  26. aegis/core/dependency_resolver.py +119 -0
  27. aegis/core/manual_updater.py +554 -0
  28. aegis/core/post_gen_tasks.py +547 -0
  29. aegis/core/service_resolver.py +261 -0
  30. aegis/core/services.py +157 -0
  31. aegis/core/template_generator.py +266 -0
  32. aegis/core/version_compatibility.py +259 -0
  33. aegis/templates/CLAUDE.md +591 -0
  34. aegis/templates/cookiecutter-aegis-project/cookiecutter.json +39 -0
  35. aegis/templates/cookiecutter-aegis-project/hooks/post_gen_project.py +214 -0
  36. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.dockerignore +71 -0
  37. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example.j2 +130 -0
  38. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.gitignore +131 -0
  39. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Dockerfile +53 -0
  40. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Makefile +236 -0
  41. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/README.md.j2 +196 -0
  42. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/alembic.ini.j2 +111 -0
  43. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/env.py.j2 +91 -0
  44. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/script.py.mako +25 -0
  45. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/versions/001_initial_auth.py.j2 +51 -0
  46. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/__init__.py +5 -0
  47. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/__init__.py +6 -0
  48. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai.py.j2 +700 -0
  49. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai_rendering.py +361 -0
  50. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/auth.py.j2 +253 -0
  51. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/health.py.j2 +419 -0
  52. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/load_test.py.j2 +656 -0
  53. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/main.py.j2 +65 -0
  54. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/marko_terminal_renderer.py +489 -0
  55. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/tasks.py.j2 +328 -0
  56. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/{% if cookiecutter.include_scheduler == /"yes/" %}tasks.py{% endif %}" +340 -0
  57. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/__init__.py +0 -0
  58. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/__init__.py +0 -0
  59. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/__init__.py +8 -0
  60. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/router.py +329 -0
  61. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/__init__.py +1 -0
  62. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/router.py +64 -0
  63. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/deps.py +58 -0
  64. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/health.py +163 -0
  65. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/models.py.j2 +280 -0
  66. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/routing.py.j2 +32 -0
  67. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/scheduler.py.j2 +121 -0
  68. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/worker.py.j2 +478 -0
  69. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/hooks.py +144 -0
  70. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/main.py +31 -0
  71. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/__init__.py +1 -0
  72. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/cors.py +20 -0
  73. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/__init__.py +1 -0
  74. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/cleanup.py +14 -0
  75. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/__init__.py +1 -0
  76. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py.j2 +418 -0
  77. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/database_init.py.j2 +83 -0
  78. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/__init__.py +5 -0
  79. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/__init__.py +27 -0
  80. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/table.py +78 -0
  81. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/text.py +142 -0
  82. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/__init__.py.j2 +47 -0
  83. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
  84. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
  85. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/base_card.py +256 -0
  86. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
  87. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
  88. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/database_card.py +420 -0
  89. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
  90. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
  91. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
  92. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
  93. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/services_card.py +233 -0
  94. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
  95. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/main.py.j2 +653 -0
  96. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/theme.py +48 -0
  97. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/__init__.py +1 -0
  98. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/main.py.j2 +156 -0
  99. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/CLAUDE.md.j2 +213 -0
  100. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/__init__.py +6 -0
  101. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/constants.py.j2 +30 -0
  102. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/pools.py +97 -0
  103. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/__init__.py +1 -0
  104. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/load_test.py +55 -0
  105. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/media.py +49 -0
  106. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/system.py +44 -0
  107. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/registry.py +139 -0
  108. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/__init__.py +120 -0
  109. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/load_tasks.py +507 -0
  110. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/simple_system_tasks.py +33 -0
  111. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/system_tasks.py +281 -0
  112. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/config.py.j2 +178 -0
  113. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/constants.py +58 -0
  114. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/db.py.j2 +176 -0
  115. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/log.py +92 -0
  116. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/security.py +62 -0
  117. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/__init__.py +1 -0
  118. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/webserver.py +40 -0
  119. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/{% if cookiecutter.include_scheduler == /"yes/" %}scheduler.py{% endif %}" +21 -0
  120. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/__init__.py +0 -0
  121. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/main.py +62 -0
  122. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/__init__.py +1 -0
  123. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/user.py +44 -0
  124. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/py.typed +0 -0
  125. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/__init__.py +1 -0
  126. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/__init__.py +8 -0
  127. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/config.py +130 -0
  128. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/conversation.py +213 -0
  129. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/health.py +96 -0
  130. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/models.py +229 -0
  131. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/providers.py +370 -0
  132. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/service.py +388 -0
  133. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/__init__.py +1 -0
  134. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/auth_service.py +41 -0
  135. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/health.py +164 -0
  136. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/user_service.py +83 -0
  137. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/middleware_inspector.py.j2 +223 -0
  138. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/models.py.j2 +70 -0
  139. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/route_inspector.py.j2 +155 -0
  140. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test.py +679 -0
  141. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test_models.py +266 -0
  142. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/__init__.py.j2 +21 -0
  143. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/models.py.j2 +119 -0
  144. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/scheduled_task_manager.py.j2 +273 -0
  145. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/task_monitor.py.j2 +189 -0
  146. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/__init__.py +15 -0
  147. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/models.py +26 -0
  148. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/__init__.py +52 -0
  149. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/alerts.py +94 -0
  150. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/backup.py.j2 +119 -0
  151. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/health.py.j2 +1333 -0
  152. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/models.py +243 -0
  153. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/ui.py +52 -0
  154. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-dark.png +0 -0
  155. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-square-backup.png +0 -0
  156. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto.png +0 -0
  157. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.dockerignore +71 -0
  158. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.env.example.j2 +64 -0
  159. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.gitignore +131 -0
  160. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Dockerfile +53 -0
  161. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Makefile +211 -0
  162. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/README.md.j2 +172 -0
  163. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/docker-compose.yml.j2 +78 -0
  164. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/mkdocs.yml.j2 +62 -0
  165. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/pyproject.toml.j2 +120 -0
  166. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/uv.lock +1673 -0
  167. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docker-compose.yml.j2 +200 -0
  168. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/api.md +191 -0
  169. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md +0 -0
  170. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md.j2 +621 -0
  171. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/development.md +215 -0
  172. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/health.md +240 -0
  173. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/javascripts/mermaid-config.js +62 -0
  174. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/stylesheets/mermaid.css +95 -0
  175. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/mkdocs.yml.j2 +62 -0
  176. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/pyproject.toml.j2 +131 -0
  177. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh +87 -0
  178. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh.j2 +93 -0
  179. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/gen_docs.py +16 -0
  180. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/__init__.py +1 -0
  181. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_auth_endpoints.py.j2 +307 -0
  182. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_health_endpoints.py.j2 +262 -0
  183. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_scheduler_endpoints.py.j2 +214 -0
  184. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_worker_endpoints.py.j2 +165 -0
  185. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_ai_rendering.py +427 -0
  186. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_conversation_memory.py +465 -0
  187. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/components/test_scheduler.py +43 -0
  188. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/conftest.py.j2 +195 -0
  189. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/__init__.py +1 -0
  190. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/__init__.py +1 -0
  191. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/conftest.py +78 -0
  192. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_health.py +157 -0
  193. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_models.py +164 -0
  194. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_service.py +198 -0
  195. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_auth_integration.py.j2 +528 -0
  196. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_component_integration.py.j2 +387 -0
  197. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_conversation_persistence.py +342 -0
  198. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_health_logic.py.j2 +663 -0
  199. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_models.py +619 -0
  200. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_service.py +603 -0
  201. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_middleware_inspector.py.j2 +248 -0
  202. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_scheduled_task_manager.py.j2 +292 -0
  203. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_system_service.py +98 -0
  204. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_worker_health_registration.py.j2 +257 -0
  205. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/test_core.py +49 -0
  206. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/uv.lock +1673 -0
  207. aegis/templates/copier-aegis-project/{{ project_slug }}/.copier-answers.yml.jinja +21 -0
  208. aegis/templates/copier-aegis-project/{{ project_slug }}/.dockerignore +71 -0
  209. aegis/templates/copier-aegis-project/{{ project_slug }}/.env.example.jinja +130 -0
  210. aegis/templates/copier-aegis-project/{{ project_slug }}/.gitignore +131 -0
  211. aegis/templates/copier-aegis-project/{{ project_slug }}/Dockerfile +53 -0
  212. aegis/templates/copier-aegis-project/{{ project_slug }}/Makefile.jinja +236 -0
  213. aegis/templates/copier-aegis-project/{{ project_slug }}/README.md.jinja +196 -0
  214. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/alembic.ini.jinja +111 -0
  215. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/env.py.jinja +91 -0
  216. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/script.py.mako +25 -0
  217. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/versions/001_initial_auth.py.jinja +51 -0
  218. aegis/templates/copier-aegis-project/{{ project_slug }}/app/__init__.py.jinja +5 -0
  219. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/__init__.py.jinja +6 -0
  220. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai.py.jinja +700 -0
  221. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai_rendering.py +360 -0
  222. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/auth.py.jinja +253 -0
  223. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/health.py.jinja +419 -0
  224. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/load_test.py.jinja +656 -0
  225. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/main.py.jinja +65 -0
  226. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/marko_terminal_renderer.py +489 -0
  227. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/tasks.py.jinja +328 -0
  228. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/__init__.py +0 -0
  229. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/__init__.py +0 -0
  230. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/__init__.py +8 -0
  231. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/router.py +329 -0
  232. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/__init__.py +1 -0
  233. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/router.py +64 -0
  234. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/deps.py +58 -0
  235. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/health.py.jinja +163 -0
  236. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/models.py.jinja +280 -0
  237. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/routing.py.jinja +32 -0
  238. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/scheduler.py.jinja +121 -0
  239. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/worker.py.jinja +478 -0
  240. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/hooks.py +144 -0
  241. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/main.py +31 -0
  242. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/__init__.py +1 -0
  243. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/cors.py +20 -0
  244. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/__init__.py +1 -0
  245. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/cleanup.py +14 -0
  246. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/__init__.py +1 -0
  247. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/component_health.py.jinja +418 -0
  248. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/database_init.py.jinja +83 -0
  249. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/__init__.py +5 -0
  250. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/__init__.py +27 -0
  251. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/table.py +78 -0
  252. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/text.py +142 -0
  253. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/__init__.py.jinja +47 -0
  254. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
  255. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
  256. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/base_card.py +256 -0
  257. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
  258. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
  259. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/database_card.py +420 -0
  260. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
  261. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
  262. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
  263. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
  264. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/services_card.py +233 -0
  265. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
  266. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/main.py.jinja +653 -0
  267. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/theme.py +48 -0
  268. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/__init__.py +1 -0
  269. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/main.py.jinja +156 -0
  270. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/CLAUDE.md.jinja +213 -0
  271. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/__init__.py +6 -0
  272. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/constants.py.jinja +30 -0
  273. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/pools.py +97 -0
  274. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/__init__.py +1 -0
  275. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/load_test.py +55 -0
  276. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/media.py +49 -0
  277. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/system.py +44 -0
  278. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/registry.py +139 -0
  279. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/__init__.py +120 -0
  280. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/load_tasks.py +507 -0
  281. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/simple_system_tasks.py +33 -0
  282. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/system_tasks.py +281 -0
  283. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/config.py.jinja +178 -0
  284. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/constants.py +58 -0
  285. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/db.py.jinja +176 -0
  286. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/log.py +92 -0
  287. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/security.py +62 -0
  288. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/__init__.py +1 -0
  289. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/scheduler.py.jinja +21 -0
  290. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/webserver.py +39 -0
  291. aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/__init__.py +0 -0
  292. aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/main.py +61 -0
  293. aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/__init__.py +1 -0
  294. aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/user.py +44 -0
  295. aegis/templates/copier-aegis-project/{{ project_slug }}/app/py.typed +0 -0
  296. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/__init__.py +1 -0
  297. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/__init__.py +8 -0
  298. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/config.py +130 -0
  299. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/conversation.py +213 -0
  300. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/health.py +96 -0
  301. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/models.py +229 -0
  302. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/providers.py.jinja +370 -0
  303. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/service.py +387 -0
  304. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/__init__.py +1 -0
  305. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/auth_service.py +40 -0
  306. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/health.py +162 -0
  307. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/user_service.py +82 -0
  308. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/middleware_inspector.py.jinja +223 -0
  309. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/models.py.jinja +70 -0
  310. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/route_inspector.py.jinja +155 -0
  311. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test.py +678 -0
  312. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test_models.py +265 -0
  313. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/__init__.py.jinja +21 -0
  314. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/models.py.jinja +119 -0
  315. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/scheduled_task_manager.py.jinja +273 -0
  316. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/task_monitor.py.jinja +189 -0
  317. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/__init__.py +15 -0
  318. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/models.py +26 -0
  319. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/__init__.py +52 -0
  320. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/alerts.py +94 -0
  321. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/backup.py.jinja +119 -0
  322. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/health.py.jinja +1333 -0
  323. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/models.py +243 -0
  324. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/ui.py +52 -0
  325. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57223!aegis-manifesto.png +0 -0
  326. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57224!aegis-manifesto-dark.png +0 -0
  327. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57225!aegis-manifesto-square-backup.png +0 -0
  328. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57533!aegis-manifesto.png +0 -0
  329. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57534!aegis-manifesto-dark.png +0 -0
  330. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57538!aegis-manifesto-square-backup.png +0 -0
  331. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57897!aegis-manifesto.png +0 -0
  332. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57898!aegis-manifesto-dark.png +0 -0
  333. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57904!aegis-manifesto-square-backup.png +0 -0
  334. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58315!aegis-manifesto.png +0 -0
  335. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58316!aegis-manifesto-dark.png +0 -0
  336. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58324!aegis-manifesto-square-backup.png +0 -0
  337. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58837!aegis-manifesto.png +0 -0
  338. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58838!aegis-manifesto-dark.png +0 -0
  339. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58849!aegis-manifesto-square-backup.png +0 -0
  340. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-dark.png +0 -0
  341. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-square-backup.png +0 -0
  342. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto.png +0 -0
  343. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/.env.example.jinja +64 -0
  344. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/README.md.jinja +172 -0
  345. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/docker-compose.yml.jinja +78 -0
  346. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/mkdocs.yml.jinja +62 -0
  347. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/pyproject.toml.jinja +120 -0
  348. aegis/templates/copier-aegis-project/{{ project_slug }}/docker-compose.yml.jinja +200 -0
  349. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/api.md.jinja +191 -0
  350. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md +0 -0
  351. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md.jinja +621 -0
  352. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/development.md.jinja +215 -0
  353. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/health.md.jinja +240 -0
  354. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/javascripts/mermaid-config.js +62 -0
  355. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/stylesheets/mermaid.css +95 -0
  356. aegis/templates/copier-aegis-project/{{ project_slug }}/mkdocs.yml.jinja +62 -0
  357. aegis/templates/copier-aegis-project/{{ project_slug }}/pyproject.toml.jinja +131 -0
  358. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh +87 -0
  359. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh.jinja +93 -0
  360. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/gen_docs.py +16 -0
  361. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/__init__.py +1 -0
  362. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_auth_endpoints.py.jinja +307 -0
  363. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_health_endpoints.py.jinja +262 -0
  364. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_scheduler_endpoints.py.jinja +214 -0
  365. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_worker_endpoints.py.jinja +165 -0
  366. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_ai_rendering.py +427 -0
  367. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_conversation_memory.py +465 -0
  368. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/components/test_scheduler.py +43 -0
  369. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/conftest.py.jinja +195 -0
  370. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/__init__.py +1 -0
  371. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/__init__.py +1 -0
  372. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/conftest.py +78 -0
  373. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_health.py +157 -0
  374. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_models.py +164 -0
  375. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_service.py +198 -0
  376. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_auth_integration.py.jinja +528 -0
  377. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_component_integration.py.jinja +387 -0
  378. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_conversation_persistence.py +342 -0
  379. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_health_logic.py.jinja +663 -0
  380. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_models.py +619 -0
  381. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_service.py +603 -0
  382. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_middleware_inspector.py.jinja +248 -0
  383. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_scheduled_task_manager.py.jinja +292 -0
  384. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_system_service.py +98 -0
  385. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_worker_health_registration.py.jinja +257 -0
  386. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/test_core.py +49 -0
  387. aegis/templates/copier-aegis-project/{{ project_slug }}/uv.lock +1673 -0
  388. aegis_stack-0.2.0rc2.dist-info/METADATA +165 -0
  389. aegis_stack-0.2.0rc2.dist-info/RECORD +392 -0
  390. aegis_stack-0.2.0rc2.dist-info/WHEEL +4 -0
  391. aegis_stack-0.2.0rc2.dist-info/entry_points.txt +3 -0
  392. aegis_stack-0.2.0rc2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,619 @@
1
+ """
2
+ Unit tests for load test Pydantic models.
3
+
4
+ Tests validation, constraints, and data transformation for all load test models.
5
+ """
6
+
7
+ import pytest
8
+ from app.components.worker.constants import LoadTestTypes
9
+ from app.services.load_test_models import (
10
+ LoadTestConfiguration,
11
+ LoadTestMetrics,
12
+ LoadTestResult,
13
+ OrchestratorRawResult,
14
+ PerformanceAnalysis,
15
+ ValidationStatus,
16
+ )
17
+ from app.services.load_test_models import (
18
+ LoadTestErrorModel as LoadTestError,
19
+ )
20
+ from pydantic import ValidationError
21
+
22
+
23
+ class TestLoadTestConfiguration:
24
+ """Test LoadTestConfiguration model validation."""
25
+
26
+ def test_valid_configuration(self):
27
+ """Test creating valid configuration."""
28
+ config = LoadTestConfiguration(
29
+ task_type=LoadTestTypes.CPU_INTENSIVE,
30
+ num_tasks=100,
31
+ batch_size=10,
32
+ delay_ms=50,
33
+ target_queue="load_test",
34
+ )
35
+
36
+ assert config.task_type == LoadTestTypes.CPU_INTENSIVE
37
+ assert config.num_tasks == 100
38
+ assert config.batch_size == 10
39
+ assert config.delay_ms == 50
40
+ assert config.target_queue == "load_test"
41
+
42
+ def test_num_tasks_validation(self):
43
+ """Test num_tasks constraints."""
44
+ # Valid range
45
+ config = LoadTestConfiguration(
46
+ task_type=LoadTestTypes.CPU_INTENSIVE,
47
+ num_tasks=50,
48
+ batch_size=10,
49
+ target_queue="load_test",
50
+ )
51
+ assert config.num_tasks == 50
52
+
53
+ # Too low
54
+ with pytest.raises(ValidationError, match="greater than or equal to 10"):
55
+ LoadTestConfiguration(
56
+ task_type=LoadTestTypes.CPU_INTENSIVE,
57
+ num_tasks=5,
58
+ batch_size=10,
59
+ target_queue="load_test",
60
+ )
61
+
62
+ # Too high
63
+ with pytest.raises(ValidationError, match="less than or equal to 10000"):
64
+ LoadTestConfiguration(
65
+ task_type=LoadTestTypes.CPU_INTENSIVE,
66
+ num_tasks=20000,
67
+ batch_size=10,
68
+ target_queue="load_test",
69
+ )
70
+
71
+ def test_batch_size_validation(self):
72
+ """Test batch_size constraints."""
73
+ # Valid range
74
+ config = LoadTestConfiguration(
75
+ task_type=LoadTestTypes.CPU_INTENSIVE,
76
+ num_tasks=100,
77
+ batch_size=25,
78
+ target_queue="load_test",
79
+ )
80
+ assert config.batch_size == 25
81
+
82
+ # Too low
83
+ with pytest.raises(ValidationError, match="greater than or equal to 1"):
84
+ LoadTestConfiguration(
85
+ task_type=LoadTestTypes.CPU_INTENSIVE,
86
+ num_tasks=100,
87
+ batch_size=0,
88
+ target_queue="load_test",
89
+ )
90
+
91
+ # Too high
92
+ with pytest.raises(ValidationError, match="less than or equal to 100"):
93
+ LoadTestConfiguration(
94
+ task_type=LoadTestTypes.CPU_INTENSIVE,
95
+ num_tasks=100,
96
+ batch_size=150,
97
+ target_queue="load_test",
98
+ )
99
+
100
+ def test_delay_ms_validation(self):
101
+ """Test delay_ms constraints."""
102
+ # Valid range
103
+ config = LoadTestConfiguration(
104
+ task_type=LoadTestTypes.CPU_INTENSIVE,
105
+ num_tasks=100,
106
+ batch_size=10,
107
+ delay_ms=1000,
108
+ target_queue="load_test",
109
+ )
110
+ assert config.delay_ms == 1000
111
+
112
+ # Too low (negative)
113
+ with pytest.raises(ValidationError, match="greater than or equal to 0"):
114
+ LoadTestConfiguration(
115
+ task_type=LoadTestTypes.CPU_INTENSIVE,
116
+ num_tasks=100,
117
+ batch_size=10,
118
+ delay_ms=-100,
119
+ target_queue="load_test",
120
+ )
121
+
122
+ # Too high
123
+ with pytest.raises(ValidationError, match="less than or equal to 5000"):
124
+ LoadTestConfiguration(
125
+ task_type=LoadTestTypes.CPU_INTENSIVE,
126
+ num_tasks=100,
127
+ batch_size=10,
128
+ delay_ms=10000,
129
+ target_queue="load_test",
130
+ )
131
+
132
+
133
+ class TestLoadTestMetrics:
134
+ """Test LoadTestMetrics model validation."""
135
+
136
+ def test_valid_metrics(self):
137
+ """Test creating valid metrics."""
138
+ metrics = LoadTestMetrics(
139
+ tasks_sent=100,
140
+ tasks_completed=95,
141
+ tasks_failed=5,
142
+ total_duration_seconds=30.5,
143
+ overall_throughput=3.1,
144
+ failure_rate_percent=5.0,
145
+ )
146
+
147
+ assert metrics.tasks_sent == 100
148
+ assert metrics.tasks_completed == 95
149
+ assert metrics.tasks_failed == 5
150
+ assert metrics.total_duration_seconds == 30.5
151
+ assert metrics.overall_throughput == 3.1
152
+ assert metrics.failure_rate_percent == 5.0
153
+
154
+ def test_completed_not_exceed_sent_validator(self):
155
+ """Test that completed tasks cannot exceed sent tasks."""
156
+ # Valid case
157
+ metrics = LoadTestMetrics(
158
+ tasks_sent=100, tasks_completed=90, total_duration_seconds=30.0
159
+ )
160
+ assert metrics.tasks_completed == 90
161
+
162
+ # Invalid case - more completed than sent
163
+ with pytest.raises(
164
+ ValidationError,
165
+ match="Completed tasks \\(150\\) cannot exceed sent tasks \\(100\\)",
166
+ ):
167
+ LoadTestMetrics(
168
+ tasks_sent=100, tasks_completed=150, total_duration_seconds=30.0
169
+ )
170
+
171
+ def test_failure_rate_consistency_validator(self):
172
+ """Test that failure rate matches task counts."""
173
+ # Valid case - 10 failed out of 100 = 10%
174
+ metrics = LoadTestMetrics(
175
+ tasks_sent=100,
176
+ tasks_completed=90,
177
+ tasks_failed=10,
178
+ total_duration_seconds=30.0,
179
+ failure_rate_percent=10.0,
180
+ )
181
+ assert metrics.failure_rate_percent == 10.0
182
+
183
+ # Invalid case - mismatch between counts and percentage
184
+ with pytest.raises(
185
+ ValidationError, match="Failure rate 50.0% doesn't match task counts"
186
+ ):
187
+ LoadTestMetrics(
188
+ tasks_sent=100,
189
+ tasks_completed=90,
190
+ tasks_failed=10, # Should be 10%, not 50%
191
+ total_duration_seconds=30.0,
192
+ failure_rate_percent=50.0,
193
+ )
194
+
195
+ def test_negative_values_rejected(self):
196
+ """Test that negative values are rejected."""
197
+ with pytest.raises(ValidationError, match="greater than or equal to 0"):
198
+ LoadTestMetrics(
199
+ tasks_sent=-10, tasks_completed=0, total_duration_seconds=30.0
200
+ )
201
+
202
+ with pytest.raises(ValidationError, match="greater than or equal to 0"):
203
+ LoadTestMetrics(
204
+ tasks_sent=100, tasks_completed=-5, total_duration_seconds=30.0
205
+ )
206
+
207
+ def test_percentage_bounds(self):
208
+ """Test percentage fields are within valid ranges."""
209
+ # Valid percentages
210
+ metrics = LoadTestMetrics(
211
+ tasks_sent=100,
212
+ tasks_completed=100,
213
+ total_duration_seconds=30.0,
214
+ failure_rate_percent=0.0,
215
+ completion_percentage=100.0,
216
+ )
217
+ assert metrics.failure_rate_percent == 0.0
218
+ assert metrics.completion_percentage == 100.0
219
+
220
+ # Invalid percentage - over 100%
221
+ with pytest.raises(ValidationError, match="less than or equal to 100"):
222
+ LoadTestMetrics(
223
+ tasks_sent=100,
224
+ tasks_completed=100,
225
+ total_duration_seconds=30.0,
226
+ failure_rate_percent=150.0,
227
+ )
228
+
229
+
230
+ class TestPerformanceAnalysis:
231
+ """Test PerformanceAnalysis model validation."""
232
+
233
+ def test_valid_ratings(self):
234
+ """Test valid rating values."""
235
+ analysis = PerformanceAnalysis(
236
+ throughput_rating="excellent",
237
+ efficiency_rating="good",
238
+ queue_pressure="low",
239
+ )
240
+
241
+ assert analysis.throughput_rating == "excellent"
242
+ assert analysis.efficiency_rating == "good"
243
+ assert analysis.queue_pressure == "low"
244
+
245
+ def test_invalid_rating_values(self):
246
+ """Test that invalid rating values are rejected."""
247
+ with pytest.raises(ValidationError, match="String should match pattern"):
248
+ PerformanceAnalysis(
249
+ throughput_rating="amazing", # Not in allowed values
250
+ efficiency_rating="good",
251
+ queue_pressure="low",
252
+ )
253
+
254
+ with pytest.raises(ValidationError, match="String should match pattern"):
255
+ PerformanceAnalysis(
256
+ throughput_rating="excellent",
257
+ efficiency_rating="terrible", # Not in allowed values
258
+ queue_pressure="low",
259
+ )
260
+
261
+ def test_all_valid_rating_combinations(self):
262
+ """Test all valid rating value combinations."""
263
+ valid_ratings = ["unknown", "poor", "fair", "good", "excellent"]
264
+ valid_pressures = ["unknown", "low", "medium", "high"]
265
+
266
+ for throughput in valid_ratings:
267
+ for efficiency in valid_ratings:
268
+ for pressure in valid_pressures:
269
+ analysis = PerformanceAnalysis(
270
+ throughput_rating=throughput,
271
+ efficiency_rating=efficiency,
272
+ queue_pressure=pressure,
273
+ )
274
+ assert analysis.throughput_rating == throughput
275
+ assert analysis.efficiency_rating == efficiency
276
+ assert analysis.queue_pressure == pressure
277
+
278
+
279
+ class TestValidationStatus:
280
+ """Test ValidationStatus model validation."""
281
+
282
+ def test_valid_status(self):
283
+ """Test creating valid validation status."""
284
+ status = ValidationStatus(
285
+ test_type_verified=True,
286
+ expected_metrics_present=True,
287
+ performance_signature_match="verified",
288
+ issues=["Some issue"],
289
+ )
290
+
291
+ assert status.test_type_verified is True
292
+ assert status.expected_metrics_present is True
293
+ assert status.performance_signature_match == "verified"
294
+ assert status.issues == ["Some issue"]
295
+
296
+ def test_default_values(self):
297
+ """Test default values for optional fields."""
298
+ status = ValidationStatus()
299
+
300
+ assert status.test_type_verified is False
301
+ assert status.expected_metrics_present is False
302
+ assert status.performance_signature_match == "unknown"
303
+ assert status.issues == []
304
+
305
+ def test_invalid_signature_match_values(self):
306
+ """Test that invalid signature match values are rejected."""
307
+ with pytest.raises(ValidationError, match="String should match pattern"):
308
+ ValidationStatus(
309
+ performance_signature_match="definitely" # Not in allowed values
310
+ )
311
+
312
+
313
+ class TestLoadTestResult:
314
+ """Test LoadTestResult model validation."""
315
+
316
+ def test_valid_result(self):
317
+ """Test creating valid load test result."""
318
+ config = LoadTestConfiguration(
319
+ task_type=LoadTestTypes.CPU_INTENSIVE,
320
+ num_tasks=100,
321
+ batch_size=10,
322
+ target_queue="load_test",
323
+ )
324
+
325
+ metrics = LoadTestMetrics(
326
+ tasks_sent=100, tasks_completed=100, total_duration_seconds=30.0
327
+ )
328
+
329
+ result = LoadTestResult(
330
+ status="completed",
331
+ test_id="test-123",
332
+ configuration=config,
333
+ metrics=metrics,
334
+ )
335
+
336
+ assert result.status == "completed"
337
+ assert result.test_id == "test-123"
338
+ assert result.task == "load_test_orchestrator" # Default value
339
+ assert result.configuration.task_type == LoadTestTypes.CPU_INTENSIVE
340
+ assert result.metrics.tasks_sent == 100
341
+
342
+ def test_invalid_status_values(self):
343
+ """Test that invalid status values are rejected."""
344
+ config = LoadTestConfiguration(
345
+ task_type=LoadTestTypes.CPU_INTENSIVE,
346
+ num_tasks=100,
347
+ batch_size=10,
348
+ target_queue="load_test",
349
+ )
350
+
351
+ metrics = LoadTestMetrics(
352
+ tasks_sent=100, tasks_completed=100, total_duration_seconds=30.0
353
+ )
354
+
355
+ with pytest.raises(ValidationError, match="String should match pattern"):
356
+ LoadTestResult(
357
+ status="running", # Not in allowed values
358
+ test_id="test-123",
359
+ configuration=config,
360
+ metrics=metrics,
361
+ )
362
+
363
+ def test_status_consistency_validator(self):
364
+ """Test status consistency validation."""
365
+ config = LoadTestConfiguration(
366
+ task_type=LoadTestTypes.CPU_INTENSIVE,
367
+ num_tasks=100,
368
+ batch_size=10,
369
+ target_queue="load_test",
370
+ )
371
+
372
+ # Failed status without error should be rejected
373
+ metrics = LoadTestMetrics(
374
+ tasks_sent=100, tasks_completed=0, total_duration_seconds=30.0
375
+ )
376
+
377
+ with pytest.raises(
378
+ ValidationError, match="Failed status requires error message"
379
+ ):
380
+ LoadTestResult(
381
+ status="failed",
382
+ test_id="test-123",
383
+ configuration=config,
384
+ metrics=metrics,
385
+ # Missing error field
386
+ )
387
+
388
+
389
+ class TestOrchestratorRawResult:
390
+ """Test OrchestratorRawResult model and transformation."""
391
+
392
+ def test_valid_raw_result(self):
393
+ """Test creating valid orchestrator raw result."""
394
+ raw_result = OrchestratorRawResult(
395
+ test_id="test-123",
396
+ task_type=LoadTestTypes.CPU_INTENSIVE.value,
397
+ tasks_sent=100,
398
+ tasks_completed=95,
399
+ tasks_failed=5,
400
+ total_duration_seconds=30.5,
401
+ overall_throughput_per_second=3.1,
402
+ failure_rate_percent=5.0,
403
+ completion_percentage=95.0,
404
+ average_throughput_per_second=3.1,
405
+ monitor_duration_seconds=30.0,
406
+ batch_size=10,
407
+ target_queue="load_test",
408
+ )
409
+
410
+ assert raw_result.test_id == "test-123"
411
+ assert raw_result.task_type == LoadTestTypes.CPU_INTENSIVE.value
412
+ assert raw_result.tasks_sent == 100
413
+ assert raw_result.tasks_completed == 95
414
+
415
+ def test_to_load_test_result_transformation(self):
416
+ """Test transformation from raw result to LoadTestResult."""
417
+ raw_result = OrchestratorRawResult(
418
+ test_id="test-123",
419
+ task_type="io_simulation",
420
+ tasks_sent=50,
421
+ tasks_completed=48,
422
+ tasks_failed=2,
423
+ total_duration_seconds=15.5,
424
+ overall_throughput_per_second=3.1,
425
+ failure_rate_percent=4.0,
426
+ completion_percentage=96.0,
427
+ average_throughput_per_second=3.1,
428
+ monitor_duration_seconds=15.0,
429
+ batch_size=5,
430
+ target_queue="load_test",
431
+ start_time="2023-01-01T10:00:00",
432
+ end_time="2023-01-01T10:00:15",
433
+ task_ids=["task1", "task2", "task3"],
434
+ )
435
+
436
+ result = raw_result.to_load_test_result()
437
+
438
+ # Check main fields
439
+ assert result.status == "completed"
440
+ assert result.test_id == "test-123"
441
+ assert result.task == "load_test_orchestrator"
442
+
443
+ # Check configuration transformation
444
+ assert result.configuration.task_type == LoadTestTypes.IO_SIMULATION
445
+ assert result.configuration.num_tasks == 50
446
+ assert result.configuration.batch_size == 5
447
+ assert result.configuration.target_queue == "load_test"
448
+
449
+ # Check metrics transformation
450
+ assert result.metrics.tasks_sent == 50
451
+ assert result.metrics.tasks_completed == 48
452
+ assert result.metrics.tasks_failed == 2
453
+ assert result.metrics.total_duration_seconds == 15.5
454
+ assert result.metrics.overall_throughput == 3.1
455
+ assert result.metrics.failure_rate_percent == 4.0
456
+
457
+ # Check optional fields
458
+ assert result.start_time == "2023-01-01T10:00:00"
459
+ assert result.end_time == "2023-01-01T10:00:15"
460
+ assert result.task_ids == ["task1", "task2", "task3"]
461
+
462
+ def test_transformation_with_minimal_data(self):
463
+ """Test transformation with only required fields."""
464
+ raw_result = OrchestratorRawResult(
465
+ test_id="minimal-test",
466
+ task_type="memory_operations",
467
+ tasks_sent=10,
468
+ tasks_completed=10,
469
+ total_duration_seconds=5.0,
470
+ batch_size=10,
471
+ target_queue="system",
472
+ )
473
+
474
+ result = raw_result.to_load_test_result()
475
+
476
+ assert result.test_id == "minimal-test"
477
+ assert result.configuration.task_type == LoadTestTypes.MEMORY_OPERATIONS
478
+ assert result.metrics.tasks_sent == 10
479
+ assert result.metrics.tasks_completed == 10
480
+ assert result.metrics.tasks_failed == 0 # Default value
481
+ assert result.start_time is None
482
+ assert result.end_time is None
483
+ assert result.task_ids == []
484
+
485
+
486
+ class TestLoadTestError:
487
+ """Test LoadTestError model validation."""
488
+
489
+ def test_valid_error(self):
490
+ """Test creating valid load test error."""
491
+ error = LoadTestError(
492
+ status="failed",
493
+ test_id="error-test-123",
494
+ error="Task execution timeout",
495
+ partial_info="Some tasks may have completed",
496
+ tasks_sent=100,
497
+ )
498
+
499
+ assert error.task == "load_test_orchestrator" # Default
500
+ assert error.status == "failed"
501
+ assert error.test_id == "error-test-123"
502
+ assert error.error == "Task execution timeout"
503
+ assert error.partial_info == "Some tasks may have completed"
504
+ assert error.tasks_sent == 100
505
+
506
+ def test_invalid_status_values(self):
507
+ """Test that invalid status values are rejected."""
508
+ with pytest.raises(ValidationError, match="String should match pattern"):
509
+ LoadTestError(
510
+ status="completed", # Should only be failed or timed_out
511
+ test_id="error-test-123",
512
+ error="Some error",
513
+ )
514
+
515
+ def test_required_fields(self):
516
+ """Test that required fields are validated."""
517
+ # Missing test_id
518
+ with pytest.raises(ValidationError, match="Field required"):
519
+ LoadTestError(status="failed", error="Some error")
520
+
521
+ # Missing error
522
+ with pytest.raises(ValidationError, match="Field required"):
523
+ LoadTestError(status="timed_out", test_id="error-test-123")
524
+
525
+
526
+ # Integration test for real-world data shapes
527
+ class TestRealWorldDataShapes:
528
+ """Test models with real-world data patterns."""
529
+
530
+ def test_typical_successful_load_test_flow(self):
531
+ """Test the complete flow with typical successful data."""
532
+ # Raw orchestrator result (what comes from Redis)
533
+ raw_data = {
534
+ "test_id": "6273dc3c0a87424e93318244e1baf73b",
535
+ "task_type": "io_simulation",
536
+ "tasks_sent": 10,
537
+ "tasks_completed": 10,
538
+ "tasks_failed": 0,
539
+ "total_duration_seconds": 2.02,
540
+ "overall_throughput_per_second": 4.96,
541
+ "failure_rate_percent": 0.0,
542
+ "completion_percentage": 100.0,
543
+ "average_throughput_per_second": 4.98,
544
+ "monitor_duration_seconds": 2.01,
545
+ "batch_size": 10,
546
+ "delay_ms": 0,
547
+ "target_queue": "load_test",
548
+ "start_time": "2025-08-16T16:07:46.080128",
549
+ "end_time": "2025-08-16T16:07:48.097005",
550
+ "task_ids": [
551
+ "ba4c043531c645f8956616eb60df1cc4",
552
+ "8669123b761c4284a0423ccaa362e0b8",
553
+ ],
554
+ }
555
+
556
+ # Validate raw result
557
+ orchestrator_result = OrchestratorRawResult(**raw_data)
558
+ assert orchestrator_result.test_id == "6273dc3c0a87424e93318244e1baf73b"
559
+
560
+ # Transform to standard result
561
+ load_test_result = orchestrator_result.to_load_test_result()
562
+ assert load_test_result.status == "completed"
563
+ assert load_test_result.metrics.tasks_completed == 10
564
+ assert load_test_result.metrics.failure_rate_percent == 0.0
565
+
566
+ # This validates that our Pydantic models handle real Redis data correctly
567
+
568
+ def test_partial_failure_scenario(self):
569
+ """Test handling of partial failures."""
570
+ raw_data = {
571
+ "test_id": "partial-fail-test",
572
+ "task_type": "cpu_intensive",
573
+ "tasks_sent": 100,
574
+ "tasks_completed": 85,
575
+ "tasks_failed": 15,
576
+ "total_duration_seconds": 45.0,
577
+ "overall_throughput_per_second": 1.89,
578
+ "failure_rate_percent": 15.0,
579
+ "completion_percentage": 85.0,
580
+ "average_throughput_per_second": 1.89,
581
+ "monitor_duration_seconds": 45.0,
582
+ "batch_size": 20,
583
+ "delay_ms": 100,
584
+ "target_queue": "system",
585
+ }
586
+
587
+ # Should validate successfully despite failures
588
+ orchestrator_result = OrchestratorRawResult(**raw_data)
589
+ load_test_result = orchestrator_result.to_load_test_result()
590
+
591
+ assert load_test_result.metrics.tasks_completed == 85
592
+ assert load_test_result.metrics.tasks_failed == 15
593
+ assert load_test_result.metrics.failure_rate_percent == 15.0
594
+
595
+ def test_edge_case_minimum_values(self):
596
+ """Test edge cases with minimum allowed values."""
597
+ raw_data = {
598
+ "test_id": "minimal",
599
+ "task_type": "cpu_intensive",
600
+ "tasks_sent": 10, # Minimum allowed
601
+ "tasks_completed": 1,
602
+ "tasks_failed": 9,
603
+ "total_duration_seconds": 0.1,
604
+ "overall_throughput_per_second": 10.0,
605
+ "failure_rate_percent": 90.0,
606
+ "completion_percentage": 10.0,
607
+ "average_throughput_per_second": 10.0,
608
+ "monitor_duration_seconds": 0.1,
609
+ "batch_size": 1, # Minimum allowed
610
+ "delay_ms": 0,
611
+ "target_queue": "load_test",
612
+ }
613
+
614
+ orchestrator_result = OrchestratorRawResult(**raw_data)
615
+ load_test_result = orchestrator_result.to_load_test_result()
616
+
617
+ assert load_test_result.configuration.num_tasks == 10
618
+ assert load_test_result.configuration.batch_size == 1
619
+ assert load_test_result.metrics.failure_rate_percent == 90.0