nodejs-quickstart-structure 1.19.0 → 1.19.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/CHANGELOG.md +309 -301
- package/LICENSE +15 -15
- package/lib/generator.js +139 -139
- package/lib/modules/app-setup.js +401 -401
- package/lib/modules/config-files.js +151 -151
- package/lib/modules/database-setup.js +116 -116
- package/lib/modules/project-setup.js +32 -32
- package/lib/prompts.js +100 -100
- package/package.json +78 -78
- package/templates/clean-architecture/js/src/domain/models/User.js +9 -9
- package/templates/clean-architecture/js/src/errors/ApiError.js +14 -14
- package/templates/clean-architecture/js/src/index.js.ejs +55 -55
- package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -47
- package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -36
- package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -63
- package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -30
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +89 -89
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -13
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -31
- package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -5
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -38
- package/templates/clean-architecture/js/src/usecases/CreateUser.js +14 -14
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -51
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -12
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -61
- package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -9
- package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -46
- package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -6
- package/templates/clean-architecture/ts/src/domain/user.ts +7 -7
- package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -15
- package/templates/clean-architecture/ts/src/index.ts.ejs +139 -139
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -63
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -36
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -32
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -17
- package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -3
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -4
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -4
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -40
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -51
- package/templates/clean-architecture/ts/src/usecases/createUser.ts +13 -13
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -63
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -10
- package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -27
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -7
- package/templates/common/.cursorrules.ejs +60 -60
- package/templates/common/.dockerignore +12 -12
- package/templates/common/.env.example.ejs +41 -41
- package/templates/common/.gitlab-ci.yml.ejs +86 -86
- package/templates/common/.lintstagedrc +6 -6
- package/templates/common/.prettierrc +7 -7
- package/templates/common/Dockerfile +73 -73
- package/templates/common/Jenkinsfile.ejs +87 -87
- package/templates/common/SECURITY.md +20 -20
- package/templates/common/_github/workflows/ci.yml.ejs +46 -46
- package/templates/common/_github/workflows/security.yml.ejs +36 -36
- package/templates/common/_gitignore +5 -5
- package/templates/common/_husky/pre-commit +4 -4
- package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -29
- package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -37
- package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -27
- package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -34
- package/templates/common/caching/js/memoryCache.js.ejs +60 -60
- package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -101
- package/templates/common/caching/js/redisClient.js.ejs +75 -75
- package/templates/common/caching/js/redisClient.spec.js.ejs +147 -147
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -102
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -157
- package/templates/common/database/js/database.js.ejs +19 -19
- package/templates/common/database/js/database.spec.js.ejs +56 -56
- package/templates/common/database/js/mongoose.js.ejs +33 -33
- package/templates/common/database/js/mongoose.spec.js.ejs +43 -43
- package/templates/common/database/ts/database.spec.ts.ejs +56 -56
- package/templates/common/database/ts/database.ts.ejs +21 -21
- package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -42
- package/templates/common/database/ts/mongoose.ts.ejs +28 -28
- package/templates/common/docker-compose.yml.ejs +159 -159
- package/templates/common/ecosystem.config.js.ejs +40 -40
- package/templates/common/eslint.config.mjs.ejs +77 -77
- package/templates/common/health/js/healthRoute.spec.js.ejs +70 -70
- package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -76
- package/templates/common/jest.config.js.ejs +32 -32
- package/templates/common/kafka/js/config/kafka.js +9 -9
- package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -27
- package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -58
- package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -27
- package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -106
- package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -27
- package/templates/common/kafka/ts/config/kafka.ts +7 -7
- package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -50
- package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -27
- package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -81
- package/templates/common/migrate-mongo-config.js.ejs +31 -31
- package/templates/common/migrations/init.js.ejs +23 -23
- package/templates/common/package.json.ejs +119 -118
- package/templates/common/prompts/add-feature.md.ejs +26 -26
- package/templates/common/prompts/project-context.md.ejs +43 -43
- package/templates/common/prompts/troubleshoot.md.ejs +28 -28
- package/templates/common/public/css/style.css +147 -147
- package/templates/common/scripts/run-e2e.js.ejs +63 -63
- package/templates/common/sonar-project.properties.ejs +27 -27
- package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -79
- package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -94
- package/templates/common/tsconfig.json +22 -22
- package/templates/common/views/ejs/index.ejs +55 -55
- package/templates/common/views/pug/index.pug +40 -40
- package/templates/mvc/js/src/config/env.js.ejs +46 -46
- package/templates/mvc/js/src/config/swagger.js.ejs +6 -6
- package/templates/mvc/js/src/errors/ApiError.js +14 -14
- package/templates/mvc/js/src/graphql/context.js.ejs +7 -7
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -29
- package/templates/mvc/js/src/graphql/index.js.ejs +5 -5
- package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -6
- package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -6
- package/templates/mvc/js/src/index.js.ejs +136 -136
- package/templates/mvc/js/src/utils/errorMiddleware.js +29 -29
- package/templates/mvc/js/src/utils/httpCodes.js +9 -9
- package/templates/mvc/js/src/utils/logger.js +40 -40
- package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -63
- package/templates/mvc/ts/src/config/env.ts.ejs +45 -45
- package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -6
- package/templates/mvc/ts/src/errors/ApiError.ts +15 -15
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -30
- package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -12
- package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -3
- package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -4
- package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -4
- package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -27
- package/templates/mvc/ts/src/utils/httpCodes.ts +7 -7
- package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -63
- package/templates/mvc/ts/src/utils/logger.ts +36 -36
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import request from 'supertest';
|
|
2
|
-
import express, { Express } from 'express';
|
|
3
|
-
import router from '@/interfaces/routes/userRoutes';
|
|
4
|
-
|
|
5
|
-
const mockGetUsers = jest.fn().mockImplementation((req, res) => res.status(200).json([{ id: '1', name: 'John Doe' }]));
|
|
6
|
-
const mockCreateUser = jest.fn().mockImplementation((req, res) => res.status(201).json({ id: '1', name: 'Test' }));
|
|
7
|
-
|
|
8
|
-
jest.mock('@/interfaces/controllers/userController', () => {
|
|
9
|
-
return {
|
|
10
|
-
UserController: jest.fn().mockImplementation(() => ({
|
|
11
|
-
getUsers: (...args: unknown[]) => mockGetUsers(...args),
|
|
12
|
-
createUser: (...args: unknown[]) => mockCreateUser(...args)
|
|
13
|
-
}))
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe('UserRoutes', () => {
|
|
18
|
-
let app: Express;
|
|
19
|
-
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
app = express();
|
|
22
|
-
app.use(express.json());
|
|
23
|
-
app.use('/users', router);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('POST /users should call controller.createUser', async () => {
|
|
27
|
-
await request(app)
|
|
28
|
-
.post('/users')
|
|
29
|
-
.send({ name: 'Test', email: 'test@example.com' });
|
|
30
|
-
|
|
31
|
-
expect(mockCreateUser).toHaveBeenCalledTimes(1);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('GET /users should call controller.getUsers', async () => {
|
|
35
|
-
await request(app)
|
|
36
|
-
.get('/users');
|
|
37
|
-
|
|
38
|
-
expect(mockGetUsers).toHaveBeenCalledTimes(1);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
1
|
+
import request from 'supertest';
|
|
2
|
+
import express, { Express } from 'express';
|
|
3
|
+
import router from '@/interfaces/routes/userRoutes';
|
|
4
|
+
|
|
5
|
+
const mockGetUsers = jest.fn().mockImplementation((req, res) => res.status(200).json([{ id: '1', name: 'John Doe' }]));
|
|
6
|
+
const mockCreateUser = jest.fn().mockImplementation((req, res) => res.status(201).json({ id: '1', name: 'Test' }));
|
|
7
|
+
|
|
8
|
+
jest.mock('@/interfaces/controllers/userController', () => {
|
|
9
|
+
return {
|
|
10
|
+
UserController: jest.fn().mockImplementation(() => ({
|
|
11
|
+
getUsers: (...args: unknown[]) => mockGetUsers(...args),
|
|
12
|
+
createUser: (...args: unknown[]) => mockCreateUser(...args)
|
|
13
|
+
}))
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('UserRoutes', () => {
|
|
18
|
+
let app: Express;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
app = express();
|
|
22
|
+
app.use(express.json());
|
|
23
|
+
app.use('/users', router);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('POST /users should call controller.createUser', async () => {
|
|
27
|
+
await request(app)
|
|
28
|
+
.post('/users')
|
|
29
|
+
.send({ name: 'Test', email: 'test@example.com' });
|
|
30
|
+
|
|
31
|
+
expect(mockCreateUser).toHaveBeenCalledTimes(1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('GET /users should call controller.getUsers', async () => {
|
|
35
|
+
await request(app)
|
|
36
|
+
.get('/users');
|
|
37
|
+
|
|
38
|
+
expect(mockGetUsers).toHaveBeenCalledTimes(1);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import CreateUser from '@/usecases/createUser';
|
|
2
|
-
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
-
<%_ if (caching !== 'None') { -%>
|
|
4
|
-
import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
|
|
5
|
-
<%_ } -%>
|
|
6
|
-
|
|
7
|
-
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
8
|
-
<%_ if (caching !== 'None') { -%>
|
|
9
|
-
jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
|
|
10
|
-
get: jest.fn(),
|
|
11
|
-
set: jest.fn(),
|
|
12
|
-
del: jest.fn()
|
|
13
|
-
}));
|
|
14
|
-
<%_ } -%>
|
|
15
|
-
|
|
16
|
-
describe('CreateUser UseCase', () => {
|
|
17
|
-
let createUser: CreateUser;
|
|
18
|
-
let mockUserRepository: jest.Mocked<UserRepository>;
|
|
19
|
-
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
mockUserRepository = new UserRepository() as jest.Mocked<UserRepository>;
|
|
22
|
-
createUser = new CreateUser(mockUserRepository);
|
|
23
|
-
jest.clearAllMocks();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should create and save a new user', async () => {
|
|
27
|
-
const name = 'Test User';
|
|
28
|
-
const email = 'test@example.com';
|
|
29
|
-
const expectedResult = { id: 1, name, email };
|
|
30
|
-
|
|
31
|
-
mockUserRepository.save.mockResolvedValue(expectedResult as any);
|
|
32
|
-
|
|
33
|
-
const result = await createUser.execute(name, email);
|
|
34
|
-
|
|
35
|
-
expect(mockUserRepository.save).toHaveBeenCalledTimes(1);
|
|
36
|
-
const savedUser = mockUserRepository.save.mock.calls[0][0];
|
|
37
|
-
expect(savedUser.name).toBe(name);
|
|
38
|
-
expect(savedUser.email).toBe(email);
|
|
39
|
-
expect(result).toEqual(expectedResult);
|
|
40
|
-
<%_ if (caching !== 'None') { -%>
|
|
41
|
-
expect(cacheService.del).toHaveBeenCalledWith('users:all');
|
|
42
|
-
<%_ } %>
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should throw an error if repository fails', async () => {
|
|
46
|
-
const error = new Error('Database error');
|
|
47
|
-
mockUserRepository.save.mockRejectedValue(error);
|
|
48
|
-
|
|
49
|
-
await expect(createUser.execute('Test', 'test@test.com')).rejects.toThrow(error);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
1
|
+
import CreateUser from '@/usecases/createUser';
|
|
2
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
+
<%_ if (caching !== 'None') { -%>
|
|
4
|
+
import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
|
|
5
|
+
<%_ } -%>
|
|
6
|
+
|
|
7
|
+
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
8
|
+
<%_ if (caching !== 'None') { -%>
|
|
9
|
+
jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
|
|
10
|
+
get: jest.fn(),
|
|
11
|
+
set: jest.fn(),
|
|
12
|
+
del: jest.fn()
|
|
13
|
+
}));
|
|
14
|
+
<%_ } -%>
|
|
15
|
+
|
|
16
|
+
describe('CreateUser UseCase', () => {
|
|
17
|
+
let createUser: CreateUser;
|
|
18
|
+
let mockUserRepository: jest.Mocked<UserRepository>;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
mockUserRepository = new UserRepository() as jest.Mocked<UserRepository>;
|
|
22
|
+
createUser = new CreateUser(mockUserRepository);
|
|
23
|
+
jest.clearAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should create and save a new user', async () => {
|
|
27
|
+
const name = 'Test User';
|
|
28
|
+
const email = 'test@example.com';
|
|
29
|
+
const expectedResult = { id: 1, name, email };
|
|
30
|
+
|
|
31
|
+
mockUserRepository.save.mockResolvedValue(expectedResult as any);
|
|
32
|
+
|
|
33
|
+
const result = await createUser.execute(name, email);
|
|
34
|
+
|
|
35
|
+
expect(mockUserRepository.save).toHaveBeenCalledTimes(1);
|
|
36
|
+
const savedUser = mockUserRepository.save.mock.calls[0][0];
|
|
37
|
+
expect(savedUser.name).toBe(name);
|
|
38
|
+
expect(savedUser.email).toBe(email);
|
|
39
|
+
expect(result).toEqual(expectedResult);
|
|
40
|
+
<%_ if (caching !== 'None') { -%>
|
|
41
|
+
expect(cacheService.del).toHaveBeenCalledWith('users:all');
|
|
42
|
+
<%_ } %>
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should throw an error if repository fails', async () => {
|
|
46
|
+
const error = new Error('Database error');
|
|
47
|
+
mockUserRepository.save.mockRejectedValue(error);
|
|
48
|
+
|
|
49
|
+
await expect(createUser.execute('Test', 'test@test.com')).rejects.toThrow(error);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { User } from '@/domain/user';
|
|
2
|
-
|
|
3
|
-
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
4
|
-
|
|
5
|
-
export default class CreateUser {
|
|
6
|
-
constructor(private userRepository: UserRepository) {}
|
|
7
|
-
|
|
8
|
-
async execute(name: string, email: string) {
|
|
9
|
-
const user = new User(null, name, email);
|
|
10
|
-
return this.userRepository.save(user);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
1
|
+
import { User } from '@/domain/user';
|
|
2
|
+
|
|
3
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
4
|
+
|
|
5
|
+
export default class CreateUser {
|
|
6
|
+
constructor(private userRepository: UserRepository) {}
|
|
7
|
+
|
|
8
|
+
async execute(name: string, email: string) {
|
|
9
|
+
const user = new User(null, name, email);
|
|
10
|
+
return this.userRepository.save(user);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
import GetAllUsers from '@/usecases/getAllUsers';
|
|
2
|
-
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
-
<%_ if (caching !== 'None') { -%>
|
|
4
|
-
import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
|
|
5
|
-
<%_ } -%>
|
|
6
|
-
|
|
7
|
-
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
8
|
-
<%_ if (caching !== 'None') { -%>
|
|
9
|
-
jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
|
|
10
|
-
get: jest.fn(),
|
|
11
|
-
set: jest.fn(),
|
|
12
|
-
del: jest.fn()
|
|
13
|
-
}));
|
|
14
|
-
<%_ } -%>
|
|
15
|
-
|
|
16
|
-
describe('GetAllUsers UseCase', () => {
|
|
17
|
-
let getAllUsers: GetAllUsers;
|
|
18
|
-
let mockUserRepository: jest.Mocked<UserRepository>;
|
|
19
|
-
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
mockUserRepository = new UserRepository() as jest.Mocked<UserRepository>;
|
|
22
|
-
getAllUsers = new GetAllUsers(mockUserRepository);
|
|
23
|
-
jest.clearAllMocks();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should retrieve all users', async () => {
|
|
27
|
-
const expectedUsers = [{ id: 1, name: 'Alice', email: 'alice@example.com' }];
|
|
28
|
-
mockUserRepository.getUsers.mockResolvedValue(expectedUsers as any);
|
|
29
|
-
<%_ if (caching !== 'None') { -%>
|
|
30
|
-
(cacheService.get as jest.Mock).mockResolvedValue(null);
|
|
31
|
-
<%_ } %>
|
|
32
|
-
|
|
33
|
-
const result = await getAllUsers.execute();
|
|
34
|
-
|
|
35
|
-
expect(mockUserRepository.getUsers).toHaveBeenCalledTimes(1);
|
|
36
|
-
expect(result).toEqual(expectedUsers);
|
|
37
|
-
<%_ if (caching !== 'None') { -%>
|
|
38
|
-
expect(cacheService.set).toHaveBeenCalled();
|
|
39
|
-
<%_ } %>
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
<%_ if (caching !== 'None') { -%>
|
|
43
|
-
it('should return from cache if available', async () => {
|
|
44
|
-
const cachedUsers = [{ id: 1, name: 'Cached', email: 'cached@example.com' }];
|
|
45
|
-
(cacheService.get as jest.Mock).mockResolvedValue(cachedUsers);
|
|
46
|
-
|
|
47
|
-
const result = await getAllUsers.execute();
|
|
48
|
-
|
|
49
|
-
expect(mockUserRepository.getUsers).not.toHaveBeenCalled();
|
|
50
|
-
expect(result).toEqual(cachedUsers);
|
|
51
|
-
});
|
|
52
|
-
<%_ } -%>
|
|
53
|
-
|
|
54
|
-
it('should throw an error if repository fails', async () => {
|
|
55
|
-
const error = new Error('Database error');
|
|
56
|
-
mockUserRepository.getUsers.mockRejectedValue(error);
|
|
57
|
-
<%_ if (caching !== 'None') { -%>
|
|
58
|
-
(cacheService.get as jest.Mock).mockResolvedValue(null);
|
|
59
|
-
<%_ } -%>
|
|
60
|
-
|
|
61
|
-
await expect(getAllUsers.execute()).rejects.toThrow(error);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
1
|
+
import GetAllUsers from '@/usecases/getAllUsers';
|
|
2
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
+
<%_ if (caching !== 'None') { -%>
|
|
4
|
+
import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
|
|
5
|
+
<%_ } -%>
|
|
6
|
+
|
|
7
|
+
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
8
|
+
<%_ if (caching !== 'None') { -%>
|
|
9
|
+
jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
|
|
10
|
+
get: jest.fn(),
|
|
11
|
+
set: jest.fn(),
|
|
12
|
+
del: jest.fn()
|
|
13
|
+
}));
|
|
14
|
+
<%_ } -%>
|
|
15
|
+
|
|
16
|
+
describe('GetAllUsers UseCase', () => {
|
|
17
|
+
let getAllUsers: GetAllUsers;
|
|
18
|
+
let mockUserRepository: jest.Mocked<UserRepository>;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
mockUserRepository = new UserRepository() as jest.Mocked<UserRepository>;
|
|
22
|
+
getAllUsers = new GetAllUsers(mockUserRepository);
|
|
23
|
+
jest.clearAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should retrieve all users', async () => {
|
|
27
|
+
const expectedUsers = [{ id: 1, name: 'Alice', email: 'alice@example.com' }];
|
|
28
|
+
mockUserRepository.getUsers.mockResolvedValue(expectedUsers as any);
|
|
29
|
+
<%_ if (caching !== 'None') { -%>
|
|
30
|
+
(cacheService.get as jest.Mock).mockResolvedValue(null);
|
|
31
|
+
<%_ } %>
|
|
32
|
+
|
|
33
|
+
const result = await getAllUsers.execute();
|
|
34
|
+
|
|
35
|
+
expect(mockUserRepository.getUsers).toHaveBeenCalledTimes(1);
|
|
36
|
+
expect(result).toEqual(expectedUsers);
|
|
37
|
+
<%_ if (caching !== 'None') { -%>
|
|
38
|
+
expect(cacheService.set).toHaveBeenCalled();
|
|
39
|
+
<%_ } %>
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
<%_ if (caching !== 'None') { -%>
|
|
43
|
+
it('should return from cache if available', async () => {
|
|
44
|
+
const cachedUsers = [{ id: 1, name: 'Cached', email: 'cached@example.com' }];
|
|
45
|
+
(cacheService.get as jest.Mock).mockResolvedValue(cachedUsers);
|
|
46
|
+
|
|
47
|
+
const result = await getAllUsers.execute();
|
|
48
|
+
|
|
49
|
+
expect(mockUserRepository.getUsers).not.toHaveBeenCalled();
|
|
50
|
+
expect(result).toEqual(cachedUsers);
|
|
51
|
+
});
|
|
52
|
+
<%_ } -%>
|
|
53
|
+
|
|
54
|
+
it('should throw an error if repository fails', async () => {
|
|
55
|
+
const error = new Error('Database error');
|
|
56
|
+
mockUserRepository.getUsers.mockRejectedValue(error);
|
|
57
|
+
<%_ if (caching !== 'None') { -%>
|
|
58
|
+
(cacheService.get as jest.Mock).mockResolvedValue(null);
|
|
59
|
+
<%_ } -%>
|
|
60
|
+
|
|
61
|
+
await expect(getAllUsers.execute()).rejects.toThrow(error);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
2
|
-
|
|
3
|
-
export default class GetAllUsers {
|
|
4
|
-
constructor(private userRepository: UserRepository) {}
|
|
5
|
-
|
|
6
|
-
async execute() {
|
|
7
|
-
return this.userRepository.getUsers();
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
1
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
2
|
+
|
|
3
|
+
export default class GetAllUsers {
|
|
4
|
+
constructor(private userRepository: UserRepository) {}
|
|
5
|
+
|
|
6
|
+
async execute() {
|
|
7
|
+
return this.userRepository.getUsers();
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { Request, Response } from 'express';
|
|
2
|
-
import logger from '@/infrastructure/log/logger';
|
|
3
|
-
import { ApiError } from '@/errors/ApiError';
|
|
4
|
-
import { HTTP_STATUS } from '@/utils/httpCodes';
|
|
5
|
-
|
|
6
|
-
export const errorMiddleware = (err: Error, req: Request, res: Response, _next: unknown) => {
|
|
7
|
-
let error = err;
|
|
8
|
-
|
|
9
|
-
if (!(error instanceof ApiError)) {
|
|
10
|
-
const statusCode = HTTP_STATUS.INTERNAL_SERVER_ERROR;
|
|
11
|
-
const message = error.message || 'Internal Server Error';
|
|
12
|
-
error = new ApiError(statusCode, message, false, err.stack);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const { statusCode, message } = error as ApiError;
|
|
16
|
-
|
|
17
|
-
if (statusCode === HTTP_STATUS.INTERNAL_SERVER_ERROR) {
|
|
18
|
-
logger.error(`${statusCode} - ${message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
|
|
19
|
-
logger.error(error.stack || 'No stack trace');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
res.status(statusCode).json({
|
|
23
|
-
statusCode,
|
|
24
|
-
message,
|
|
25
|
-
...(process.env.NODE_ENV === 'development' && { stack: error.stack }),
|
|
26
|
-
});
|
|
27
|
-
};
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import logger from '@/infrastructure/log/logger';
|
|
3
|
+
import { ApiError } from '@/errors/ApiError';
|
|
4
|
+
import { HTTP_STATUS } from '@/utils/httpCodes';
|
|
5
|
+
|
|
6
|
+
export const errorMiddleware = (err: Error, req: Request, res: Response, _next: unknown) => {
|
|
7
|
+
let error = err;
|
|
8
|
+
|
|
9
|
+
if (!(error instanceof ApiError)) {
|
|
10
|
+
const statusCode = HTTP_STATUS.INTERNAL_SERVER_ERROR;
|
|
11
|
+
const message = error.message || 'Internal Server Error';
|
|
12
|
+
error = new ApiError(statusCode, message, false, err.stack);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { statusCode, message } = error as ApiError;
|
|
16
|
+
|
|
17
|
+
if (statusCode === HTTP_STATUS.INTERNAL_SERVER_ERROR) {
|
|
18
|
+
logger.error(`${statusCode} - ${message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
|
|
19
|
+
logger.error(error.stack || 'No stack trace');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
res.status(statusCode).json({
|
|
23
|
+
statusCode,
|
|
24
|
+
message,
|
|
25
|
+
...(process.env.NODE_ENV === 'development' && { stack: error.stack }),
|
|
26
|
+
});
|
|
27
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export const HTTP_STATUS = {
|
|
2
|
-
OK: 200,
|
|
3
|
-
CREATED: 201,
|
|
4
|
-
BAD_REQUEST: 400,
|
|
5
|
-
NOT_FOUND: 404,
|
|
6
|
-
INTERNAL_SERVER_ERROR: 500
|
|
7
|
-
} as const;
|
|
1
|
+
export const HTTP_STATUS = {
|
|
2
|
+
OK: 200,
|
|
3
|
+
CREATED: 201,
|
|
4
|
+
BAD_REQUEST: 400,
|
|
5
|
+
NOT_FOUND: 404,
|
|
6
|
+
INTERNAL_SERVER_ERROR: 500
|
|
7
|
+
} as const;
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
# Cursor AI Coding Rules for <%= projectName %>
|
|
2
|
-
|
|
3
|
-
## Project Context
|
|
4
|
-
You are an expert working on **<%= projectName %>**.
|
|
5
|
-
- **Project Goal**: [Replace this with your business logic, e.g., E-commerce API]
|
|
6
|
-
- **Language**: <%= language %>
|
|
7
|
-
- **Architecture**: <%= architecture %>
|
|
8
|
-
- **Database**: <%= database %>
|
|
9
|
-
- **Communication**: <%= communication %>
|
|
10
|
-
|
|
11
|
-
## Excluded Files/Folders
|
|
12
|
-
When indexing or searching the workspace, ignore the following paths to prevent context pollution:
|
|
13
|
-
- `node_modules/`
|
|
14
|
-
- `dist/`
|
|
15
|
-
- `build/`
|
|
16
|
-
- `coverage/`
|
|
17
|
-
- `.git/`
|
|
18
|
-
|
|
19
|
-
## Strict Rules
|
|
20
|
-
|
|
21
|
-
### 1. Testing First
|
|
22
|
-
- Every new service or controller method MUST have a test file in `tests/`.
|
|
23
|
-
- **Coverage Gate**: Aim for > 80% coverage (Statement/Line/Function/Branch).
|
|
24
|
-
- **Format**: Use Jest with the AAA (Arrange, Act, Assert) pattern.
|
|
25
|
-
- **Isolation**: Mock external dependencies (DB, Redis, etc.) using `jest.mock()`.
|
|
26
|
-
|
|
27
|
-
### 2. Error Handling
|
|
28
|
-
- Do NOT use generic `Error`.
|
|
29
|
-
- Use custom classes from `src/errors/` (e.g., `ApiError`, `NotFoundError`, `BadRequestError`).
|
|
30
|
-
<% if (language === 'TypeScript') { -%>
|
|
31
|
-
- Use `HTTP_STATUS` constants from `@/utils/httpCodes` for status codes.
|
|
32
|
-
<% } else { -%>
|
|
33
|
-
- Use `HTTP_STATUS` constants from `../utils/httpCodes.js` for status codes.
|
|
34
|
-
<% } -%>
|
|
35
|
-
|
|
36
|
-
### 3. File Naming & Style
|
|
37
|
-
- **Controllers**: camelCase (e.g., `userController.<% if (language === 'TypeScript') { %>ts<% } else { %>js<% } %>`).
|
|
38
|
-
- **Services**: camelCase (e.g., `userService.<% if (language === 'TypeScript') { %>ts<% } else { %>js<% } %>`).
|
|
39
|
-
- **Routes**: camelCase (e.g., `userRoutes.<% if (language === 'TypeScript') { %>ts<% } else { %>js<% } %>`).
|
|
40
|
-
<% if (language === 'TypeScript') { -%>
|
|
41
|
-
- **Imports**: Use path aliases (e.g., `@/services/...`) instead of relative paths.
|
|
42
|
-
- **Typing**: Ensure strong typing for interfaces and DTOs. Do not use `any` unless absolutely necessary.
|
|
43
|
-
<% } else { -%>
|
|
44
|
-
- **Imports**: Use relative paths as dictated by the directory structure.
|
|
45
|
-
<% } -%>
|
|
46
|
-
|
|
47
|
-
### 4. Architecture Standards
|
|
48
|
-
<% if (architecture === 'Clean Architecture') { -%>
|
|
49
|
-
- Enforce strict separation of concerns:
|
|
50
|
-
- `domain`: Entities and enterprise business rules.
|
|
51
|
-
- `usecases`: Application business rules.
|
|
52
|
-
- `interfaces`: Controllers and Routes.
|
|
53
|
-
- `infrastructure`: Frameworks, Database, Caching, and Web Server.
|
|
54
|
-
- Dependencies point inward toward the `domain`.
|
|
55
|
-
<% } else { -%>
|
|
56
|
-
- Enforce MVC standards:
|
|
57
|
-
- `models`: Data layer.
|
|
58
|
-
- `controllers`: Request handlers and business logic.
|
|
59
|
-
- `routes`: Define endpoints routing to controllers.
|
|
60
|
-
<% } -%>
|
|
1
|
+
# Cursor AI Coding Rules for <%= projectName %>
|
|
2
|
+
|
|
3
|
+
## Project Context
|
|
4
|
+
You are an expert working on **<%= projectName %>**.
|
|
5
|
+
- **Project Goal**: [Replace this with your business logic, e.g., E-commerce API]
|
|
6
|
+
- **Language**: <%= language %>
|
|
7
|
+
- **Architecture**: <%= architecture %>
|
|
8
|
+
- **Database**: <%= database %>
|
|
9
|
+
- **Communication**: <%= communication %>
|
|
10
|
+
|
|
11
|
+
## Excluded Files/Folders
|
|
12
|
+
When indexing or searching the workspace, ignore the following paths to prevent context pollution:
|
|
13
|
+
- `node_modules/`
|
|
14
|
+
- `dist/`
|
|
15
|
+
- `build/`
|
|
16
|
+
- `coverage/`
|
|
17
|
+
- `.git/`
|
|
18
|
+
|
|
19
|
+
## Strict Rules
|
|
20
|
+
|
|
21
|
+
### 1. Testing First
|
|
22
|
+
- Every new service or controller method MUST have a test file in `tests/`.
|
|
23
|
+
- **Coverage Gate**: Aim for > 80% coverage (Statement/Line/Function/Branch).
|
|
24
|
+
- **Format**: Use Jest with the AAA (Arrange, Act, Assert) pattern.
|
|
25
|
+
- **Isolation**: Mock external dependencies (DB, Redis, etc.) using `jest.mock()`.
|
|
26
|
+
|
|
27
|
+
### 2. Error Handling
|
|
28
|
+
- Do NOT use generic `Error`.
|
|
29
|
+
- Use custom classes from `src/errors/` (e.g., `ApiError`, `NotFoundError`, `BadRequestError`).
|
|
30
|
+
<% if (language === 'TypeScript') { -%>
|
|
31
|
+
- Use `HTTP_STATUS` constants from `@/utils/httpCodes` for status codes.
|
|
32
|
+
<% } else { -%>
|
|
33
|
+
- Use `HTTP_STATUS` constants from `../utils/httpCodes.js` for status codes.
|
|
34
|
+
<% } -%>
|
|
35
|
+
|
|
36
|
+
### 3. File Naming & Style
|
|
37
|
+
- **Controllers**: camelCase (e.g., `userController.<% if (language === 'TypeScript') { %>ts<% } else { %>js<% } %>`).
|
|
38
|
+
- **Services**: camelCase (e.g., `userService.<% if (language === 'TypeScript') { %>ts<% } else { %>js<% } %>`).
|
|
39
|
+
- **Routes**: camelCase (e.g., `userRoutes.<% if (language === 'TypeScript') { %>ts<% } else { %>js<% } %>`).
|
|
40
|
+
<% if (language === 'TypeScript') { -%>
|
|
41
|
+
- **Imports**: Use path aliases (e.g., `@/services/...`) instead of relative paths.
|
|
42
|
+
- **Typing**: Ensure strong typing for interfaces and DTOs. Do not use `any` unless absolutely necessary.
|
|
43
|
+
<% } else { -%>
|
|
44
|
+
- **Imports**: Use relative paths as dictated by the directory structure.
|
|
45
|
+
<% } -%>
|
|
46
|
+
|
|
47
|
+
### 4. Architecture Standards
|
|
48
|
+
<% if (architecture === 'Clean Architecture') { -%>
|
|
49
|
+
- Enforce strict separation of concerns:
|
|
50
|
+
- `domain`: Entities and enterprise business rules.
|
|
51
|
+
- `usecases`: Application business rules.
|
|
52
|
+
- `interfaces`: Controllers and Routes.
|
|
53
|
+
- `infrastructure`: Frameworks, Database, Caching, and Web Server.
|
|
54
|
+
- Dependencies point inward toward the `domain`.
|
|
55
|
+
<% } else { -%>
|
|
56
|
+
- Enforce MVC standards:
|
|
57
|
+
- `models`: Data layer.
|
|
58
|
+
- `controllers`: Request handlers and business logic.
|
|
59
|
+
- `routes`: Define endpoints routing to controllers.
|
|
60
|
+
<% } -%>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
node_modules
|
|
2
|
-
npm-debug.log
|
|
3
|
-
dist
|
|
4
|
-
.env
|
|
5
|
-
.git
|
|
6
|
-
.gitignore
|
|
7
|
-
README.md
|
|
8
|
-
docker-compose.yml
|
|
9
|
-
test_results.log
|
|
10
|
-
flyway/sql
|
|
11
|
-
.cursorrules
|
|
12
|
-
prompts
|
|
1
|
+
node_modules
|
|
2
|
+
npm-debug.log
|
|
3
|
+
dist
|
|
4
|
+
.env
|
|
5
|
+
.git
|
|
6
|
+
.gitignore
|
|
7
|
+
README.md
|
|
8
|
+
docker-compose.yml
|
|
9
|
+
test_results.log
|
|
10
|
+
flyway/sql
|
|
11
|
+
.cursorrules
|
|
12
|
+
prompts
|