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,118 @@
1
+ """Tests for pipeline infrastructure."""
2
+
3
+ import pytest
4
+
5
+ from app.pipelines.base import BasePipeline, PipelineResult
6
+
7
+
8
+ class TestPipelineResult:
9
+ """Tests for PipelineResult dataclass."""
10
+
11
+ def test_success_rate_all_processed(self):
12
+ """Test success rate when all items processed."""
13
+ result = PipelineResult(processed=10, failed=0)
14
+ assert result.success_rate == 100.0
15
+
16
+ def test_success_rate_with_failures(self):
17
+ """Test success rate with some failures."""
18
+ result = PipelineResult(processed=8, failed=2)
19
+ assert result.success_rate == 80.0
20
+
21
+ def test_success_rate_all_failed(self):
22
+ """Test success rate when all items failed."""
23
+ result = PipelineResult(processed=0, failed=10)
24
+ assert result.success_rate == 0.0
25
+
26
+ def test_success_rate_empty(self):
27
+ """Test success rate with no items."""
28
+ result = PipelineResult(processed=0, failed=0)
29
+ assert result.success_rate == 100.0
30
+
31
+ def test_has_errors_with_failures(self):
32
+ """Test has_errors returns True when failures exist."""
33
+ result = PipelineResult(processed=5, failed=1)
34
+ assert result.has_errors is True
35
+
36
+ def test_has_errors_with_error_messages(self):
37
+ """Test has_errors returns True when error messages exist."""
38
+ result = PipelineResult(processed=5, failed=0, errors=["Error 1"])
39
+ assert result.has_errors is True
40
+
41
+ def test_has_errors_no_errors(self):
42
+ """Test has_errors returns False when no errors."""
43
+ result = PipelineResult(processed=5, failed=0)
44
+ assert result.has_errors is False
45
+
46
+ def test_default_values(self):
47
+ """Test default values are set correctly."""
48
+ result = PipelineResult(processed=5)
49
+ assert result.failed == 0
50
+ assert result.errors == []
51
+ assert result.metadata == {}
52
+
53
+
54
+ class TestBasePipeline:
55
+ """Tests for BasePipeline abstract class."""
56
+
57
+ @pytest.mark.anyio
58
+ async def test_validate_returns_true_by_default(self):
59
+ """Test validate method returns True by default."""
60
+
61
+ class TestPipeline(BasePipeline):
62
+ async def run(self) -> PipelineResult:
63
+ return PipelineResult(processed=0)
64
+
65
+ pipeline = TestPipeline()
66
+ assert await pipeline.validate() is True
67
+
68
+ @pytest.mark.anyio
69
+ async def test_cleanup_does_nothing_by_default(self):
70
+ """Test cleanup method does nothing by default."""
71
+
72
+ class TestPipeline(BasePipeline):
73
+ async def run(self) -> PipelineResult:
74
+ return PipelineResult(processed=0)
75
+
76
+ pipeline = TestPipeline()
77
+ await pipeline.cleanup() # Should not raise
78
+
79
+ @pytest.mark.anyio
80
+ async def test_run_must_be_implemented(self):
81
+ """Test that run method must be implemented by subclasses."""
82
+ # This test verifies the abstract method requirement
83
+ with pytest.raises(TypeError, match="Can't instantiate abstract class"):
84
+ BasePipeline()
85
+
86
+ @pytest.mark.anyio
87
+ async def test_custom_pipeline_implementation(self):
88
+ """Test a custom pipeline implementation."""
89
+
90
+ class MyPipeline(BasePipeline):
91
+ def __init__(self, items: list):
92
+ self.items = items
93
+
94
+ async def run(self) -> PipelineResult:
95
+ processed = 0
96
+ failed = 0
97
+ errors = []
98
+
99
+ for item in self.items:
100
+ if item > 0:
101
+ processed += 1
102
+ else:
103
+ failed += 1
104
+ errors.append(f"Invalid item: {item}")
105
+
106
+ return PipelineResult(
107
+ processed=processed,
108
+ failed=failed,
109
+ errors=errors,
110
+ )
111
+
112
+ pipeline = MyPipeline([1, 2, 3, -1, 5])
113
+ result = await pipeline.run()
114
+
115
+ assert result.processed == 4
116
+ assert result.failed == 1
117
+ assert len(result.errors) == 1
118
+ assert result.success_rate == 80.0
@@ -0,0 +1,181 @@
1
+ {%- if cookiecutter.use_database %}
2
+ """Tests for repository layer."""
3
+
4
+ from unittest.mock import AsyncMock, MagicMock
5
+ from uuid import uuid4
6
+
7
+ import pytest
8
+ from pydantic import BaseModel
9
+
10
+ from app.repositories.base import BaseRepository
11
+
12
+
13
+ class MockModel:
14
+ """Mock SQLAlchemy model for testing."""
15
+
16
+ def __init__(self, **kwargs):
17
+ self.id = kwargs.get("id", uuid4())
18
+ for key, value in kwargs.items():
19
+ setattr(self, key, value)
20
+
21
+
22
+ class MockCreateSchema(BaseModel):
23
+ """Mock create schema."""
24
+
25
+ name: str
26
+
27
+
28
+ class MockUpdateSchema(BaseModel):
29
+ """Mock update schema."""
30
+
31
+ name: str | None = None
32
+
33
+
34
+ class TestBaseRepository:
35
+ """Tests for BaseRepository."""
36
+
37
+ @pytest.fixture
38
+ def repository(self):
39
+ """Create a test repository."""
40
+ return BaseRepository[MockModel, MockCreateSchema, MockUpdateSchema](MockModel)
41
+
42
+ @pytest.fixture
43
+ def mock_session(self):
44
+ """Create a mock async session."""
45
+ session = MagicMock()
46
+ session.get = AsyncMock()
47
+ session.execute = AsyncMock()
48
+ session.add = MagicMock()
49
+ session.flush = AsyncMock()
50
+ session.refresh = AsyncMock()
51
+ session.delete = AsyncMock()
52
+ return session
53
+
54
+ @pytest.mark.anyio
55
+ async def test_get_returns_model(self, repository, mock_session):
56
+ """Test get returns a model by ID."""
57
+ mock_obj = MockModel(name="test")
58
+ mock_session.get.return_value = mock_obj
59
+
60
+ result = await repository.get(mock_session, mock_obj.id)
61
+
62
+ assert result == mock_obj
63
+ mock_session.get.assert_called_once_with(MockModel, mock_obj.id)
64
+
65
+ @pytest.mark.anyio
66
+ async def test_get_returns_none_when_not_found(self, repository, mock_session):
67
+ """Test get returns None when not found."""
68
+ mock_session.get.return_value = None
69
+
70
+ result = await repository.get(mock_session, uuid4())
71
+
72
+ assert result is None
73
+
74
+ # Note: test_get_multi_returns_list is skipped because it requires a real
75
+ # SQLAlchemy model. The select() function cannot work with a mock class.
76
+ # For proper integration testing, use actual SQLAlchemy models with a test DB.
77
+
78
+ @pytest.mark.anyio
79
+ async def test_create_adds_and_returns_model(self, repository, mock_session):
80
+ """Test create adds a new model."""
81
+ create_data = MockCreateSchema(name="new item")
82
+
83
+ # Mock the model creation
84
+ async def refresh_side_effect(obj):
85
+ obj.id = uuid4()
86
+
87
+ mock_session.refresh.side_effect = refresh_side_effect
88
+
89
+ result = await repository.create(mock_session, obj_in=create_data)
90
+
91
+ assert result.name == "new item"
92
+ mock_session.add.assert_called_once()
93
+ mock_session.flush.assert_called_once()
94
+ mock_session.refresh.assert_called_once()
95
+
96
+ @pytest.mark.anyio
97
+ async def test_update_with_schema(self, repository, mock_session):
98
+ """Test update with Pydantic schema."""
99
+ db_obj = MockModel(name="old name")
100
+ update_data = MockUpdateSchema(name="new name")
101
+
102
+ result = await repository.update(mock_session, db_obj=db_obj, obj_in=update_data)
103
+
104
+ assert result.name == "new name"
105
+ mock_session.add.assert_called_once()
106
+ mock_session.flush.assert_called_once()
107
+
108
+ @pytest.mark.anyio
109
+ async def test_update_with_dict(self, repository, mock_session):
110
+ """Test update with dictionary."""
111
+ db_obj = MockModel(name="old name")
112
+ update_data = {"name": "new name"}
113
+
114
+ result = await repository.update(mock_session, db_obj=db_obj, obj_in=update_data)
115
+
116
+ assert result.name == "new name"
117
+
118
+ @pytest.mark.anyio
119
+ async def test_delete_removes_and_returns_model(self, repository, mock_session):
120
+ """Test delete removes and returns model."""
121
+ mock_obj = MockModel(name="to delete")
122
+ mock_session.get.return_value = mock_obj
123
+
124
+ result = await repository.delete(mock_session, id=mock_obj.id)
125
+
126
+ assert result == mock_obj
127
+ mock_session.delete.assert_called_once_with(mock_obj)
128
+ mock_session.flush.assert_called_once()
129
+
130
+ @pytest.mark.anyio
131
+ async def test_delete_returns_none_when_not_found(self, repository, mock_session):
132
+ """Test delete returns None when not found."""
133
+ mock_session.get.return_value = None
134
+
135
+ result = await repository.delete(mock_session, id=uuid4())
136
+
137
+ assert result is None
138
+ mock_session.delete.assert_not_called()
139
+
140
+
141
+ {%- if cookiecutter.use_jwt %}
142
+
143
+
144
+ class TestUserRepository:
145
+ """Tests for user repository functions."""
146
+
147
+ @pytest.fixture
148
+ def mock_session(self):
149
+ """Create a mock async session."""
150
+ session = MagicMock()
151
+ session.execute = AsyncMock()
152
+ return session
153
+
154
+ @pytest.mark.anyio
155
+ async def test_get_by_email(self, mock_session):
156
+ """Test get_by_email returns user."""
157
+ from app.repositories import user as user_repo
158
+
159
+ mock_user = MagicMock()
160
+ mock_result = MagicMock()
161
+ mock_result.scalar_one_or_none.return_value = mock_user
162
+ mock_session.execute.return_value = mock_result
163
+
164
+ result = await user_repo.get_by_email(mock_session, "test@example.com")
165
+
166
+ assert result == mock_user
167
+
168
+ @pytest.mark.anyio
169
+ async def test_get_by_email_not_found(self, mock_session):
170
+ """Test get_by_email returns None when not found."""
171
+ from app.repositories import user as user_repo
172
+
173
+ mock_result = MagicMock()
174
+ mock_result.scalar_one_or_none.return_value = None
175
+ mock_session.execute.return_value = mock_result
176
+
177
+ result = await user_repo.get_by_email(mock_session, "notfound@example.com")
178
+
179
+ assert result is None
180
+ {%- endif %}
181
+ {%- endif %}
@@ -0,0 +1,124 @@
1
+ {%- if cookiecutter.use_jwt %}
2
+ """Tests for security module."""
3
+
4
+ from datetime import timedelta
5
+
6
+ from app.core.security import (
7
+ create_access_token,
8
+ create_refresh_token,
9
+ get_password_hash,
10
+ verify_password,
11
+ verify_token,
12
+ )
13
+
14
+
15
+ class TestPasswordHashing:
16
+ """Tests for password hashing functions."""
17
+
18
+ def test_hash_password(self):
19
+ """Test password hashing."""
20
+ password = "mysecretpassword"
21
+ hashed = get_password_hash(password)
22
+
23
+ assert hashed != password
24
+ assert len(hashed) > 0
25
+ assert hashed.startswith("$2") # bcrypt prefix
26
+
27
+ def test_verify_password_correct(self):
28
+ """Test verifying correct password."""
29
+ password = "mysecretpassword"
30
+ hashed = get_password_hash(password)
31
+
32
+ assert verify_password(password, hashed) is True
33
+
34
+ def test_verify_password_incorrect(self):
35
+ """Test verifying incorrect password."""
36
+ password = "mysecretpassword"
37
+ wrong_password = "wrongpassword"
38
+ hashed = get_password_hash(password)
39
+
40
+ assert verify_password(wrong_password, hashed) is False
41
+
42
+
43
+ class TestAccessToken:
44
+ """Tests for access token functions."""
45
+
46
+ def test_create_access_token(self):
47
+ """Test creating access token."""
48
+ subject = "user123"
49
+ token = create_access_token(subject)
50
+
51
+ assert isinstance(token, str)
52
+ assert len(token) > 0
53
+
54
+ def test_create_access_token_with_expires_delta(self):
55
+ """Test creating access token with custom expiration."""
56
+ subject = "user123"
57
+ expires = timedelta(hours=2)
58
+ token = create_access_token(subject, expires_delta=expires)
59
+
60
+ assert isinstance(token, str)
61
+ payload = verify_token(token)
62
+ assert payload is not None
63
+ assert payload["sub"] == subject
64
+ assert payload["type"] == "access"
65
+
66
+ def test_verify_access_token(self):
67
+ """Test verifying access token."""
68
+ subject = "user123"
69
+ token = create_access_token(subject)
70
+ payload = verify_token(token)
71
+
72
+ assert payload is not None
73
+ assert payload["sub"] == subject
74
+ assert payload["type"] == "access"
75
+
76
+ def test_verify_invalid_token(self):
77
+ """Test verifying invalid token."""
78
+ payload = verify_token("invalid.token.here")
79
+
80
+ assert payload is None
81
+
82
+ def test_verify_expired_token(self):
83
+ """Test verifying expired token."""
84
+ subject = "user123"
85
+ # Create token that expires immediately
86
+ token = create_access_token(subject, expires_delta=timedelta(seconds=-1))
87
+ payload = verify_token(token)
88
+
89
+ assert payload is None
90
+
91
+
92
+ class TestRefreshToken:
93
+ """Tests for refresh token functions."""
94
+
95
+ def test_create_refresh_token(self):
96
+ """Test creating refresh token."""
97
+ subject = "user123"
98
+ token = create_refresh_token(subject)
99
+
100
+ assert isinstance(token, str)
101
+ assert len(token) > 0
102
+
103
+ def test_create_refresh_token_with_expires_delta(self):
104
+ """Test creating refresh token with custom expiration."""
105
+ subject = "user123"
106
+ expires = timedelta(days=7)
107
+ token = create_refresh_token(subject, expires_delta=expires)
108
+
109
+ assert isinstance(token, str)
110
+ payload = verify_token(token)
111
+ assert payload is not None
112
+ assert payload["sub"] == subject
113
+ assert payload["type"] == "refresh"
114
+
115
+ def test_verify_refresh_token(self):
116
+ """Test verifying refresh token."""
117
+ subject = "user123"
118
+ token = create_refresh_token(subject)
119
+ payload = verify_token(token)
120
+
121
+ assert payload is not None
122
+ assert payload["sub"] == subject
123
+ assert payload["type"] == "refresh"
124
+ {%- endif %}