create-tigra 1.1.0 → 2.0.1

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 (243) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +80 -87
  3. package/bin/create-tigra.js +259 -308
  4. package/package.json +49 -41
  5. package/template/_claude/QUICK_REFERENCE.md +193 -0
  6. package/template/_claude/README.md +53 -0
  7. package/template/_claude/commands/create-client.md +881 -0
  8. package/template/_claude/commands/create-server.md +383 -0
  9. package/template/_claude/rules/client/01-project-structure.md +133 -0
  10. package/template/_claude/rules/client/02-components-and-types.md +146 -0
  11. package/template/_claude/rules/client/03-data-and-state.md +156 -0
  12. package/template/_claude/rules/client/04-design-system.md +185 -0
  13. package/template/_claude/rules/client/05-security.md +55 -0
  14. package/template/_claude/rules/client/06-ux-checklist.md +81 -0
  15. package/template/_claude/rules/client/core.md +42 -0
  16. package/template/_claude/rules/global/core.md +77 -0
  17. package/template/_claude/rules/server/core.md +50 -0
  18. package/template/_claude/rules/server/database.md +124 -0
  19. package/template/_claude/rules/server/project-conventions.md +150 -0
  20. package/template/_claude/rules/server/response-handling.md +144 -0
  21. package/template/client/.env.example +5 -0
  22. package/template/client/README.md +36 -0
  23. package/template/client/components.json +23 -0
  24. package/template/client/eslint.config.mjs +18 -0
  25. package/template/client/next.config.ts +34 -0
  26. package/template/client/package.json +44 -0
  27. package/template/client/postcss.config.mjs +7 -0
  28. package/template/client/src/app/(auth)/layout.tsx +18 -0
  29. package/template/client/src/app/(auth)/login/page.tsx +13 -0
  30. package/template/client/src/app/(auth)/register/page.tsx +13 -0
  31. package/template/client/src/app/(main)/dashboard/page.tsx +22 -0
  32. package/template/client/src/app/(main)/layout.tsx +11 -0
  33. package/template/client/src/app/error.tsx +27 -0
  34. package/template/client/src/app/favicon.ico +0 -0
  35. package/template/client/src/app/globals.css +145 -0
  36. package/template/client/src/app/layout.tsx +36 -0
  37. package/template/client/src/app/loading.tsx +11 -0
  38. package/template/client/src/app/not-found.tsx +23 -0
  39. package/template/client/src/app/page.tsx +45 -0
  40. package/template/client/src/app/providers.tsx +43 -0
  41. package/template/client/src/components/common/ConfirmDialog.tsx +56 -0
  42. package/template/client/src/components/common/EmptyState.tsx +31 -0
  43. package/template/client/src/components/common/LoadingSpinner.tsx +30 -0
  44. package/template/client/src/components/common/Pagination.tsx +55 -0
  45. package/template/client/src/components/layout/Footer.tsx +17 -0
  46. package/template/client/src/components/layout/Header.tsx +173 -0
  47. package/template/client/src/components/layout/MainLayout.tsx +18 -0
  48. package/template/client/src/components/ui/alert-dialog.tsx +196 -0
  49. package/template/client/src/components/ui/badge.tsx +48 -0
  50. package/template/client/src/components/ui/button.tsx +64 -0
  51. package/template/client/src/components/ui/card.tsx +92 -0
  52. package/template/client/src/components/ui/input.tsx +21 -0
  53. package/template/client/src/components/ui/label.tsx +24 -0
  54. package/template/client/src/components/ui/select.tsx +190 -0
  55. package/template/client/src/components/ui/skeleton.tsx +13 -0
  56. package/template/client/src/components/ui/table.tsx +116 -0
  57. package/template/client/src/features/auth/components/AuthInitializer.tsx +55 -0
  58. package/template/client/src/features/auth/components/LoginForm.tsx +107 -0
  59. package/template/client/src/features/auth/components/RegisterForm.tsx +178 -0
  60. package/template/client/src/features/auth/hooks/useAuth.ts +84 -0
  61. package/template/client/src/features/auth/services/auth.service.ts +52 -0
  62. package/template/client/src/features/auth/store/authSlice.ts +38 -0
  63. package/template/client/src/features/auth/types/auth.types.ts +32 -0
  64. package/template/client/src/hooks/useDebounce.ts +14 -0
  65. package/template/client/src/hooks/useLocalStorage.ts +55 -0
  66. package/template/client/src/hooks/useMediaQuery.ts +27 -0
  67. package/template/client/src/lib/api/api.types.ts +34 -0
  68. package/template/client/src/lib/api/axios.config.ts +98 -0
  69. package/template/client/src/lib/constants/api-endpoints.ts +18 -0
  70. package/template/client/src/lib/constants/app.constants.ts +12 -0
  71. package/template/client/src/lib/constants/routes.ts +9 -0
  72. package/template/client/src/lib/utils/error.ts +32 -0
  73. package/template/client/src/lib/utils/format.ts +37 -0
  74. package/template/client/src/lib/utils/security.ts +34 -0
  75. package/template/client/src/lib/utils.ts +6 -0
  76. package/template/client/src/middleware.ts +57 -0
  77. package/template/client/src/store/hooks.ts +7 -0
  78. package/template/client/src/store/index.ts +12 -0
  79. package/template/client/src/types/index.ts +3 -0
  80. package/template/client/tsconfig.json +34 -0
  81. package/template/gitignore +34 -0
  82. package/template/server/.dockerignore +66 -0
  83. package/template/server/.env.example +96 -69
  84. package/template/server/.env.production.example +90 -0
  85. package/template/server/Dockerfile +94 -0
  86. package/template/server/docker-compose.yml +82 -111
  87. package/template/server/docs/logging.md +62 -0
  88. package/template/server/eslint.config.mjs +17 -0
  89. package/template/server/package.json +68 -81
  90. package/template/server/phpmyadmin-config.php +26 -0
  91. package/template/server/postman_collection.json +666 -0
  92. package/template/server/prisma/schema.prisma +77 -93
  93. package/template/server/prisma/seed.ts +46 -142
  94. package/template/server/scripts/flush-redis.ts +41 -0
  95. package/template/server/src/app.ts +243 -71
  96. package/template/server/src/config/env.ts +67 -94
  97. package/template/server/src/libs/auth.ts +88 -0
  98. package/template/server/src/libs/cleanup.ts +35 -0
  99. package/template/server/src/libs/cookies.ts +46 -0
  100. package/template/server/src/libs/logger.ts +33 -60
  101. package/template/server/src/libs/monitoring.ts +205 -0
  102. package/template/server/src/libs/password.ts +38 -0
  103. package/template/server/src/libs/prisma.ts +68 -0
  104. package/template/server/src/libs/redis.ts +60 -79
  105. package/template/server/src/libs/requestLogger.ts +66 -0
  106. package/template/server/src/libs/storage/file-storage.service.ts +211 -0
  107. package/template/server/src/libs/storage/file-validator.ts +97 -0
  108. package/template/server/src/libs/storage/filename-sanitizer.ts +71 -0
  109. package/template/server/src/libs/storage/image-optimizer.service.ts +144 -0
  110. package/template/server/src/modules/auth/__tests__/auth.service.test.ts +365 -0
  111. package/template/server/src/modules/auth/auth.controller.ts +90 -141
  112. package/template/server/src/modules/auth/auth.repo.ts +120 -218
  113. package/template/server/src/modules/auth/auth.routes.ts +96 -83
  114. package/template/server/src/modules/auth/auth.schemas.ts +35 -137
  115. package/template/server/src/modules/auth/auth.service.ts +286 -329
  116. package/template/server/src/modules/auth/session.repo.ts +110 -0
  117. package/template/server/src/modules/users/users.controller.ts +120 -0
  118. package/template/server/src/modules/users/users.repo.ts +77 -0
  119. package/template/server/src/modules/users/users.routes.ts +89 -0
  120. package/template/server/src/modules/users/users.schemas.ts +21 -0
  121. package/template/server/src/modules/users/users.service.ts +169 -0
  122. package/template/server/src/server.ts +58 -139
  123. package/template/server/src/shared/errors/AppError.ts +21 -0
  124. package/template/server/src/shared/errors/errors.ts +43 -0
  125. package/template/server/src/shared/responses/paginatedResponse.ts +38 -0
  126. package/template/server/src/shared/responses/successResponse.ts +17 -0
  127. package/template/server/src/shared/schemas/pagination.schema.ts +12 -0
  128. package/template/server/src/shared/types/index.ts +26 -0
  129. package/template/server/src/test/setup.ts +74 -38
  130. package/template/server/tsconfig.json +27 -89
  131. package/template/server/uploads/avatars/.gitkeep +1 -0
  132. package/template/server/vitest.config.ts +43 -98
  133. package/template/.agent/rules/client/01-project-structure.md +0 -326
  134. package/template/.agent/rules/client/02-component-patterns.md +0 -249
  135. package/template/.agent/rules/client/03-typescript-rules.md +0 -226
  136. package/template/.agent/rules/client/04-state-management.md +0 -474
  137. package/template/.agent/rules/client/05-api-integration.md +0 -129
  138. package/template/.agent/rules/client/06-forms-validation.md +0 -129
  139. package/template/.agent/rules/client/07-common-patterns.md +0 -150
  140. package/template/.agent/rules/client/08-color-system.md +0 -93
  141. package/template/.agent/rules/client/09-security-rules.md +0 -97
  142. package/template/.agent/rules/client/10-testing-strategy.md +0 -370
  143. package/template/.agent/rules/global/ai-edit-safety.md +0 -38
  144. package/template/.agent/rules/server/01-db-and-migrations.md +0 -242
  145. package/template/.agent/rules/server/02-general-rules.md +0 -111
  146. package/template/.agent/rules/server/03-migrations.md +0 -20
  147. package/template/.agent/rules/server/04-pagination.md +0 -130
  148. package/template/.agent/rules/server/05-project-conventions.md +0 -71
  149. package/template/.agent/rules/server/06-response-handling.md +0 -173
  150. package/template/.agent/rules/server/07-testing-strategy.md +0 -506
  151. package/template/.agent/rules/server/08-observability.md +0 -180
  152. package/template/.agent/rules/server/10-background-jobs-v2.md +0 -185
  153. package/template/.agent/rules/server/11-rate-limiting-v2.md +0 -210
  154. package/template/.agent/rules/server/12-performance-optimization.md +0 -567
  155. package/template/.claude/rules/client-01-project-structure.md +0 -327
  156. package/template/.claude/rules/client-02-component-patterns.md +0 -250
  157. package/template/.claude/rules/client-03-typescript-rules.md +0 -227
  158. package/template/.claude/rules/client-04-state-management.md +0 -475
  159. package/template/.claude/rules/client-05-api-integration.md +0 -130
  160. package/template/.claude/rules/client-06-forms-validation.md +0 -130
  161. package/template/.claude/rules/client-07-common-patterns.md +0 -151
  162. package/template/.claude/rules/client-08-color-system.md +0 -94
  163. package/template/.claude/rules/client-09-security-rules.md +0 -98
  164. package/template/.claude/rules/client-10-testing-strategy.md +0 -371
  165. package/template/.claude/rules/global-ai-edit-safety.md +0 -39
  166. package/template/.claude/rules/server-01-db-and-migrations.md +0 -243
  167. package/template/.claude/rules/server-02-general-rules.md +0 -112
  168. package/template/.claude/rules/server-03-migrations.md +0 -21
  169. package/template/.claude/rules/server-04-pagination.md +0 -131
  170. package/template/.claude/rules/server-05-project-conventions.md +0 -72
  171. package/template/.claude/rules/server-06-response-handling.md +0 -174
  172. package/template/.claude/rules/server-07-testing-strategy.md +0 -507
  173. package/template/.claude/rules/server-08-observability.md +0 -181
  174. package/template/.claude/rules/server-10-background-jobs-v2.md +0 -186
  175. package/template/.claude/rules/server-11-rate-limiting-v2.md +0 -211
  176. package/template/.claude/rules/server-12-performance-optimization.md +0 -568
  177. package/template/.cursor/rules/client-01-project-structure.mdc +0 -327
  178. package/template/.cursor/rules/client-02-component-patterns.mdc +0 -250
  179. package/template/.cursor/rules/client-03-typescript-rules.mdc +0 -227
  180. package/template/.cursor/rules/client-04-state-management.mdc +0 -475
  181. package/template/.cursor/rules/client-05-api-integration.mdc +0 -130
  182. package/template/.cursor/rules/client-06-forms-validation.mdc +0 -130
  183. package/template/.cursor/rules/client-07-common-patterns.mdc +0 -151
  184. package/template/.cursor/rules/client-08-color-system.mdc +0 -94
  185. package/template/.cursor/rules/client-09-security-rules.mdc +0 -98
  186. package/template/.cursor/rules/client-10-testing-strategy.mdc +0 -371
  187. package/template/.cursor/rules/global-ai-edit-safety.mdc +0 -39
  188. package/template/.cursor/rules/server-01-db-and-migrations.mdc +0 -243
  189. package/template/.cursor/rules/server-02-general-rules.mdc +0 -112
  190. package/template/.cursor/rules/server-03-migrations.mdc +0 -21
  191. package/template/.cursor/rules/server-04-pagination.mdc +0 -131
  192. package/template/.cursor/rules/server-05-project-conventions.mdc +0 -72
  193. package/template/.cursor/rules/server-06-response-handling.mdc +0 -174
  194. package/template/.cursor/rules/server-07-testing-strategy.mdc +0 -507
  195. package/template/.cursor/rules/server-08-observability.mdc +0 -181
  196. package/template/.cursor/rules/server-09-api-documentation-v2.mdc +0 -169
  197. package/template/.cursor/rules/server-10-background-jobs-v2.mdc +0 -186
  198. package/template/.cursor/rules/server-11-rate-limiting-v2.mdc +0 -211
  199. package/template/.cursor/rules/server-12-performance-optimization.mdc +0 -568
  200. package/template/CLAUDE.md +0 -207
  201. package/template/server/.tsc-aliasrc.json +0 -13
  202. package/template/server/IMPORT_FIX_CHECKLIST.md +0 -98
  203. package/template/server/IMPORT_FIX_COMPLETE.md +0 -89
  204. package/template/server/README.md +0 -183
  205. package/template/server/REMAINING_IMPORT_FIXES.md +0 -150
  206. package/template/server/SECURITY.md +0 -190
  207. package/template/server/Tigra-API.postman_collection.json +0 -733
  208. package/template/server/biome.json +0 -42
  209. package/template/server/scripts/fix-all-imports.ps1 +0 -52
  210. package/template/server/scripts/fix-imports-reference.ps1 +0 -16
  211. package/template/server/scripts/fix-imports.mjs +0 -55
  212. package/template/server/scripts/setup-env.js +0 -50
  213. package/template/server/scripts/wait-for-db.js +0 -60
  214. package/template/server/src/hooks/request-timing.hook.ts +0 -26
  215. package/template/server/src/libs/auth/authenticate.middleware.ts +0 -22
  216. package/template/server/src/libs/auth/rbac.middleware.test.ts +0 -134
  217. package/template/server/src/libs/auth/rbac.middleware.ts +0 -147
  218. package/template/server/src/libs/db.ts +0 -76
  219. package/template/server/src/libs/error-handler.ts +0 -89
  220. package/template/server/src/libs/queue.ts +0 -79
  221. package/template/server/src/modules/admin/admin.controller.ts +0 -122
  222. package/template/server/src/modules/admin/admin.routes.ts +0 -62
  223. package/template/server/src/modules/admin/admin.schemas.ts +0 -35
  224. package/template/server/src/modules/admin/admin.service.ts +0 -167
  225. package/template/server/src/modules/auth/auth.integration.test.ts +0 -150
  226. package/template/server/src/modules/auth/auth.service.test.ts +0 -119
  227. package/template/server/src/modules/auth/auth.types.ts +0 -97
  228. package/template/server/src/modules/resources/resources.controller.ts +0 -218
  229. package/template/server/src/modules/resources/resources.repo.ts +0 -253
  230. package/template/server/src/modules/resources/resources.routes.ts +0 -116
  231. package/template/server/src/modules/resources/resources.schemas.ts +0 -146
  232. package/template/server/src/modules/resources/resources.service.ts +0 -218
  233. package/template/server/src/modules/resources/resources.types.ts +0 -73
  234. package/template/server/src/plugins/rate-limit.plugin.ts +0 -21
  235. package/template/server/src/plugins/security.plugin.ts +0 -21
  236. package/template/server/src/routes/health.routes.ts +0 -31
  237. package/template/server/src/types/fastify.d.ts +0 -36
  238. package/template/server/src/utils/errors.ts +0 -108
  239. package/template/server/src/utils/pagination.ts +0 -120
  240. package/template/server/src/utils/response.ts +0 -110
  241. package/template/server/src/workers/file.worker.ts +0 -106
  242. package/template/server/tsconfig.build.json +0 -30
  243. package/template/server/tsconfig.test.json +0 -22
@@ -1,507 +0,0 @@
1
- ---
2
- trigger: always_on
3
- globs: "server/**/*"
4
- ---
5
-
6
- > **SCOPE**: These rules apply specifically to the **server** directory.
7
-
8
- # Testing Strategy & Best Practices
9
-
10
- ## Test Philosophy
11
-
12
- - **Test the behavior, not the implementation**
13
- - **Aim for 70%+ coverage** on services and critical utilities
14
- - **Fast tests** - Unit tests < 50ms, Integration < 500ms each
15
- - **Deterministic** - Tests must pass/fail consistently
16
-
17
- ---
18
-
19
- ## Test Stack
20
-
21
- ```json
22
- {
23
- "devDependencies": {
24
- "vitest": "^1.0.0",
25
- "supertest": "^6.3.0",
26
- "@faker-js/faker": "^8.0.0",
27
- "@testcontainers/mysql": "^10.0.0"
28
- }
29
- }
30
- ```
31
-
32
- **Tools:**
33
- - **Vitest** - Test runner (faster than Jest)
34
- - **Supertest** - HTTP endpoint testing
35
- - **Faker** - Generate realistic test data
36
- - **Testcontainers** - Isolated MySQL for integration tests
37
-
38
- ---
39
-
40
- ## Folder Structure
41
-
42
- ```
43
- src/
44
- ├── modules/
45
- │ └── users/
46
- │ ├── users.service.ts
47
- │ ├── users.service.test.ts # Unit tests
48
- │ ├── users.controller.ts
49
- │ └── users.integration.test.ts # Integration tests
50
- ├── libs/
51
- │ └── auth/
52
- │ ├── jwt.ts
53
- │ └── jwt.test.ts
54
- └── __tests__/
55
- ├── setup.ts # Global test setup
56
- ├── helpers/ # Test utilities
57
- │ ├── factory.ts # Data factories
58
- │ └── db.ts # Test DB helpers
59
- └── e2e/ # End-to-end tests
60
- └── auth-flow.test.ts
61
- ```
62
-
63
- ---
64
-
65
- ## Test Types & When to Use
66
-
67
- ### 1. Unit Tests (70% of tests)
68
- **Test:** Services, utilities, helpers
69
- **Mock:** Database, external APIs
70
- **Speed:** < 50ms per test
71
-
72
- ```typescript
73
- // users.service.test.ts
74
- import { describe, it, expect, vi, beforeEach } from 'vitest';
75
- import { UserService } from './users.service';
76
- import { UserRepository } from './users.repo';
77
-
78
- vi.mock('./users.repo');
79
-
80
- describe('UserService', () => {
81
- let service: UserService;
82
- let mockRepo: any;
83
-
84
- beforeEach(() => {
85
- mockRepo = {
86
- findById: vi.fn(),
87
- create: vi.fn(),
88
- };
89
- service = new UserService(mockRepo);
90
- });
91
-
92
- describe('getUserById', () => {
93
- it('should return user when found', async () => {
94
- const mockUser = { id: '1', email: 'test@example.com' };
95
- mockRepo.findById.mockResolvedValue(mockUser);
96
-
97
- const result = await service.getUserById('1');
98
-
99
- expect(result).toEqual(mockUser);
100
- expect(mockRepo.findById).toHaveBeenCalledWith('1');
101
- });
102
-
103
- it('should throw NotFoundError when user does not exist', async () => {
104
- mockRepo.findById.mockResolvedValue(null);
105
-
106
- await expect(service.getUserById('999'))
107
- .rejects.toThrow('User not found');
108
- });
109
- });
110
- });
111
- ```
112
-
113
- ### 2. Integration Tests (20% of tests)
114
- **Test:** API endpoints with real DB
115
- **Mock:** External APIs only
116
- **Speed:** < 500ms per test
117
-
118
- ```typescript
119
- // users.integration.test.ts
120
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
121
- import { build } from '../app';
122
- import { setupTestDB, cleanupTestDB } from '../__tests__/helpers/db';
123
-
124
- describe('Users API', () => {
125
- let app: any;
126
-
127
- beforeAll(async () => {
128
- await setupTestDB();
129
- app = await build();
130
- });
131
-
132
- afterAll(async () => {
133
- await app.close();
134
- await cleanupTestDB();
135
- });
136
-
137
- describe('POST /api/v1/users', () => {
138
- it('should create user with valid data', async () => {
139
- const response = await app.inject({
140
- method: 'POST',
141
- url: '/api/v1/users',
142
- payload: {
143
- email: 'test@example.com',
144
- password: 'SecurePass123!',
145
- },
146
- });
147
-
148
- expect(response.statusCode).toBe(201);
149
- expect(response.json()).toMatchObject({
150
- success: true,
151
- message: 'User created successfully',
152
- data: {
153
- email: 'test@example.com',
154
- },
155
- });
156
- });
157
-
158
- it('should reject invalid email', async () => {
159
- const response = await app.inject({
160
- method: 'POST',
161
- url: '/api/v1/users',
162
- payload: {
163
- email: 'invalid-email',
164
- password: 'SecurePass123!',
165
- },
166
- });
167
-
168
- expect(response.statusCode).toBe(400);
169
- expect(response.json().success).toBe(false);
170
- });
171
- });
172
- });
173
- ```
174
-
175
- ### 3. E2E Tests (10% of tests)
176
- **Test:** Complete user flows
177
- **Mock:** Nothing (real environment)
178
- **Speed:** < 2s per flow
179
-
180
- ```typescript
181
- // __tests__/e2e/auth-flow.test.ts
182
- describe('Authentication Flow', () => {
183
- it('should complete full auth lifecycle', async () => {
184
- // 1. Register
185
- const registerRes = await app.inject({
186
- method: 'POST',
187
- url: '/api/v1/auth/register',
188
- payload: { email: 'user@test.com', password: 'Pass123!' },
189
- });
190
- expect(registerRes.statusCode).toBe(201);
191
-
192
- // 2. Login
193
- const loginRes = await app.inject({
194
- method: 'POST',
195
- url: '/api/v1/auth/login',
196
- payload: { email: 'user@test.com', password: 'Pass123!' },
197
- });
198
- const { accessToken } = loginRes.json().data;
199
-
200
- // 3. Access protected route
201
- const profileRes = await app.inject({
202
- method: 'GET',
203
- url: '/api/v1/users/me',
204
- headers: { Authorization: `Bearer ${accessToken}` },
205
- });
206
- expect(profileRes.statusCode).toBe(200);
207
-
208
- // 4. Logout
209
- const logoutRes = await app.inject({
210
- method: 'POST',
211
- url: '/api/v1/auth/logout',
212
- headers: { Authorization: `Bearer ${accessToken}` },
213
- });
214
- expect(logoutRes.statusCode).toBe(200);
215
- });
216
- });
217
- ```
218
-
219
- ---
220
-
221
- ## Test Database Setup
222
-
223
- ### Option 1: Testcontainers (Recommended)
224
- Real MySQL in Docker, isolated per test suite.
225
-
226
- ```typescript
227
- // __tests__/helpers/db.ts
228
- import { MySqlContainer } from '@testcontainers/mysql';
229
- import { PrismaClient } from '@prisma/client';
230
-
231
- let container: MySqlContainer;
232
- let prisma: PrismaClient;
233
-
234
- export async function setupTestDB() {
235
- container = await new MySqlContainer('mysql:8.0')
236
- .withDatabase('test_db')
237
- .start();
238
-
239
- const DATABASE_URL = container.getConnectionUri();
240
- process.env.DATABASE_URL = DATABASE_URL;
241
-
242
- prisma = new PrismaClient();
243
- await prisma.$executeRaw`SET FOREIGN_KEY_CHECKS = 0`;
244
- // Run migrations
245
- await prisma.$executeRaw`/* Your schema setup */`;
246
- }
247
-
248
- export async function cleanupTestDB() {
249
- await prisma.$disconnect();
250
- await container.stop();
251
- }
252
-
253
- export async function clearDB() {
254
- const tables = await prisma.$queryRaw`SHOW TABLES`;
255
- for (const table of tables) {
256
- await prisma.$executeRawUnsafe(`TRUNCATE TABLE ${Object.values(table)[0]}`);
257
- }
258
- }
259
- ```
260
-
261
- ### Option 2: Separate Test Database
262
- Simpler but requires manual setup.
263
-
264
- ```typescript
265
- // __tests__/helpers/db.ts
266
- export async function setupTestDB() {
267
- process.env.DATABASE_URL = 'mysql://root:pass@localhost:3306/test_db';
268
- await prisma.$executeRaw`DROP DATABASE IF EXISTS test_db`;
269
- await prisma.$executeRaw`CREATE DATABASE test_db`;
270
- // Run migrations
271
- }
272
- ```
273
-
274
- ---
275
-
276
- ## Data Factories
277
-
278
- Use factories to create consistent test data:
279
-
280
- ```typescript
281
- // __tests__/helpers/factory.ts
282
- import { faker } from '@faker-js/faker';
283
-
284
- export const UserFactory = {
285
- build: (overrides = {}) => ({
286
- id: faker.string.uuid(),
287
- email: faker.internet.email(),
288
- password: 'SecurePass123!',
289
- createdAt: new Date(),
290
- ...overrides,
291
- }),
292
-
293
- create: async (overrides = {}) => {
294
- const data = UserFactory.build(overrides);
295
- return await prisma.user.create({ data });
296
- },
297
- };
298
-
299
- // Usage in tests
300
- it('should update user', async () => {
301
- const user = await UserFactory.create();
302
- // Test with real DB entity
303
- });
304
- ```
305
-
306
- ---
307
-
308
- ## Mocking Patterns
309
-
310
- ### Mock External APIs
311
- ```typescript
312
- import { vi } from 'vitest';
313
-
314
- vi.mock('../libs/stripe', () => ({
315
- stripeClient: {
316
- charges: {
317
- create: vi.fn().mockResolvedValue({ id: 'ch_123', status: 'succeeded' }),
318
- },
319
- },
320
- }));
321
- ```
322
-
323
- ### Mock Redis
324
- ```typescript
325
- vi.mock('../libs/redis', () => ({
326
- redis: {
327
- get: vi.fn(),
328
- set: vi.fn(),
329
- del: vi.fn(),
330
- },
331
- }));
332
- ```
333
-
334
- ### Mock Environment Variables
335
- ```typescript
336
- beforeEach(() => {
337
- process.env.JWT_SECRET = 'test-secret';
338
- process.env.NODE_ENV = 'test';
339
- });
340
- ```
341
-
342
- ---
343
-
344
- ## Testing Best Practices
345
-
346
- ### ✅ DO:
347
- - **Test edge cases**: Empty arrays, null values, boundary conditions
348
- - **Use descriptive test names**: `it('should throw error when email is already taken')`
349
- - **One assertion per test** (or closely related assertions)
350
- - **Clean up after tests**: Reset mocks, clear DB
351
- - **Use factories** for consistent test data
352
-
353
- ### ❌ DON'T:
354
- - Test Prisma/Fastify internals (trust the library)
355
- - Write tests that depend on execution order
356
- - Mock everything (integration tests need real DB)
357
- - Use production database for tests
358
- - Commit `.env.test` with real credentials
359
-
360
- ---
361
-
362
- ## Coverage Requirements
363
-
364
- ```json
365
- // package.json
366
- {
367
- "scripts": {
368
- "test": "vitest run",
369
- "test:watch": "vitest",
370
- "test:coverage": "vitest run --coverage"
371
- }
372
- }
373
- ```
374
-
375
- **Minimum Coverage:**
376
- - **Services**: 80%
377
- - **Controllers**: 60% (mostly happy paths)
378
- - **Utilities**: 90%
379
- - **Overall**: 70%
380
-
381
- **Ignore from coverage:**
382
- - Migration files
383
- - Type definitions
384
- - Config files
385
-
386
- ---
387
-
388
- ## CI Integration
389
-
390
- ```yaml
391
- # .github/workflows/test.yml
392
- name: Tests
393
- on: [push, pull_request]
394
- jobs:
395
- test:
396
- runs-on: ubuntu-latest
397
- services:
398
- mysql:
399
- image: mysql:8.0
400
- env:
401
- MYSQL_ROOT_PASSWORD: test
402
- MYSQL_DATABASE: test_db
403
- ports:
404
- - 3306:3306
405
- steps:
406
- - uses: actions/checkout@v3
407
- - uses: actions/setup-node@v3
408
- - run: npm ci
409
- - run: npm run test:coverage
410
- - run: npm run test:e2e
411
- ```
412
-
413
- ---
414
-
415
- ## Common Test Scenarios
416
-
417
- ### Testing Authentication
418
- ```typescript
419
- describe('Protected Routes', () => {
420
- it('should reject requests without token', async () => {
421
- const res = await app.inject({
422
- method: 'GET',
423
- url: '/api/v1/users/me',
424
- });
425
- expect(res.statusCode).toBe(401);
426
- });
427
-
428
- it('should accept valid token', async () => {
429
- const token = generateTestToken({ userId: '123' });
430
- const res = await app.inject({
431
- method: 'GET',
432
- url: '/api/v1/users/me',
433
- headers: { Authorization: `Bearer ${token}` },
434
- });
435
- expect(res.statusCode).toBe(200);
436
- });
437
- });
438
- ```
439
-
440
- ### Testing Pagination
441
- ```typescript
442
- it('should paginate results correctly', async () => {
443
- await UserFactory.create({ count: 25 }); // Create 25 users
444
-
445
- const res = await app.inject({
446
- method: 'GET',
447
- url: '/api/v1/users?page=2&limit=10',
448
- });
449
-
450
- const { data } = res.json();
451
- expect(data.items).toHaveLength(10);
452
- expect(data.pagination).toMatchObject({
453
- page: 2,
454
- limit: 10,
455
- totalItems: 25,
456
- totalPages: 3,
457
- hasNextPage: true,
458
- hasPreviousPage: true,
459
- });
460
- });
461
- ```
462
-
463
- ### Testing Transactions
464
- ```typescript
465
- it('should rollback on error', async () => {
466
- await expect(
467
- prisma.$transaction(async (tx) => {
468
- await tx.user.create({ data: { email: 'test@example.com' } });
469
- throw new Error('Simulated error');
470
- })
471
- ).rejects.toThrow();
472
-
473
- const users = await prisma.user.findMany();
474
- expect(users).toHaveLength(0); // Transaction rolled back
475
- });
476
- ```
477
-
478
- ---
479
-
480
- ## Debugging Tests
481
-
482
- ```typescript
483
- // Add logging to failing tests
484
- it.only('should debug this specific test', async () => {
485
- console.log('Request payload:', payload);
486
- const res = await app.inject({ ... });
487
- console.log('Response:', res.json());
488
- expect(res.statusCode).toBe(200);
489
- });
490
- ```
491
-
492
- **Vitest UI** for visual debugging:
493
- ```bash
494
- npm run test -- --ui
495
- ```
496
-
497
- ---
498
-
499
- ## Checklist
500
-
501
- - [ ] Unit tests for all services
502
- - [ ] Integration tests for all API endpoints
503
- - [ ] E2E test for critical user flows
504
- - [ ] 70%+ code coverage
505
- - [ ] Tests pass in CI
506
- - [ ] No hardcoded credentials in tests
507
- - [ ] Test DB isolated from development DB
@@ -1,181 +0,0 @@
1
- ---
2
- trigger: always_on
3
- globs: "server/**/*"
4
- ---
5
-
6
- > **SCOPE**: These rules apply specifically to the **server** directory.
7
-
8
- # Observability, Logging & Monitoring
9
-
10
- ## Logging Stack
11
-
12
- ```json
13
- {
14
- "dependencies": {
15
- "pino": "^8.16.0",
16
- "pino-pretty": "^10.2.0"
17
- }
18
- }
19
- ```
20
-
21
- ## Logger Setup
22
-
23
- ```typescript
24
- // libs/logger.ts
25
- import pino from 'pino';
26
-
27
- export const logger = pino({
28
- level: process.env.LOG_LEVEL || 'info',
29
- transport: process.env.NODE_ENV === 'development'
30
- ? { target: 'pino-pretty', options: { colorize: true } }
31
- : undefined,
32
- redact: {
33
- paths: ['req.headers.authorization', '*.password', '*.token'],
34
- censor: '[REDACTED]',
35
- },
36
- });
37
- ```
38
-
39
- ## Fastify Integration
40
-
41
- ```typescript
42
- // app.ts
43
- export const app = fastify({
44
- logger: {
45
- level: 'info',
46
- serializers: {
47
- req: (req) => ({ method: req.method, url: req.url, requestId: req.id }),
48
- res: (res) => ({ statusCode: res.statusCode }),
49
- },
50
- },
51
- requestIdHeader: 'x-request-id',
52
- genReqId: (req) => req.headers['x-request-id'] || crypto.randomUUID(),
53
- });
54
- ```
55
-
56
- ## Log Levels
57
-
58
- ```typescript
59
- logger.info({ userId }, 'Normal operations');
60
- logger.warn({ attempts: 3 }, 'Approaching limit');
61
- logger.error({ error, userId }, 'Errors needing attention');
62
- ```
63
-
64
- **Production:** `info` | **Development:** `debug`
65
-
66
- ## Error Logging
67
-
68
- ```typescript
69
- app.setErrorHandler((error, request, reply) => {
70
- request.log.error({
71
- err: error,
72
- requestId: request.id,
73
- userId: request.user?.id,
74
- }, error.message);
75
-
76
- reply.status(error.statusCode || 500).send({
77
- success: false,
78
- error: { code: error.code || 'INTERNAL_ERROR', message: error.message },
79
- });
80
- });
81
- ```
82
-
83
- ## Slow Query Logging
84
-
85
- ```typescript
86
- // libs/db.ts
87
- prisma.$on('query', (e) => {
88
- if (e.duration > 1000) {
89
- logger.warn({ query: e.query, duration: e.duration }, 'Slow query');
90
- }
91
- });
92
- ```
93
-
94
- ## Error Tracking (Sentry)
95
-
96
- ```typescript
97
- // libs/sentry.ts
98
- import * as Sentry from '@sentry/node';
99
-
100
- export function initSentry() {
101
- if (process.env.NODE_ENV === 'production') {
102
- Sentry.init({
103
- dsn: process.env.SENTRY_DSN,
104
- tracesSampleRate: 0.1,
105
- });
106
- }
107
- }
108
- ```
109
-
110
- ## Health Check Endpoint
111
-
112
- ```typescript
113
- app.get('/health', async (request, reply) => {
114
- const checks = {
115
- status: 'healthy',
116
- database: 'unknown',
117
- redis: 'unknown',
118
- };
119
-
120
- try {
121
- await prisma.$queryRaw`SELECT 1`;
122
- checks.database = 'connected';
123
- } catch {
124
- checks.database = 'disconnected';
125
- checks.status = 'unhealthy';
126
- }
127
-
128
- try {
129
- await redis.ping();
130
- checks.redis = 'connected';
131
- } catch {
132
- checks.redis = 'disconnected';
133
- checks.status = 'unhealthy';
134
- }
135
-
136
- return reply.status(checks.status === 'healthy' ? 200 : 503).send(checks);
137
- });
138
- ```
139
-
140
- ## Performance Tracking
141
-
142
- ```typescript
143
- app.addHook('onRequest', (request, reply, done) => {
144
- request.startTime = Date.now();
145
- done();
146
- });
147
-
148
- app.addHook('onResponse', (request, reply, done) => {
149
- const duration = Date.now() - request.startTime;
150
-
151
- if (duration > 500) {
152
- logger.warn({ url: request.url, duration }, 'Slow response');
153
- }
154
-
155
- done();
156
- });
157
- ```
158
-
159
- ## Best Practices
160
-
161
- ### ✅ DO:
162
- - Use structured logging (JSON)
163
- - Include request IDs for tracing
164
- - Redact sensitive data
165
- - Log errors with full context
166
- - Monitor response times
167
-
168
- ### ❌ DON'T:
169
- - Log passwords, tokens, API keys
170
- - Use `console.log` in production
171
- - Log sensitive user data
172
- - Skip error context
173
-
174
- ## Checklist
175
-
176
- - [ ] Pino logger configured
177
- - [ ] Request ID tracking enabled
178
- - [ ] Sensitive data redacted
179
- - [ ] Health check endpoint
180
- - [ ] Slow query logging
181
- - [ ] Error tracking (Sentry) in production