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.
- package/.eslintrc.cjs +40 -0
- package/DOCS.md +63 -0
- package/README.md +160 -0
- package/agentTestSandbox/cli-test-zone/README.md +0 -0
- package/agentTestSandbox/cli-test-zone/imlil.blueprint.json +5 -0
- package/agentTestSandbox/cli-test-zone/notes-warning.md +3 -0
- package/agentTestSandbox/cli-test-zone/package.json +0 -0
- package/agentTestSandbox/cli-test-zone/public/index.html +0 -0
- package/agentTestSandbox/cli-test-zone/src/App.js +0 -0
- package/agentTestSandbox/cli-test-zone/src/App.jsx +29 -0
- package/agentTestSandbox/cli-test-zone/src/__tests__/App.test.jsx +48 -0
- package/agentTestSandbox/cli-test-zone/src/components/AddTodo.js +0 -0
- package/agentTestSandbox/cli-test-zone/src/components/Navigation/Navigation.jsx +48 -0
- package/agentTestSandbox/cli-test-zone/src/components/Navigation/__tests__/Navigation.module.test.js +45 -0
- package/agentTestSandbox/cli-test-zone/src/components/Navigation/__tests__/Navigation.test.jsx +47 -0
- package/agentTestSandbox/cli-test-zone/src/components/Navigation.js +0 -0
- package/agentTestSandbox/cli-test-zone/src/components/TodoItem/TodoItem.jsx +41 -0
- package/agentTestSandbox/cli-test-zone/src/components/TodoItem/__tests__/TodoItem.test.jsx +65 -0
- package/agentTestSandbox/cli-test-zone/src/components/TodoItem.js +0 -0
- package/agentTestSandbox/cli-test-zone/src/components/TodoList/TodoList.module.css +62 -0
- package/agentTestSandbox/cli-test-zone/src/components/TodoList.js +0 -0
- package/agentTestSandbox/cli-test-zone/src/index.js +0 -0
- package/agentTestSandbox/cli-test-zone/src/pages/About.js +0 -0
- package/agentTestSandbox/cli-test-zone/src/pages/Home.js +0 -0
- package/agentTestSandbox/cli-test-zone/src/store/TodoContext.js +0 -0
- package/agentTestSandbox/cli-test-zone/src/styles/Todo.css +0 -0
- package/agentTestSandbox/cli-test-zone/src/styles/index.css +0 -0
- package/agentTestSandbox/cli-test-zone/src/utils/__tests__/localStorage.test.js +48 -0
- package/agentTestSandbox/cli-test-zone/src/utils/localStorage.js +38 -0
- package/agentTestSandbox/parallel-test/.env.example +0 -0
- package/agentTestSandbox/parallel-test/.eslintrc.json +0 -0
- package/agentTestSandbox/parallel-test/.github/workflows/__tests__/workflows.test.ts +115 -0
- package/agentTestSandbox/parallel-test/.github/workflows/cd.yml +0 -0
- package/agentTestSandbox/parallel-test/.github/workflows/ci.yml +4 -0
- package/agentTestSandbox/parallel-test/.imlil/plan-2026-02-08.md +186 -0
- package/agentTestSandbox/parallel-test/.prettierrc +0 -0
- package/agentTestSandbox/parallel-test/Dockerfile +0 -0
- package/agentTestSandbox/parallel-test/README.md +3 -0
- package/agentTestSandbox/parallel-test/ast.json +74 -0
- package/agentTestSandbox/parallel-test/docker-compose.yml +4 -0
- package/agentTestSandbox/parallel-test/jest.config.js +61 -0
- package/agentTestSandbox/parallel-test/k8s/__tests__/deployment.test.ts +168 -0
- package/agentTestSandbox/parallel-test/k8s/frontend-deployment.yaml +4 -0
- package/agentTestSandbox/parallel-test/nginx/nginx.conf +0 -0
- package/agentTestSandbox/parallel-test/package.json +50 -0
- package/agentTestSandbox/parallel-test/prisma/__tests__/schema.test.ts +176 -0
- package/agentTestSandbox/parallel-test/prisma/schema.prisma +109 -0
- package/agentTestSandbox/parallel-test/server/__tests__/controllers/dashboard.controller.test.ts +127 -0
- package/agentTestSandbox/parallel-test/server/__tests__/index.test.ts +60 -0
- package/agentTestSandbox/parallel-test/server/__tests__/models/user.model.test.ts +111 -0
- package/agentTestSandbox/parallel-test/server/config/__tests__/swagger.test.ts +128 -0
- package/agentTestSandbox/parallel-test/server/config/database.ts +0 -0
- package/agentTestSandbox/parallel-test/server/config/redis.ts +0 -0
- package/agentTestSandbox/parallel-test/server/config/swagger.ts +0 -0
- package/agentTestSandbox/parallel-test/server/controllers/__tests__/auth.controller.test.ts +178 -0
- package/agentTestSandbox/parallel-test/server/controllers/__tests__/user.controller.test.ts +105 -0
- package/agentTestSandbox/parallel-test/server/controllers/auth.controller.ts +148 -0
- package/agentTestSandbox/parallel-test/server/controllers/dashboard.controller.ts +137 -0
- package/agentTestSandbox/parallel-test/server/controllers/user.controller.ts +161 -0
- package/agentTestSandbox/parallel-test/server/index.ts +62 -0
- package/agentTestSandbox/parallel-test/server/middleware/__tests__/auth.middleware.test.ts +74 -0
- package/agentTestSandbox/parallel-test/server/middleware/auth.middleware.ts +55 -0
- package/agentTestSandbox/parallel-test/server/middleware/error.middleware.ts +0 -0
- package/agentTestSandbox/parallel-test/server/middleware/validation.middleware.ts +0 -0
- package/agentTestSandbox/parallel-test/server/models/analytics.model.ts +0 -0
- package/agentTestSandbox/parallel-test/server/models/profile.model.ts +0 -0
- package/agentTestSandbox/parallel-test/server/models/user.model.ts +78 -0
- package/agentTestSandbox/parallel-test/server/routes/auth.routes.ts +0 -0
- package/agentTestSandbox/parallel-test/server/routes/dashboard.routes.ts +0 -0
- package/agentTestSandbox/parallel-test/server/routes/user.routes.ts +0 -0
- package/agentTestSandbox/parallel-test/src/App.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/__tests__/config.test.ts +127 -0
- package/agentTestSandbox/parallel-test/src/__tests__/index.test.tsx +36 -0
- package/agentTestSandbox/parallel-test/src/__tests__/setup.test.ts +34 -0
- package/agentTestSandbox/parallel-test/src/__tests__/setupTest.test.ts +44 -0
- package/agentTestSandbox/parallel-test/src/components/common/Button/Button.tsx +80 -0
- package/agentTestSandbox/parallel-test/src/components/common/Button/__tests__/Button.test.tsx +75 -0
- package/agentTestSandbox/parallel-test/src/components/common/Card/Card.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/components/common/Input/Input.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/components/common/Table/Table.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/components/features/Authentication/LoginForm.tsx +75 -0
- package/agentTestSandbox/parallel-test/src/components/features/Authentication/RegisterForm.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/components/features/Authentication/__tests__/LoginForm.test.tsx +101 -0
- package/agentTestSandbox/parallel-test/src/components/features/Dashboard/AnalyticsChart.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/components/features/Dashboard/DashboardStats.tsx +81 -0
- package/agentTestSandbox/parallel-test/src/components/features/Dashboard/__tests__/DashboardStats.test.tsx +122 -0
- package/agentTestSandbox/parallel-test/src/components/layouts/Header.tsx +70 -0
- package/agentTestSandbox/parallel-test/src/components/layouts/MainLayout.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/components/layouts/Sidebar.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/components/layouts/__tests__/MainLayout.test.tsx +65 -0
- package/agentTestSandbox/parallel-test/src/hooks/__tests__/useAuth.test.ts +75 -0
- package/agentTestSandbox/parallel-test/src/hooks/useApi.ts +0 -0
- package/agentTestSandbox/parallel-test/src/hooks/useAuth.ts +54 -0
- package/agentTestSandbox/parallel-test/src/hooks/useTheme.ts +0 -0
- package/agentTestSandbox/parallel-test/src/index.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/services/__tests__/api.service.test.ts +48 -0
- package/agentTestSandbox/parallel-test/src/services/analytics.service.ts +0 -0
- package/agentTestSandbox/parallel-test/src/services/api.service.ts +59 -0
- package/agentTestSandbox/parallel-test/src/services/api.ts +0 -0
- package/agentTestSandbox/parallel-test/src/services/auth.service.ts +0 -0
- package/agentTestSandbox/parallel-test/src/services/user.service.ts +0 -0
- package/agentTestSandbox/parallel-test/src/store/__tests__/store.test.ts +60 -0
- package/agentTestSandbox/parallel-test/src/store/index.ts +23 -0
- package/agentTestSandbox/parallel-test/src/store/slices/authSlice.ts +0 -0
- package/agentTestSandbox/parallel-test/src/store/slices/dashboardSlice.ts +0 -0
- package/agentTestSandbox/parallel-test/src/store/slices/userSlice.ts +0 -0
- package/agentTestSandbox/parallel-test/src/types/auth.types.ts +0 -0
- package/agentTestSandbox/parallel-test/src/types/dashboard.types.ts +0 -0
- package/agentTestSandbox/parallel-test/src/types/user.types.ts +0 -0
- package/agentTestSandbox/parallel-test/src/utils/constants.ts +0 -0
- package/agentTestSandbox/parallel-test/src/utils/formatters.ts +0 -0
- package/agentTestSandbox/parallel-test/src/utils/validation.ts +0 -0
- package/agentTestSandbox/parallel-test/src/views/Dashboard.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/views/Login.tsx +31 -0
- package/agentTestSandbox/parallel-test/src/views/Profile.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/views/Register.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/views/Settings.tsx +0 -0
- package/agentTestSandbox/parallel-test/src/views/__tests__/Login.test.tsx +62 -0
- package/agentTestSandbox/parallel-test/src/vite-env.d.ts +1 -0
- package/agentTestSandbox/parallel-test/tailwind.config.js +0 -0
- package/agentTestSandbox/parallel-test/tests/integration/api/auth.test.ts +120 -0
- package/agentTestSandbox/parallel-test/tests/unit/components/Button.test.tsx +35 -0
- package/agentTestSandbox/parallel-test/tests/unit/config/jest.config.test.js +62 -0
- package/agentTestSandbox/parallel-test/tests/unit/config/jest.setup.test.js +52 -0
- package/agentTestSandbox/parallel-test/tests/unit/infrastructure/__tests__/docker-config.test.ts +107 -0
- package/agentTestSandbox/parallel-test/tsconfig.json +0 -0
- package/agentTestSandbox/zone2/Makefile +58 -0
- package/agentTestSandbox/zone2/README.md +0 -0
- package/agentTestSandbox/zone2/docs/API.md +0 -0
- package/agentTestSandbox/zone2/docs/CONTRIBUTING.md +0 -0
- package/agentTestSandbox/zone2/imlil.blueprint.json +5 -0
- package/agentTestSandbox/zone2/notes-warning.md +3 -0
- package/agentTestSandbox/zone2/src/calculator.c +0 -0
- package/agentTestSandbox/zone2/src/calculator.h +0 -0
- package/agentTestSandbox/zone2/src/core/__tests__/test_memory.c +89 -0
- package/agentTestSandbox/zone2/src/core/memory.c +60 -0
- package/agentTestSandbox/zone2/src/display.c +0 -0
- package/agentTestSandbox/zone2/src/display.h +0 -0
- package/agentTestSandbox/zone2/src/input_handler.c +0 -0
- package/agentTestSandbox/zone2/src/input_handler.h +0 -0
- package/agentTestSandbox/zone2/src/main.c +0 -0
- package/agentTestSandbox/zone2/src/utils/error_handling.c +0 -0
- package/agentTestSandbox/zone2/src/utils/error_handling.h +0 -0
- package/agentTestSandbox/zone2/src/utils/input.c +0 -0
- package/agentTestSandbox/zone2/src/utils/input.h +0 -0
- package/agentTestSandbox/zone2/src/utils/math_utils.c +0 -0
- package/agentTestSandbox/zone2/src/utils/math_utils.h +0 -0
- package/agentTestSandbox/zone2/src/utils/stack.c +0 -0
- package/agentTestSandbox/zone2/src/utils/stack.h +0 -0
- package/agentTestSandbox/zone2/src/utils.c +34 -0
- package/agentTestSandbox/zone2/tests/__tests__/test_makefile.c +58 -0
- package/agentTestSandbox/zone2/tests/calculator_tests.c +0 -0
- package/agentTestSandbox/zone2/tests/input_handler_tests.c +0 -0
- package/agentTestSandbox/zone2/tests/math_utils_tests.c +0 -0
- package/agentTestSandbox/zone2/tests/test_calculator.c +0 -0
- package/agentTestSandbox/zone2/tests/test_input.c +0 -0
- package/agentTestSandbox/zone2/tests/test_stack.c +0 -0
- package/agentTestSandbox/zone2/tests/test_utils.c +8 -0
- package/bin/cli.js +369 -0
- package/imlil.config.js +22 -0
- package/index.js +0 -0
- package/jest.config.js +5 -0
- package/package.json +45 -0
- package/src/__tests__/cli.test.js +5 -0
- package/src/actions/Action.js +125 -0
- package/src/agents/Agent.js +64 -0
- package/src/agents/Operator.js +147 -0
- package/src/agents/ScrumAgent.js +74 -0
- package/src/agents/SupervisorAgent.js +198 -0
- package/src/agents/ValidatorAgent.js +48 -0
- package/src/agents/coder.js +208 -0
- package/src/agents/worker.js +52 -0
- package/src/utils/db.js +40 -0
- package/src/utils/embedapi.js +19 -0
- 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
|
+
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -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
|
+
}
|