nodejs-quickstart-structure 1.12.0 → 1.14.0

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 (104) hide show
  1. package/CHANGELOG.md +26 -3
  2. package/README.md +5 -3
  3. package/lib/generator.js +17 -3
  4. package/lib/modules/app-setup.js +167 -47
  5. package/lib/modules/caching-setup.js +13 -0
  6. package/lib/modules/config-files.js +25 -62
  7. package/lib/modules/database-setup.js +35 -30
  8. package/lib/modules/kafka-setup.js +79 -13
  9. package/package.json +1 -2
  10. package/templates/clean-architecture/js/src/errors/BadRequestError.js +1 -1
  11. package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +21 -0
  12. package/templates/clean-architecture/js/src/errors/NotFoundError.js +1 -1
  13. package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +21 -0
  14. package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -0
  15. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +2 -3
  16. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +81 -0
  17. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +20 -9
  18. package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +8 -4
  19. package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +102 -0
  20. package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -0
  21. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +49 -0
  22. package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -0
  23. package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -0
  24. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -0
  25. package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +21 -0
  26. package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +1 -1
  27. package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +21 -0
  28. package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +1 -1
  29. package/templates/clean-architecture/ts/src/index.ts.ejs +15 -11
  30. package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +64 -0
  31. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +85 -0
  32. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +2 -3
  33. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +166 -0
  34. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -0
  35. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +51 -0
  36. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -0
  37. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -0
  38. package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -0
  39. package/templates/clean-architecture/ts/src/utils/{error.middleware.ts.ejs → errorMiddleware.ts.ejs} +1 -2
  40. package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -0
  41. package/templates/common/caching/js/redisClient.js.ejs +4 -0
  42. package/templates/common/caching/js/redisClient.spec.js.ejs +149 -0
  43. package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -0
  44. package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -0
  45. package/templates/common/caching/ts/redisClient.ts.ejs +4 -0
  46. package/templates/common/database/js/database.spec.js.ejs +56 -0
  47. package/templates/common/database/js/models/User.js.ejs +22 -0
  48. package/templates/common/database/js/models/User.spec.js.ejs +84 -0
  49. package/templates/common/database/js/mongoose.spec.js.ejs +43 -0
  50. package/templates/common/database/ts/database.spec.ts.ejs +56 -0
  51. package/templates/common/database/ts/models/User.spec.ts.ejs +84 -0
  52. package/templates/common/database/ts/models/User.ts.ejs +26 -0
  53. package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -0
  54. package/templates/common/eslint.config.mjs.ejs +11 -2
  55. package/templates/common/health/js/healthRoute.js.ejs +44 -0
  56. package/templates/common/health/js/healthRoute.spec.js.ejs +70 -0
  57. package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -0
  58. package/templates/common/health/ts/healthRoute.ts.ejs +43 -0
  59. package/templates/common/jest.config.js.ejs +19 -5
  60. package/templates/common/kafka/js/config/kafka.spec.js.ejs +21 -0
  61. package/templates/common/kafka/js/services/kafkaService.js.ejs +13 -4
  62. package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +60 -0
  63. package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +21 -0
  64. package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +61 -0
  65. package/templates/common/kafka/ts/services/kafkaService.ts.ejs +6 -1
  66. package/templates/common/package.json.ejs +0 -3
  67. package/templates/common/shutdown/js/gracefulShutdown.js.ejs +61 -0
  68. package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +160 -0
  69. package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +158 -0
  70. package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +58 -0
  71. package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -0
  72. package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -0
  73. package/templates/common/tsconfig.json +1 -1
  74. package/templates/mvc/js/src/controllers/userController.js.ejs +4 -31
  75. package/templates/mvc/js/src/controllers/userController.spec.js.ejs +170 -0
  76. package/templates/mvc/js/src/errors/BadRequestError.js +1 -1
  77. package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +21 -0
  78. package/templates/mvc/js/src/errors/NotFoundError.js +1 -1
  79. package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +21 -0
  80. package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -0
  81. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +47 -0
  82. package/templates/mvc/js/src/index.js.ejs +11 -9
  83. package/templates/mvc/js/src/routes/api.spec.js.ejs +36 -0
  84. package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -0
  85. package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +185 -0
  86. package/templates/mvc/ts/src/controllers/userController.ts.ejs +4 -31
  87. package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +21 -0
  88. package/templates/mvc/ts/src/errors/BadRequestError.ts +1 -1
  89. package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +21 -0
  90. package/templates/mvc/ts/src/errors/NotFoundError.ts +1 -1
  91. package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -0
  92. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +51 -0
  93. package/templates/mvc/ts/src/index.ts.ejs +13 -9
  94. package/templates/mvc/ts/src/routes/api.spec.ts.ejs +40 -0
  95. package/templates/mvc/ts/src/utils/{error.middleware.ts.ejs → errorMiddleware.ts.ejs} +1 -2
  96. package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +64 -0
  97. package/docs/demo.gif +0 -0
  98. package/docs/generateCase.md +0 -265
  99. package/docs/generatorFlow.md +0 -233
  100. package/docs/releaseNoteRule.md +0 -42
  101. package/docs/ruleDevelop.md +0 -30
  102. package/templates/common/tests/health.test.ts.ejs +0 -14
  103. /package/templates/clean-architecture/js/src/infrastructure/webserver/{middlewares/error.middleware.js → middleware/errorMiddleware.js} +0 -0
  104. /package/templates/mvc/js/src/utils/{error.middleware.js → errorMiddleware.js} +0 -0
@@ -0,0 +1,102 @@
1
+ jest.mock('node-cache', () => {
2
+ return jest.fn().mockImplementation(() => ({
3
+ get: jest.fn(),
4
+ set: jest.fn(),
5
+ del: jest.fn(),
6
+ }));
7
+ });
8
+
9
+ jest.mock('<%- loggerPath %>', () => ({
10
+ info: jest.fn(),
11
+ error: jest.fn(),
12
+ }));
13
+
14
+ describe('Memory Cache Client', () => {
15
+ let MemoryCacheService: any;
16
+
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
19
+ jest.resetModules();
20
+ MemoryCacheService = require('<% if (architecture === "MVC") { %>@/config/memoryCache<% } else { %>@/infrastructure/caching/memoryCache<% } %>').default;
21
+ });
22
+
23
+ it('should get data from memory cache', async () => {
24
+ const mockData = { test: 'data' };
25
+ MemoryCacheService.cache.get.mockReturnValue(JSON.stringify(mockData));
26
+
27
+ const data = await MemoryCacheService.get('test-key');
28
+ expect(data).toEqual(mockData);
29
+ expect(MemoryCacheService.cache.get).toHaveBeenCalledWith('test-key');
30
+ });
31
+
32
+ it('should return null when key is not found', async () => {
33
+ MemoryCacheService.cache.get.mockReturnValue(undefined);
34
+ const data = await MemoryCacheService.get('non-existent');
35
+ expect(data).toBeNull();
36
+ });
37
+
38
+ it('should return data as is if not a string', async () => {
39
+ const mockData = { test: 'data' };
40
+ MemoryCacheService.cache.get.mockReturnValue(mockData);
41
+ const data = await MemoryCacheService.get('binary-key');
42
+ expect(data).toEqual(mockData);
43
+ });
44
+
45
+ it('should handle errors in get', async () => {
46
+ MemoryCacheService.cache.get.mockImplementation(() => { throw new Error('Cache Error'); });
47
+ const data = await MemoryCacheService.get('error-key');
48
+ expect(data).toBeNull();
49
+ });
50
+
51
+ it('should set data to memory cache without TTL', async () => {
52
+ const mockData = { test: 'data' };
53
+ await MemoryCacheService.set('test-key', mockData);
54
+ expect(MemoryCacheService.cache.set).toHaveBeenCalledWith('test-key', JSON.stringify(mockData));
55
+ });
56
+
57
+ it('should handle errors in set', async () => {
58
+ MemoryCacheService.cache.set.mockImplementation(() => { throw new Error('Cache Error'); });
59
+ await MemoryCacheService.set('test-key', 'value');
60
+ // Should not throw
61
+ });
62
+
63
+ it('should handle errors in del', async () => {
64
+ MemoryCacheService.cache.del.mockImplementation(() => { throw new Error('Cache Error'); });
65
+ await MemoryCacheService.del('test-key');
66
+ // Should not throw
67
+ });
68
+
69
+ it('should use getOrSet and call fetcher if not cached', async () => {
70
+ MemoryCacheService.get = jest.fn().mockResolvedValue(null);
71
+ MemoryCacheService.set = jest.fn();
72
+ const fetcher = jest.fn().mockResolvedValue({ new: 'data' });
73
+
74
+ const result = await MemoryCacheService.getOrSet('new-key', fetcher);
75
+
76
+ expect(result).toEqual({ new: 'data' });
77
+ expect(fetcher).toHaveBeenCalled();
78
+ expect(MemoryCacheService.set).toHaveBeenCalledWith('new-key', { new: 'data' }, 3600);
79
+ });
80
+
81
+ it('should return cached data in getOrSet', async () => {
82
+ const cachedData = { cached: 'data' };
83
+ MemoryCacheService.get = jest.fn().mockResolvedValue(cachedData);
84
+ const fetcher = jest.fn();
85
+
86
+ const result = await MemoryCacheService.getOrSet('cached-key', fetcher);
87
+
88
+ expect(result).toEqual(cachedData);
89
+ expect(fetcher).not.toHaveBeenCalled();
90
+ });
91
+
92
+ it('should handle falsy data from fetcher in getOrSet', async () => {
93
+ MemoryCacheService.get = jest.fn().mockResolvedValue(null);
94
+ MemoryCacheService.set = jest.fn();
95
+ const fetcher = jest.fn().mockResolvedValue(null);
96
+
97
+ const result = await MemoryCacheService.getOrSet('empty-key', fetcher);
98
+
99
+ expect(result).toBeNull();
100
+ expect(MemoryCacheService.set).not.toHaveBeenCalled();
101
+ });
102
+ });
@@ -0,0 +1,157 @@
1
+ jest.mock('ioredis', () => {
2
+ const mRedis = jest.fn(() => ({
3
+ on: jest.fn(),
4
+ get: jest.fn(),
5
+ set: jest.fn(),
6
+ del: jest.fn(),
7
+ quit: jest.fn().mockResolvedValue('OK'),
8
+ }));
9
+ return mRedis;
10
+ });
11
+
12
+ jest.mock('dotenv', () => ({
13
+ config: jest.fn()
14
+ }));
15
+
16
+ const mockLogger = {
17
+ info: jest.fn(),
18
+ error: jest.fn(),
19
+ };
20
+
21
+ jest.mock('<%- loggerPath %>', () => mockLogger);
22
+
23
+ describe('Redis Client', () => {
24
+ let RedisService: any;
25
+ let logger: any;
26
+
27
+ beforeEach(() => {
28
+ jest.clearAllMocks();
29
+ jest.resetModules();
30
+
31
+ // Clean environment
32
+ const envVars = ['REDIS_HOST', 'REDIS_PORT', 'REDIS_PASSWORD'];
33
+ envVars.forEach(v => delete process.env[v]);
34
+
35
+ logger = require('<%- loggerPath %>');
36
+ RedisService = require('<% if (architecture === "MVC") { %>@/config/redisClient<% } else { %>@/infrastructure/caching/redisClient<% } %>').default;
37
+ });
38
+
39
+ it('should initialize with default values when env vars are missing', () => {
40
+ delete process.env.REDIS_HOST;
41
+ delete process.env.REDIS_PORT;
42
+ delete process.env.REDIS_PASSWORD;
43
+
44
+ jest.resetModules();
45
+ const NewRedisService = require('<% if (architecture === "MVC") { %>@/config/redisClient<% } else { %>@/infrastructure/caching/redisClient<% } %>').default;
46
+ expect(NewRedisService).toBeDefined();
47
+ });
48
+
49
+ it('should handle redis events', () => {
50
+ // Find the event handlers
51
+ const handlers: { [key: string]: (...args: any[]) => any } = {};
52
+ (RedisService.client.on as jest.Mock).mock.calls.forEach(([event, handler]) => {
53
+ handlers[event] = handler;
54
+ });
55
+
56
+ if (handlers['connect']) {
57
+ handlers['connect']();
58
+ expect(logger.info).toHaveBeenCalledWith('Redis connected');
59
+ }
60
+
61
+ if (handlers['error']) {
62
+ handlers['error'](new Error('Test Error'));
63
+ expect(logger.error).toHaveBeenCalledWith('Redis error:', expect.any(Error));
64
+ }
65
+ });
66
+
67
+ it('should get data from redis', async () => {
68
+ const mockData = { test: 'data' };
69
+ RedisService.client.get.mockResolvedValue(JSON.stringify(mockData));
70
+
71
+ const data = await RedisService.get('test-key');
72
+ expect(data).toEqual(mockData);
73
+ expect(RedisService.client.get).toHaveBeenCalledWith('test-key');
74
+ });
75
+
76
+ it('should return null when key not found in redis', async () => {
77
+ RedisService.client.get.mockResolvedValue(null);
78
+ const data = await RedisService.get('non-existent');
79
+ expect(data).toBeNull();
80
+ });
81
+
82
+ it('should handle errors in get', async () => {
83
+ RedisService.client.get.mockRejectedValue(new Error('Redis Error'));
84
+ const result = await RedisService.get('test-key');
85
+ expect(result).toBeNull();
86
+ expect(logger.error).toHaveBeenCalled();
87
+ });
88
+
89
+ it('should set data to redis without TTL', async () => {
90
+ const mockData = { test: 'data' };
91
+ await RedisService.set('test-key', mockData);
92
+ expect(RedisService.client.set).toHaveBeenCalledWith('test-key', JSON.stringify(mockData));
93
+ });
94
+
95
+ it('should set data to redis with TTL', async () => {
96
+ const mockData = { test: 'data' };
97
+ await RedisService.set('test-key', mockData, 3600);
98
+ expect(RedisService.client.set).toHaveBeenCalledWith('test-key', JSON.stringify(mockData), 'EX', 3600);
99
+ });
100
+
101
+ it('should handle errors in set', async () => {
102
+ RedisService.client.set.mockRejectedValue(new Error('Redis Error'));
103
+ await RedisService.set('test-key', 'value');
104
+ expect(logger.error).toHaveBeenCalled();
105
+ });
106
+
107
+ it('should delete data from redis', async () => {
108
+ await RedisService.del('test-key');
109
+ expect(RedisService.client.del).toHaveBeenCalledWith('test-key');
110
+ });
111
+
112
+ it('should handle errors in del', async () => {
113
+ RedisService.client.del.mockRejectedValue(new Error('Redis Error'));
114
+ await RedisService.del('test-key');
115
+ expect(logger.error).toHaveBeenCalled();
116
+ });
117
+
118
+ it('should use getOrSet and call fetcher if not cached', async () => {
119
+ RedisService.get = jest.fn().mockResolvedValue(null);
120
+ RedisService.set = jest.fn();
121
+ const fetcher = jest.fn().mockResolvedValue({ new: 'data' });
122
+
123
+ const result = await RedisService.getOrSet('new-key', fetcher);
124
+
125
+ expect(result).toEqual({ new: 'data' });
126
+ expect(fetcher).toHaveBeenCalled();
127
+ expect(RedisService.set).toHaveBeenCalledWith('new-key', { new: 'data' }, 3600);
128
+ });
129
+
130
+ it('should use getOrSet and return cached data', async () => {
131
+ const cachedData = { cached: 'data' };
132
+ RedisService.get = jest.fn().mockResolvedValue(cachedData);
133
+ const fetcher = jest.fn();
134
+
135
+ const result = await RedisService.getOrSet('cached-key', fetcher);
136
+
137
+ expect(result).toEqual(cachedData);
138
+ expect(fetcher).not.toHaveBeenCalled();
139
+ });
140
+
141
+ it('should handle falsy data from fetcher in getOrSet', async () => {
142
+ RedisService.get = jest.fn().mockResolvedValue(null);
143
+ RedisService.set = jest.fn();
144
+ const fetcher = jest.fn().mockResolvedValue(null);
145
+
146
+ const result = await RedisService.getOrSet('empty-key', fetcher);
147
+
148
+ expect(result).toBeNull();
149
+ expect(RedisService.set).not.toHaveBeenCalled();
150
+ });
151
+
152
+ it('should quit the redis client', async () => {
153
+ const result = await RedisService.quit();
154
+ expect(result).toBe('OK');
155
+ expect(RedisService.client.quit).toHaveBeenCalled();
156
+ });
157
+ });
@@ -71,6 +71,10 @@ class RedisService {
71
71
  if (data) await this.set(key, data, ttl);
72
72
  return data;
73
73
  }
74
+
75
+ public async quit(): Promise<'OK'> {
76
+ return await this.client.quit();
77
+ }
74
78
  }
75
79
 
76
80
  export default RedisService.getInstance();
@@ -0,0 +1,56 @@
1
+ jest.mock('sequelize', () => {
2
+ const mSequelize = jest.fn(() => ({
3
+ authenticate: jest.fn().mockResolvedValue(true),
4
+ define: jest.fn(),
5
+ }));
6
+ return { Sequelize: mSequelize };
7
+ });
8
+
9
+ jest.mock('dotenv', () => ({
10
+ config: jest.fn()
11
+ }));
12
+
13
+ describe('Database Configuration', () => {
14
+ beforeEach(() => {
15
+ jest.clearAllMocks();
16
+ jest.resetModules();
17
+
18
+ // Clean environment
19
+ const envVars = ['DB_NAME', 'DB_USER', 'DB_PASSWORD', 'DB_HOST', 'DB_PORT'];
20
+ envVars.forEach(v => delete process.env[v]);
21
+ });
22
+
23
+ it('should initialize Sequelize with environment variables', () => {
24
+ const { Sequelize: SequelizeMock } = require('sequelize');
25
+ process.env.DB_NAME = 'testdb';
26
+ process.env.DB_USER = 'testuser';
27
+ process.env.DB_PASSWORD = 'testpassword';
28
+ process.env.DB_HOST = 'localhost';
29
+ process.env.DB_PORT = '5432';
30
+
31
+ require('<% if (architecture === "MVC") { %>@/config/database<% } else { %>@/infrastructure/database/database<% } %>');
32
+
33
+ expect(SequelizeMock).toHaveBeenCalledWith(
34
+ 'testdb',
35
+ 'testuser',
36
+ 'testpassword',
37
+ expect.objectContaining({
38
+ host: 'localhost',
39
+ port: 5432
40
+ })
41
+ );
42
+ });
43
+
44
+ it('should initialize Sequelize with default values when env vars are missing', () => {
45
+ const { Sequelize: SequelizeMock } = require('sequelize');
46
+ delete process.env.DB_NAME;
47
+ delete process.env.DB_USER;
48
+ delete process.env.DB_PASSWORD;
49
+ delete process.env.DB_HOST;
50
+ delete process.env.DB_PORT;
51
+
52
+ require('<% if (architecture === "MVC") { %>@/config/database<% } else { %>@/infrastructure/database/database<% } %>');
53
+
54
+ expect(SequelizeMock).toHaveBeenCalledTimes(1);
55
+ });
56
+ });
@@ -1,3 +1,24 @@
1
+ <% if (database === 'None') { -%>
2
+ class UserModel {
3
+ static mockData = [];
4
+
5
+ static async find() {
6
+ return this.mockData;
7
+ }
8
+
9
+ static async findAll() {
10
+ return this.mockData;
11
+ }
12
+
13
+ static async create(data) {
14
+ const newUser = { id: String(this.mockData.length + 1), ...data };
15
+ this.mockData.push(newUser);
16
+ return newUser;
17
+ }
18
+ }
19
+
20
+ module.exports = UserModel;
21
+ <% } else { -%>
1
22
  const { DataTypes, Model } = require('sequelize');
2
23
  <% if (architecture === 'MVC') { %>const sequelize = require('../config/database');<% } else { %>const sequelize = require('../database');<% } %>
3
24
 
@@ -28,3 +49,4 @@ User.init(
28
49
  );
29
50
 
30
51
  module.exports = User;
52
+ <% } -%>
@@ -0,0 +1,84 @@
1
+ <% if (database === 'MongoDB') { _%>
2
+ const mongoose = require('mongoose');
3
+
4
+ jest.mock('mongoose', () => {
5
+ const mSchema = jest.fn();
6
+ const mModel = {
7
+ find: jest.fn(),
8
+ create: jest.fn(),
9
+ };
10
+ return {
11
+ Schema: mSchema,
12
+ model: jest.fn().mockReturnValue(mModel),
13
+ };
14
+ });
15
+ <% } else if (database === 'None') { _%>
16
+ // No external dependencies to mock for None DB
17
+ <% } else { _%>
18
+ const { Model } = require('sequelize');
19
+
20
+ jest.mock('sequelize', () => {
21
+ const mDataTypes = {
22
+ INTEGER: 'INTEGER',
23
+ STRING: 'STRING',
24
+ };
25
+ const mModel = class {
26
+ static init = jest.fn();
27
+ static findAll = jest.fn();
28
+ static create = jest.fn();
29
+ };
30
+ return { DataTypes: mDataTypes, Model: mModel };
31
+ });
32
+
33
+ <% if (architecture === 'MVC') { _%>
34
+ jest.mock('@/config/database', () => ({}));
35
+ <% } else { _%>
36
+ jest.mock('@/infrastructure/database/database', () => ({}));
37
+ <% } _%>
38
+ <% } _%>
39
+
40
+ describe('User Model', () => {
41
+ beforeEach(() => {
42
+ jest.clearAllMocks();
43
+ });
44
+
45
+ it('should be defined and initialized', () => {
46
+ const User = require('<% if (architecture === "MVC") { %>@/models/User<% } else { %>@/infrastructure/database/models/User<% } %>');
47
+ expect(User).toBeDefined();
48
+ <% if (database === 'MongoDB') { %>
49
+ expect(mongoose.model).toHaveBeenCalled();
50
+ <% } else if (database === 'None') { %>
51
+ // Test mockData is initialized
52
+ expect(User.mockData).toEqual([]);
53
+ <% } else { %>
54
+ // Sequelize init is called on the class
55
+ expect(Model.init).toHaveBeenCalled();
56
+ <% } %>
57
+ });
58
+
59
+ it('should handle model operations', async () => {
60
+ const User = require('<% if (architecture === "MVC") { %>@/models/User<% } else { %>@/infrastructure/database/models/User<% } %>');
61
+ const data = { name: 'Test', email: 'test@example.com' };
62
+
63
+ <% if (database === 'MongoDB') { %>
64
+ User.create.mockResolvedValue({ id: '1', ...data });
65
+ User.find.mockResolvedValue([{ id: '1', ...data }]);
66
+
67
+ const user = await User.create(data);
68
+ expect(user.name).toBe(data.name);
69
+ expect(await User.find()).toBeDefined();
70
+ <% } else if (database === 'None') { %>
71
+ const user = await User.create(data);
72
+ expect(user.name).toBe(data.name);
73
+ expect(await User.find()).toBeDefined();
74
+ expect(await User.findAll()).toBeDefined();
75
+ <% } else { %>
76
+ User.create.mockResolvedValue({ id: 1, ...data });
77
+ User.findAll.mockResolvedValue([{ id: 1, ...data }]);
78
+
79
+ const user = await User.create(data);
80
+ expect(user.name).toBe(data.name);
81
+ expect(await User.findAll()).toBeDefined();
82
+ <% } %>
83
+ });
84
+ });
@@ -0,0 +1,43 @@
1
+ jest.mock('mongoose', () => ({
2
+ connect: jest.fn().mockResolvedValue(true),
3
+ }));
4
+
5
+ const mockLogger = {
6
+ info: jest.fn(),
7
+ error: jest.fn(),
8
+ };
9
+
10
+ jest.mock('<% if (architecture === "MVC") { %>@/utils/logger<% } else { %>@/infrastructure/log/logger<% } %>', () => mockLogger);
11
+
12
+ describe('Mongoose Configuration', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ jest.resetModules();
16
+ });
17
+
18
+ it('should call mongoose.connect with correct parameters', async () => {
19
+ const connectDB = require('<% if (architecture === "MVC") { %>@/config/database<% } else { %>@/infrastructure/database/database<% } %>');
20
+ const mongoose = require('mongoose');
21
+ await connectDB();
22
+ expect(mongoose.connect).toHaveBeenCalledWith(
23
+ expect.stringContaining('mongodb://'),
24
+ expect.any(Object)
25
+ );
26
+ });
27
+
28
+ it('should handle connection failure and retry', async () => {
29
+ const connectDB = require('<% if (architecture === "MVC") { %>@/config/database<% } else { %>@/infrastructure/database/database<% } %>');
30
+ const mongoose = require('mongoose');
31
+ mongoose.connect
32
+ .mockRejectedValueOnce(new Error('Connection failed'))
33
+ .mockResolvedValueOnce(true);
34
+
35
+ const timeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(cb => cb());
36
+
37
+ await connectDB();
38
+
39
+ expect(mockLogger.error).toHaveBeenCalled();
40
+ expect(mongoose.connect).toHaveBeenCalledTimes(2);
41
+ timeoutSpy.mockRestore();
42
+ });
43
+ });
@@ -0,0 +1,56 @@
1
+ jest.mock('sequelize', () => {
2
+ const mSequelize = jest.fn(() => ({
3
+ authenticate: jest.fn().mockResolvedValue(true),
4
+ define: jest.fn(),
5
+ }));
6
+ return { Sequelize: mSequelize };
7
+ });
8
+
9
+ jest.mock('dotenv', () => ({
10
+ config: jest.fn()
11
+ }));
12
+
13
+ describe('Database Configuration', () => {
14
+ beforeEach(() => {
15
+ jest.clearAllMocks();
16
+ jest.resetModules();
17
+
18
+ // Clean environment
19
+ const envVars = ['DB_NAME', 'DB_USER', 'DB_PASSWORD', 'DB_HOST', 'DB_PORT'];
20
+ envVars.forEach(v => delete process.env[v]);
21
+ });
22
+
23
+ it('should initialize Sequelize with environment variables', () => {
24
+ const { Sequelize: SequelizeMock } = require('sequelize');
25
+ process.env.DB_NAME = 'testdb';
26
+ process.env.DB_USER = 'testuser';
27
+ process.env.DB_PASSWORD = 'testpassword';
28
+ process.env.DB_HOST = 'localhost';
29
+ process.env.DB_PORT = '5432';
30
+
31
+ require('<% if (architecture === "MVC") { %>@/config/database<% } else { %>@/infrastructure/database/database<% } %>');
32
+
33
+ expect(SequelizeMock).toHaveBeenLastCalledWith(
34
+ 'testdb',
35
+ 'testuser',
36
+ 'testpassword',
37
+ expect.objectContaining({
38
+ host: 'localhost',
39
+ port: 5432
40
+ })
41
+ );
42
+ });
43
+
44
+ it('should initialize Sequelize with default values when env vars are missing', () => {
45
+ const { Sequelize: SequelizeMock } = require('sequelize');
46
+ delete process.env.DB_NAME;
47
+ delete process.env.DB_USER;
48
+ delete process.env.DB_PASSWORD;
49
+ delete process.env.DB_HOST;
50
+ delete process.env.DB_PORT;
51
+
52
+ require('<% if (architecture === "MVC") { %>@/config/database<% } else { %>@/infrastructure/database/database<% } %>');
53
+
54
+ expect(SequelizeMock).toHaveBeenCalledTimes(1);
55
+ });
56
+ });
@@ -0,0 +1,84 @@
1
+ <% if (database === 'MongoDB') { _%>
2
+ import mongoose from 'mongoose';
3
+
4
+ jest.mock('mongoose', () => {
5
+ const mSchema = jest.fn();
6
+ const mModel = {
7
+ find: jest.fn(),
8
+ create: jest.fn(),
9
+ };
10
+ return {
11
+ Schema: mSchema,
12
+ model: jest.fn().mockReturnValue(mModel),
13
+ };
14
+ });
15
+ <% } else if (database === 'None') { _%>
16
+ // No external dependencies to mock for None DB
17
+ <% } else { _%>
18
+ import { Model } from 'sequelize';
19
+
20
+ jest.mock('sequelize', () => {
21
+ const mDataTypes = {
22
+ INTEGER: 'INTEGER',
23
+ STRING: 'STRING',
24
+ };
25
+ const mModel = class {
26
+ static init = jest.fn();
27
+ static findAll = jest.fn();
28
+ static create = jest.fn();
29
+ };
30
+ return { DataTypes: mDataTypes, Model: mModel };
31
+ });
32
+
33
+ <% if (architecture === 'MVC') { _%>
34
+ jest.mock('@/config/database', () => ({}));
35
+ <% } else { _%>
36
+ jest.mock('@/infrastructure/database/database', () => ({}));
37
+ <% } _%>
38
+ <% } _%>
39
+
40
+ describe('User Model', () => {
41
+ beforeEach(() => {
42
+ jest.clearAllMocks();
43
+ });
44
+
45
+ it('should be defined and initialized', () => {
46
+ const User = require('<% if (architecture === "MVC") { %>@/models/User<% } else { %>@/infrastructure/database/models/User<% } %>').default;
47
+ expect(User).toBeDefined();
48
+ <% if (database === 'MongoDB') { %>
49
+ expect(mongoose.model).toHaveBeenCalled();
50
+ <% } else if (database === 'None') { %>
51
+ // Test mockData is initialized
52
+ expect(User.mockData).toEqual([]);
53
+ <% } else { %>
54
+ // Sequelize init is called on the class
55
+ expect(Model.init).toHaveBeenCalled();
56
+ <% } %>
57
+ });
58
+
59
+ it('should handle model operations', async () => {
60
+ const User = require('<% if (architecture === "MVC") { %>@/models/User<% } else { %>@/infrastructure/database/models/User<% } %>').default;
61
+ const data = { name: 'Test', email: 'test@example.com' };
62
+
63
+ <% if (database === 'MongoDB') { %>
64
+ (User.create as jest.Mock).mockResolvedValue({ id: '1', ...data });
65
+ (User.find as jest.Mock).mockResolvedValue([{ id: '1', ...data }]);
66
+
67
+ const user = await User.create(data);
68
+ expect(user.name).toBe(data.name);
69
+ expect(await User.find()).toBeDefined();
70
+ <% } else if (database === 'None') { %>
71
+ const user = await User.create(data);
72
+ expect(user.name).toBe(data.name);
73
+ expect(await User.find()).toBeDefined();
74
+ expect(await User.findAll()).toBeDefined();
75
+ <% } else { %>
76
+ (User.create as jest.Mock).mockResolvedValue({ id: 1, ...data });
77
+ (User.findAll as jest.Mock).mockResolvedValue([{ id: 1, ...data }]);
78
+
79
+ const user = await User.create(data);
80
+ expect(user.name).toBe(data.name);
81
+ expect(await User.findAll()).toBeDefined();
82
+ <% } %>
83
+ });
84
+ });
@@ -1,3 +1,28 @@
1
+ <% if (database === 'None') { -%>
2
+ export interface User {
3
+ id: string;
4
+ name: string;
5
+ email: string;
6
+ }
7
+
8
+ export default class UserModel {
9
+ static mockData: User[] = [];
10
+
11
+ static async find() {
12
+ return this.mockData;
13
+ }
14
+
15
+ static async findAll() {
16
+ return this.mockData;
17
+ }
18
+
19
+ static async create(data: Omit<User, 'id'>) {
20
+ const newUser = { id: String(this.mockData.length + 1), ...data };
21
+ this.mockData.push(newUser);
22
+ return newUser as User;
23
+ }
24
+ }
25
+ <% } else { -%>
1
26
  import { DataTypes, Model } from 'sequelize';
2
27
  <% if (architecture === 'MVC') { %>import sequelize from '@/config/database';<% } else { %>import sequelize from '@/infrastructure/database/database';<% } %>
3
28
 
@@ -32,3 +57,4 @@ User.init(
32
57
  );
33
58
 
34
59
  export default User;
60
+ <% } -%>