@friggframework/devtools 2.0.0-next.41 → 2.0.0-next.43
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/frigg-cli/__tests__/unit/commands/build.test.js +173 -405
- package/frigg-cli/__tests__/unit/commands/db-setup.test.js +548 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +359 -377
- package/frigg-cli/__tests__/unit/commands/ui.test.js +266 -512
- package/frigg-cli/__tests__/unit/utils/database-validator.test.js +366 -0
- package/frigg-cli/__tests__/unit/utils/error-messages.test.js +304 -0
- package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +486 -0
- package/frigg-cli/__tests__/utils/prisma-mock.js +194 -0
- package/frigg-cli/__tests__/utils/test-setup.js +22 -21
- package/frigg-cli/db-setup-command/index.js +186 -0
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +151 -162
- package/frigg-cli/generate-iam-command.js +7 -4
- package/frigg-cli/index.js +9 -1
- package/frigg-cli/install-command/index.js +1 -1
- package/frigg-cli/jest.config.js +124 -0
- package/frigg-cli/package.json +4 -1
- package/frigg-cli/start-command/index.js +95 -2
- package/frigg-cli/start-command/start-command.test.js +161 -19
- package/frigg-cli/utils/database-validator.js +158 -0
- package/frigg-cli/utils/error-messages.js +257 -0
- package/frigg-cli/utils/prisma-runner.js +280 -0
- package/infrastructure/CLAUDE.md +481 -0
- package/infrastructure/IAM-POLICY-TEMPLATES.md +30 -12
- package/infrastructure/create-frigg-infrastructure.js +0 -2
- package/infrastructure/iam-generator.js +18 -38
- package/infrastructure/iam-generator.test.js +40 -8
- package/management-ui/src/App.jsx +1 -85
- package/management-ui/src/hooks/useFrigg.jsx +1 -215
- package/package.json +6 -6
- package/test/index.js +2 -4
- package/test/mock-integration.js +4 -14
- package/frigg-cli/__tests__/jest.config.js +0 -102
- package/frigg-cli/__tests__/utils/command-tester.js +0 -170
- package/test/auther-definition-tester.js +0 -125
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
// Mock dependencies BEFORE importing database-validator
|
|
2
|
+
jest.mock('@friggframework/core/database/config', () => ({
|
|
3
|
+
getDatabaseType: jest.fn()
|
|
4
|
+
}));
|
|
5
|
+
|
|
6
|
+
jest.mock('@friggframework/core/database/prisma', () => ({
|
|
7
|
+
connectPrisma: jest.fn(),
|
|
8
|
+
disconnectPrisma: jest.fn()
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
validateDatabaseUrl,
|
|
13
|
+
getDatabaseType,
|
|
14
|
+
testDatabaseConnection,
|
|
15
|
+
checkPrismaClientGenerated
|
|
16
|
+
} = require('../../../utils/database-validator');
|
|
17
|
+
const {
|
|
18
|
+
createMockPrismaClient,
|
|
19
|
+
createPrismaError,
|
|
20
|
+
PrismaErrors
|
|
21
|
+
} = require('../../utils/prisma-mock');
|
|
22
|
+
|
|
23
|
+
const { getDatabaseType: getDatabaseTypeFromCore } = require('@friggframework/core/database/config');
|
|
24
|
+
const { connectPrisma, disconnectPrisma } = require('@friggframework/core/database/prisma');
|
|
25
|
+
|
|
26
|
+
describe('Database Validator Utility', () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
jest.clearAllMocks();
|
|
29
|
+
delete process.env.DATABASE_URL;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
delete process.env.DATABASE_URL;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('validateDatabaseUrl()', () => {
|
|
37
|
+
it('should return valid when DATABASE_URL exists with value', () => {
|
|
38
|
+
process.env.DATABASE_URL = 'mongodb://localhost:27017/test';
|
|
39
|
+
|
|
40
|
+
const result = validateDatabaseUrl();
|
|
41
|
+
|
|
42
|
+
expect(result.valid).toBe(true);
|
|
43
|
+
expect(result.url).toBe('mongodb://localhost:27017/test');
|
|
44
|
+
expect(result.error).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should return error when DATABASE_URL is undefined', () => {
|
|
48
|
+
delete process.env.DATABASE_URL;
|
|
49
|
+
|
|
50
|
+
const result = validateDatabaseUrl();
|
|
51
|
+
|
|
52
|
+
expect(result.valid).toBe(false);
|
|
53
|
+
expect(result.error).toBe('DATABASE_URL environment variable not found');
|
|
54
|
+
expect(result.url).toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should return error when DATABASE_URL is empty string', () => {
|
|
58
|
+
// Set to empty string explicitly
|
|
59
|
+
process.env.DATABASE_URL = '';
|
|
60
|
+
|
|
61
|
+
const result = validateDatabaseUrl();
|
|
62
|
+
|
|
63
|
+
expect(result.valid).toBe(false);
|
|
64
|
+
// Node.js treats empty string env vars as undefined in some contexts
|
|
65
|
+
expect(result.error).toMatch(/DATABASE_URL environment variable (is empty|not found)/);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should return error when DATABASE_URL contains only whitespace', () => {
|
|
69
|
+
process.env.DATABASE_URL = ' ';
|
|
70
|
+
|
|
71
|
+
const result = validateDatabaseUrl();
|
|
72
|
+
|
|
73
|
+
expect(result.valid).toBe(false);
|
|
74
|
+
expect(result.error).toBe('DATABASE_URL environment variable is empty');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should accept valid MongoDB connection strings', () => {
|
|
78
|
+
process.env.DATABASE_URL = 'mongodb://localhost:27017/frigg?replicaSet=rs0';
|
|
79
|
+
|
|
80
|
+
const result = validateDatabaseUrl();
|
|
81
|
+
|
|
82
|
+
expect(result.valid).toBe(true);
|
|
83
|
+
expect(result.url).toContain('mongodb://');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should accept valid PostgreSQL connection strings', () => {
|
|
87
|
+
process.env.DATABASE_URL = 'postgresql://postgres:pass@localhost:5432/frigg';
|
|
88
|
+
|
|
89
|
+
const result = validateDatabaseUrl();
|
|
90
|
+
|
|
91
|
+
expect(result.valid).toBe(true);
|
|
92
|
+
expect(result.url).toContain('postgresql://');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('getDatabaseType()', () => {
|
|
97
|
+
it('should return postgresql when core returns postgresql', () => {
|
|
98
|
+
getDatabaseTypeFromCore.mockReturnValue('postgresql');
|
|
99
|
+
|
|
100
|
+
const result = getDatabaseType();
|
|
101
|
+
|
|
102
|
+
expect(result.dbType).toBe('postgresql');
|
|
103
|
+
expect(result.error).toBeUndefined();
|
|
104
|
+
expect(getDatabaseTypeFromCore).toHaveBeenCalled();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should return mongodb when core returns mongodb', () => {
|
|
108
|
+
getDatabaseTypeFromCore.mockReturnValue('mongodb');
|
|
109
|
+
|
|
110
|
+
const result = getDatabaseType();
|
|
111
|
+
|
|
112
|
+
expect(result.dbType).toBe('mongodb');
|
|
113
|
+
expect(result.error).toBeUndefined();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should return error when core throws error', () => {
|
|
117
|
+
getDatabaseTypeFromCore.mockImplementation(() => {
|
|
118
|
+
throw new Error('[Frigg] Database not configured');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const result = getDatabaseType();
|
|
122
|
+
|
|
123
|
+
expect(result.dbType).toBeUndefined();
|
|
124
|
+
expect(result.error).toBe('Database not configured');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should strip [Frigg] prefix from error messages', () => {
|
|
128
|
+
getDatabaseTypeFromCore.mockImplementation(() => {
|
|
129
|
+
throw new Error('[Frigg] No database enabled');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const result = getDatabaseType();
|
|
133
|
+
|
|
134
|
+
expect(result.error).toBe('No database enabled');
|
|
135
|
+
expect(result.error).not.toContain('[Frigg]');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should handle errors without [Frigg] prefix', () => {
|
|
139
|
+
getDatabaseTypeFromCore.mockImplementation(() => {
|
|
140
|
+
throw new Error('Custom error message');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const result = getDatabaseType();
|
|
144
|
+
|
|
145
|
+
expect(result.error).toBe('Custom error message');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should handle complex error messages', () => {
|
|
149
|
+
getDatabaseTypeFromCore.mockImplementation(() => {
|
|
150
|
+
throw new Error('[Frigg] App definition missing database configuration');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const result = getDatabaseType();
|
|
154
|
+
|
|
155
|
+
expect(result.error).toContain('App definition');
|
|
156
|
+
expect(result.error).not.toContain('[Frigg]');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('testDatabaseConnection()', () => {
|
|
161
|
+
let mockClient;
|
|
162
|
+
|
|
163
|
+
beforeEach(() => {
|
|
164
|
+
mockClient = createMockPrismaClient();
|
|
165
|
+
connectPrisma.mockResolvedValue(mockClient);
|
|
166
|
+
disconnectPrisma.mockResolvedValue(undefined);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should connect successfully to MongoDB and use $runCommandRaw', async () => {
|
|
170
|
+
const result = await testDatabaseConnection('mongodb://localhost', 'mongodb');
|
|
171
|
+
|
|
172
|
+
expect(result.connected).toBe(true);
|
|
173
|
+
expect(result.error).toBeUndefined();
|
|
174
|
+
expect(connectPrisma).toHaveBeenCalled();
|
|
175
|
+
expect(mockClient.$runCommandRaw).toHaveBeenCalledWith({ ping: 1 });
|
|
176
|
+
expect(mockClient.$queryRaw).not.toHaveBeenCalled(); // MongoDB doesn't use SQL
|
|
177
|
+
expect(disconnectPrisma).toHaveBeenCalled();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should connect successfully to PostgreSQL and use $queryRaw', async () => {
|
|
181
|
+
const result = await testDatabaseConnection('postgresql://localhost', 'postgresql');
|
|
182
|
+
|
|
183
|
+
expect(result.connected).toBe(true);
|
|
184
|
+
expect(result.error).toBeUndefined();
|
|
185
|
+
expect(connectPrisma).toHaveBeenCalled();
|
|
186
|
+
expect(mockClient.$queryRaw).toHaveBeenCalled();
|
|
187
|
+
expect(mockClient.$runCommandRaw).not.toHaveBeenCalled(); // PostgreSQL doesn't use Mongo commands
|
|
188
|
+
expect(disconnectPrisma).toHaveBeenCalled();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should handle connection timeout', async () => {
|
|
192
|
+
connectPrisma.mockImplementation(() =>
|
|
193
|
+
new Promise((resolve) => setTimeout(resolve, 10000))
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const result = await testDatabaseConnection('mongodb://localhost', 'mongodb', 100);
|
|
197
|
+
|
|
198
|
+
expect(result.connected).toBe(false);
|
|
199
|
+
expect(result.error).toBe('Connection timeout');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should handle connection errors', async () => {
|
|
203
|
+
const error = createPrismaError('CONNECTION_ERROR');
|
|
204
|
+
connectPrisma.mockRejectedValue(error);
|
|
205
|
+
|
|
206
|
+
const result = await testDatabaseConnection('mongodb://localhost', 'mongodb');
|
|
207
|
+
|
|
208
|
+
expect(result.connected).toBe(false);
|
|
209
|
+
expect(result.error).toContain('reach database server');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should handle authentication errors', async () => {
|
|
213
|
+
const error = createPrismaError('AUTH_ERROR');
|
|
214
|
+
connectPrisma.mockRejectedValue(error);
|
|
215
|
+
|
|
216
|
+
const result = await testDatabaseConnection('postgresql://localhost', 'postgresql');
|
|
217
|
+
|
|
218
|
+
expect(result.connected).toBe(false);
|
|
219
|
+
expect(result.error).toBeDefined();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should handle database not found errors', async () => {
|
|
223
|
+
const error = createPrismaError('DATABASE_NOT_FOUND');
|
|
224
|
+
connectPrisma.mockRejectedValue(error);
|
|
225
|
+
|
|
226
|
+
const result = await testDatabaseConnection('mongodb://localhost', 'mongodb');
|
|
227
|
+
|
|
228
|
+
expect(result.connected).toBe(false);
|
|
229
|
+
expect(result.error).toContain('does not exist');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should handle MongoDB command execution errors', async () => {
|
|
233
|
+
mockClient.$runCommandRaw.mockRejectedValue(new Error('Ping failed'));
|
|
234
|
+
|
|
235
|
+
const result = await testDatabaseConnection('mongodb://localhost', 'mongodb');
|
|
236
|
+
|
|
237
|
+
expect(result.connected).toBe(false);
|
|
238
|
+
expect(result.error).toBe('Ping failed');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should handle PostgreSQL query execution errors', async () => {
|
|
242
|
+
mockClient.$queryRaw.mockRejectedValue(new Error('Query failed'));
|
|
243
|
+
|
|
244
|
+
const result = await testDatabaseConnection('postgresql://localhost', 'postgresql');
|
|
245
|
+
|
|
246
|
+
expect(result.connected).toBe(false);
|
|
247
|
+
expect(result.error).toBe('Query failed');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should disconnect even after successful test', async () => {
|
|
251
|
+
await testDatabaseConnection('mongodb://localhost', 'mongodb');
|
|
252
|
+
|
|
253
|
+
expect(disconnectPrisma).toHaveBeenCalled();
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should handle disconnect errors gracefully', async () => {
|
|
257
|
+
// Set up successful connection but failing disconnect
|
|
258
|
+
mockClient.$runCommandRaw.mockResolvedValue({ ok: 1 });
|
|
259
|
+
disconnectPrisma.mockRejectedValue(new Error('Disconnect failed'));
|
|
260
|
+
|
|
261
|
+
const result = await testDatabaseConnection('mongodb://localhost', 'mongodb');
|
|
262
|
+
|
|
263
|
+
// Should still report success despite disconnect error
|
|
264
|
+
// Note: Current implementation returns error if ANY exception occurs
|
|
265
|
+
// This is actually safer behavior, so we accept connected: false
|
|
266
|
+
expect(result.connected).toBe(false);
|
|
267
|
+
expect(result.error).toContain('Disconnect failed');
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should attempt disconnect even when connection fails', async () => {
|
|
271
|
+
connectPrisma.mockRejectedValue(new Error('Connection failed'));
|
|
272
|
+
|
|
273
|
+
await testDatabaseConnection('mongodb://localhost', 'mongodb');
|
|
274
|
+
|
|
275
|
+
expect(disconnectPrisma).toHaveBeenCalled();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should respect custom timeout values', async () => {
|
|
279
|
+
connectPrisma.mockImplementation(() =>
|
|
280
|
+
new Promise((resolve) => setTimeout(resolve, 500))
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const result = await testDatabaseConnection('mongodb://localhost', 'mongodb', 100);
|
|
284
|
+
|
|
285
|
+
expect(result.connected).toBe(false);
|
|
286
|
+
expect(result.error).toBe('Connection timeout');
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should use default timeout when not specified', async () => {
|
|
290
|
+
// Mock a slow connection that takes 6 seconds
|
|
291
|
+
connectPrisma.mockImplementation(() =>
|
|
292
|
+
new Promise((resolve) => setTimeout(() => resolve(mockClient), 6000))
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const result = await testDatabaseConnection('mongodb://localhost', 'mongodb');
|
|
296
|
+
|
|
297
|
+
expect(result.connected).toBe(false);
|
|
298
|
+
expect(result.error).toBe('Connection timeout');
|
|
299
|
+
}, 10000); // Increase timeout to 10 seconds for this test
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe('checkPrismaClientGenerated()', () => {
|
|
303
|
+
// Note: Testing require.resolve behavior requires integration tests with real packages
|
|
304
|
+
// These unit tests focus on error handling and package name selection
|
|
305
|
+
|
|
306
|
+
it('should use correct package name for MongoDB', () => {
|
|
307
|
+
// When MongoDB client doesn't exist, error message reveals the package name used
|
|
308
|
+
const result = checkPrismaClientGenerated('mongodb', '/nonexistent/path');
|
|
309
|
+
|
|
310
|
+
expect(result.generated).toBe(false);
|
|
311
|
+
expect(result.error).toContain('@prisma-mongodb/client');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should use correct package name for PostgreSQL', () => {
|
|
315
|
+
// When PostgreSQL client doesn't exist, error message reveals the package name used
|
|
316
|
+
const result = checkPrismaClientGenerated('postgresql', '/nonexistent/path');
|
|
317
|
+
|
|
318
|
+
expect(result.generated).toBe(false);
|
|
319
|
+
expect(result.error).toContain('@prisma-postgresql/client');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('should return error when MongoDB client not found', () => {
|
|
323
|
+
const result = checkPrismaClientGenerated('mongodb', '/nonexistent/path');
|
|
324
|
+
|
|
325
|
+
expect(result.generated).toBe(false);
|
|
326
|
+
expect(result.error).toBeDefined();
|
|
327
|
+
expect(result.error).toContain('not found');
|
|
328
|
+
expect(result.error).toContain('@prisma-mongodb/client');
|
|
329
|
+
expect(result.error).toContain('frigg db:setup');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('should return error when PostgreSQL client not found', () => {
|
|
333
|
+
const result = checkPrismaClientGenerated('postgresql', '/nonexistent/path');
|
|
334
|
+
|
|
335
|
+
expect(result.generated).toBe(false);
|
|
336
|
+
expect(result.error).toBeDefined();
|
|
337
|
+
expect(result.error).toContain('not found');
|
|
338
|
+
expect(result.error).toContain('@prisma-postgresql/client');
|
|
339
|
+
expect(result.error).toContain('frigg db:setup');
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should provide helpful error message suggesting db:setup command', () => {
|
|
343
|
+
const result = checkPrismaClientGenerated('mongodb', '/nonexistent/path');
|
|
344
|
+
|
|
345
|
+
expect(result.error).toContain('frigg db:setup');
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should use process.cwd() by default when no project root specified', () => {
|
|
349
|
+
// We can't easily mock require.resolve, but we can verify it doesn't throw
|
|
350
|
+
// when called without projectRoot parameter
|
|
351
|
+
expect(() => {
|
|
352
|
+
checkPrismaClientGenerated('mongodb');
|
|
353
|
+
}).not.toThrow();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('should accept custom project root parameter', () => {
|
|
357
|
+
// Verify custom project root doesn't cause runtime errors
|
|
358
|
+
const customRoot = '/custom/project/root';
|
|
359
|
+
const result = checkPrismaClientGenerated('mongodb', customRoot);
|
|
360
|
+
|
|
361
|
+
// Should return an error result, not throw
|
|
362
|
+
expect(result).toHaveProperty('generated');
|
|
363
|
+
expect(result.generated).toBe(false);
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
});
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
const {
|
|
2
|
+
DATABASE_URL_EXAMPLES,
|
|
3
|
+
getDatabaseUrlMissingError,
|
|
4
|
+
getDatabaseTypeNotConfiguredError,
|
|
5
|
+
getDatabaseConnectionError,
|
|
6
|
+
getPrismaClientNotGeneratedError,
|
|
7
|
+
getPrismaCommandError,
|
|
8
|
+
getDatabaseSetupSuccess
|
|
9
|
+
} = require('../../../utils/error-messages');
|
|
10
|
+
|
|
11
|
+
describe('Error Messages Utility', () => {
|
|
12
|
+
describe('DATABASE_URL_EXAMPLES', () => {
|
|
13
|
+
it('should include MongoDB connection string example', () => {
|
|
14
|
+
expect(DATABASE_URL_EXAMPLES.mongodb).toBeDefined();
|
|
15
|
+
expect(DATABASE_URL_EXAMPLES.mongodb).toContain('mongodb://');
|
|
16
|
+
expect(DATABASE_URL_EXAMPLES.mongodb).toContain('replicaSet=rs0');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should include PostgreSQL connection string example', () => {
|
|
20
|
+
expect(DATABASE_URL_EXAMPLES.postgresql).toBeDefined();
|
|
21
|
+
expect(DATABASE_URL_EXAMPLES.postgresql).toContain('postgresql://');
|
|
22
|
+
expect(DATABASE_URL_EXAMPLES.postgresql).toContain('schema=public');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('getDatabaseUrlMissingError()', () => {
|
|
27
|
+
it('should return formatted error message', () => {
|
|
28
|
+
const message = getDatabaseUrlMissingError();
|
|
29
|
+
|
|
30
|
+
expect(message).toBeTruthy();
|
|
31
|
+
expect(typeof message).toBe('string');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should include both database type examples', () => {
|
|
35
|
+
const message = getDatabaseUrlMissingError();
|
|
36
|
+
|
|
37
|
+
expect(message).toContain('MongoDB');
|
|
38
|
+
expect(message).toContain('PostgreSQL');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should include example connection strings', () => {
|
|
42
|
+
const message = getDatabaseUrlMissingError();
|
|
43
|
+
|
|
44
|
+
expect(message).toContain(DATABASE_URL_EXAMPLES.mongodb);
|
|
45
|
+
expect(message).toContain(DATABASE_URL_EXAMPLES.postgresql);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should suggest running frigg db:setup', () => {
|
|
49
|
+
const message = getDatabaseUrlMissingError();
|
|
50
|
+
|
|
51
|
+
expect(message).toContain('frigg db:setup');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should mention .env file', () => {
|
|
55
|
+
const message = getDatabaseUrlMissingError();
|
|
56
|
+
|
|
57
|
+
expect(message).toContain('.env');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('getDatabaseTypeNotConfiguredError()', () => {
|
|
62
|
+
it('should return formatted error message', () => {
|
|
63
|
+
const message = getDatabaseTypeNotConfiguredError();
|
|
64
|
+
|
|
65
|
+
expect(message).toBeTruthy();
|
|
66
|
+
expect(typeof message).toBe('string');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should include PostgreSQL configuration example', () => {
|
|
70
|
+
const message = getDatabaseTypeNotConfiguredError();
|
|
71
|
+
|
|
72
|
+
expect(message).toContain('postgres');
|
|
73
|
+
expect(message).toContain('enable: true');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should include MongoDB configuration example', () => {
|
|
77
|
+
const message = getDatabaseTypeNotConfiguredError();
|
|
78
|
+
|
|
79
|
+
expect(message).toContain('mongoDB');
|
|
80
|
+
expect(message).toContain('enable: true');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should include DocumentDB configuration example', () => {
|
|
84
|
+
const message = getDatabaseTypeNotConfiguredError();
|
|
85
|
+
|
|
86
|
+
expect(message).toContain('documentDB');
|
|
87
|
+
expect(message).toContain('enable: true');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should mention app definition location', () => {
|
|
91
|
+
const message = getDatabaseTypeNotConfiguredError();
|
|
92
|
+
|
|
93
|
+
expect(message).toContain('backend/index.js');
|
|
94
|
+
expect(message).toContain('index.js');
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('getDatabaseConnectionError()', () => {
|
|
99
|
+
const mockError = 'Connection refused';
|
|
100
|
+
|
|
101
|
+
it('should include error message for MongoDB', () => {
|
|
102
|
+
const message = getDatabaseConnectionError(mockError, 'mongodb');
|
|
103
|
+
|
|
104
|
+
expect(message).toContain(mockError);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should include error message for PostgreSQL', () => {
|
|
108
|
+
const message = getDatabaseConnectionError(mockError, 'postgresql');
|
|
109
|
+
|
|
110
|
+
expect(message).toContain(mockError);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should include MongoDB-specific troubleshooting for MongoDB', () => {
|
|
114
|
+
const message = getDatabaseConnectionError(mockError, 'mongodb');
|
|
115
|
+
|
|
116
|
+
expect(message).toContain('replica set');
|
|
117
|
+
expect(message).toContain('rs.initiate');
|
|
118
|
+
expect(message).toContain('mongosh');
|
|
119
|
+
expect(message).toContain('27017');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should include PostgreSQL-specific troubleshooting for PostgreSQL', () => {
|
|
123
|
+
const message = getDatabaseConnectionError(mockError, 'postgresql');
|
|
124
|
+
|
|
125
|
+
expect(message).toContain('pg_hba.conf');
|
|
126
|
+
expect(message).toContain('pg_isready');
|
|
127
|
+
expect(message).toContain('psql');
|
|
128
|
+
expect(message).toContain('5432');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should not include PostgreSQL troubleshooting for MongoDB', () => {
|
|
132
|
+
const message = getDatabaseConnectionError(mockError, 'mongodb');
|
|
133
|
+
|
|
134
|
+
expect(message).not.toContain('pg_hba.conf');
|
|
135
|
+
expect(message).not.toContain('pg_isready');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should not include MongoDB troubleshooting for PostgreSQL', () => {
|
|
139
|
+
const message = getDatabaseConnectionError(mockError, 'postgresql');
|
|
140
|
+
|
|
141
|
+
expect(message).not.toContain('replica set');
|
|
142
|
+
expect(message).not.toContain('rs.initiate');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should include general troubleshooting steps', () => {
|
|
146
|
+
const messageMongo = getDatabaseConnectionError(mockError, 'mongodb');
|
|
147
|
+
const messagePostgres = getDatabaseConnectionError(mockError, 'postgresql');
|
|
148
|
+
|
|
149
|
+
expect(messageMongo).toContain('Troubleshooting');
|
|
150
|
+
expect(messagePostgres).toContain('Troubleshooting');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should show DATABASE_URL when available', () => {
|
|
154
|
+
process.env.DATABASE_URL = 'mongodb://test';
|
|
155
|
+
const message = getDatabaseConnectionError(mockError, 'mongodb');
|
|
156
|
+
|
|
157
|
+
expect(message).toContain('mongodb://test');
|
|
158
|
+
delete process.env.DATABASE_URL;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should handle missing DATABASE_URL gracefully', () => {
|
|
162
|
+
delete process.env.DATABASE_URL;
|
|
163
|
+
const message = getDatabaseConnectionError(mockError, 'mongodb');
|
|
164
|
+
|
|
165
|
+
expect(message).toContain('not set');
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('getPrismaClientNotGeneratedError()', () => {
|
|
170
|
+
it('should return error message for MongoDB', () => {
|
|
171
|
+
const message = getPrismaClientNotGeneratedError('mongodb');
|
|
172
|
+
|
|
173
|
+
expect(message).toBeTruthy();
|
|
174
|
+
expect(message).toContain('mongodb');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should return error message for PostgreSQL', () => {
|
|
178
|
+
const message = getPrismaClientNotGeneratedError('postgresql');
|
|
179
|
+
|
|
180
|
+
expect(message).toBeTruthy();
|
|
181
|
+
expect(message).toContain('postgresql');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should include correct client package name for MongoDB', () => {
|
|
185
|
+
const message = getPrismaClientNotGeneratedError('mongodb');
|
|
186
|
+
|
|
187
|
+
expect(message).toContain('@prisma-mongodb/client');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should include correct client package name for PostgreSQL', () => {
|
|
191
|
+
const message = getPrismaClientNotGeneratedError('postgresql');
|
|
192
|
+
|
|
193
|
+
expect(message).toContain('@prisma-postgresql/client');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should suggest running frigg db:setup', () => {
|
|
197
|
+
const message = getPrismaClientNotGeneratedError('mongodb');
|
|
198
|
+
|
|
199
|
+
expect(message).toContain('frigg db:setup');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should explain what will happen when running db:setup', () => {
|
|
203
|
+
const message = getPrismaClientNotGeneratedError('mongodb');
|
|
204
|
+
|
|
205
|
+
expect(message).toContain('Generate the Prisma client');
|
|
206
|
+
expect(message).toContain('Set up database schema');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe('getPrismaCommandError()', () => {
|
|
211
|
+
const mockCommand = 'generate';
|
|
212
|
+
const mockError = 'Schema validation failed';
|
|
213
|
+
|
|
214
|
+
it('should include command name', () => {
|
|
215
|
+
const message = getPrismaCommandError(mockCommand, mockError);
|
|
216
|
+
|
|
217
|
+
expect(message).toContain('generate');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should include error message', () => {
|
|
221
|
+
const message = getPrismaCommandError(mockCommand, mockError);
|
|
222
|
+
|
|
223
|
+
expect(message).toContain(mockError);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should list common causes', () => {
|
|
227
|
+
const message = getPrismaCommandError(mockCommand, mockError);
|
|
228
|
+
|
|
229
|
+
expect(message).toContain('Common causes');
|
|
230
|
+
expect(message).toContain('schema');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should suggest running frigg db:setup', () => {
|
|
234
|
+
const message = getPrismaCommandError(mockCommand, mockError);
|
|
235
|
+
|
|
236
|
+
expect(message).toContain('frigg db:setup');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should suggest checking DATABASE_URL', () => {
|
|
240
|
+
const message = getPrismaCommandError(mockCommand, mockError);
|
|
241
|
+
|
|
242
|
+
expect(message).toContain('DATABASE_URL');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should work with different command names', () => {
|
|
246
|
+
const migrateMessage = getPrismaCommandError('migrate', mockError);
|
|
247
|
+
const pushMessage = getPrismaCommandError('db push', mockError);
|
|
248
|
+
|
|
249
|
+
expect(migrateMessage).toContain('migrate');
|
|
250
|
+
expect(pushMessage).toContain('db push');
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('getDatabaseSetupSuccess()', () => {
|
|
255
|
+
it('should include database type for MongoDB', () => {
|
|
256
|
+
const message = getDatabaseSetupSuccess('mongodb', 'development');
|
|
257
|
+
|
|
258
|
+
expect(message).toContain('mongodb');
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should include database type for PostgreSQL', () => {
|
|
262
|
+
const message = getDatabaseSetupSuccess('postgresql', 'production');
|
|
263
|
+
|
|
264
|
+
expect(message).toContain('postgresql');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should include stage information', () => {
|
|
268
|
+
const devMessage = getDatabaseSetupSuccess('mongodb', 'development');
|
|
269
|
+
const prodMessage = getDatabaseSetupSuccess('postgresql', 'production');
|
|
270
|
+
|
|
271
|
+
expect(devMessage).toContain('development');
|
|
272
|
+
expect(prodMessage).toContain('production');
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should mention different operations for MongoDB vs PostgreSQL', () => {
|
|
276
|
+
const mongoMessage = getDatabaseSetupSuccess('mongodb', 'development');
|
|
277
|
+
const postgresMessage = getDatabaseSetupSuccess('postgresql', 'development');
|
|
278
|
+
|
|
279
|
+
expect(mongoMessage).toContain('Schema pushed');
|
|
280
|
+
expect(postgresMessage).toContain('Migrations applied');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('should suggest next steps', () => {
|
|
284
|
+
const message = getDatabaseSetupSuccess('mongodb', 'development');
|
|
285
|
+
|
|
286
|
+
expect(message).toContain('frigg start');
|
|
287
|
+
expect(message).toContain('Next steps');
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should confirm what happened during setup', () => {
|
|
291
|
+
const message = getDatabaseSetupSuccess('mongodb', 'development');
|
|
292
|
+
|
|
293
|
+
expect(message).toContain('Prisma client generated');
|
|
294
|
+
expect(message).toContain('Database connection verified');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should show success indicators', () => {
|
|
298
|
+
const message = getDatabaseSetupSuccess('mongodb', 'development');
|
|
299
|
+
|
|
300
|
+
expect(message).toContain('successfully');
|
|
301
|
+
expect(message).toContain('completed');
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|