@veloxts/cli 0.4.0 → 0.4.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 (139) hide show
  1. package/dist/cli.js +2 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/generate.d.ts +17 -0
  4. package/dist/commands/generate.d.ts.map +1 -0
  5. package/dist/commands/generate.js +219 -0
  6. package/dist/commands/generate.js.map +1 -0
  7. package/dist/commands/migrate.d.ts +8 -3
  8. package/dist/commands/migrate.d.ts.map +1 -1
  9. package/dist/commands/migrate.js +17 -123
  10. package/dist/commands/migrate.js.map +1 -1
  11. package/dist/generators/base.d.ts +76 -0
  12. package/dist/generators/base.d.ts.map +1 -0
  13. package/dist/generators/base.js +271 -0
  14. package/dist/generators/base.js.map +1 -0
  15. package/dist/generators/generators/index.d.ts +17 -0
  16. package/dist/generators/generators/index.d.ts.map +1 -0
  17. package/dist/generators/generators/index.js +43 -0
  18. package/dist/generators/generators/index.js.map +1 -0
  19. package/dist/generators/generators/migration.d.ts +43 -0
  20. package/dist/generators/generators/migration.d.ts.map +1 -0
  21. package/dist/generators/generators/migration.js +121 -0
  22. package/dist/generators/generators/migration.js.map +1 -0
  23. package/dist/generators/generators/model.d.ts +38 -0
  24. package/dist/generators/generators/model.d.ts.map +1 -0
  25. package/dist/generators/generators/model.js +108 -0
  26. package/dist/generators/generators/model.js.map +1 -0
  27. package/dist/generators/generators/procedure.d.ts +37 -0
  28. package/dist/generators/generators/procedure.d.ts.map +1 -0
  29. package/dist/generators/generators/procedure.js +99 -0
  30. package/dist/generators/generators/procedure.js.map +1 -0
  31. package/dist/generators/generators/resource.d.ts +29 -0
  32. package/dist/generators/generators/resource.d.ts.map +1 -0
  33. package/dist/generators/generators/resource.js +124 -0
  34. package/dist/generators/generators/resource.js.map +1 -0
  35. package/dist/generators/generators/schema.d.ts +28 -0
  36. package/dist/generators/generators/schema.d.ts.map +1 -0
  37. package/dist/generators/generators/schema.js +83 -0
  38. package/dist/generators/generators/schema.js.map +1 -0
  39. package/dist/generators/generators/test.d.ts +28 -0
  40. package/dist/generators/generators/test.d.ts.map +1 -0
  41. package/dist/generators/generators/test.js +96 -0
  42. package/dist/generators/generators/test.js.map +1 -0
  43. package/dist/generators/index.d.ts +16 -0
  44. package/dist/generators/index.d.ts.map +1 -0
  45. package/dist/generators/index.js +16 -0
  46. package/dist/generators/index.js.map +1 -0
  47. package/dist/generators/registry.d.ts +97 -0
  48. package/dist/generators/registry.d.ts.map +1 -0
  49. package/dist/generators/registry.js +253 -0
  50. package/dist/generators/registry.js.map +1 -0
  51. package/dist/generators/templates/migration.d.ts +23 -0
  52. package/dist/generators/templates/migration.d.ts.map +1 -0
  53. package/dist/generators/templates/migration.js +389 -0
  54. package/dist/generators/templates/migration.js.map +1 -0
  55. package/dist/generators/templates/model.d.ts +37 -0
  56. package/dist/generators/templates/model.d.ts.map +1 -0
  57. package/dist/generators/templates/model.js +374 -0
  58. package/dist/generators/templates/model.js.map +1 -0
  59. package/dist/generators/templates/procedure.d.ts +25 -0
  60. package/dist/generators/templates/procedure.d.ts.map +1 -0
  61. package/dist/generators/templates/procedure.js +274 -0
  62. package/dist/generators/templates/procedure.js.map +1 -0
  63. package/dist/generators/templates/resource.d.ts +34 -0
  64. package/dist/generators/templates/resource.d.ts.map +1 -0
  65. package/dist/generators/templates/resource.js +550 -0
  66. package/dist/generators/templates/resource.js.map +1 -0
  67. package/dist/generators/templates/schema.d.ts +33 -0
  68. package/dist/generators/templates/schema.d.ts.map +1 -0
  69. package/dist/generators/templates/schema.js +248 -0
  70. package/dist/generators/templates/schema.js.map +1 -0
  71. package/dist/generators/templates/test.d.ts +31 -0
  72. package/dist/generators/templates/test.d.ts.map +1 -0
  73. package/dist/generators/templates/test.js +882 -0
  74. package/dist/generators/templates/test.js.map +1 -0
  75. package/dist/generators/types.d.ts +211 -0
  76. package/dist/generators/types.d.ts.map +1 -0
  77. package/dist/generators/types.js +54 -0
  78. package/dist/generators/types.js.map +1 -0
  79. package/dist/generators/utils/filesystem.d.ts +68 -0
  80. package/dist/generators/utils/filesystem.d.ts.map +1 -0
  81. package/dist/generators/utils/filesystem.js +217 -0
  82. package/dist/generators/utils/filesystem.js.map +1 -0
  83. package/dist/generators/utils/naming.d.ts +122 -0
  84. package/dist/generators/utils/naming.d.ts.map +1 -0
  85. package/dist/generators/utils/naming.js +198 -0
  86. package/dist/generators/utils/naming.js.map +1 -0
  87. package/dist/index.d.ts +4 -0
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +4 -0
  90. package/dist/index.js.map +1 -1
  91. package/dist/migrations/commands/fresh.d.ts +11 -0
  92. package/dist/migrations/commands/fresh.d.ts.map +1 -0
  93. package/dist/migrations/commands/fresh.js +164 -0
  94. package/dist/migrations/commands/fresh.js.map +1 -0
  95. package/dist/migrations/commands/index.d.ts +11 -0
  96. package/dist/migrations/commands/index.d.ts.map +1 -0
  97. package/dist/migrations/commands/index.js +11 -0
  98. package/dist/migrations/commands/index.js.map +1 -0
  99. package/dist/migrations/commands/reset.d.ts +11 -0
  100. package/dist/migrations/commands/reset.d.ts.map +1 -0
  101. package/dist/migrations/commands/reset.js +258 -0
  102. package/dist/migrations/commands/reset.js.map +1 -0
  103. package/dist/migrations/commands/rollback.d.ts +11 -0
  104. package/dist/migrations/commands/rollback.d.ts.map +1 -0
  105. package/dist/migrations/commands/rollback.js +241 -0
  106. package/dist/migrations/commands/rollback.js.map +1 -0
  107. package/dist/migrations/commands/run.d.ts +11 -0
  108. package/dist/migrations/commands/run.d.ts.map +1 -0
  109. package/dist/migrations/commands/run.js +183 -0
  110. package/dist/migrations/commands/run.js.map +1 -0
  111. package/dist/migrations/commands/status.d.ts +11 -0
  112. package/dist/migrations/commands/status.d.ts.map +1 -0
  113. package/dist/migrations/commands/status.js +154 -0
  114. package/dist/migrations/commands/status.js.map +1 -0
  115. package/dist/migrations/errors.d.ts +74 -0
  116. package/dist/migrations/errors.d.ts.map +1 -0
  117. package/dist/migrations/errors.js +155 -0
  118. package/dist/migrations/errors.js.map +1 -0
  119. package/dist/migrations/index.d.ts +13 -0
  120. package/dist/migrations/index.d.ts.map +1 -0
  121. package/dist/migrations/index.js +17 -0
  122. package/dist/migrations/index.js.map +1 -0
  123. package/dist/migrations/loader.d.ts +44 -0
  124. package/dist/migrations/loader.d.ts.map +1 -0
  125. package/dist/migrations/loader.js +181 -0
  126. package/dist/migrations/loader.js.map +1 -0
  127. package/dist/migrations/prisma-wrapper.d.ts +60 -0
  128. package/dist/migrations/prisma-wrapper.d.ts.map +1 -0
  129. package/dist/migrations/prisma-wrapper.js +184 -0
  130. package/dist/migrations/prisma-wrapper.js.map +1 -0
  131. package/dist/migrations/rollback-runner.d.ts +40 -0
  132. package/dist/migrations/rollback-runner.d.ts.map +1 -0
  133. package/dist/migrations/rollback-runner.js +191 -0
  134. package/dist/migrations/rollback-runner.js.map +1 -0
  135. package/dist/migrations/types.d.ts +214 -0
  136. package/dist/migrations/types.d.ts.map +1 -0
  137. package/dist/migrations/types.js +19 -0
  138. package/dist/migrations/types.js.map +1 -0
  139. package/package.json +29 -8
@@ -0,0 +1,882 @@
1
+ /**
2
+ * Test Template
3
+ *
4
+ * Generates Vitest test files for VeloxTS applications.
5
+ */
6
+ // ============================================================================
7
+ // Template Functions
8
+ // ============================================================================
9
+ /**
10
+ * Generate unit test for procedures
11
+ */
12
+ function generateProcedureUnitTest(entity) {
13
+ const { pascal, camel, kebab } = entity;
14
+ return `/**
15
+ * ${pascal} Procedures - Unit Tests
16
+ *
17
+ * Tests for ${pascal} procedure handlers.
18
+ */
19
+
20
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
21
+ // import { ${camel}Procedures } from '../procedures/${kebab}.js';
22
+
23
+ describe('${pascal} Procedures', () => {
24
+ // Mock context
25
+ const mockCtx = {
26
+ db: {
27
+ ${camel}: {
28
+ findUnique: vi.fn(),
29
+ findMany: vi.fn(),
30
+ create: vi.fn(),
31
+ update: vi.fn(),
32
+ delete: vi.fn(),
33
+ },
34
+ },
35
+ };
36
+
37
+ beforeEach(() => {
38
+ vi.clearAllMocks();
39
+ });
40
+
41
+ describe('get${pascal}', () => {
42
+ it('should return a ${camel} by id', async () => {
43
+ const mock${pascal} = {
44
+ id: 'test-uuid-1234',
45
+ // TODO: Add expected fields
46
+ createdAt: new Date(),
47
+ updatedAt: new Date(),
48
+ };
49
+
50
+ mockCtx.db.${camel}.findUnique.mockResolvedValue(mock${pascal});
51
+
52
+ // TODO: Call the procedure
53
+ // const result = await ${camel}Procedures.get${pascal}({
54
+ // input: { id: 'test-uuid-1234' },
55
+ // ctx: mockCtx,
56
+ // });
57
+
58
+ // expect(result).toEqual(mock${pascal});
59
+ // expect(mockCtx.db.${camel}.findUnique).toHaveBeenCalledWith({
60
+ // where: { id: 'test-uuid-1234' },
61
+ // });
62
+ });
63
+
64
+ it('should return null for non-existent ${camel}', async () => {
65
+ mockCtx.db.${camel}.findUnique.mockResolvedValue(null);
66
+
67
+ // TODO: Test not found case
68
+ });
69
+ });
70
+
71
+ describe('list${pascal}s', () => {
72
+ it('should return paginated ${camel}s', async () => {
73
+ const mock${pascal}s = [
74
+ { id: 'uuid-1', createdAt: new Date(), updatedAt: new Date() },
75
+ { id: 'uuid-2', createdAt: new Date(), updatedAt: new Date() },
76
+ ];
77
+
78
+ mockCtx.db.${camel}.findMany.mockResolvedValue(mock${pascal}s);
79
+
80
+ // TODO: Test list procedure
81
+ });
82
+ });
83
+
84
+ describe('create${pascal}', () => {
85
+ it('should create a new ${camel}', async () => {
86
+ const input = {
87
+ // TODO: Add create input fields
88
+ };
89
+
90
+ const created${pascal} = {
91
+ id: 'new-uuid',
92
+ ...input,
93
+ createdAt: new Date(),
94
+ updatedAt: new Date(),
95
+ };
96
+
97
+ mockCtx.db.${camel}.create.mockResolvedValue(created${pascal});
98
+
99
+ // TODO: Test create procedure
100
+ });
101
+
102
+ it('should validate required fields', async () => {
103
+ // TODO: Test validation errors
104
+ });
105
+ });
106
+
107
+ describe('update${pascal}', () => {
108
+ it('should update an existing ${camel}', async () => {
109
+ const existing${pascal} = {
110
+ id: 'test-uuid',
111
+ createdAt: new Date(),
112
+ updatedAt: new Date(),
113
+ };
114
+
115
+ mockCtx.db.${camel}.findUnique.mockResolvedValue(existing${pascal});
116
+ mockCtx.db.${camel}.update.mockResolvedValue({
117
+ ...existing${pascal},
118
+ // updated fields
119
+ });
120
+
121
+ // TODO: Test update procedure
122
+ });
123
+ });
124
+
125
+ describe('delete${pascal}', () => {
126
+ it('should delete a ${camel}', async () => {
127
+ mockCtx.db.${camel}.delete.mockResolvedValue({ id: 'test-uuid' });
128
+
129
+ // TODO: Test delete procedure
130
+ });
131
+ });
132
+ });
133
+ `;
134
+ }
135
+ /**
136
+ * Generate unit test for schemas
137
+ */
138
+ function generateSchemaUnitTest(entity) {
139
+ const { pascal, camel, kebab } = entity;
140
+ return `/**
141
+ * ${pascal} Schema - Unit Tests
142
+ *
143
+ * Tests for ${pascal} Zod validation schemas.
144
+ */
145
+
146
+ import { describe, it, expect } from 'vitest';
147
+ // import {
148
+ // ${camel}Schema,
149
+ // create${pascal}InputSchema,
150
+ // update${pascal}InputSchema,
151
+ // ${camel}IdParamSchema,
152
+ // } from '../schemas/${kebab}.schema.js';
153
+
154
+ describe('${pascal} Schema', () => {
155
+ describe('${camel}Schema', () => {
156
+ it('should validate a valid ${camel}', () => {
157
+ const valid${pascal} = {
158
+ id: '550e8400-e29b-41d4-a716-446655440000',
159
+ // TODO: Add required fields
160
+ createdAt: new Date(),
161
+ updatedAt: new Date(),
162
+ };
163
+
164
+ // const result = ${camel}Schema.safeParse(valid${pascal});
165
+ // expect(result.success).toBe(true);
166
+ });
167
+
168
+ it('should reject invalid id format', () => {
169
+ const invalid${pascal} = {
170
+ id: 'not-a-uuid',
171
+ createdAt: new Date(),
172
+ updatedAt: new Date(),
173
+ };
174
+
175
+ // const result = ${camel}Schema.safeParse(invalid${pascal});
176
+ // expect(result.success).toBe(false);
177
+ });
178
+
179
+ it('should require mandatory fields', () => {
180
+ const incomplete${pascal} = {
181
+ id: '550e8400-e29b-41d4-a716-446655440000',
182
+ // Missing required fields
183
+ };
184
+
185
+ // const result = ${camel}Schema.safeParse(incomplete${pascal});
186
+ // expect(result.success).toBe(false);
187
+ });
188
+ });
189
+
190
+ describe('create${pascal}InputSchema', () => {
191
+ it('should validate create input', () => {
192
+ const validInput = {
193
+ // TODO: Add create input fields
194
+ };
195
+
196
+ // const result = create${pascal}InputSchema.safeParse(validInput);
197
+ // expect(result.success).toBe(true);
198
+ });
199
+
200
+ it('should omit auto-generated fields', () => {
201
+ const inputWithId = {
202
+ id: '550e8400-e29b-41d4-a716-446655440000',
203
+ createdAt: new Date(),
204
+ // other fields
205
+ };
206
+
207
+ // The schema should strip id and timestamps
208
+ // const result = create${pascal}InputSchema.safeParse(inputWithId);
209
+ // expect(result.success).toBe(true);
210
+ // expect(result.data).not.toHaveProperty('id');
211
+ });
212
+ });
213
+
214
+ describe('update${pascal}InputSchema', () => {
215
+ it('should validate full update input', () => {
216
+ const validInput = {
217
+ // TODO: Add all updatable fields
218
+ };
219
+
220
+ // const result = update${pascal}InputSchema.safeParse(validInput);
221
+ // expect(result.success).toBe(true);
222
+ });
223
+ });
224
+
225
+ describe('${camel}IdParamSchema', () => {
226
+ it('should validate valid UUID', () => {
227
+ const validParam = { id: '550e8400-e29b-41d4-a716-446655440000' };
228
+
229
+ // const result = ${camel}IdParamSchema.safeParse(validParam);
230
+ // expect(result.success).toBe(true);
231
+ });
232
+
233
+ it('should reject invalid UUID', () => {
234
+ const invalidParam = { id: 'invalid' };
235
+
236
+ // const result = ${camel}IdParamSchema.safeParse(invalidParam);
237
+ // expect(result.success).toBe(false);
238
+ });
239
+ });
240
+ });
241
+ `;
242
+ }
243
+ /**
244
+ * Generate integration test
245
+ */
246
+ function generateIntegrationTest(entity) {
247
+ const { pascal, camel } = entity;
248
+ return `/**
249
+ * ${pascal} - Integration Tests
250
+ *
251
+ * Tests ${pascal} procedures with actual database interactions.
252
+ */
253
+
254
+ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
255
+ // import { createTestApp, cleanupTestApp } from '../test/setup.js';
256
+ // import type { TestApp } from '../test/setup.js';
257
+
258
+ describe('${pascal} Integration', () => {
259
+ // let app: TestApp;
260
+
261
+ beforeAll(async () => {
262
+ // app = await createTestApp();
263
+ });
264
+
265
+ afterAll(async () => {
266
+ // await cleanupTestApp(app);
267
+ });
268
+
269
+ beforeEach(async () => {
270
+ // Clean up test data before each test
271
+ // await app.db.${camel}.deleteMany();
272
+ });
273
+
274
+ describe('CRUD Operations', () => {
275
+ it('should create and retrieve a ${camel}', async () => {
276
+ // const created = await app.procedures.${camel}.create${pascal}({
277
+ // // input fields
278
+ // });
279
+
280
+ // expect(created.id).toBeDefined();
281
+
282
+ // const retrieved = await app.procedures.${camel}.get${pascal}({
283
+ // id: created.id,
284
+ // });
285
+
286
+ // expect(retrieved).toEqual(created);
287
+ });
288
+
289
+ it('should update a ${camel}', async () => {
290
+ // const created = await app.procedures.${camel}.create${pascal}({
291
+ // // input fields
292
+ // });
293
+
294
+ // const updated = await app.procedures.${camel}.update${pascal}({
295
+ // id: created.id,
296
+ // // updated fields
297
+ // });
298
+
299
+ // expect(updated.id).toBe(created.id);
300
+ // expect(updated.updatedAt).not.toBe(created.updatedAt);
301
+ });
302
+
303
+ it('should delete a ${camel}', async () => {
304
+ // const created = await app.procedures.${camel}.create${pascal}({
305
+ // // input fields
306
+ // });
307
+
308
+ // await app.procedures.${camel}.delete${pascal}({
309
+ // id: created.id,
310
+ // });
311
+
312
+ // const deleted = await app.procedures.${camel}.get${pascal}({
313
+ // id: created.id,
314
+ // });
315
+
316
+ // expect(deleted).toBeNull();
317
+ });
318
+
319
+ it('should list ${camel}s with pagination', async () => {
320
+ // Create multiple ${camel}s
321
+ // for (let i = 0; i < 5; i++) {
322
+ // await app.procedures.${camel}.create${pascal}({
323
+ // // input fields
324
+ // });
325
+ // }
326
+
327
+ // const page1 = await app.procedures.${camel}.list${pascal}s({
328
+ // page: 1,
329
+ // limit: 2,
330
+ // });
331
+
332
+ // expect(page1.data).toHaveLength(2);
333
+ // expect(page1.meta.total).toBe(5);
334
+ // expect(page1.meta.totalPages).toBe(3);
335
+ });
336
+ });
337
+
338
+ describe('Error Handling', () => {
339
+ it('should handle not found errors', async () => {
340
+ // const result = await app.procedures.${camel}.get${pascal}({
341
+ // id: 'non-existent-uuid',
342
+ // });
343
+
344
+ // expect(result).toBeNull();
345
+ });
346
+
347
+ it('should handle validation errors', async () => {
348
+ // await expect(
349
+ // app.procedures.${camel}.create${pascal}({
350
+ // // invalid input
351
+ // })
352
+ // ).rejects.toThrow();
353
+ });
354
+ });
355
+ });
356
+ `;
357
+ }
358
+ /**
359
+ * Generate E2E test
360
+ */
361
+ function generateE2ETest(entity) {
362
+ const { pascal, camel, kebab } = entity;
363
+ return `/**
364
+ * ${pascal} - E2E Tests
365
+ *
366
+ * End-to-end tests for ${pascal} REST API endpoints.
367
+ */
368
+
369
+ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
370
+ // import { createTestServer, cleanupTestServer } from '../test/e2e-setup.js';
371
+ // import type { TestServer } from '../test/e2e-setup.js';
372
+
373
+ describe('${pascal} API E2E', () => {
374
+ // let server: TestServer;
375
+ const baseUrl = 'http://localhost:3000/api';
376
+
377
+ beforeAll(async () => {
378
+ // server = await createTestServer();
379
+ });
380
+
381
+ afterAll(async () => {
382
+ // await cleanupTestServer(server);
383
+ });
384
+
385
+ beforeEach(async () => {
386
+ // Clean up test data
387
+ // await server.db.${camel}.deleteMany();
388
+ });
389
+
390
+ describe('GET /api/${kebab}s', () => {
391
+ it('should return a list of ${camel}s', async () => {
392
+ // const response = await fetch(\`\${baseUrl}/${kebab}s\`);
393
+ // const data = await response.json();
394
+
395
+ // expect(response.status).toBe(200);
396
+ // expect(data).toHaveProperty('data');
397
+ // expect(data).toHaveProperty('meta');
398
+ });
399
+
400
+ it('should support pagination', async () => {
401
+ // const response = await fetch(\`\${baseUrl}/${kebab}s?page=1&limit=10\`);
402
+ // const data = await response.json();
403
+
404
+ // expect(response.status).toBe(200);
405
+ // expect(data.meta.page).toBe(1);
406
+ // expect(data.meta.limit).toBe(10);
407
+ });
408
+ });
409
+
410
+ describe('GET /api/${kebab}s/:id', () => {
411
+ it('should return a single ${camel}', async () => {
412
+ // First create a ${camel}
413
+ // const createResponse = await fetch(\`\${baseUrl}/${kebab}s\`, {
414
+ // method: 'POST',
415
+ // headers: { 'Content-Type': 'application/json' },
416
+ // body: JSON.stringify({ /* fields */ }),
417
+ // });
418
+ // const created = await createResponse.json();
419
+
420
+ // const response = await fetch(\`\${baseUrl}/${kebab}s/\${created.id}\`);
421
+ // const data = await response.json();
422
+
423
+ // expect(response.status).toBe(200);
424
+ // expect(data.id).toBe(created.id);
425
+ });
426
+
427
+ it('should return 404 for non-existent ${camel}', async () => {
428
+ // const response = await fetch(\`\${baseUrl}/${kebab}s/non-existent-id\`);
429
+
430
+ // expect(response.status).toBe(404);
431
+ });
432
+ });
433
+
434
+ describe('POST /api/${kebab}s', () => {
435
+ it('should create a new ${camel}', async () => {
436
+ // const payload = {
437
+ // // TODO: Add required fields
438
+ // };
439
+
440
+ // const response = await fetch(\`\${baseUrl}/${kebab}s\`, {
441
+ // method: 'POST',
442
+ // headers: { 'Content-Type': 'application/json' },
443
+ // body: JSON.stringify(payload),
444
+ // });
445
+ // const data = await response.json();
446
+
447
+ // expect(response.status).toBe(201);
448
+ // expect(data.id).toBeDefined();
449
+ });
450
+
451
+ it('should return 400 for invalid input', async () => {
452
+ // const response = await fetch(\`\${baseUrl}/${kebab}s\`, {
453
+ // method: 'POST',
454
+ // headers: { 'Content-Type': 'application/json' },
455
+ // body: JSON.stringify({}),
456
+ // });
457
+
458
+ // expect(response.status).toBe(400);
459
+ });
460
+ });
461
+
462
+ describe('PUT /api/${kebab}s/:id', () => {
463
+ it('should update a ${camel}', async () => {
464
+ // Create first
465
+ // const createResponse = await fetch(\`\${baseUrl}/${kebab}s\`, {
466
+ // method: 'POST',
467
+ // headers: { 'Content-Type': 'application/json' },
468
+ // body: JSON.stringify({ /* fields */ }),
469
+ // });
470
+ // const created = await createResponse.json();
471
+
472
+ // Update
473
+ // const response = await fetch(\`\${baseUrl}/${kebab}s/\${created.id}\`, {
474
+ // method: 'PUT',
475
+ // headers: { 'Content-Type': 'application/json' },
476
+ // body: JSON.stringify({ /* updated fields */ }),
477
+ // });
478
+ // const data = await response.json();
479
+
480
+ // expect(response.status).toBe(200);
481
+ });
482
+ });
483
+
484
+ describe('DELETE /api/${kebab}s/:id', () => {
485
+ it('should delete a ${camel}', async () => {
486
+ // Create first
487
+ // const createResponse = await fetch(\`\${baseUrl}/${kebab}s\`, {
488
+ // method: 'POST',
489
+ // headers: { 'Content-Type': 'application/json' },
490
+ // body: JSON.stringify({ /* fields */ }),
491
+ // });
492
+ // const created = await createResponse.json();
493
+
494
+ // Delete
495
+ // const response = await fetch(\`\${baseUrl}/${kebab}s/\${created.id}\`, {
496
+ // method: 'DELETE',
497
+ // });
498
+
499
+ // expect(response.status).toBe(200);
500
+
501
+ // Verify deleted
502
+ // const getResponse = await fetch(\`\${baseUrl}/${kebab}s/\${created.id}\`);
503
+ // expect(getResponse.status).toBe(404);
504
+ });
505
+ });
506
+ });
507
+ `;
508
+ }
509
+ /**
510
+ * Generate generic unit test
511
+ */
512
+ function generateGenericUnitTest(entity) {
513
+ const { pascal, camel, kebab } = entity;
514
+ return `/**
515
+ * ${pascal} - Unit Tests
516
+ *
517
+ * Tests for ${pascal} module.
518
+ */
519
+
520
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
521
+ // import { ${camel} } from '../${kebab}.js';
522
+
523
+ describe('${pascal}', () => {
524
+ beforeEach(() => {
525
+ vi.clearAllMocks();
526
+ });
527
+
528
+ describe('initialization', () => {
529
+ it('should initialize correctly', () => {
530
+ // TODO: Test initialization
531
+ expect(true).toBe(true);
532
+ });
533
+ });
534
+
535
+ describe('core functionality', () => {
536
+ it('should perform main operation', () => {
537
+ // TODO: Test main functionality
538
+ });
539
+
540
+ it('should handle edge cases', () => {
541
+ // TODO: Test edge cases
542
+ });
543
+ });
544
+
545
+ describe('error handling', () => {
546
+ it('should handle invalid input', () => {
547
+ // TODO: Test error scenarios
548
+ });
549
+
550
+ it('should throw on failure', () => {
551
+ // TODO: Test error throwing
552
+ });
553
+ });
554
+ });
555
+ `;
556
+ }
557
+ /**
558
+ * Generate service unit test
559
+ */
560
+ function generateServiceUnitTest(entity) {
561
+ const { pascal, camel, kebab } = entity;
562
+ return `/**
563
+ * ${pascal}Service - Unit Tests
564
+ *
565
+ * Tests for ${pascal}Service business logic.
566
+ */
567
+
568
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
569
+ // import { ${pascal}Service } from '../services/${kebab}.service.js';
570
+
571
+ describe('${pascal}Service', () => {
572
+ // let service: ${pascal}Service;
573
+ // let mockDb: any;
574
+
575
+ beforeEach(() => {
576
+ vi.clearAllMocks();
577
+
578
+ // mockDb = {
579
+ // ${camel}: {
580
+ // findUnique: vi.fn(),
581
+ // findMany: vi.fn(),
582
+ // create: vi.fn(),
583
+ // update: vi.fn(),
584
+ // delete: vi.fn(),
585
+ // },
586
+ // };
587
+
588
+ // service = new ${pascal}Service(mockDb);
589
+ });
590
+
591
+ describe('findById', () => {
592
+ it('should find ${camel} by id', async () => {
593
+ // const mock${pascal} = { id: 'test-id', name: 'Test' };
594
+ // mockDb.${camel}.findUnique.mockResolvedValue(mock${pascal});
595
+
596
+ // const result = await service.findById('test-id');
597
+
598
+ // expect(result).toEqual(mock${pascal});
599
+ // expect(mockDb.${camel}.findUnique).toHaveBeenCalledWith({
600
+ // where: { id: 'test-id' },
601
+ // });
602
+ });
603
+
604
+ it('should return null when not found', async () => {
605
+ // mockDb.${camel}.findUnique.mockResolvedValue(null);
606
+
607
+ // const result = await service.findById('non-existent');
608
+
609
+ // expect(result).toBeNull();
610
+ });
611
+ });
612
+
613
+ describe('create', () => {
614
+ it('should create a new ${camel}', async () => {
615
+ // const input = { name: 'New ${pascal}' };
616
+ // const created = { id: 'new-id', ...input };
617
+ // mockDb.${camel}.create.mockResolvedValue(created);
618
+
619
+ // const result = await service.create(input);
620
+
621
+ // expect(result).toEqual(created);
622
+ });
623
+
624
+ it('should validate input before creation', async () => {
625
+ // TODO: Test validation
626
+ });
627
+ });
628
+
629
+ describe('update', () => {
630
+ it('should update an existing ${camel}', async () => {
631
+ // const existing = { id: 'test-id', name: 'Old' };
632
+ // const updated = { ...existing, name: 'New' };
633
+ // mockDb.${camel}.findUnique.mockResolvedValue(existing);
634
+ // mockDb.${camel}.update.mockResolvedValue(updated);
635
+
636
+ // const result = await service.update('test-id', { name: 'New' });
637
+
638
+ // expect(result.name).toBe('New');
639
+ });
640
+ });
641
+
642
+ describe('delete', () => {
643
+ it('should delete a ${camel}', async () => {
644
+ // mockDb.${camel}.delete.mockResolvedValue({ id: 'test-id' });
645
+
646
+ // await service.delete('test-id');
647
+
648
+ // expect(mockDb.${camel}.delete).toHaveBeenCalledWith({
649
+ // where: { id: 'test-id' },
650
+ // });
651
+ });
652
+ });
653
+
654
+ describe('business logic', () => {
655
+ it('should apply business rules', async () => {
656
+ // TODO: Test specific business logic
657
+ });
658
+ });
659
+ });
660
+ `;
661
+ }
662
+ /**
663
+ * Generate model unit test
664
+ */
665
+ function generateModelUnitTest(entity) {
666
+ const { pascal, camel, kebab } = entity;
667
+ return `/**
668
+ * ${pascal} Model - Unit Tests
669
+ *
670
+ * Tests for ${pascal} Prisma model operations.
671
+ */
672
+
673
+ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
674
+ // import { PrismaClient } from '@prisma/client';
675
+ // import { mockDeep, mockReset, DeepMockProxy } from 'vitest-mock-extended';
676
+
677
+ describe('${pascal} Model', () => {
678
+ // let prisma: DeepMockProxy<PrismaClient>;
679
+
680
+ beforeEach(() => {
681
+ // mockReset(prisma);
682
+ });
683
+
684
+ describe('findUnique', () => {
685
+ it('should find ${camel} by id', async () => {
686
+ // const mock${pascal} = {
687
+ // id: 'test-${kebab}-uuid',
688
+ // createdAt: new Date(),
689
+ // updatedAt: new Date(),
690
+ // };
691
+
692
+ // prisma.${camel}.findUnique.mockResolvedValue(mock${pascal});
693
+
694
+ // const result = await prisma.${camel}.findUnique({
695
+ // where: { id: 'test-uuid' },
696
+ // });
697
+
698
+ // expect(result).toEqual(mock${pascal});
699
+ });
700
+ });
701
+
702
+ describe('findMany', () => {
703
+ it('should find all ${camel}s', async () => {
704
+ // const mock${pascal}s = [
705
+ // { id: 'uuid-1', createdAt: new Date(), updatedAt: new Date() },
706
+ // { id: 'uuid-2', createdAt: new Date(), updatedAt: new Date() },
707
+ // ];
708
+
709
+ // prisma.${camel}.findMany.mockResolvedValue(mock${pascal}s);
710
+
711
+ // const result = await prisma.${camel}.findMany();
712
+
713
+ // expect(result).toHaveLength(2);
714
+ });
715
+
716
+ it('should filter ${camel}s', async () => {
717
+ // prisma.${camel}.findMany.mockResolvedValue([]);
718
+
719
+ // const result = await prisma.${camel}.findMany({
720
+ // where: { /* filter */ },
721
+ // });
722
+
723
+ // expect(result).toEqual([]);
724
+ });
725
+ });
726
+
727
+ describe('create', () => {
728
+ it('should create a ${camel}', async () => {
729
+ // const input = {
730
+ // // TODO: Add fields
731
+ // };
732
+
733
+ // const created = {
734
+ // id: 'new-uuid',
735
+ // ...input,
736
+ // createdAt: new Date(),
737
+ // updatedAt: new Date(),
738
+ // };
739
+
740
+ // prisma.${camel}.create.mockResolvedValue(created);
741
+
742
+ // const result = await prisma.${camel}.create({ data: input });
743
+
744
+ // expect(result.id).toBeDefined();
745
+ });
746
+ });
747
+
748
+ describe('update', () => {
749
+ it('should update a ${camel}', async () => {
750
+ // const updated = {
751
+ // id: 'test-uuid',
752
+ // // updated fields
753
+ // createdAt: new Date(),
754
+ // updatedAt: new Date(),
755
+ // };
756
+
757
+ // prisma.${camel}.update.mockResolvedValue(updated);
758
+
759
+ // const result = await prisma.${camel}.update({
760
+ // where: { id: 'test-uuid' },
761
+ // data: { /* updates */ },
762
+ // });
763
+
764
+ // expect(result).toEqual(updated);
765
+ });
766
+ });
767
+
768
+ describe('delete', () => {
769
+ it('should delete a ${camel}', async () => {
770
+ // const deleted = { id: 'test-uuid' };
771
+
772
+ // prisma.${camel}.delete.mockResolvedValue(deleted);
773
+
774
+ // const result = await prisma.${camel}.delete({
775
+ // where: { id: 'test-uuid' },
776
+ // });
777
+
778
+ // expect(result.id).toBe('test-uuid');
779
+ });
780
+ });
781
+ });
782
+ `;
783
+ }
784
+ // ============================================================================
785
+ // Template Export
786
+ // ============================================================================
787
+ /**
788
+ * Test template function
789
+ */
790
+ export const testTemplate = (ctx) => {
791
+ const { type, target } = ctx.options;
792
+ // E2E tests
793
+ if (type === 'e2e') {
794
+ return generateE2ETest(ctx.entity);
795
+ }
796
+ // Integration tests
797
+ if (type === 'integration') {
798
+ return generateIntegrationTest(ctx.entity);
799
+ }
800
+ // Unit tests by target
801
+ switch (target) {
802
+ case 'procedure':
803
+ return generateProcedureUnitTest(ctx.entity);
804
+ case 'schema':
805
+ return generateSchemaUnitTest(ctx.entity);
806
+ case 'model':
807
+ return generateModelUnitTest(ctx.entity);
808
+ case 'service':
809
+ return generateServiceUnitTest(ctx.entity);
810
+ default:
811
+ return generateGenericUnitTest(ctx.entity);
812
+ }
813
+ };
814
+ /**
815
+ * Get an output path for a test file
816
+ */
817
+ export function getTestPath(entity, options) {
818
+ const { type, target } = options;
819
+ const suffix = type === 'e2e' ? 'e2e' : type === 'integration' ? 'integration' : 'test';
820
+ // Determine directory based on test type
821
+ if (type === 'e2e') {
822
+ return `tests/e2e/${entity.kebab}.${suffix}.ts`;
823
+ }
824
+ if (type === 'integration') {
825
+ return `tests/integration/${entity.kebab}.${suffix}.ts`;
826
+ }
827
+ // Unit tests go alongside the source or in __tests__
828
+ switch (target) {
829
+ case 'procedure':
830
+ return `src/procedures/__tests__/${entity.kebab}.${suffix}.ts`;
831
+ case 'schema':
832
+ return `src/schemas/__tests__/${entity.kebab}.${suffix}.ts`;
833
+ case 'model':
834
+ return `src/models/__tests__/${entity.kebab}.${suffix}.ts`;
835
+ case 'service':
836
+ return `src/services/__tests__/${entity.kebab}.${suffix}.ts`;
837
+ default:
838
+ return `src/__tests__/${entity.kebab}.${suffix}.ts`;
839
+ }
840
+ }
841
+ /**
842
+ * Generate all files for a test
843
+ */
844
+ export function generateTestFiles(ctx) {
845
+ const content = testTemplate(ctx);
846
+ return [
847
+ {
848
+ path: getTestPath(ctx.entity, ctx.options),
849
+ content,
850
+ },
851
+ ];
852
+ }
853
+ /**
854
+ * Generate post-generation instructions
855
+ */
856
+ export function getTestInstructions(entityName, options) {
857
+ const { type } = options;
858
+ const runCommand = type === 'e2e'
859
+ ? 'pnpm test:e2e'
860
+ : type === 'integration'
861
+ ? 'pnpm test:integration'
862
+ : 'pnpm test';
863
+ return `
864
+ 1. Uncomment and customize the test cases in the generated file.
865
+
866
+ 2. Run the tests:
867
+
868
+ ${runCommand}
869
+
870
+ Or run specific test file:
871
+ pnpm vitest ${getTestPath({ kebab: entityName.toLowerCase() }, options)}
872
+
873
+ 3. Test types:
874
+ - Unit tests: Fast, isolated, mock dependencies
875
+ - Integration tests: Test with real database
876
+ - E2E tests: Full HTTP request/response cycle
877
+
878
+ Tip: Start with unit tests, add integration tests for critical paths,
879
+ and E2E tests for user-facing workflows.
880
+ `;
881
+ }
882
+ //# sourceMappingURL=test.js.map