imlil 1.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 (175) hide show
  1. package/.eslintrc.cjs +40 -0
  2. package/DOCS.md +63 -0
  3. package/README.md +160 -0
  4. package/agentTestSandbox/cli-test-zone/README.md +0 -0
  5. package/agentTestSandbox/cli-test-zone/imlil.blueprint.json +5 -0
  6. package/agentTestSandbox/cli-test-zone/notes-warning.md +3 -0
  7. package/agentTestSandbox/cli-test-zone/package.json +0 -0
  8. package/agentTestSandbox/cli-test-zone/public/index.html +0 -0
  9. package/agentTestSandbox/cli-test-zone/src/App.js +0 -0
  10. package/agentTestSandbox/cli-test-zone/src/App.jsx +29 -0
  11. package/agentTestSandbox/cli-test-zone/src/__tests__/App.test.jsx +48 -0
  12. package/agentTestSandbox/cli-test-zone/src/components/AddTodo.js +0 -0
  13. package/agentTestSandbox/cli-test-zone/src/components/Navigation/Navigation.jsx +48 -0
  14. package/agentTestSandbox/cli-test-zone/src/components/Navigation/__tests__/Navigation.module.test.js +45 -0
  15. package/agentTestSandbox/cli-test-zone/src/components/Navigation/__tests__/Navigation.test.jsx +47 -0
  16. package/agentTestSandbox/cli-test-zone/src/components/Navigation.js +0 -0
  17. package/agentTestSandbox/cli-test-zone/src/components/TodoItem/TodoItem.jsx +41 -0
  18. package/agentTestSandbox/cli-test-zone/src/components/TodoItem/__tests__/TodoItem.test.jsx +65 -0
  19. package/agentTestSandbox/cli-test-zone/src/components/TodoItem.js +0 -0
  20. package/agentTestSandbox/cli-test-zone/src/components/TodoList/TodoList.module.css +62 -0
  21. package/agentTestSandbox/cli-test-zone/src/components/TodoList.js +0 -0
  22. package/agentTestSandbox/cli-test-zone/src/index.js +0 -0
  23. package/agentTestSandbox/cli-test-zone/src/pages/About.js +0 -0
  24. package/agentTestSandbox/cli-test-zone/src/pages/Home.js +0 -0
  25. package/agentTestSandbox/cli-test-zone/src/store/TodoContext.js +0 -0
  26. package/agentTestSandbox/cli-test-zone/src/styles/Todo.css +0 -0
  27. package/agentTestSandbox/cli-test-zone/src/styles/index.css +0 -0
  28. package/agentTestSandbox/cli-test-zone/src/utils/__tests__/localStorage.test.js +48 -0
  29. package/agentTestSandbox/cli-test-zone/src/utils/localStorage.js +38 -0
  30. package/agentTestSandbox/parallel-test/.env.example +0 -0
  31. package/agentTestSandbox/parallel-test/.eslintrc.json +0 -0
  32. package/agentTestSandbox/parallel-test/.github/workflows/__tests__/workflows.test.ts +115 -0
  33. package/agentTestSandbox/parallel-test/.github/workflows/cd.yml +0 -0
  34. package/agentTestSandbox/parallel-test/.github/workflows/ci.yml +4 -0
  35. package/agentTestSandbox/parallel-test/.imlil/plan-2026-02-08.md +186 -0
  36. package/agentTestSandbox/parallel-test/.prettierrc +0 -0
  37. package/agentTestSandbox/parallel-test/Dockerfile +0 -0
  38. package/agentTestSandbox/parallel-test/README.md +3 -0
  39. package/agentTestSandbox/parallel-test/ast.json +74 -0
  40. package/agentTestSandbox/parallel-test/docker-compose.yml +4 -0
  41. package/agentTestSandbox/parallel-test/jest.config.js +61 -0
  42. package/agentTestSandbox/parallel-test/k8s/__tests__/deployment.test.ts +168 -0
  43. package/agentTestSandbox/parallel-test/k8s/frontend-deployment.yaml +4 -0
  44. package/agentTestSandbox/parallel-test/nginx/nginx.conf +0 -0
  45. package/agentTestSandbox/parallel-test/package.json +50 -0
  46. package/agentTestSandbox/parallel-test/prisma/__tests__/schema.test.ts +176 -0
  47. package/agentTestSandbox/parallel-test/prisma/schema.prisma +109 -0
  48. package/agentTestSandbox/parallel-test/server/__tests__/controllers/dashboard.controller.test.ts +127 -0
  49. package/agentTestSandbox/parallel-test/server/__tests__/index.test.ts +60 -0
  50. package/agentTestSandbox/parallel-test/server/__tests__/models/user.model.test.ts +111 -0
  51. package/agentTestSandbox/parallel-test/server/config/__tests__/swagger.test.ts +128 -0
  52. package/agentTestSandbox/parallel-test/server/config/database.ts +0 -0
  53. package/agentTestSandbox/parallel-test/server/config/redis.ts +0 -0
  54. package/agentTestSandbox/parallel-test/server/config/swagger.ts +0 -0
  55. package/agentTestSandbox/parallel-test/server/controllers/__tests__/auth.controller.test.ts +178 -0
  56. package/agentTestSandbox/parallel-test/server/controllers/__tests__/user.controller.test.ts +105 -0
  57. package/agentTestSandbox/parallel-test/server/controllers/auth.controller.ts +148 -0
  58. package/agentTestSandbox/parallel-test/server/controllers/dashboard.controller.ts +137 -0
  59. package/agentTestSandbox/parallel-test/server/controllers/user.controller.ts +161 -0
  60. package/agentTestSandbox/parallel-test/server/index.ts +62 -0
  61. package/agentTestSandbox/parallel-test/server/middleware/__tests__/auth.middleware.test.ts +74 -0
  62. package/agentTestSandbox/parallel-test/server/middleware/auth.middleware.ts +55 -0
  63. package/agentTestSandbox/parallel-test/server/middleware/error.middleware.ts +0 -0
  64. package/agentTestSandbox/parallel-test/server/middleware/validation.middleware.ts +0 -0
  65. package/agentTestSandbox/parallel-test/server/models/analytics.model.ts +0 -0
  66. package/agentTestSandbox/parallel-test/server/models/profile.model.ts +0 -0
  67. package/agentTestSandbox/parallel-test/server/models/user.model.ts +78 -0
  68. package/agentTestSandbox/parallel-test/server/routes/auth.routes.ts +0 -0
  69. package/agentTestSandbox/parallel-test/server/routes/dashboard.routes.ts +0 -0
  70. package/agentTestSandbox/parallel-test/server/routes/user.routes.ts +0 -0
  71. package/agentTestSandbox/parallel-test/src/App.tsx +0 -0
  72. package/agentTestSandbox/parallel-test/src/__tests__/config.test.ts +127 -0
  73. package/agentTestSandbox/parallel-test/src/__tests__/index.test.tsx +36 -0
  74. package/agentTestSandbox/parallel-test/src/__tests__/setup.test.ts +34 -0
  75. package/agentTestSandbox/parallel-test/src/__tests__/setupTest.test.ts +44 -0
  76. package/agentTestSandbox/parallel-test/src/components/common/Button/Button.tsx +80 -0
  77. package/agentTestSandbox/parallel-test/src/components/common/Button/__tests__/Button.test.tsx +75 -0
  78. package/agentTestSandbox/parallel-test/src/components/common/Card/Card.tsx +0 -0
  79. package/agentTestSandbox/parallel-test/src/components/common/Input/Input.tsx +0 -0
  80. package/agentTestSandbox/parallel-test/src/components/common/Table/Table.tsx +0 -0
  81. package/agentTestSandbox/parallel-test/src/components/features/Authentication/LoginForm.tsx +75 -0
  82. package/agentTestSandbox/parallel-test/src/components/features/Authentication/RegisterForm.tsx +0 -0
  83. package/agentTestSandbox/parallel-test/src/components/features/Authentication/__tests__/LoginForm.test.tsx +101 -0
  84. package/agentTestSandbox/parallel-test/src/components/features/Dashboard/AnalyticsChart.tsx +0 -0
  85. package/agentTestSandbox/parallel-test/src/components/features/Dashboard/DashboardStats.tsx +81 -0
  86. package/agentTestSandbox/parallel-test/src/components/features/Dashboard/__tests__/DashboardStats.test.tsx +122 -0
  87. package/agentTestSandbox/parallel-test/src/components/layouts/Header.tsx +70 -0
  88. package/agentTestSandbox/parallel-test/src/components/layouts/MainLayout.tsx +0 -0
  89. package/agentTestSandbox/parallel-test/src/components/layouts/Sidebar.tsx +0 -0
  90. package/agentTestSandbox/parallel-test/src/components/layouts/__tests__/MainLayout.test.tsx +65 -0
  91. package/agentTestSandbox/parallel-test/src/hooks/__tests__/useAuth.test.ts +75 -0
  92. package/agentTestSandbox/parallel-test/src/hooks/useApi.ts +0 -0
  93. package/agentTestSandbox/parallel-test/src/hooks/useAuth.ts +54 -0
  94. package/agentTestSandbox/parallel-test/src/hooks/useTheme.ts +0 -0
  95. package/agentTestSandbox/parallel-test/src/index.tsx +0 -0
  96. package/agentTestSandbox/parallel-test/src/services/__tests__/api.service.test.ts +48 -0
  97. package/agentTestSandbox/parallel-test/src/services/analytics.service.ts +0 -0
  98. package/agentTestSandbox/parallel-test/src/services/api.service.ts +59 -0
  99. package/agentTestSandbox/parallel-test/src/services/api.ts +0 -0
  100. package/agentTestSandbox/parallel-test/src/services/auth.service.ts +0 -0
  101. package/agentTestSandbox/parallel-test/src/services/user.service.ts +0 -0
  102. package/agentTestSandbox/parallel-test/src/store/__tests__/store.test.ts +60 -0
  103. package/agentTestSandbox/parallel-test/src/store/index.ts +23 -0
  104. package/agentTestSandbox/parallel-test/src/store/slices/authSlice.ts +0 -0
  105. package/agentTestSandbox/parallel-test/src/store/slices/dashboardSlice.ts +0 -0
  106. package/agentTestSandbox/parallel-test/src/store/slices/userSlice.ts +0 -0
  107. package/agentTestSandbox/parallel-test/src/types/auth.types.ts +0 -0
  108. package/agentTestSandbox/parallel-test/src/types/dashboard.types.ts +0 -0
  109. package/agentTestSandbox/parallel-test/src/types/user.types.ts +0 -0
  110. package/agentTestSandbox/parallel-test/src/utils/constants.ts +0 -0
  111. package/agentTestSandbox/parallel-test/src/utils/formatters.ts +0 -0
  112. package/agentTestSandbox/parallel-test/src/utils/validation.ts +0 -0
  113. package/agentTestSandbox/parallel-test/src/views/Dashboard.tsx +0 -0
  114. package/agentTestSandbox/parallel-test/src/views/Login.tsx +31 -0
  115. package/agentTestSandbox/parallel-test/src/views/Profile.tsx +0 -0
  116. package/agentTestSandbox/parallel-test/src/views/Register.tsx +0 -0
  117. package/agentTestSandbox/parallel-test/src/views/Settings.tsx +0 -0
  118. package/agentTestSandbox/parallel-test/src/views/__tests__/Login.test.tsx +62 -0
  119. package/agentTestSandbox/parallel-test/src/vite-env.d.ts +1 -0
  120. package/agentTestSandbox/parallel-test/tailwind.config.js +0 -0
  121. package/agentTestSandbox/parallel-test/tests/integration/api/auth.test.ts +120 -0
  122. package/agentTestSandbox/parallel-test/tests/unit/components/Button.test.tsx +35 -0
  123. package/agentTestSandbox/parallel-test/tests/unit/config/jest.config.test.js +62 -0
  124. package/agentTestSandbox/parallel-test/tests/unit/config/jest.setup.test.js +52 -0
  125. package/agentTestSandbox/parallel-test/tests/unit/infrastructure/__tests__/docker-config.test.ts +107 -0
  126. package/agentTestSandbox/parallel-test/tsconfig.json +0 -0
  127. package/agentTestSandbox/zone2/Makefile +58 -0
  128. package/agentTestSandbox/zone2/README.md +0 -0
  129. package/agentTestSandbox/zone2/docs/API.md +0 -0
  130. package/agentTestSandbox/zone2/docs/CONTRIBUTING.md +0 -0
  131. package/agentTestSandbox/zone2/imlil.blueprint.json +5 -0
  132. package/agentTestSandbox/zone2/notes-warning.md +3 -0
  133. package/agentTestSandbox/zone2/src/calculator.c +0 -0
  134. package/agentTestSandbox/zone2/src/calculator.h +0 -0
  135. package/agentTestSandbox/zone2/src/core/__tests__/test_memory.c +89 -0
  136. package/agentTestSandbox/zone2/src/core/memory.c +60 -0
  137. package/agentTestSandbox/zone2/src/display.c +0 -0
  138. package/agentTestSandbox/zone2/src/display.h +0 -0
  139. package/agentTestSandbox/zone2/src/input_handler.c +0 -0
  140. package/agentTestSandbox/zone2/src/input_handler.h +0 -0
  141. package/agentTestSandbox/zone2/src/main.c +0 -0
  142. package/agentTestSandbox/zone2/src/utils/error_handling.c +0 -0
  143. package/agentTestSandbox/zone2/src/utils/error_handling.h +0 -0
  144. package/agentTestSandbox/zone2/src/utils/input.c +0 -0
  145. package/agentTestSandbox/zone2/src/utils/input.h +0 -0
  146. package/agentTestSandbox/zone2/src/utils/math_utils.c +0 -0
  147. package/agentTestSandbox/zone2/src/utils/math_utils.h +0 -0
  148. package/agentTestSandbox/zone2/src/utils/stack.c +0 -0
  149. package/agentTestSandbox/zone2/src/utils/stack.h +0 -0
  150. package/agentTestSandbox/zone2/src/utils.c +34 -0
  151. package/agentTestSandbox/zone2/tests/__tests__/test_makefile.c +58 -0
  152. package/agentTestSandbox/zone2/tests/calculator_tests.c +0 -0
  153. package/agentTestSandbox/zone2/tests/input_handler_tests.c +0 -0
  154. package/agentTestSandbox/zone2/tests/math_utils_tests.c +0 -0
  155. package/agentTestSandbox/zone2/tests/test_calculator.c +0 -0
  156. package/agentTestSandbox/zone2/tests/test_input.c +0 -0
  157. package/agentTestSandbox/zone2/tests/test_stack.c +0 -0
  158. package/agentTestSandbox/zone2/tests/test_utils.c +8 -0
  159. package/bin/cli.js +369 -0
  160. package/imlil.config.js +22 -0
  161. package/index.js +0 -0
  162. package/jest.config.js +5 -0
  163. package/package.json +45 -0
  164. package/src/__tests__/cli.test.js +5 -0
  165. package/src/actions/Action.js +125 -0
  166. package/src/agents/Agent.js +64 -0
  167. package/src/agents/Operator.js +147 -0
  168. package/src/agents/ScrumAgent.js +74 -0
  169. package/src/agents/SupervisorAgent.js +198 -0
  170. package/src/agents/ValidatorAgent.js +48 -0
  171. package/src/agents/coder.js +208 -0
  172. package/src/agents/worker.js +52 -0
  173. package/src/utils/db.js +40 -0
  174. package/src/utils/embedapi.js +19 -0
  175. package/test-api.js +24 -0
@@ -0,0 +1,111 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import { User } from '../models/user.model';
3
+
4
+ // Mock Prisma Client
5
+ jest.mock('@prisma/client');
6
+
7
+ describe('User Model', () => {
8
+ let prisma: jest.Mocked<PrismaClient>;
9
+
10
+ beforeEach(() => {
11
+ // Clear all mocks before each test
12
+ jest.clearAllMocks();
13
+ prisma = new PrismaClient() as jest.Mocked<PrismaClient>;
14
+ });
15
+
16
+ describe('createUser', () => {
17
+ const mockUserData = {
18
+ email: 'test@example.com',
19
+ password: 'hashedPassword123',
20
+ username: 'testuser',
21
+ };
22
+
23
+ it('should create a new user successfully', async () => {
24
+ const expectedUser = {
25
+ id: 1,
26
+ ...mockUserData,
27
+ createdAt: new Date(),
28
+ updatedAt: new Date(),
29
+ };
30
+
31
+ prisma.user.create.mockResolvedValue(expectedUser);
32
+
33
+ const result = await User.create(mockUserData);
34
+
35
+ expect(prisma.user.create).toHaveBeenCalledWith({
36
+ data: mockUserData
37
+ });
38
+ expect(result).toEqual(expectedUser);
39
+ });
40
+
41
+ it('should throw an error if user creation fails', async () => {
42
+ const error = new Error('Database error');
43
+ prisma.user.create.mockRejectedValue(error);
44
+
45
+ await expect(User.create(mockUserData)).rejects.toThrow('Database error');
46
+ });
47
+ });
48
+
49
+ describe('findByEmail', () => {
50
+ const email = 'test@example.com';
51
+
52
+ it('should find user by email', async () => {
53
+ const expectedUser = {
54
+ id: 1,
55
+ email,
56
+ username: 'testuser',
57
+ password: 'hashedPassword123',
58
+ createdAt: new Date(),
59
+ updatedAt: new Date(),
60
+ };
61
+
62
+ prisma.user.findUnique.mockResolvedValue(expectedUser);
63
+
64
+ const result = await User.findByEmail(email);
65
+
66
+ expect(prisma.user.findUnique).toHaveBeenCalledWith({
67
+ where: { email }
68
+ });
69
+ expect(result).toEqual(expectedUser);
70
+ });
71
+
72
+ it('should return null if user not found', async () => {
73
+ prisma.user.findUnique.mockResolvedValue(null);
74
+
75
+ const result = await User.findByEmail(email);
76
+
77
+ expect(prisma.user.findUnique).toHaveBeenCalledWith({
78
+ where: { email }
79
+ });
80
+ expect(result).toBeNull();
81
+ });
82
+ });
83
+
84
+ describe('updateUser', () => {
85
+ const userId = 1;
86
+ const updateData = {
87
+ username: 'newusername'
88
+ };
89
+
90
+ it('should update user successfully', async () => {
91
+ const expectedUser = {
92
+ id: userId,
93
+ email: 'test@example.com',
94
+ username: 'newusername',
95
+ password: 'hashedPassword123',
96
+ createdAt: new Date(),
97
+ updatedAt: new Date(),
98
+ };
99
+
100
+ prisma.user.update.mockResolvedValue(expectedUser);
101
+
102
+ const result = await User.update(userId, updateData);
103
+
104
+ expect(prisma.user.update).toHaveBeenCalledWith({
105
+ where: { id: userId },
106
+ data: updateData
107
+ });
108
+ expect(result).toEqual(expectedUser);
109
+ });
110
+ });
111
+ });
@@ -0,0 +1,128 @@
1
+ import swaggerConfig from '../swagger';
2
+ import { OpenAPIV3 } from 'openapi-types';
3
+
4
+ describe('Swagger Configuration', () => {
5
+ let config: OpenAPIV3.Document;
6
+
7
+ beforeAll(() => {
8
+ config = swaggerConfig;
9
+ });
10
+
11
+ describe('Basic Configuration', () => {
12
+ test('should have valid OpenAPI specification', () => {
13
+ expect(config.openapi).toBe('3.0.0');
14
+ expect(config.info).toBeDefined();
15
+ expect(config.info.title).toBe('Admin Dashboard API');
16
+ expect(config.info.version).toBeDefined();
17
+ });
18
+
19
+ test('should include server configurations', () => {
20
+ expect(config.servers).toBeDefined();
21
+ expect(Array.isArray(config.servers)).toBe(true);
22
+ expect(config.servers.length).toBeGreaterThan(0);
23
+ });
24
+ });
25
+
26
+ describe('API Endpoints Documentation', () => {
27
+ test('should include authentication paths', () => {
28
+ expect(config.paths['/api/auth/login']).toBeDefined();
29
+ expect(config.paths['/api/auth/register']).toBeDefined();
30
+ expect(config.paths['/api/auth/refresh']).toBeDefined();
31
+ });
32
+
33
+ test('should include user management paths', () => {
34
+ expect(config.paths['/api/users']).toBeDefined();
35
+ expect(config.paths['/api/users/{id}']).toBeDefined();
36
+ });
37
+
38
+ test('should include dashboard paths', () => {
39
+ expect(config.paths['/api/dashboard/stats']).toBeDefined();
40
+ expect(config.paths['/api/dashboard/analytics']).toBeDefined();
41
+ });
42
+ });
43
+
44
+ describe('Security Schemes', () => {
45
+ test('should define security schemes', () => {
46
+ expect(config.components?.securitySchemes).toBeDefined();
47
+ expect(config.components?.securitySchemes?.bearerAuth).toBeDefined();
48
+ });
49
+
50
+ test('should have proper bearer auth configuration', () => {
51
+ const bearerAuth = config.components?.securitySchemes?.bearerAuth;
52
+ expect(bearerAuth?.type).toBe('http');
53
+ expect(bearerAuth?.scheme).toBe('bearer');
54
+ expect(bearerAuth?.bearerFormat).toBe('JWT');
55
+ });
56
+ });
57
+
58
+ describe('Schema Definitions', () => {
59
+ test('should include common request/response schemas', () => {
60
+ expect(config.components?.schemas?.Error).toBeDefined();
61
+ expect(config.components?.schemas?.Success).toBeDefined();
62
+ });
63
+
64
+ test('should include user-related schemas', () => {
65
+ expect(config.components?.schemas?.User).toBeDefined();
66
+ expect(config.components?.schemas?.UserCreate).toBeDefined();
67
+ expect(config.components?.schemas?.UserUpdate).toBeDefined();
68
+ });
69
+
70
+ test('should include authentication-related schemas', () => {
71
+ expect(config.components?.schemas?.LoginRequest).toBeDefined();
72
+ expect(config.components?.schemas?.LoginResponse).toBeDefined();
73
+ expect(config.components?.schemas?.RegisterRequest).toBeDefined();
74
+ });
75
+ });
76
+ });
77
+
78
+ // README Validation Tests
79
+ describe('README Validation', () => {
80
+ const fs = require('fs');
81
+ const path = require('path');
82
+
83
+ let readmeContent: string;
84
+
85
+ beforeAll(() => {
86
+ readmeContent = fs.readFileSync(path.join(process.cwd(), 'README.md'), 'utf8');
87
+ });
88
+
89
+ test('should include project title and description', () => {
90
+ expect(readmeContent).toMatch(/# Admin Dashboard/);
91
+ expect(readmeContent).toMatch(/## Description/);
92
+ });
93
+
94
+ test('should include installation instructions', () => {
95
+ expect(readmeContent).toMatch(/## Installation/);
96
+ expect(readmeContent).toMatch(/npm install/);
97
+ });
98
+
99
+ test('should include environment setup section', () => {
100
+ expect(readmeContent).toMatch(/## Environment Setup/);
101
+ expect(readmeContent).toMatch(/\.env/);
102
+ });
103
+
104
+ test('should include API documentation section', () => {
105
+ expect(readmeContent).toMatch(/## API Documentation/);
106
+ expect(readmeContent).toMatch(/Swagger/);
107
+ });
108
+
109
+ test('should include tech stack information', () => {
110
+ expect(readmeContent).toMatch(/## Tech Stack/);
111
+ expect(readmeContent).toMatch(/React/);
112
+ expect(readmeContent).toMatch(/Node\.js/);
113
+ expect(readmeContent).toMatch(/PostgreSQL/);
114
+ });
115
+
116
+ test('should include development commands', () => {
117
+ expect(readmeContent).toMatch(/## Available Scripts/);
118
+ expect(readmeContent).toMatch(/npm run/);
119
+ });
120
+
121
+ test('should include deployment instructions', () => {
122
+ expect(readmeContent).toMatch(/## Deployment/);
123
+ });
124
+
125
+ test('should include project structure', () => {
126
+ expect(readmeContent).toMatch(/## Project Structure/);
127
+ });
128
+ });
@@ -0,0 +1,178 @@
1
+ import { Request, Response } from 'express';
2
+ import { AuthController } from '../auth.controller';
3
+ import prisma from '../../config/database';
4
+ import bcrypt from 'bcrypt';
5
+ import jwt from 'jsonwebtoken';
6
+
7
+ // Mock dependencies
8
+ jest.mock('../../config/database');
9
+ jest.mock('bcrypt');
10
+ jest.mock('jsonwebtoken');
11
+
12
+ describe('AuthController', () => {
13
+ let mockRequest: Partial<Request>;
14
+ let mockResponse: Partial<Response>;
15
+ let mockNext: jest.Mock;
16
+
17
+ beforeEach(() => {
18
+ mockRequest = {
19
+ body: {},
20
+ };
21
+ mockResponse = {
22
+ status: jest.fn().mockReturnThis(),
23
+ json: jest.fn().mockReturnThis(),
24
+ };
25
+ mockNext = jest.fn();
26
+ });
27
+
28
+ afterEach(() => {
29
+ jest.clearAllMocks();
30
+ });
31
+
32
+ describe('register', () => {
33
+ const validRegisterData = {
34
+ email: 'test@example.com',
35
+ password: 'Password123!',
36
+ name: 'Test User',
37
+ };
38
+
39
+ it('should successfully register a new user', async () => {
40
+ // Arrange
41
+ mockRequest.body = validRegisterData;
42
+ const hashedPassword = 'hashedPassword123';
43
+ (bcrypt.hash as jest.Mock).mockResolvedValue(hashedPassword);
44
+ (prisma.user.create as jest.Mock).mockResolvedValue({
45
+ id: 1,
46
+ ...validRegisterData,
47
+ password: hashedPassword,
48
+ });
49
+
50
+ // Act
51
+ await AuthController.register(
52
+ mockRequest as Request,
53
+ mockResponse as Response,
54
+ mockNext
55
+ );
56
+
57
+ // Assert
58
+ expect(bcrypt.hash).toHaveBeenCalledWith(validRegisterData.password, 10);
59
+ expect(prisma.user.create).toHaveBeenCalledWith({
60
+ data: {
61
+ ...validRegisterData,
62
+ password: hashedPassword,
63
+ },
64
+ });
65
+ expect(mockResponse.status).toHaveBeenCalledWith(201);
66
+ expect(mockResponse.json).toHaveBeenCalledWith({
67
+ message: 'User registered successfully',
68
+ user: expect.objectContaining({
69
+ email: validRegisterData.email,
70
+ name: validRegisterData.name,
71
+ }),
72
+ });
73
+ });
74
+
75
+ it('should handle duplicate email registration', async () => {
76
+ // Arrange
77
+ mockRequest.body = validRegisterData;
78
+ (prisma.user.create as jest.Mock).mockRejectedValue({
79
+ code: 'P2002',
80
+ meta: { target: ['email'] },
81
+ });
82
+
83
+ // Act
84
+ await AuthController.register(
85
+ mockRequest as Request,
86
+ mockResponse as Response,
87
+ mockNext
88
+ );
89
+
90
+ // Assert
91
+ expect(mockResponse.status).toHaveBeenCalledWith(400);
92
+ expect(mockResponse.json).toHaveBeenCalledWith({
93
+ message: 'Email already exists',
94
+ });
95
+ });
96
+ });
97
+
98
+ describe('login', () => {
99
+ const validLoginData = {
100
+ email: 'test@example.com',
101
+ password: 'Password123!',
102
+ };
103
+
104
+ it('should successfully login a user', async () => {
105
+ // Arrange
106
+ mockRequest.body = validLoginData;
107
+ const mockUser = {
108
+ id: 1,
109
+ ...validLoginData,
110
+ password: 'hashedPassword',
111
+ };
112
+ (prisma.user.findUnique as jest.Mock).mockResolvedValue(mockUser);
113
+ (bcrypt.compare as jest.Mock).mockResolvedValue(true);
114
+ (jwt.sign as jest.Mock).mockReturnValue('mockToken');
115
+
116
+ // Act
117
+ await AuthController.login(
118
+ mockRequest as Request,
119
+ mockResponse as Response,
120
+ mockNext
121
+ );
122
+
123
+ // Assert
124
+ expect(prisma.user.findUnique).toHaveBeenCalledWith({
125
+ where: { email: validLoginData.email },
126
+ });
127
+ expect(bcrypt.compare).toHaveBeenCalledWith(
128
+ validLoginData.password,
129
+ mockUser.password
130
+ );
131
+ expect(jwt.sign).toHaveBeenCalled();
132
+ expect(mockResponse.status).toHaveBeenCalledWith(200);
133
+ expect(mockResponse.json).toHaveBeenCalledWith({
134
+ message: 'Login successful',
135
+ token: 'mockToken',
136
+ user: expect.objectContaining({
137
+ email: validLoginData.email,
138
+ }),
139
+ });
140
+ });
141
+
142
+ it('should handle invalid credentials', async () => {
143
+ // Arrange
144
+ mockRequest.body = validLoginData;
145
+ (prisma.user.findUnique as jest.Mock).mockResolvedValue(null);
146
+
147
+ // Act
148
+ await AuthController.login(
149
+ mockRequest as Request,
150
+ mockResponse as Response,
151
+ mockNext
152
+ );
153
+
154
+ // Assert
155
+ expect(mockResponse.status).toHaveBeenCalledWith(401);
156
+ expect(mockResponse.json).toHaveBeenCalledWith({
157
+ message: 'Invalid credentials',
158
+ });
159
+ });
160
+ });
161
+
162
+ describe('logout', () => {
163
+ it('should successfully logout a user', async () => {
164
+ // Act
165
+ await AuthController.logout(
166
+ mockRequest as Request,
167
+ mockResponse as Response,
168
+ mockNext
169
+ );
170
+
171
+ // Assert
172
+ expect(mockResponse.status).toHaveBeenCalledWith(200);
173
+ expect(mockResponse.json).toHaveBeenCalledWith({
174
+ message: 'Logged out successfully',
175
+ });
176
+ });
177
+ });
178
+ });
@@ -0,0 +1,105 @@
1
+ import { Request, Response } from 'express';
2
+ import { UserController } from '../user.controller';
3
+ import prisma from '../../config/database';
4
+
5
+ // Mock Prisma
6
+ jest.mock('../../config/database', () => ({
7
+ user: {
8
+ create: jest.fn(),
9
+ findMany: jest.fn(),
10
+ findUnique: jest.fn(),
11
+ update: jest.fn(),
12
+ delete: jest.fn(),
13
+ },
14
+ }));
15
+
16
+ describe('UserController', () => {
17
+ let mockRequest: Partial<Request>;
18
+ let mockResponse: Partial<Response>;
19
+ let mockNext: jest.Mock;
20
+
21
+ beforeEach(() => {
22
+ mockRequest = {
23
+ body: {},
24
+ params: {},
25
+ };
26
+ mockResponse = {
27
+ status: jest.fn().mockReturnThis(),
28
+ json: jest.fn().mockReturnThis(),
29
+ };
30
+ mockNext = jest.fn();
31
+ });
32
+
33
+ afterEach(() => {
34
+ jest.clearAllMocks();
35
+ });
36
+
37
+ describe('createUser', () => {
38
+ const validUserData = {
39
+ email: 'test@example.com',
40
+ password: 'Password123!',
41
+ firstName: 'John',
42
+ lastName: 'Doe',
43
+ role: 'USER',
44
+ };
45
+
46
+ it('should create a new user successfully', async () => {
47
+ mockRequest.body = validUserData;
48
+ const mockCreatedUser = { id: 1, ...validUserData };
49
+
50
+ (prisma.user.create as jest.Mock).mockResolvedValue(mockCreatedUser);
51
+
52
+ await UserController.createUser(
53
+ mockRequest as Request,
54
+ mockResponse as Response,
55
+ mockNext
56
+ );
57
+
58
+ expect(prisma.user.create).toHaveBeenCalledWith({
59
+ data: validUserData,
60
+ });
61
+ expect(mockResponse.status).toHaveBeenCalledWith(201);
62
+ expect(mockResponse.json).toHaveBeenCalledWith({
63
+ success: true,
64
+ data: mockCreatedUser,
65
+ });
66
+ });
67
+
68
+ it('should handle validation errors', async () => {
69
+ mockRequest.body = { email: 'invalid-email' };
70
+
71
+ await UserController.createUser(
72
+ mockRequest as Request,
73
+ mockResponse as Response,
74
+ mockNext
75
+ );
76
+
77
+ expect(mockNext).toHaveBeenCalled();
78
+ expect(mockNext.mock.calls[0][0]).toBeInstanceOf(Error);
79
+ });
80
+
81
+ it('should handle duplicate email errors', async () => {
82
+ mockRequest.body = validUserData;
83
+
84
+ (prisma.user.create as jest.Mock).mockRejectedValue({
85
+ code: 'P2002',
86
+ meta: { target: ['email'] },
87
+ });
88
+
89
+ await UserController.createUser(
90
+ mockRequest as Request,
91
+ mockResponse as Response,
92
+ mockNext
93
+ );
94
+
95
+ expect(mockNext).toHaveBeenCalled();
96
+ expect(mockNext.mock.calls[0][0].message).toContain('Email already exists');
97
+ });
98
+ });
99
+
100
+ // Additional test cases could include:
101
+ // - getUsers
102
+ // - getUserById
103
+ // - updateUser
104
+ // - deleteUser
105
+ });
@@ -0,0 +1,148 @@
1
+ import { Request, Response } from 'express';
2
+ import { PrismaClient } from '@prisma/client';
3
+ import bcrypt from 'bcrypt';
4
+ import jwt from 'jsonwebtoken';
5
+ import { Joi } from 'joi';
6
+
7
+ const prisma = new PrismaClient();
8
+
9
+ // Validation schemas
10
+ const registerSchema = Joi.object({
11
+ email: Joi.string().email().required(),
12
+ password: Joi.string().min(8).required(),
13
+ name: Joi.string().required(),
14
+ });
15
+
16
+ const loginSchema = Joi.object({
17
+ email: Joi.string().email().required(),
18
+ password: Joi.string().required(),
19
+ });
20
+
21
+ export class AuthController {
22
+ // Register new user
23
+ static async register(req: Request, res: Response) {
24
+ try {
25
+ // Validate request body
26
+ const { error } = registerSchema.validate(req.body);
27
+ if (error) {
28
+ return res.status(400).json({ error: error.details[0].message });
29
+ }
30
+
31
+ const { email, password, name } = req.body;
32
+
33
+ // Check if user already exists
34
+ const existingUser = await prisma.user.findUnique({
35
+ where: { email },
36
+ });
37
+
38
+ if (existingUser) {
39
+ return res.status(400).json({ error: 'User already exists' });
40
+ }
41
+
42
+ // Hash password
43
+ const salt = await bcrypt.genSalt(10);
44
+ const hashedPassword = await bcrypt.hash(password, salt);
45
+
46
+ // Create user
47
+ const user = await prisma.user.create({
48
+ data: {
49
+ email,
50
+ password: hashedPassword,
51
+ name,
52
+ },
53
+ });
54
+
55
+ // Generate JWT token
56
+ const token = jwt.sign(
57
+ { userId: user.id },
58
+ process.env.JWT_SECRET || 'default-secret',
59
+ { expiresIn: '24h' }
60
+ );
61
+
62
+ // Return user data (excluding password) and token
63
+ const { password: _, ...userWithoutPassword } = user;
64
+ return res.status(201).json({
65
+ user: userWithoutPassword,
66
+ token,
67
+ });
68
+ } catch (error) {
69
+ console.error('Register error:', error);
70
+ return res.status(500).json({ error: 'Internal server error' });
71
+ }
72
+ }
73
+
74
+ // Login user
75
+ static async login(req: Request, res: Response) {
76
+ try {
77
+ // Validate request body
78
+ const { error } = loginSchema.validate(req.body);
79
+ if (error) {
80
+ return res.status(400).json({ error: error.details[0].message });
81
+ }
82
+
83
+ const { email, password } = req.body;
84
+
85
+ // Find user
86
+ const user = await prisma.user.findUnique({
87
+ where: { email },
88
+ });
89
+
90
+ if (!user) {
91
+ return res.status(401).json({ error: 'Invalid credentials' });
92
+ }
93
+
94
+ // Verify password
95
+ const validPassword = await bcrypt.compare(password, user.password);
96
+ if (!validPassword) {
97
+ return res.status(401).json({ error: 'Invalid credentials' });
98
+ }
99
+
100
+ // Generate JWT token
101
+ const token = jwt.sign(
102
+ { userId: user.id },
103
+ process.env.JWT_SECRET || 'default-secret',
104
+ { expiresIn: '24h' }
105
+ );
106
+
107
+ // Return user data (excluding password) and token
108
+ const { password: _, ...userWithoutPassword } = user;
109
+ return res.status(200).json({
110
+ user: userWithoutPassword,
111
+ token,
112
+ });
113
+ } catch (error) {
114
+ console.error('Login error:', error);
115
+ return res.status(500).json({ error: 'Internal server error' });
116
+ }
117
+ }
118
+
119
+ // Get current user
120
+ static async getCurrentUser(req: Request, res: Response) {
121
+ try {
122
+ const userId = req.user?.id; // Set by auth middleware
123
+ if (!userId) {
124
+ return res.status(401).json({ error: 'Unauthorized' });
125
+ }
126
+
127
+ const user = await prisma.user.findUnique({
128
+ where: { id: userId },
129
+ });
130
+
131
+ if (!user) {
132
+ return res.status(404).json({ error: 'User not found' });
133
+ }
134
+
135
+ // Return user data without password
136
+ const { password: _, ...userWithoutPassword } = user;
137
+ return res.status(200).json(userWithoutPassword);
138
+ } catch (error) {
139
+ console.error('Get current user error:', error);
140
+ return res.status(500).json({ error: 'Internal server error' });
141
+ }
142
+ }
143
+
144
+ // Logout (token invalidation would be handled on the client side)
145
+ static async logout(_req: Request, res: Response) {
146
+ return res.status(200).json({ message: 'Logged out successfully' });
147
+ }
148
+ }