@friggframework/devtools 2.0.0--canary.546.74db90f.0 → 2.0.0--canary.545.e7becd9.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.
- package/frigg-cli/README.md +1 -1
- package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +326 -0
- package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +337 -0
- package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +373 -0
- package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +313 -0
- package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +269 -0
- package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +82 -0
- package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +408 -0
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +583 -0
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +314 -0
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +383 -0
- package/frigg-cli/__tests__/unit/commands/build.test.js +1 -1
- package/frigg-cli/__tests__/unit/commands/doctor.test.js +0 -2
- package/frigg-cli/__tests__/unit/commands/init.test.js +406 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +23 -19
- package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +383 -0
- package/frigg-cli/__tests__/unit/commands/repair.test.js +275 -0
- package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
- package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +411 -0
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +405 -0
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +496 -0
- package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +474 -0
- package/frigg-cli/__tests__/unit/utils/output.test.js +196 -0
- package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +93 -0
- package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +93 -0
- package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +103 -0
- package/frigg-cli/build-command/index.js +123 -11
- package/frigg-cli/container.js +172 -0
- package/frigg-cli/deploy-command/index.js +83 -1
- package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +286 -0
- package/frigg-cli/doctor-command/index.js +37 -16
- package/frigg-cli/domain/entities/ApiModule.js +272 -0
- package/frigg-cli/domain/entities/AppDefinition.js +227 -0
- package/frigg-cli/domain/entities/Integration.js +198 -0
- package/frigg-cli/domain/exceptions/DomainException.js +24 -0
- package/frigg-cli/domain/ports/IApiModuleRepository.js +53 -0
- package/frigg-cli/domain/ports/IAppDefinitionRepository.js +43 -0
- package/frigg-cli/domain/ports/IIntegrationRepository.js +61 -0
- package/frigg-cli/domain/services/IntegrationValidator.js +185 -0
- package/frigg-cli/domain/value-objects/IntegrationId.js +42 -0
- package/frigg-cli/domain/value-objects/IntegrationName.js +60 -0
- package/frigg-cli/domain/value-objects/SemanticVersion.js +70 -0
- package/frigg-cli/generate-iam-command.js +21 -1
- package/frigg-cli/index.js +21 -6
- package/frigg-cli/index.test.js +7 -2
- package/frigg-cli/infrastructure/UnitOfWork.js +46 -0
- package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +197 -0
- package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +224 -0
- package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +249 -0
- package/frigg-cli/infrastructure/adapters/SchemaValidator.js +92 -0
- package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +373 -0
- package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +116 -0
- package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +277 -0
- package/frigg-cli/init-command/backend-first-handler.js +124 -42
- package/frigg-cli/init-command/index.js +2 -1
- package/frigg-cli/init-command/template-handler.js +13 -3
- package/frigg-cli/install-command/backend-js.js +3 -3
- package/frigg-cli/install-command/environment-variables.js +16 -19
- package/frigg-cli/install-command/environment-variables.test.js +12 -13
- package/frigg-cli/install-command/index.js +14 -9
- package/frigg-cli/install-command/integration-file.js +3 -3
- package/frigg-cli/install-command/validate-package.js +5 -9
- package/frigg-cli/jest.config.js +4 -1
- package/frigg-cli/package-lock.json +16226 -0
- package/frigg-cli/repair-command/index.js +121 -128
- package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +376 -0
- package/frigg-cli/start-command/index.js +324 -2
- package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +591 -0
- package/frigg-cli/start-command/infrastructure/DockerAdapter.js +306 -0
- package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +329 -0
- package/frigg-cli/templates/backend/.env.example +62 -0
- package/frigg-cli/templates/backend/.eslintrc.json +12 -0
- package/frigg-cli/templates/backend/.prettierrc +6 -0
- package/frigg-cli/templates/backend/docker-compose.yml +22 -0
- package/frigg-cli/templates/backend/index.js +96 -0
- package/frigg-cli/templates/backend/infrastructure.js +12 -0
- package/frigg-cli/templates/backend/jest.config.js +17 -0
- package/frigg-cli/templates/backend/package.json +50 -0
- package/frigg-cli/templates/backend/src/api-modules/.gitkeep +10 -0
- package/frigg-cli/templates/backend/src/base/.gitkeep +7 -0
- package/frigg-cli/templates/backend/src/integrations/.gitkeep +10 -0
- package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +65 -0
- package/frigg-cli/templates/backend/src/utils/.gitkeep +7 -0
- package/frigg-cli/templates/backend/test/setup.js +30 -0
- package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
- package/frigg-cli/templates/backend/ui-extensions/README.md +77 -0
- package/frigg-cli/ui-command/index.js +58 -36
- package/frigg-cli/utils/__tests__/provider-helper.test.js +55 -0
- package/frigg-cli/utils/__tests__/repo-detection.test.js +436 -0
- package/frigg-cli/utils/output.js +382 -0
- package/frigg-cli/utils/provider-helper.js +75 -0
- package/frigg-cli/utils/repo-detection.js +85 -37
- package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +205 -0
- package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +104 -0
- package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +153 -0
- package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +162 -0
- package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +152 -0
- package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +332 -0
- package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +191 -0
- package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +146 -0
- package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +155 -0
- package/frigg-cli/validate-command/adapters/cli/validate-command.js +199 -0
- package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +35 -0
- package/frigg-cli/validate-command/domain/entities/validation-result.js +74 -0
- package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +74 -0
- package/frigg-cli/validate-command/domain/value-objects/validation-error.js +68 -0
- package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +181 -0
- package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +128 -0
- package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +113 -0
- package/infrastructure/create-frigg-infrastructure.js +93 -0
- package/infrastructure/docs/iam-policy-templates.md +1 -1
- package/infrastructure/domains/admin-scripts/admin-script-builder.js +200 -0
- package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +499 -0
- package/infrastructure/domains/admin-scripts/index.js +5 -0
- package/infrastructure/domains/networking/vpc-builder.test.js +2 -4
- package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
- package/infrastructure/domains/shared/cloudformation-discovery.test.js +4 -7
- package/infrastructure/domains/shared/resource-discovery.js +5 -5
- package/infrastructure/domains/shared/types/app-definition.js +21 -0
- package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +10 -1
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
- package/infrastructure/infrastructure-composer.js +2 -0
- package/infrastructure/infrastructure-composer.test.js +2 -2
- package/infrastructure/jest.config.js +16 -0
- package/management-ui/README.md +245 -109
- package/package.json +8 -7
- package/frigg-cli/install-command/logger.js +0 -12
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DockerAdapter Tests
|
|
3
|
+
* Infrastructure adapter for Docker operations - used by pre-flight checks
|
|
4
|
+
*
|
|
5
|
+
* Tests follow TDD pattern - written BEFORE implementation
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Mock child_process before importing
|
|
9
|
+
jest.mock('child_process', () => ({
|
|
10
|
+
exec: jest.fn(),
|
|
11
|
+
spawn: jest.fn()
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
// Mock fs for docker-compose file detection
|
|
15
|
+
jest.mock('fs', () => ({
|
|
16
|
+
existsSync: jest.fn(),
|
|
17
|
+
promises: {
|
|
18
|
+
access: jest.fn()
|
|
19
|
+
}
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
const { exec, spawn } = require('child_process');
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
|
|
26
|
+
// Import after mocks are set up
|
|
27
|
+
const { DockerAdapter } = require('../../../../start-command/infrastructure/DockerAdapter');
|
|
28
|
+
|
|
29
|
+
describe('DockerAdapter', () => {
|
|
30
|
+
let adapter;
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
jest.clearAllMocks();
|
|
34
|
+
adapter = new DockerAdapter();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('isDockerInstalled()', () => {
|
|
38
|
+
it('should return true when docker CLI is available', async () => {
|
|
39
|
+
exec.mockImplementation((cmd, callback) => {
|
|
40
|
+
callback(null, 'Docker version 24.0.7, build afdd53b', '');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const result = await adapter.isDockerInstalled();
|
|
44
|
+
|
|
45
|
+
expect(result).toBe(true);
|
|
46
|
+
expect(exec).toHaveBeenCalledWith('docker --version', expect.any(Function));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should return false when docker CLI is not found', async () => {
|
|
50
|
+
exec.mockImplementation((cmd, callback) => {
|
|
51
|
+
const error = new Error('command not found: docker');
|
|
52
|
+
error.code = 127;
|
|
53
|
+
callback(error, '', 'command not found: docker');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const result = await adapter.isDockerInstalled();
|
|
57
|
+
|
|
58
|
+
expect(result).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should return false when docker command fails', async () => {
|
|
62
|
+
exec.mockImplementation((cmd, callback) => {
|
|
63
|
+
callback(new Error('Docker not installed'), '', '');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const result = await adapter.isDockerInstalled();
|
|
67
|
+
|
|
68
|
+
expect(result).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('isDockerRunning()', () => {
|
|
73
|
+
it('should return true when Docker daemon is running', async () => {
|
|
74
|
+
exec.mockImplementation((cmd, callback) => {
|
|
75
|
+
callback(null, '', '');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const result = await adapter.isDockerRunning();
|
|
79
|
+
|
|
80
|
+
expect(result).toBe(true);
|
|
81
|
+
expect(exec).toHaveBeenCalledWith('docker info', expect.any(Function));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should return false when Docker daemon is not running', async () => {
|
|
85
|
+
exec.mockImplementation((cmd, callback) => {
|
|
86
|
+
const error = new Error('Cannot connect to the Docker daemon');
|
|
87
|
+
callback(error, '', 'Cannot connect to the Docker daemon');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const result = await adapter.isDockerRunning();
|
|
91
|
+
|
|
92
|
+
expect(result).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should return false when docker info command times out', async () => {
|
|
96
|
+
exec.mockImplementation((cmd, callback) => {
|
|
97
|
+
const error = new Error('ETIMEDOUT');
|
|
98
|
+
error.code = 'ETIMEDOUT';
|
|
99
|
+
callback(error, '', '');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const result = await adapter.isDockerRunning();
|
|
103
|
+
|
|
104
|
+
expect(result).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('findDockerComposeFile()', () => {
|
|
109
|
+
const projectPath = '/test/project';
|
|
110
|
+
|
|
111
|
+
it('should find docker-compose.yml in project root', async () => {
|
|
112
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
113
|
+
return filePath === path.join(projectPath, 'docker-compose.yml');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const result = await adapter.findDockerComposeFile(projectPath);
|
|
117
|
+
|
|
118
|
+
expect(result).toBe(path.join(projectPath, 'docker-compose.yml'));
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should find docker-compose.yaml in project root', async () => {
|
|
122
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
123
|
+
return filePath === path.join(projectPath, 'docker-compose.yaml');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const result = await adapter.findDockerComposeFile(projectPath);
|
|
127
|
+
|
|
128
|
+
expect(result).toBe(path.join(projectPath, 'docker-compose.yaml'));
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should find compose.yml in project root', async () => {
|
|
132
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
133
|
+
return filePath === path.join(projectPath, 'compose.yml');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const result = await adapter.findDockerComposeFile(projectPath);
|
|
137
|
+
|
|
138
|
+
expect(result).toBe(path.join(projectPath, 'compose.yml'));
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should find compose.yaml in project root', async () => {
|
|
142
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
143
|
+
return filePath === path.join(projectPath, 'compose.yaml');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const result = await adapter.findDockerComposeFile(projectPath);
|
|
147
|
+
|
|
148
|
+
expect(result).toBe(path.join(projectPath, 'compose.yaml'));
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should return null when no docker-compose file exists', async () => {
|
|
152
|
+
fs.existsSync.mockReturnValue(false);
|
|
153
|
+
|
|
154
|
+
const result = await adapter.findDockerComposeFile(projectPath);
|
|
155
|
+
|
|
156
|
+
expect(result).toBeNull();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should prefer docker-compose.yml over other variants', async () => {
|
|
160
|
+
// All variants exist
|
|
161
|
+
fs.existsSync.mockReturnValue(true);
|
|
162
|
+
|
|
163
|
+
const result = await adapter.findDockerComposeFile(projectPath);
|
|
164
|
+
|
|
165
|
+
// Should return the first one checked (docker-compose.yml)
|
|
166
|
+
expect(result).toBe(path.join(projectPath, 'docker-compose.yml'));
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should search in parent directory if not found in project path', async () => {
|
|
170
|
+
const backendPath = '/test/project/backend';
|
|
171
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
172
|
+
// Only exists in parent directory
|
|
173
|
+
return filePath === path.join('/test/project', 'docker-compose.yml');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const result = await adapter.findDockerComposeFile(backendPath);
|
|
177
|
+
|
|
178
|
+
expect(result).toBe(path.join('/test/project', 'docker-compose.yml'));
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('startDockerCompose()', () => {
|
|
183
|
+
const composePath = '/test/project/docker-compose.yml';
|
|
184
|
+
|
|
185
|
+
it('should run docker compose up -d successfully', async () => {
|
|
186
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
187
|
+
if (typeof opts === 'function') {
|
|
188
|
+
callback = opts;
|
|
189
|
+
}
|
|
190
|
+
callback(null, 'Container started', '');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const result = await adapter.startDockerCompose(composePath);
|
|
194
|
+
|
|
195
|
+
expect(result.success).toBe(true);
|
|
196
|
+
expect(exec).toHaveBeenCalledWith(
|
|
197
|
+
expect.stringContaining('docker compose'),
|
|
198
|
+
expect.any(Object),
|
|
199
|
+
expect.any(Function)
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should use correct docker-compose file path', async () => {
|
|
204
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
205
|
+
if (typeof opts === 'function') {
|
|
206
|
+
callback = opts;
|
|
207
|
+
}
|
|
208
|
+
callback(null, '', '');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
await adapter.startDockerCompose(composePath);
|
|
212
|
+
|
|
213
|
+
expect(exec).toHaveBeenCalledWith(
|
|
214
|
+
expect.stringContaining(`-f ${composePath}`),
|
|
215
|
+
expect.any(Object),
|
|
216
|
+
expect.any(Function)
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should return error when docker compose fails', async () => {
|
|
221
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
222
|
+
if (typeof opts === 'function') {
|
|
223
|
+
callback = opts;
|
|
224
|
+
}
|
|
225
|
+
const error = new Error('Service failed to start');
|
|
226
|
+
callback(error, '', 'Service failed to start');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const result = await adapter.startDockerCompose(composePath);
|
|
230
|
+
|
|
231
|
+
expect(result.success).toBe(false);
|
|
232
|
+
expect(result.error).toContain('Service failed to start');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should run in detached mode by default', async () => {
|
|
236
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
237
|
+
if (typeof opts === 'function') {
|
|
238
|
+
callback = opts;
|
|
239
|
+
}
|
|
240
|
+
callback(null, '', '');
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
await adapter.startDockerCompose(composePath);
|
|
244
|
+
|
|
245
|
+
expect(exec).toHaveBeenCalledWith(
|
|
246
|
+
expect.stringContaining('up -d'),
|
|
247
|
+
expect.any(Object),
|
|
248
|
+
expect.any(Function)
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should set working directory to compose file directory', async () => {
|
|
253
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
254
|
+
if (typeof opts === 'function') {
|
|
255
|
+
callback = opts;
|
|
256
|
+
}
|
|
257
|
+
callback(null, '', '');
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
await adapter.startDockerCompose(composePath);
|
|
261
|
+
|
|
262
|
+
expect(exec).toHaveBeenCalledWith(
|
|
263
|
+
expect.any(String),
|
|
264
|
+
expect.objectContaining({
|
|
265
|
+
cwd: path.dirname(composePath)
|
|
266
|
+
}),
|
|
267
|
+
expect.any(Function)
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe('stopDockerCompose()', () => {
|
|
273
|
+
const composePath = '/test/project/docker-compose.yml';
|
|
274
|
+
|
|
275
|
+
it('should run docker compose down successfully', async () => {
|
|
276
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
277
|
+
if (typeof opts === 'function') {
|
|
278
|
+
callback = opts;
|
|
279
|
+
}
|
|
280
|
+
callback(null, 'Containers stopped', '');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const result = await adapter.stopDockerCompose(composePath);
|
|
284
|
+
|
|
285
|
+
expect(result.success).toBe(true);
|
|
286
|
+
expect(exec).toHaveBeenCalledWith(
|
|
287
|
+
expect.stringContaining('docker compose'),
|
|
288
|
+
expect.any(Object),
|
|
289
|
+
expect.any(Function)
|
|
290
|
+
);
|
|
291
|
+
expect(exec).toHaveBeenCalledWith(
|
|
292
|
+
expect.stringContaining('down'),
|
|
293
|
+
expect.any(Object),
|
|
294
|
+
expect.any(Function)
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('should return error when docker compose down fails', async () => {
|
|
299
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
300
|
+
if (typeof opts === 'function') {
|
|
301
|
+
callback = opts;
|
|
302
|
+
}
|
|
303
|
+
callback(new Error('Failed to stop'), '', 'Failed to stop');
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const result = await adapter.stopDockerCompose(composePath);
|
|
307
|
+
|
|
308
|
+
expect(result.success).toBe(false);
|
|
309
|
+
expect(result.error).toContain('Failed to stop');
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
describe('startDockerDesktop()', () => {
|
|
314
|
+
it('should open Docker Desktop on macOS', async () => {
|
|
315
|
+
const originalPlatform = process.platform;
|
|
316
|
+
Object.defineProperty(process, 'platform', { value: 'darwin' });
|
|
317
|
+
|
|
318
|
+
exec.mockImplementation((cmd, callback) => {
|
|
319
|
+
callback(null, '', '');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const result = await adapter.startDockerDesktop();
|
|
323
|
+
|
|
324
|
+
expect(result.success).toBe(true);
|
|
325
|
+
expect(exec).toHaveBeenCalledWith(
|
|
326
|
+
expect.stringContaining('open'),
|
|
327
|
+
expect.any(Function)
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
Object.defineProperty(process, 'platform', { value: originalPlatform });
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('should start Docker Desktop on Windows', async () => {
|
|
334
|
+
const originalPlatform = process.platform;
|
|
335
|
+
Object.defineProperty(process, 'platform', { value: 'win32' });
|
|
336
|
+
|
|
337
|
+
exec.mockImplementation((cmd, callback) => {
|
|
338
|
+
callback(null, '', '');
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const result = await adapter.startDockerDesktop();
|
|
342
|
+
|
|
343
|
+
expect(result.success).toBe(true);
|
|
344
|
+
expect(exec).toHaveBeenCalledWith(
|
|
345
|
+
expect.stringContaining('Docker Desktop'),
|
|
346
|
+
expect.any(Function)
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
Object.defineProperty(process, 'platform', { value: originalPlatform });
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('should start docker service on Linux', async () => {
|
|
353
|
+
const originalPlatform = process.platform;
|
|
354
|
+
Object.defineProperty(process, 'platform', { value: 'linux' });
|
|
355
|
+
|
|
356
|
+
exec.mockImplementation((cmd, callback) => {
|
|
357
|
+
callback(null, '', '');
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const result = await adapter.startDockerDesktop();
|
|
361
|
+
|
|
362
|
+
expect(result.success).toBe(true);
|
|
363
|
+
expect(exec).toHaveBeenCalledWith(
|
|
364
|
+
expect.stringMatching(/systemctl|service/),
|
|
365
|
+
expect.any(Function)
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
Object.defineProperty(process, 'platform', { value: originalPlatform });
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should return error when Docker Desktop fails to start', async () => {
|
|
372
|
+
exec.mockImplementation((cmd, callback) => {
|
|
373
|
+
callback(new Error('Failed to start Docker Desktop'), '', '');
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const result = await adapter.startDockerDesktop();
|
|
377
|
+
|
|
378
|
+
expect(result.success).toBe(false);
|
|
379
|
+
expect(result.error).toBeDefined();
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('getDockerComposeServices()', () => {
|
|
384
|
+
const composePath = '/test/project/docker-compose.yml';
|
|
385
|
+
|
|
386
|
+
it('should list running services', async () => {
|
|
387
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
388
|
+
if (typeof opts === 'function') {
|
|
389
|
+
callback = opts;
|
|
390
|
+
}
|
|
391
|
+
callback(null, 'mongodb\nredis\n', '');
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const result = await adapter.getDockerComposeServices(composePath);
|
|
395
|
+
|
|
396
|
+
expect(result).toEqual(['mongodb', 'redis']);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('should return empty array when no services running', async () => {
|
|
400
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
401
|
+
if (typeof opts === 'function') {
|
|
402
|
+
callback = opts;
|
|
403
|
+
}
|
|
404
|
+
callback(null, '', '');
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const result = await adapter.getDockerComposeServices(composePath);
|
|
408
|
+
|
|
409
|
+
expect(result).toEqual([]);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('should handle docker compose ps command failure', async () => {
|
|
413
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
414
|
+
if (typeof opts === 'function') {
|
|
415
|
+
callback = opts;
|
|
416
|
+
}
|
|
417
|
+
callback(new Error('Failed'), '', '');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
const result = await adapter.getDockerComposeServices(composePath);
|
|
421
|
+
|
|
422
|
+
expect(result).toEqual([]);
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
describe('isServiceRunning()', () => {
|
|
427
|
+
const composePath = '/test/project/docker-compose.yml';
|
|
428
|
+
|
|
429
|
+
it('should return true when specific service is running', async () => {
|
|
430
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
431
|
+
if (typeof opts === 'function') {
|
|
432
|
+
callback = opts;
|
|
433
|
+
}
|
|
434
|
+
// docker compose ps --services --filter "status=running"
|
|
435
|
+
callback(null, 'mongodb\nredis\n', '');
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
const result = await adapter.isServiceRunning(composePath, 'mongodb');
|
|
439
|
+
|
|
440
|
+
expect(result).toBe(true);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('should return false when service is not running', async () => {
|
|
444
|
+
exec.mockImplementation((cmd, opts, callback) => {
|
|
445
|
+
if (typeof opts === 'function') {
|
|
446
|
+
callback = opts;
|
|
447
|
+
}
|
|
448
|
+
callback(null, 'redis\n', '');
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
const result = await adapter.isServiceRunning(composePath, 'mongodb');
|
|
452
|
+
|
|
453
|
+
expect(result).toBe(false);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
describe('waitForDockerReady()', () => {
|
|
458
|
+
it('should resolve when Docker becomes ready', async () => {
|
|
459
|
+
let callCount = 0;
|
|
460
|
+
exec.mockImplementation((cmd, callback) => {
|
|
461
|
+
callCount++;
|
|
462
|
+
if (callCount >= 3) {
|
|
463
|
+
// Docker is ready on third try
|
|
464
|
+
callback(null, '', '');
|
|
465
|
+
} else {
|
|
466
|
+
callback(new Error('Not ready'), '', '');
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
const result = await adapter.waitForDockerReady({ maxAttempts: 5, intervalMs: 10 });
|
|
471
|
+
|
|
472
|
+
expect(result).toBe(true);
|
|
473
|
+
expect(callCount).toBe(3);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it('should return false after max attempts exceeded', async () => {
|
|
477
|
+
exec.mockImplementation((cmd, callback) => {
|
|
478
|
+
callback(new Error('Not ready'), '', '');
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
const result = await adapter.waitForDockerReady({ maxAttempts: 3, intervalMs: 10 });
|
|
482
|
+
|
|
483
|
+
expect(result).toBe(false);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('should use default options when not specified', async () => {
|
|
487
|
+
exec.mockImplementation((cmd, callback) => {
|
|
488
|
+
callback(null, '', '');
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
const result = await adapter.waitForDockerReady();
|
|
492
|
+
|
|
493
|
+
expect(result).toBe(true);
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
});
|