@friggframework/devtools 2.0.0-next.47 → 2.0.0-next.48
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/README.md +1290 -0
- package/frigg-cli/__tests__/unit/commands/build.test.js +279 -0
- package/frigg-cli/__tests__/unit/commands/db-setup.test.js +548 -0
- package/frigg-cli/__tests__/unit/commands/deploy.test.js +320 -0
- package/frigg-cli/__tests__/unit/commands/doctor.test.js +309 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +400 -0
- package/frigg-cli/__tests__/unit/commands/ui.test.js +346 -0
- package/frigg-cli/__tests__/unit/dependencies.test.js +74 -0
- 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/version-detection.test.js +171 -0
- package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
- package/frigg-cli/__tests__/utils/prisma-mock.js +194 -0
- package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
- package/frigg-cli/__tests__/utils/test-setup.js +287 -0
- package/frigg-cli/build-command/index.js +66 -0
- package/frigg-cli/db-setup-command/index.js +193 -0
- package/frigg-cli/deploy-command/SPEC-DEPLOY-DRY-RUN.md +981 -0
- package/frigg-cli/deploy-command/index.js +302 -0
- package/frigg-cli/doctor-command/index.js +335 -0
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +301 -0
- package/frigg-cli/generate-command/azure-generator.js +43 -0
- package/frigg-cli/generate-command/gcp-generator.js +47 -0
- package/frigg-cli/generate-command/index.js +332 -0
- package/frigg-cli/generate-command/terraform-generator.js +555 -0
- package/frigg-cli/generate-iam-command.js +118 -0
- package/frigg-cli/index.js +173 -0
- package/frigg-cli/index.test.js +158 -0
- package/frigg-cli/init-command/backend-first-handler.js +756 -0
- package/frigg-cli/init-command/index.js +93 -0
- package/frigg-cli/init-command/template-handler.js +143 -0
- package/frigg-cli/install-command/backend-js.js +33 -0
- package/frigg-cli/install-command/commit-changes.js +16 -0
- package/frigg-cli/install-command/environment-variables.js +127 -0
- package/frigg-cli/install-command/environment-variables.test.js +136 -0
- package/frigg-cli/install-command/index.js +54 -0
- package/frigg-cli/install-command/install-package.js +13 -0
- package/frigg-cli/install-command/integration-file.js +30 -0
- package/frigg-cli/install-command/logger.js +12 -0
- package/frigg-cli/install-command/template.js +90 -0
- package/frigg-cli/install-command/validate-package.js +75 -0
- package/frigg-cli/jest.config.js +124 -0
- package/frigg-cli/package.json +63 -0
- package/frigg-cli/repair-command/index.js +564 -0
- package/frigg-cli/start-command/index.js +149 -0
- package/frigg-cli/start-command/start-command.test.js +297 -0
- package/frigg-cli/test/init-command.test.js +180 -0
- package/frigg-cli/test/npm-registry.test.js +319 -0
- package/frigg-cli/ui-command/index.js +154 -0
- package/frigg-cli/utils/app-resolver.js +319 -0
- package/frigg-cli/utils/backend-path.js +25 -0
- package/frigg-cli/utils/database-validator.js +154 -0
- package/frigg-cli/utils/error-messages.js +257 -0
- package/frigg-cli/utils/npm-registry.js +167 -0
- package/frigg-cli/utils/process-manager.js +199 -0
- package/frigg-cli/utils/repo-detection.js +405 -0
- package/infrastructure/create-frigg-infrastructure.js +125 -12
- package/infrastructure/docs/PRE-DEPLOYMENT-HEALTH-CHECK-SPEC.md +1317 -0
- package/infrastructure/domains/shared/resource-discovery.enhanced.test.js +306 -0
- package/infrastructure/domains/shared/resource-discovery.js +31 -2
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -1
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +109 -5
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +310 -4
- package/infrastructure/domains/shared/validation/plugin-validator.js +187 -0
- package/infrastructure/domains/shared/validation/plugin-validator.test.js +323 -0
- package/infrastructure/infrastructure-composer.js +22 -0
- package/layers/prisma/.build-complete +3 -0
- package/package.json +18 -7
- package/management-ui/package-lock.json +0 -16517
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version Detection Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the CLI version detection wrapper that prefers local installations
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const semver = require('semver');
|
|
10
|
+
|
|
11
|
+
describe('Version Detection Logic', () => {
|
|
12
|
+
describe('semver comparison', () => {
|
|
13
|
+
test('should prefer local when local version is newer', () => {
|
|
14
|
+
const localVersion = '2.1.0';
|
|
15
|
+
const globalVersion = '2.0.0';
|
|
16
|
+
|
|
17
|
+
const comparison = semver.compare(localVersion, globalVersion);
|
|
18
|
+
|
|
19
|
+
expect(comparison).toBeGreaterThan(0);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('should prefer local when versions are equal', () => {
|
|
23
|
+
const localVersion = '2.0.0';
|
|
24
|
+
const globalVersion = '2.0.0';
|
|
25
|
+
|
|
26
|
+
const comparison = semver.compare(localVersion, globalVersion);
|
|
27
|
+
|
|
28
|
+
expect(comparison).toBe(0);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('should warn when global is newer than local', () => {
|
|
32
|
+
const localVersion = '2.0.0';
|
|
33
|
+
const globalVersion = '2.1.0';
|
|
34
|
+
|
|
35
|
+
const comparison = semver.compare(localVersion, globalVersion);
|
|
36
|
+
|
|
37
|
+
expect(comparison).toBeLessThan(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should handle prerelease versions correctly', () => {
|
|
41
|
+
const localVersion = '2.0.0-next.1';
|
|
42
|
+
const globalVersion = '2.0.0-next.0';
|
|
43
|
+
|
|
44
|
+
const comparison = semver.compare(localVersion, globalVersion);
|
|
45
|
+
|
|
46
|
+
expect(comparison).toBeGreaterThan(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('should prefer release over prerelease', () => {
|
|
50
|
+
const localVersion = '2.0.0';
|
|
51
|
+
const globalVersion = '2.0.0-next.0';
|
|
52
|
+
|
|
53
|
+
const comparison = semver.compare(localVersion, globalVersion);
|
|
54
|
+
|
|
55
|
+
expect(comparison).toBeGreaterThan(0);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('local CLI detection', () => {
|
|
60
|
+
test('should find local installation in node_modules', () => {
|
|
61
|
+
const cwd = process.cwd();
|
|
62
|
+
const localCliPath = path.join(cwd, 'node_modules', '@friggframework', 'frigg-cli');
|
|
63
|
+
|
|
64
|
+
// This test validates the path construction logic
|
|
65
|
+
expect(localCliPath).toContain('node_modules');
|
|
66
|
+
expect(localCliPath).toContain('@friggframework');
|
|
67
|
+
expect(localCliPath).toContain('frigg-cli');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('should construct correct paths for package.json and index.js', () => {
|
|
71
|
+
const cwd = process.cwd();
|
|
72
|
+
const localCliPath = path.join(cwd, 'node_modules', '@friggframework', 'frigg-cli');
|
|
73
|
+
const localCliPackageJson = path.join(localCliPath, 'package.json');
|
|
74
|
+
const localCliIndex = path.join(localCliPath, 'index.js');
|
|
75
|
+
|
|
76
|
+
expect(localCliPackageJson).toContain('package.json');
|
|
77
|
+
expect(localCliIndex).toContain('index.js');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('environment variable check', () => {
|
|
82
|
+
test('should skip version check when FRIGG_CLI_SKIP_VERSION_CHECK is true', () => {
|
|
83
|
+
const originalEnv = process.env.FRIGG_CLI_SKIP_VERSION_CHECK;
|
|
84
|
+
|
|
85
|
+
process.env.FRIGG_CLI_SKIP_VERSION_CHECK = 'true';
|
|
86
|
+
const shouldSkip = process.env.FRIGG_CLI_SKIP_VERSION_CHECK === 'true';
|
|
87
|
+
expect(shouldSkip).toBe(true);
|
|
88
|
+
|
|
89
|
+
// Restore
|
|
90
|
+
if (originalEnv !== undefined) {
|
|
91
|
+
process.env.FRIGG_CLI_SKIP_VERSION_CHECK = originalEnv;
|
|
92
|
+
} else {
|
|
93
|
+
delete process.env.FRIGG_CLI_SKIP_VERSION_CHECK;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('should not skip when environment variable is not set', () => {
|
|
98
|
+
const originalEnv = process.env.FRIGG_CLI_SKIP_VERSION_CHECK;
|
|
99
|
+
delete process.env.FRIGG_CLI_SKIP_VERSION_CHECK;
|
|
100
|
+
|
|
101
|
+
const shouldSkip = process.env.FRIGG_CLI_SKIP_VERSION_CHECK === 'true';
|
|
102
|
+
expect(shouldSkip).toBe(false);
|
|
103
|
+
|
|
104
|
+
// Restore
|
|
105
|
+
if (originalEnv !== undefined) {
|
|
106
|
+
process.env.FRIGG_CLI_SKIP_VERSION_CHECK = originalEnv;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('version detection decision matrix', () => {
|
|
112
|
+
const scenarios = [
|
|
113
|
+
{
|
|
114
|
+
name: 'local newer than global',
|
|
115
|
+
local: '2.1.0',
|
|
116
|
+
global: '2.0.0',
|
|
117
|
+
expected: 'use_local',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'local equal to global',
|
|
121
|
+
local: '2.0.0',
|
|
122
|
+
global: '2.0.0',
|
|
123
|
+
expected: 'use_local',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'global newer than local',
|
|
127
|
+
local: '2.0.0',
|
|
128
|
+
global: '2.1.0',
|
|
129
|
+
expected: 'warn_and_use_global',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'local prerelease newer',
|
|
133
|
+
local: '2.0.0-next.1',
|
|
134
|
+
global: '2.0.0-next.0',
|
|
135
|
+
expected: 'use_local',
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
scenarios.forEach(({ name, local, global, expected }) => {
|
|
140
|
+
test(`should ${expected.replace('_', ' ')} when ${name}`, () => {
|
|
141
|
+
const comparison = semver.compare(local, global);
|
|
142
|
+
|
|
143
|
+
if (expected === 'use_local') {
|
|
144
|
+
expect(comparison).toBeGreaterThanOrEqual(0);
|
|
145
|
+
} else if (expected === 'warn_and_use_global') {
|
|
146
|
+
expect(comparison).toBeLessThan(0);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('argument forwarding', () => {
|
|
153
|
+
test('should extract arguments correctly from process.argv', () => {
|
|
154
|
+
// Simulate process.argv
|
|
155
|
+
const mockArgv = ['node', '/path/to/frigg', 'doctor', 'my-stack', '--region', 'us-east-1'];
|
|
156
|
+
const args = mockArgv.slice(2);
|
|
157
|
+
|
|
158
|
+
expect(args).toEqual(['doctor', 'my-stack', '--region', 'us-east-1']);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('should forward all arguments to local CLI', () => {
|
|
162
|
+
const mockArgv = ['node', '/path/to/frigg', 'repair', 'my-stack', '--import', '--yes'];
|
|
163
|
+
const args = mockArgv.slice(2);
|
|
164
|
+
|
|
165
|
+
expect(args).toContain('repair');
|
|
166
|
+
expect(args).toContain('my-stack');
|
|
167
|
+
expect(args).toContain('--import');
|
|
168
|
+
expect(args).toContain('--yes');
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockFactory - Factory for creating standardized mocks
|
|
3
|
+
* Provides consistent mock implementations across all CLI tests
|
|
4
|
+
*/
|
|
5
|
+
class MockFactory {
|
|
6
|
+
/**
|
|
7
|
+
* Create a file system mock
|
|
8
|
+
* @returns {object} - Mock fs implementation
|
|
9
|
+
*/
|
|
10
|
+
static createFileSystem() {
|
|
11
|
+
return {
|
|
12
|
+
existsSync: jest.fn().mockReturnValue(true),
|
|
13
|
+
readFileSync: jest.fn().mockReturnValue('{}'),
|
|
14
|
+
writeFileSync: jest.fn(),
|
|
15
|
+
mkdirSync: jest.fn(),
|
|
16
|
+
readdirSync: jest.fn().mockReturnValue([]),
|
|
17
|
+
statSync: jest.fn().mockReturnValue({
|
|
18
|
+
isDirectory: () => false,
|
|
19
|
+
isFile: () => true
|
|
20
|
+
}),
|
|
21
|
+
copyFileSync: jest.fn(),
|
|
22
|
+
unlinkSync: jest.fn(),
|
|
23
|
+
rmdirSync: jest.fn(),
|
|
24
|
+
constants: {
|
|
25
|
+
F_OK: 0,
|
|
26
|
+
R_OK: 4,
|
|
27
|
+
W_OK: 2,
|
|
28
|
+
X_OK: 1
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a logger mock
|
|
35
|
+
* @returns {object} - Mock logger implementation
|
|
36
|
+
*/
|
|
37
|
+
static createLogger() {
|
|
38
|
+
return {
|
|
39
|
+
info: jest.fn(),
|
|
40
|
+
error: jest.fn(),
|
|
41
|
+
debug: jest.fn(),
|
|
42
|
+
warn: jest.fn(),
|
|
43
|
+
logInfo: jest.fn(),
|
|
44
|
+
logError: jest.fn(),
|
|
45
|
+
logDebug: jest.fn(),
|
|
46
|
+
logWarn: jest.fn()
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create a package manager mock
|
|
52
|
+
* @returns {object} - Mock package manager implementation
|
|
53
|
+
*/
|
|
54
|
+
static createPackageManager() {
|
|
55
|
+
return {
|
|
56
|
+
install: jest.fn().mockResolvedValue({ success: true }),
|
|
57
|
+
list: jest.fn().mockResolvedValue([]),
|
|
58
|
+
exists: jest.fn().mockResolvedValue(true),
|
|
59
|
+
getInfo: jest.fn().mockResolvedValue({
|
|
60
|
+
name: 'test-package',
|
|
61
|
+
version: '1.0.0'
|
|
62
|
+
}),
|
|
63
|
+
search: jest.fn().mockResolvedValue([])
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create a child process mock
|
|
69
|
+
* @returns {object} - Mock child_process implementation
|
|
70
|
+
*/
|
|
71
|
+
static createChildProcess() {
|
|
72
|
+
return {
|
|
73
|
+
execSync: jest.fn().mockReturnValue(''),
|
|
74
|
+
exec: jest.fn(),
|
|
75
|
+
spawn: jest.fn().mockReturnValue({
|
|
76
|
+
stdout: { on: jest.fn() },
|
|
77
|
+
stderr: { on: jest.fn() },
|
|
78
|
+
on: jest.fn((event, callback) => {
|
|
79
|
+
if (event === 'close') {
|
|
80
|
+
callback(0);
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
}),
|
|
84
|
+
fork: jest.fn()
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create a git mock
|
|
90
|
+
* @returns {object} - Mock git implementation
|
|
91
|
+
*/
|
|
92
|
+
static createGit() {
|
|
93
|
+
return {
|
|
94
|
+
init: jest.fn(),
|
|
95
|
+
add: jest.fn(),
|
|
96
|
+
commit: jest.fn(),
|
|
97
|
+
status: jest.fn().mockReturnValue({
|
|
98
|
+
clean: true,
|
|
99
|
+
files: []
|
|
100
|
+
}),
|
|
101
|
+
branch: jest.fn().mockReturnValue('main'),
|
|
102
|
+
remote: jest.fn().mockReturnValue('origin'),
|
|
103
|
+
isRepo: jest.fn().mockReturnValue(true)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Create a config loader mock
|
|
109
|
+
* @returns {object} - Mock config loader implementation
|
|
110
|
+
*/
|
|
111
|
+
static createConfigLoader() {
|
|
112
|
+
return {
|
|
113
|
+
load: jest.fn().mockReturnValue({
|
|
114
|
+
stage: 'dev',
|
|
115
|
+
region: 'us-east-1',
|
|
116
|
+
profile: 'default'
|
|
117
|
+
}),
|
|
118
|
+
validate: jest.fn().mockReturnValue(true),
|
|
119
|
+
save: jest.fn(),
|
|
120
|
+
merge: jest.fn()
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Create an app resolver mock
|
|
126
|
+
* @returns {object} - Mock app resolver implementation
|
|
127
|
+
*/
|
|
128
|
+
static createAppResolver() {
|
|
129
|
+
return {
|
|
130
|
+
resolveAppPath: jest.fn().mockReturnValue('/mock/app/path'),
|
|
131
|
+
findNearestBackendPackageJson: jest.fn().mockReturnValue('/mock/backend/package.json'),
|
|
132
|
+
validateBackendPath: jest.fn().mockReturnValue(true),
|
|
133
|
+
getProjectRoot: jest.fn().mockReturnValue('/mock/project'),
|
|
134
|
+
findConfigFile: jest.fn().mockReturnValue('/mock/config.json')
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Create a network mock
|
|
140
|
+
* @returns {object} - Mock network implementation
|
|
141
|
+
*/
|
|
142
|
+
static createNetwork() {
|
|
143
|
+
return {
|
|
144
|
+
get: jest.fn().mockResolvedValue({
|
|
145
|
+
status: 200,
|
|
146
|
+
data: {}
|
|
147
|
+
}),
|
|
148
|
+
post: jest.fn().mockResolvedValue({
|
|
149
|
+
status: 200,
|
|
150
|
+
data: {}
|
|
151
|
+
}),
|
|
152
|
+
put: jest.fn().mockResolvedValue({
|
|
153
|
+
status: 200,
|
|
154
|
+
data: {}
|
|
155
|
+
}),
|
|
156
|
+
delete: jest.fn().mockResolvedValue({
|
|
157
|
+
status: 200,
|
|
158
|
+
data: {}
|
|
159
|
+
})
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Create a comprehensive mock environment
|
|
165
|
+
* @returns {object} - Complete mock environment
|
|
166
|
+
*/
|
|
167
|
+
static createMockEnvironment() {
|
|
168
|
+
return {
|
|
169
|
+
fs: this.createFileSystem(),
|
|
170
|
+
logger: this.createLogger(),
|
|
171
|
+
packageManager: this.createPackageManager(),
|
|
172
|
+
childProcess: this.createChildProcess(),
|
|
173
|
+
git: this.createGit(),
|
|
174
|
+
config: this.createConfigLoader(),
|
|
175
|
+
appResolver: this.createAppResolver(),
|
|
176
|
+
network: this.createNetwork()
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Create a mock for process.env
|
|
182
|
+
* @param {object} customEnv - Custom environment variables
|
|
183
|
+
* @returns {object} - Mock environment
|
|
184
|
+
*/
|
|
185
|
+
static createProcessEnv(customEnv = {}) {
|
|
186
|
+
return {
|
|
187
|
+
NODE_ENV: 'test',
|
|
188
|
+
HOME: '/mock/home',
|
|
189
|
+
PATH: '/mock/path',
|
|
190
|
+
...customEnv
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Create success response mock
|
|
196
|
+
* @param {any} data - Response data
|
|
197
|
+
* @returns {object} - Success response
|
|
198
|
+
*/
|
|
199
|
+
static createSuccessResponse(data = {}) {
|
|
200
|
+
return {
|
|
201
|
+
success: true,
|
|
202
|
+
data,
|
|
203
|
+
message: 'Operation completed successfully'
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Create error response mock
|
|
209
|
+
* @param {string} message - Error message
|
|
210
|
+
* @param {string} code - Error code
|
|
211
|
+
* @returns {object} - Error response
|
|
212
|
+
*/
|
|
213
|
+
static createErrorResponse(message = 'An error occurred', code = 'GENERIC_ERROR') {
|
|
214
|
+
return {
|
|
215
|
+
success: false,
|
|
216
|
+
error: {
|
|
217
|
+
message,
|
|
218
|
+
code,
|
|
219
|
+
stack: 'Mock stack trace'
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Create package.json mock
|
|
226
|
+
* @param {object} overrides - Custom package.json properties
|
|
227
|
+
* @returns {object} - Mock package.json
|
|
228
|
+
*/
|
|
229
|
+
static createPackageJson(overrides = {}) {
|
|
230
|
+
return {
|
|
231
|
+
name: 'test-package',
|
|
232
|
+
version: '1.0.0',
|
|
233
|
+
description: 'Test package',
|
|
234
|
+
main: 'index.js',
|
|
235
|
+
scripts: {
|
|
236
|
+
test: 'jest',
|
|
237
|
+
start: 'node index.js'
|
|
238
|
+
},
|
|
239
|
+
dependencies: {},
|
|
240
|
+
devDependencies: {},
|
|
241
|
+
...overrides
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Create frigg config mock
|
|
247
|
+
* @param {object} overrides - Custom config properties
|
|
248
|
+
* @returns {object} - Mock frigg config
|
|
249
|
+
*/
|
|
250
|
+
static createFriggConfig(overrides = {}) {
|
|
251
|
+
return {
|
|
252
|
+
stage: 'dev',
|
|
253
|
+
region: 'us-east-1',
|
|
254
|
+
profile: 'default',
|
|
255
|
+
backend: {
|
|
256
|
+
runtime: 'nodejs18.x',
|
|
257
|
+
timeout: 30,
|
|
258
|
+
memory: 128
|
|
259
|
+
},
|
|
260
|
+
frontend: {
|
|
261
|
+
framework: 'react',
|
|
262
|
+
buildCommand: 'npm run build',
|
|
263
|
+
outputDir: 'dist'
|
|
264
|
+
},
|
|
265
|
+
...overrides
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
module.exports = { MockFactory };
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prisma Mock Utilities
|
|
3
|
+
* Standardized mocking for Prisma Client in CLI tests
|
|
4
|
+
* Following official Prisma testing patterns with jest-mock-extended
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a mock Prisma client with common operations
|
|
9
|
+
* @returns {object} Mock Prisma client
|
|
10
|
+
*/
|
|
11
|
+
function createMockPrismaClient() {
|
|
12
|
+
return {
|
|
13
|
+
$connect: jest.fn().mockResolvedValue(undefined),
|
|
14
|
+
$disconnect: jest.fn().mockResolvedValue(undefined),
|
|
15
|
+
$queryRaw: jest.fn().mockResolvedValue([{ result: 1 }]),
|
|
16
|
+
$executeRaw: jest.fn().mockResolvedValue(1),
|
|
17
|
+
$runCommandRaw: jest.fn().mockResolvedValue({ ok: 1 }), // MongoDB command support
|
|
18
|
+
$transaction: jest.fn().mockImplementation(async (fn) => fn(createMockPrismaClient())),
|
|
19
|
+
// Common model operations
|
|
20
|
+
user: {
|
|
21
|
+
findMany: jest.fn().mockResolvedValue([]),
|
|
22
|
+
findUnique: jest.fn().mockResolvedValue(null),
|
|
23
|
+
create: jest.fn().mockResolvedValue({}),
|
|
24
|
+
update: jest.fn().mockResolvedValue({}),
|
|
25
|
+
delete: jest.fn().mockResolvedValue({}),
|
|
26
|
+
},
|
|
27
|
+
credential: {
|
|
28
|
+
findMany: jest.fn().mockResolvedValue([]),
|
|
29
|
+
findUnique: jest.fn().mockResolvedValue(null),
|
|
30
|
+
create: jest.fn().mockResolvedValue({}),
|
|
31
|
+
update: jest.fn().mockResolvedValue({}),
|
|
32
|
+
delete: jest.fn().mockResolvedValue({}),
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates a mock for successful Prisma connection
|
|
39
|
+
* @param {object} mockClient - Optional pre-configured mock client
|
|
40
|
+
* @returns {jest.Mock} Mock connectPrisma function
|
|
41
|
+
*/
|
|
42
|
+
function createMockConnectPrisma(mockClient = null) {
|
|
43
|
+
const client = mockClient || createMockPrismaClient();
|
|
44
|
+
return jest.fn().mockResolvedValue(client);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates a mock for successful Prisma disconnection
|
|
49
|
+
* @returns {jest.Mock} Mock disconnectPrisma function
|
|
50
|
+
*/
|
|
51
|
+
function createMockDisconnectPrisma() {
|
|
52
|
+
return jest.fn().mockResolvedValue(undefined);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Creates a mock for Prisma connection that fails
|
|
57
|
+
* @param {string} errorMessage - Error message to throw
|
|
58
|
+
* @returns {jest.Mock} Mock connectPrisma that throws error
|
|
59
|
+
*/
|
|
60
|
+
function createMockConnectPrismaWithError(errorMessage = 'Connection failed') {
|
|
61
|
+
return jest.fn().mockRejectedValue(new Error(errorMessage));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Creates a mock for Prisma query that times out
|
|
66
|
+
* @param {number} timeout - Timeout in milliseconds
|
|
67
|
+
* @returns {jest.Mock} Mock that times out
|
|
68
|
+
*/
|
|
69
|
+
function createMockPrismaTimeout(timeout = 5000) {
|
|
70
|
+
return jest.fn().mockImplementation(() =>
|
|
71
|
+
new Promise((_, reject) =>
|
|
72
|
+
setTimeout(() => reject(new Error('Connection timeout')), timeout)
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Common Prisma error types for testing
|
|
79
|
+
*/
|
|
80
|
+
const PrismaErrors = {
|
|
81
|
+
/**
|
|
82
|
+
* Connection error (P1001)
|
|
83
|
+
*/
|
|
84
|
+
CONNECTION_ERROR: {
|
|
85
|
+
code: 'P1001',
|
|
86
|
+
message: 'Can\'t reach database server',
|
|
87
|
+
clientVersion: '5.0.0',
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Authentication error (P1002)
|
|
92
|
+
*/
|
|
93
|
+
AUTH_ERROR: {
|
|
94
|
+
code: 'P1002',
|
|
95
|
+
message: 'The database server was reached but timed out',
|
|
96
|
+
clientVersion: '5.0.0',
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Database not found error (P1003)
|
|
101
|
+
*/
|
|
102
|
+
DATABASE_NOT_FOUND: {
|
|
103
|
+
code: 'P1003',
|
|
104
|
+
message: 'Database does not exist',
|
|
105
|
+
clientVersion: '5.0.0',
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Connection timeout (P1008)
|
|
110
|
+
*/
|
|
111
|
+
TIMEOUT_ERROR: {
|
|
112
|
+
code: 'P1008',
|
|
113
|
+
message: 'Operations timed out',
|
|
114
|
+
clientVersion: '5.0.0',
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Invalid credentials
|
|
119
|
+
*/
|
|
120
|
+
INVALID_CREDENTIALS: {
|
|
121
|
+
code: 'P1000',
|
|
122
|
+
message: 'Authentication failed against database server',
|
|
123
|
+
clientVersion: '5.0.0',
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Creates a Prisma error object
|
|
129
|
+
* @param {string} errorType - Error type from PrismaErrors
|
|
130
|
+
* @param {object} customFields - Additional custom fields
|
|
131
|
+
* @returns {Error} Prisma-style error object
|
|
132
|
+
*/
|
|
133
|
+
function createPrismaError(errorType = 'CONNECTION_ERROR', customFields = {}) {
|
|
134
|
+
const baseError = PrismaErrors[errorType] || PrismaErrors.CONNECTION_ERROR;
|
|
135
|
+
const error = new Error(baseError.message);
|
|
136
|
+
error.code = baseError.code;
|
|
137
|
+
error.clientVersion = baseError.clientVersion;
|
|
138
|
+
Object.assign(error, customFields);
|
|
139
|
+
return error;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Creates a mock for database validator functions
|
|
144
|
+
* @param {object} overrides - Override specific mock behaviors
|
|
145
|
+
* @returns {object} Mock database validator
|
|
146
|
+
*/
|
|
147
|
+
function createMockDatabaseValidator(overrides = {}) {
|
|
148
|
+
return {
|
|
149
|
+
validateDatabaseUrl: jest.fn().mockReturnValue({
|
|
150
|
+
valid: true,
|
|
151
|
+
url: 'mongodb://localhost:27017/test'
|
|
152
|
+
}),
|
|
153
|
+
getDatabaseType: jest.fn().mockReturnValue({
|
|
154
|
+
dbType: 'mongodb'
|
|
155
|
+
}),
|
|
156
|
+
testDatabaseConnection: jest.fn().mockResolvedValue({
|
|
157
|
+
connected: true
|
|
158
|
+
}),
|
|
159
|
+
checkPrismaClientGenerated: jest.fn().mockReturnValue({
|
|
160
|
+
generated: true,
|
|
161
|
+
path: '/mock/path'
|
|
162
|
+
}),
|
|
163
|
+
...overrides,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Creates a mock for Prisma runner functions
|
|
169
|
+
* @param {object} overrides - Override specific mock behaviors
|
|
170
|
+
* @returns {object} Mock Prisma runner
|
|
171
|
+
*/
|
|
172
|
+
function createMockPrismaRunner(overrides = {}) {
|
|
173
|
+
return {
|
|
174
|
+
getPrismaSchemaPath: jest.fn().mockReturnValue('/mock/schema.prisma'),
|
|
175
|
+
runPrismaGenerate: jest.fn().mockResolvedValue({ success: true }),
|
|
176
|
+
checkDatabaseState: jest.fn().mockResolvedValue({ upToDate: true }),
|
|
177
|
+
runPrismaMigrate: jest.fn().mockResolvedValue({ success: true }),
|
|
178
|
+
runPrismaDbPush: jest.fn().mockResolvedValue({ success: true }),
|
|
179
|
+
getMigrationCommand: jest.fn().mockReturnValue('dev'),
|
|
180
|
+
...overrides,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = {
|
|
185
|
+
createMockPrismaClient,
|
|
186
|
+
createMockConnectPrisma,
|
|
187
|
+
createMockDisconnectPrisma,
|
|
188
|
+
createMockConnectPrismaWithError,
|
|
189
|
+
createMockPrismaTimeout,
|
|
190
|
+
createPrismaError,
|
|
191
|
+
PrismaErrors,
|
|
192
|
+
createMockDatabaseValidator,
|
|
193
|
+
createMockPrismaRunner,
|
|
194
|
+
};
|