fastapi-fullstack 0.1.2__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 (221) hide show
  1. fastapi_fullstack-0.1.2.dist-info/METADATA +545 -0
  2. fastapi_fullstack-0.1.2.dist-info/RECORD +221 -0
  3. fastapi_fullstack-0.1.2.dist-info/WHEEL +4 -0
  4. fastapi_fullstack-0.1.2.dist-info/entry_points.txt +2 -0
  5. fastapi_fullstack-0.1.2.dist-info/licenses/LICENSE +21 -0
  6. fastapi_gen/__init__.py +3 -0
  7. fastapi_gen/cli.py +256 -0
  8. fastapi_gen/config.py +255 -0
  9. fastapi_gen/generator.py +181 -0
  10. fastapi_gen/prompts.py +648 -0
  11. fastapi_gen/template/cookiecutter.json +76 -0
  12. fastapi_gen/template/hooks/post_gen_project.py +111 -0
  13. fastapi_gen/template/{{cookiecutter.project_slug}}/.env.example +136 -0
  14. fastapi_gen/template/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +150 -0
  15. fastapi_gen/template/{{cookiecutter.project_slug}}/.gitignore +108 -0
  16. fastapi_gen/template/{{cookiecutter.project_slug}}/CLAUDE.md +357 -0
  17. fastapi_gen/template/{{cookiecutter.project_slug}}/Makefile +298 -0
  18. fastapi_gen/template/{{cookiecutter.project_slug}}/README.md +723 -0
  19. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.dockerignore +60 -0
  20. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml +32 -0
  21. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/Dockerfile +56 -0
  22. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/env.py +76 -0
  23. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako +30 -0
  24. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep +0 -0
  25. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic.ini +48 -0
  26. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/__init__.py +3 -0
  27. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/admin.py +115 -0
  28. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py +13 -0
  29. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py +202 -0
  30. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/__init__.py +13 -0
  31. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/datetime_tool.py +17 -0
  32. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py +1 -0
  33. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +528 -0
  34. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py +85 -0
  35. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/router.py +10 -0
  36. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py +9 -0
  37. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py +87 -0
  38. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py +448 -0
  39. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +395 -0
  40. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/conversations.py +490 -0
  41. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py +227 -0
  42. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/items.py +275 -0
  43. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py +205 -0
  44. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/sessions.py +168 -0
  45. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/users.py +333 -0
  46. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/webhooks.py +477 -0
  47. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/ws.py +46 -0
  48. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +221 -0
  49. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/__init__.py +14 -0
  50. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/redis.py +88 -0
  51. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/__init__.py +117 -0
  52. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/cleanup.py +75 -0
  53. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/example.py +28 -0
  54. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/seed.py +266 -0
  55. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/__init__.py +5 -0
  56. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py +23 -0
  57. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/config.py +247 -0
  58. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +153 -0
  59. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py +122 -0
  60. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py +101 -0
  61. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py +99 -0
  62. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py +23 -0
  63. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +58 -0
  64. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py +271 -0
  65. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +102 -0
  66. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py +7 -0
  67. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/base.py +41 -0
  68. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/__init__.py +31 -0
  69. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/conversation.py +319 -0
  70. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/item.py +96 -0
  71. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/session.py +126 -0
  72. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +218 -0
  73. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py +244 -0
  74. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/session.py +113 -0
  75. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/main.py +326 -0
  76. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/__init__.py +9 -0
  77. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/base.py +73 -0
  78. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py +49 -0
  79. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/base.py +154 -0
  80. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/conversation.py +760 -0
  81. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/item.py +222 -0
  82. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/session.py +318 -0
  83. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/user.py +322 -0
  84. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/webhook.py +358 -0
  85. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/__init__.py +50 -0
  86. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/base.py +57 -0
  87. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/conversation.py +195 -0
  88. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/item.py +52 -0
  89. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/session.py +42 -0
  90. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py +31 -0
  91. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py +64 -0
  92. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +89 -0
  93. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py +38 -0
  94. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/conversation.py +797 -0
  95. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/item.py +246 -0
  96. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/session.py +333 -0
  97. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +432 -0
  98. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py +561 -0
  99. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py +5 -0
  100. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py +64 -0
  101. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/taskiq_app.py +38 -0
  102. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/__init__.py +25 -0
  103. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/examples.py +106 -0
  104. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/schedules.py +29 -0
  105. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/taskiq_examples.py +92 -0
  106. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py +1 -0
  107. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/commands.py +438 -0
  108. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +158 -0
  109. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep +0 -0
  110. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py +1 -0
  111. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py +1 -0
  112. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +242 -0
  113. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +151 -0
  114. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py +113 -0
  115. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_items.py +310 -0
  116. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py +253 -0
  117. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +151 -0
  118. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py +121 -0
  119. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py +183 -0
  120. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py +173 -0
  121. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +143 -0
  122. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_pipelines.py +118 -0
  123. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py +181 -0
  124. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +124 -0
  125. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +363 -0
  126. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_worker.py +85 -0
  127. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.dev.yml +242 -0
  128. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.frontend.yml +31 -0
  129. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.prod.yml +382 -0
  130. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.yml +241 -0
  131. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.env.example +12 -0
  132. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.gitignore +45 -0
  133. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierignore +19 -0
  134. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierrc +11 -0
  135. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/Dockerfile +44 -0
  136. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/README.md +693 -0
  137. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.setup.ts +49 -0
  138. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.spec.ts +134 -0
  139. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/chat.spec.ts +207 -0
  140. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/home.spec.ts +73 -0
  141. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/instrumentation.ts +14 -0
  142. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/en.json +84 -0
  143. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/pl.json +84 -0
  144. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/next.config.ts +76 -0
  145. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/package.json +66 -0
  146. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/playwright.config.ts +101 -0
  147. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/postcss.config.mjs +7 -0
  148. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/layout.tsx +11 -0
  149. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/login/page.tsx +5 -0
  150. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/register/page.tsx +5 -0
  151. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/chat/page.tsx +20 -0
  152. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/dashboard/page.tsx +99 -0
  153. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/layout.tsx +17 -0
  154. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/profile/page.tsx +156 -0
  155. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/login/route.ts +58 -0
  156. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/logout/route.ts +24 -0
  157. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/me/route.ts +39 -0
  158. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/oauth-callback/route.ts +50 -0
  159. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/refresh/route.ts +54 -0
  160. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/register/route.ts +26 -0
  161. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/messages/route.ts +41 -0
  162. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/route.ts +108 -0
  163. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/route.ts +73 -0
  164. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/health/route.ts +21 -0
  165. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/auth/callback/page.tsx +96 -0
  166. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/globals.css +108 -0
  167. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/layout.tsx +25 -0
  168. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/page.tsx +73 -0
  169. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/providers.tsx +29 -0
  170. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/index.ts +2 -0
  171. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/login-form.tsx +120 -0
  172. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/register-form.tsx +153 -0
  173. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-container.tsx +135 -0
  174. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-input.tsx +73 -0
  175. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/conversation-sidebar.tsx +261 -0
  176. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/index.ts +8 -0
  177. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-item.tsx +63 -0
  178. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-list.tsx +18 -0
  179. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-call-card.tsx +60 -0
  180. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/google-icon.tsx +32 -0
  181. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/index.ts +3 -0
  182. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/language-switcher.tsx +97 -0
  183. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/header.tsx +45 -0
  184. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/index.ts +2 -0
  185. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/sidebar.tsx +48 -0
  186. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/index.ts +7 -0
  187. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-provider.tsx +53 -0
  188. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-toggle.tsx +83 -0
  189. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/badge.tsx +35 -0
  190. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.test.tsx +75 -0
  191. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.tsx +54 -0
  192. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/card.tsx +82 -0
  193. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/index.ts +12 -0
  194. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/input.tsx +21 -0
  195. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/label.tsx +21 -0
  196. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/index.ts +6 -0
  197. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-auth.ts +97 -0
  198. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-chat.ts +203 -0
  199. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-conversations.ts +175 -0
  200. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-websocket.ts +105 -0
  201. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/i18n.ts +32 -0
  202. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/api-client.ts +90 -0
  203. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/constants.ts +39 -0
  204. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/server-api.ts +78 -0
  205. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.test.ts +44 -0
  206. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.ts +44 -0
  207. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/middleware.ts +33 -0
  208. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.test.ts +72 -0
  209. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.ts +48 -0
  210. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-store.ts +65 -0
  211. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/conversation-store.ts +76 -0
  212. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/index.ts +6 -0
  213. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/theme-store.ts +44 -0
  214. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/api.ts +27 -0
  215. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/auth.ts +52 -0
  216. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/chat.ts +81 -0
  217. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/conversation.ts +49 -0
  218. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/index.ts +10 -0
  219. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/tsconfig.json +28 -0
  220. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.config.ts +36 -0
  221. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.setup.ts +56 -0
@@ -0,0 +1,121 @@
1
+ {%- if cookiecutter.enable_ai_agent %}
2
+ """Tests for AI agent module."""
3
+
4
+ from unittest.mock import MagicMock, patch
5
+
6
+ import pytest
7
+
8
+ from app.agents.assistant import AssistantAgent, Deps, get_agent, run_agent
9
+ from app.agents.tools.datetime_tool import get_current_datetime
10
+
11
+
12
+ class TestDeps:
13
+ """Tests for Deps dataclass."""
14
+
15
+ def test_deps_default_values(self):
16
+ """Test Deps has correct default values."""
17
+ deps = Deps()
18
+ assert deps.user_id is None
19
+ assert deps.user_name is None
20
+ assert deps.metadata == {}
21
+
22
+ def test_deps_with_values(self):
23
+ """Test Deps with custom values."""
24
+ deps = Deps(user_id="123", user_name="Test User", metadata={"key": "value"})
25
+ assert deps.user_id == "123"
26
+ assert deps.user_name == "Test User"
27
+ assert deps.metadata == {"key": "value"}
28
+
29
+
30
+ class TestGetCurrentDatetime:
31
+ """Tests for get_current_datetime tool."""
32
+
33
+ def test_returns_formatted_string(self):
34
+ """Test get_current_datetime returns formatted string."""
35
+ result = get_current_datetime()
36
+ assert isinstance(result, str)
37
+ # Should contain year, month, day
38
+ assert len(result) > 10
39
+
40
+
41
+ class TestAssistantAgent:
42
+ """Tests for AssistantAgent class."""
43
+
44
+ def test_init_with_defaults(self):
45
+ """Test AssistantAgent initializes with defaults."""
46
+ agent = AssistantAgent()
47
+ assert agent.system_prompt == "You are a helpful assistant."
48
+ assert agent._agent is None
49
+
50
+ def test_init_with_custom_values(self):
51
+ """Test AssistantAgent with custom configuration."""
52
+ agent = AssistantAgent(
53
+ model_name="gpt-4",
54
+ temperature=0.5,
55
+ system_prompt="Custom prompt",
56
+ )
57
+ assert agent.model_name == "gpt-4"
58
+ assert agent.temperature == 0.5
59
+ assert agent.system_prompt == "Custom prompt"
60
+
61
+ @patch("app.agents.assistant.OpenAIProvider")
62
+ @patch("app.agents.assistant.OpenAIChatModel")
63
+ def test_agent_property_creates_agent(self, mock_model, mock_provider):
64
+ """Test agent property creates agent on first access."""
65
+ agent = AssistantAgent()
66
+ _ = agent.agent
67
+ assert agent._agent is not None
68
+ mock_model.assert_called_once()
69
+
70
+ @patch("app.agents.assistant.OpenAIProvider")
71
+ @patch("app.agents.assistant.OpenAIChatModel")
72
+ def test_agent_property_caches_agent(self, mock_model, mock_provider):
73
+ """Test agent property caches the agent instance."""
74
+ agent = AssistantAgent()
75
+ agent1 = agent.agent
76
+ agent2 = agent.agent
77
+ assert agent1 is agent2
78
+ mock_model.assert_called_once()
79
+
80
+
81
+ class TestGetAgent:
82
+ """Tests for get_agent factory function."""
83
+
84
+ def test_returns_assistant_agent(self):
85
+ """Test get_agent returns AssistantAgent."""
86
+ agent = get_agent()
87
+ assert isinstance(agent, AssistantAgent)
88
+
89
+
90
+ class TestAgentRoutes:
91
+ """Tests for agent WebSocket routes."""
92
+
93
+ @pytest.mark.anyio
94
+ async def test_agent_websocket_connection(self, client):
95
+ """Test WebSocket connection to agent endpoint."""
96
+ # This test verifies the WebSocket endpoint is accessible
97
+ # Actual agent testing would require mocking OpenAI
98
+ pass
99
+
100
+
101
+ class TestHistoryConversion:
102
+ """Tests for conversation history conversion."""
103
+
104
+ def test_empty_history(self):
105
+ """Test with empty history."""
106
+ _agent = AssistantAgent() # noqa: F841
107
+ # History conversion happens inside run/iter methods
108
+ # We test the structure here
109
+ history = []
110
+ assert len(history) == 0
111
+
112
+ def test_history_roles(self):
113
+ """Test history with different roles."""
114
+ history = [
115
+ {"role": "user", "content": "Hello"},
116
+ {"role": "assistant", "content": "Hi there!"},
117
+ {"role": "system", "content": "You are helpful"},
118
+ ]
119
+ assert len(history) == 3
120
+ assert all("role" in msg and "content" in msg for msg in history)
121
+ {%- endif %}
@@ -0,0 +1,183 @@
1
+ {%- if cookiecutter.enable_redis %}
2
+ """Tests for client modules."""
3
+
4
+ from unittest.mock import AsyncMock, MagicMock, patch
5
+
6
+ import pytest
7
+
8
+ from app.clients.redis import RedisClient
9
+
10
+
11
+ class TestRedisClient:
12
+ """Tests for RedisClient."""
13
+
14
+ @pytest.fixture
15
+ def redis_client(self) -> RedisClient:
16
+ """Create a RedisClient instance."""
17
+ return RedisClient(url="redis://localhost:6379")
18
+
19
+ @pytest.fixture
20
+ def mock_aioredis(self) -> MagicMock:
21
+ """Create a mock aioredis client."""
22
+ mock = MagicMock()
23
+ mock.get = AsyncMock(return_value="value")
24
+ mock.set = AsyncMock()
25
+ mock.delete = AsyncMock(return_value=1)
26
+ mock.exists = AsyncMock(return_value=1)
27
+ mock.ping = AsyncMock()
28
+ mock.close = AsyncMock()
29
+ return mock
30
+
31
+ @pytest.mark.anyio
32
+ async def test_connect(self, redis_client: RedisClient):
33
+ """Test Redis connection."""
34
+ with patch("app.clients.redis.aioredis") as mock_aioredis:
35
+ mock_client = MagicMock()
36
+ mock_aioredis.from_url.return_value = mock_client
37
+
38
+ await redis_client.connect()
39
+
40
+ assert redis_client.client is not None
41
+ mock_aioredis.from_url.assert_called_once()
42
+
43
+ @pytest.mark.anyio
44
+ async def test_close(self, redis_client: RedisClient, mock_aioredis: MagicMock):
45
+ """Test closing Redis connection."""
46
+ redis_client.client = mock_aioredis
47
+
48
+ await redis_client.close()
49
+
50
+ mock_aioredis.close.assert_called_once()
51
+ assert redis_client.client is None
52
+
53
+ @pytest.mark.anyio
54
+ async def test_close_when_not_connected(self, redis_client: RedisClient):
55
+ """Test closing when not connected does nothing."""
56
+ redis_client.client = None
57
+
58
+ await redis_client.close() # Should not raise
59
+
60
+ @pytest.mark.anyio
61
+ async def test_get(self, redis_client: RedisClient, mock_aioredis: MagicMock):
62
+ """Test getting a value."""
63
+ redis_client.client = mock_aioredis
64
+
65
+ result = await redis_client.get("test_key")
66
+
67
+ assert result == "value"
68
+ mock_aioredis.get.assert_called_once_with("test_key")
69
+
70
+ @pytest.mark.anyio
71
+ async def test_get_not_connected(self, redis_client: RedisClient):
72
+ """Test getting when not connected raises error."""
73
+ redis_client.client = None
74
+
75
+ with pytest.raises(RuntimeError, match="not connected"):
76
+ await redis_client.get("test_key")
77
+
78
+ @pytest.mark.anyio
79
+ async def test_set(self, redis_client: RedisClient, mock_aioredis: MagicMock):
80
+ """Test setting a value."""
81
+ redis_client.client = mock_aioredis
82
+
83
+ await redis_client.set("test_key", "test_value")
84
+
85
+ mock_aioredis.set.assert_called_once_with("test_key", "test_value", ex=None)
86
+
87
+ @pytest.mark.anyio
88
+ async def test_set_with_ttl(self, redis_client: RedisClient, mock_aioredis: MagicMock):
89
+ """Test setting a value with TTL."""
90
+ redis_client.client = mock_aioredis
91
+
92
+ await redis_client.set("test_key", "test_value", ttl=60)
93
+
94
+ mock_aioredis.set.assert_called_once_with("test_key", "test_value", ex=60)
95
+
96
+ @pytest.mark.anyio
97
+ async def test_set_not_connected(self, redis_client: RedisClient):
98
+ """Test setting when not connected raises error."""
99
+ redis_client.client = None
100
+
101
+ with pytest.raises(RuntimeError, match="not connected"):
102
+ await redis_client.set("test_key", "test_value")
103
+
104
+ @pytest.mark.anyio
105
+ async def test_delete(self, redis_client: RedisClient, mock_aioredis: MagicMock):
106
+ """Test deleting a key."""
107
+ redis_client.client = mock_aioredis
108
+
109
+ result = await redis_client.delete("test_key")
110
+
111
+ assert result == 1
112
+ mock_aioredis.delete.assert_called_once_with("test_key")
113
+
114
+ @pytest.mark.anyio
115
+ async def test_delete_not_connected(self, redis_client: RedisClient):
116
+ """Test deleting when not connected raises error."""
117
+ redis_client.client = None
118
+
119
+ with pytest.raises(RuntimeError, match="not connected"):
120
+ await redis_client.delete("test_key")
121
+
122
+ @pytest.mark.anyio
123
+ async def test_exists(self, redis_client: RedisClient, mock_aioredis: MagicMock):
124
+ """Test checking if key exists."""
125
+ redis_client.client = mock_aioredis
126
+
127
+ result = await redis_client.exists("test_key")
128
+
129
+ assert result is True
130
+ mock_aioredis.exists.assert_called_once_with("test_key")
131
+
132
+ @pytest.mark.anyio
133
+ async def test_exists_not_connected(self, redis_client: RedisClient):
134
+ """Test exists when not connected raises error."""
135
+ redis_client.client = None
136
+
137
+ with pytest.raises(RuntimeError, match="not connected"):
138
+ await redis_client.exists("test_key")
139
+
140
+ @pytest.mark.anyio
141
+ async def test_ping_connected(self, redis_client: RedisClient, mock_aioredis: MagicMock):
142
+ """Test ping when connected."""
143
+ redis_client.client = mock_aioredis
144
+
145
+ result = await redis_client.ping()
146
+
147
+ assert result is True
148
+ mock_aioredis.ping.assert_called_once()
149
+
150
+ @pytest.mark.anyio
151
+ async def test_ping_not_connected(self, redis_client: RedisClient):
152
+ """Test ping when not connected returns False."""
153
+ redis_client.client = None
154
+
155
+ result = await redis_client.ping()
156
+
157
+ assert result is False
158
+
159
+ @pytest.mark.anyio
160
+ async def test_ping_exception(self, redis_client: RedisClient, mock_aioredis: MagicMock):
161
+ """Test ping when exception occurs returns False."""
162
+ redis_client.client = mock_aioredis
163
+ mock_aioredis.ping = AsyncMock(side_effect=Exception("Connection error"))
164
+
165
+ result = await redis_client.ping()
166
+
167
+ assert result is False
168
+
169
+ def test_raw_property(self, redis_client: RedisClient, mock_aioredis: MagicMock):
170
+ """Test accessing raw client."""
171
+ redis_client.client = mock_aioredis
172
+
173
+ result = redis_client.raw
174
+
175
+ assert result == mock_aioredis
176
+
177
+ def test_raw_property_not_connected(self, redis_client: RedisClient):
178
+ """Test accessing raw client when not connected raises error."""
179
+ redis_client.client = None
180
+
181
+ with pytest.raises(RuntimeError, match="not connected"):
182
+ _ = redis_client.raw
183
+ {%- endif %}
@@ -0,0 +1,173 @@
1
+ """Tests for CLI commands module."""
2
+
3
+ import click
4
+ from click.testing import CliRunner
5
+
6
+ from app.commands import (
7
+ command,
8
+ discover_commands,
9
+ error,
10
+ info,
11
+ register_commands,
12
+ success,
13
+ warning,
14
+ )
15
+
16
+
17
+ class TestCommandDecorator:
18
+ """Tests for the command decorator."""
19
+
20
+ def test_command_registers_function(self):
21
+ """Test that @command decorator registers a click command."""
22
+ from app.commands import _commands
23
+
24
+ initial_count = len(_commands)
25
+
26
+ @command("test-cmd", help="Test command")
27
+ def test_func():
28
+ pass
29
+
30
+ assert len(_commands) == initial_count + 1
31
+ assert _commands[-1].name == "test-cmd"
32
+
33
+ def test_command_uses_function_name_as_default(self):
34
+ """Test that command name defaults to function name."""
35
+ from app.commands import _commands
36
+
37
+ @command()
38
+ def my_test_command():
39
+ pass
40
+
41
+ assert _commands[-1].name == "my-test-command"
42
+
43
+
44
+ class TestHelperFunctions:
45
+ """Tests for helper output functions."""
46
+
47
+ def test_success_prints_green(self, capsys):
48
+ """Test success prints in green."""
49
+ success("Test message")
50
+ # Click uses escape codes for colors
51
+ captured = capsys.readouterr()
52
+ assert "Test message" in captured.out
53
+
54
+ def test_error_prints_red(self, capsys):
55
+ """Test error prints in red."""
56
+ error("Error message")
57
+ captured = capsys.readouterr()
58
+ assert "Error message" in captured.out
59
+
60
+ def test_warning_prints_yellow(self, capsys):
61
+ """Test warning prints in yellow."""
62
+ warning("Warning message")
63
+ captured = capsys.readouterr()
64
+ assert "Warning message" in captured.out
65
+
66
+ def test_info_prints_plain(self, capsys):
67
+ """Test info prints plain text."""
68
+ info("Info message")
69
+ captured = capsys.readouterr()
70
+ assert "Info message" in captured.out
71
+
72
+
73
+ class TestDiscoverCommands:
74
+ """Tests for command discovery."""
75
+
76
+ def test_discover_commands_returns_list(self):
77
+ """Test that discover_commands returns a list."""
78
+ commands = discover_commands()
79
+ assert isinstance(commands, list)
80
+
81
+ def test_discover_commands_caches_results(self):
82
+ """Test that discover_commands caches on second call."""
83
+ commands1 = discover_commands()
84
+ commands2 = discover_commands()
85
+ assert commands1 is commands2
86
+
87
+
88
+ class TestRegisterCommands:
89
+ """Tests for registering commands."""
90
+
91
+ def test_register_commands_adds_to_group(self):
92
+ """Test that register_commands adds discovered commands to CLI group."""
93
+ @click.group()
94
+ def cli():
95
+ pass
96
+
97
+ register_commands(cli)
98
+ # After registration, cli should have commands
99
+ # We can't assert exact count since it depends on what's discovered
100
+
101
+
102
+ {%- if cookiecutter.use_database %}
103
+
104
+
105
+ class TestSeedCommand:
106
+ """Tests for the seed command."""
107
+
108
+ def test_seed_dry_run(self):
109
+ """Test seed command with --dry-run."""
110
+ from app.commands.seed import seed
111
+
112
+ runner = CliRunner()
113
+ result = runner.invoke(seed, ["--dry-run", "--count", "5"])
114
+ assert result.exit_code == 0
115
+ assert "[DRY RUN]" in result.output
116
+ assert "5" in result.output
117
+
118
+ def test_seed_dry_run_with_clear(self):
119
+ """Test seed command with --dry-run and --clear."""
120
+ from app.commands.seed import seed
121
+
122
+ runner = CliRunner()
123
+ result = runner.invoke(seed, ["--dry-run", "--clear"])
124
+ assert result.exit_code == 0
125
+ assert "Would clear existing data" in result.output
126
+ {%- endif %}
127
+
128
+
129
+ class TestHelloCommand:
130
+ """Tests for the hello command."""
131
+
132
+ def test_hello_command_runs(self):
133
+ """Test hello command executes."""
134
+ from app.commands.example import hello
135
+
136
+ runner = CliRunner()
137
+ result = runner.invoke(hello)
138
+ assert result.exit_code == 0
139
+ assert "Hello" in result.output
140
+
141
+ def test_hello_command_with_name(self):
142
+ """Test hello command with --name option."""
143
+ from app.commands.example import hello
144
+
145
+ runner = CliRunner()
146
+ result = runner.invoke(hello, ["--name", "Alice"])
147
+ assert result.exit_code == 0
148
+ assert "Alice" in result.output
149
+
150
+
151
+ {%- if cookiecutter.use_database %}
152
+
153
+
154
+ class TestCleanupCommand:
155
+ """Tests for the cleanup command."""
156
+
157
+ def test_cleanup_dry_run(self):
158
+ """Test cleanup command with --dry-run."""
159
+ from app.commands.cleanup import cleanup
160
+
161
+ runner = CliRunner()
162
+ result = runner.invoke(cleanup, ["--dry-run"])
163
+ assert result.exit_code == 0
164
+ assert "[DRY RUN]" in result.output
165
+
166
+ def test_cleanup_with_days_option(self):
167
+ """Test cleanup command with --days option."""
168
+ from app.commands.cleanup import cleanup
169
+
170
+ runner = CliRunner()
171
+ result = runner.invoke(cleanup, ["--dry-run", "--days", "7"])
172
+ assert result.exit_code == 0
173
+ {%- endif %}
@@ -0,0 +1,143 @@
1
+ """Tests for core modules."""
2
+
3
+ from app.core.config import settings
4
+ from app.core.exceptions import (
5
+ AlreadyExistsError,
6
+ AppException,
7
+ AuthenticationError,
8
+ AuthorizationError,
9
+ NotFoundError,
10
+ ValidationError,
11
+ )
12
+
13
+
14
+ class TestSettings:
15
+ """Tests for settings configuration."""
16
+
17
+ def test_project_name_is_set(self):
18
+ """Test project name is configured."""
19
+ assert settings.PROJECT_NAME == "{{ cookiecutter.project_name }}"
20
+
21
+ def test_api_v1_str_is_set(self):
22
+ """Test API version string is set."""
23
+ assert settings.API_V1_STR == "/api/v1"
24
+
25
+ def test_debug_mode_default(self):
26
+ """Test debug mode has default value."""
27
+ assert isinstance(settings.DEBUG, bool)
28
+
29
+ {%- if cookiecutter.enable_cors %}
30
+ def test_cors_origins_is_list(self):
31
+ """Test CORS origins is a list."""
32
+ assert isinstance(settings.CORS_ORIGINS, list)
33
+ {%- endif %}
34
+
35
+
36
+ class TestExceptions:
37
+ """Tests for custom exceptions."""
38
+
39
+ def test_app_exception(self):
40
+ """Test AppException initialization."""
41
+ error = AppException(message="Test error", code="TEST_ERROR")
42
+ assert error.message == "Test error"
43
+ assert error.code == "TEST_ERROR"
44
+ assert str(error) == "Test error"
45
+
46
+ def test_not_found_error(self):
47
+ """Test NotFoundError."""
48
+ error = NotFoundError(message="Item not found")
49
+ assert error.status_code == 404
50
+ assert error.code == "NOT_FOUND"
51
+
52
+ def test_already_exists_error(self):
53
+ """Test AlreadyExistsError."""
54
+ error = AlreadyExistsError(message="Item already exists")
55
+ assert error.status_code == 409
56
+ assert error.code == "ALREADY_EXISTS"
57
+
58
+ def test_authentication_error(self):
59
+ """Test AuthenticationError."""
60
+ error = AuthenticationError(message="Invalid credentials")
61
+ assert error.status_code == 401
62
+ assert error.code == "AUTHENTICATION_ERROR"
63
+
64
+ def test_authorization_error(self):
65
+ """Test AuthorizationError."""
66
+ error = AuthorizationError(message="Not authorized")
67
+ assert error.status_code == 403
68
+ assert error.code == "AUTHORIZATION_ERROR"
69
+
70
+ def test_validation_error(self):
71
+ """Test ValidationError."""
72
+ error = ValidationError(message="Invalid input")
73
+ assert error.status_code == 422
74
+ assert error.code == "VALIDATION_ERROR"
75
+
76
+
77
+ {%- if cookiecutter.enable_redis %}
78
+
79
+
80
+ class TestCacheSetup:
81
+ """Tests for cache setup."""
82
+
83
+ def test_setup_cache_function_exists(self):
84
+ """Test setup_cache function exists."""
85
+ from app.core.cache import setup_cache
86
+
87
+ assert setup_cache is not None
88
+ assert callable(setup_cache)
89
+ {%- endif %}
90
+
91
+
92
+ class TestMiddleware:
93
+ """Tests for middleware."""
94
+
95
+ def test_request_id_middleware_exists(self):
96
+ """Test request ID middleware is configured."""
97
+ from app.core.middleware import RequestIDMiddleware
98
+
99
+ assert RequestIDMiddleware is not None
100
+
101
+
102
+ {%- if cookiecutter.enable_rate_limiting %}
103
+
104
+
105
+ class TestRateLimit:
106
+ """Tests for rate limiting."""
107
+
108
+ def test_limiter_exists(self):
109
+ """Test rate limiter is configured."""
110
+ from app.core.rate_limit import limiter
111
+
112
+ assert limiter is not None
113
+ {%- endif %}
114
+
115
+
116
+ {%- if cookiecutter.enable_logfire %}
117
+
118
+
119
+ from unittest.mock import patch # noqa: E402
120
+
121
+
122
+ class TestLogfireSetup:
123
+ """Tests for Logfire setup."""
124
+
125
+ @patch("app.core.logfire_setup.logfire")
126
+ def test_setup_logfire_configures(self, mock_logfire):
127
+ """Test setup_logfire calls configure."""
128
+ from app.core.logfire_setup import setup_logfire
129
+
130
+ setup_logfire()
131
+ mock_logfire.configure.assert_called_once()
132
+
133
+ @patch("app.core.logfire_setup.logfire")
134
+ def test_instrument_app_instruments_fastapi(self, mock_logfire):
135
+ """Test instrument_app instruments FastAPI."""
136
+ from fastapi import FastAPI
137
+
138
+ from app.core.logfire_setup import instrument_app
139
+
140
+ app = FastAPI()
141
+ instrument_app(app)
142
+ mock_logfire.instrument_fastapi.assert_called()
143
+ {%- endif %}