@kumori/aurora-backend-handler 1.1.42 → 1.1.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/.gitlab-ci.yml +21 -21
- package/jest.config.ts +1 -0
- package/package.json +1 -1
- package/test/backend-handler.test.ts +992 -730
- package/test/deploy-service-helper.test.ts +678 -274
- package/test/event-helper.test.ts +489 -104
- package/test/event-names.test.ts +186 -0
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
ServiceSpecForm,
|
|
9
9
|
} from "../api/deploy-service-helper";
|
|
10
10
|
|
|
11
|
+
jest.mock("@kumori/kumori-dsl-generator", () => ({
|
|
12
|
+
buildServiceDeploymentModule: jest.fn().mockResolvedValue(new Blob(["bundleDSL"])),
|
|
13
|
+
}));
|
|
14
|
+
|
|
11
15
|
jest.mock("@kumori/kumori-module-generator", () => ({
|
|
12
16
|
buildServiceDeploymentModuleWithLocalComponent: jest
|
|
13
17
|
.fn()
|
|
@@ -22,11 +26,14 @@ jest.mock("../websocket-manager", () => ({
|
|
|
22
26
|
getReferenceDomain: jest.fn().mockReturnValue("test-domain.com"),
|
|
23
27
|
}));
|
|
24
28
|
|
|
29
|
+
// ── withDefaultValue ─────────────────────────────────────────────────────────
|
|
30
|
+
|
|
25
31
|
describe("withDefaultValue", () => {
|
|
26
|
-
it("retorna el valor si
|
|
32
|
+
it("retorna el valor si no es null ni undefined", () => {
|
|
27
33
|
expect(withDefaultValue("valor", "default")).toBe("valor");
|
|
28
34
|
expect(withDefaultValue(0, 10)).toBe(0);
|
|
29
35
|
expect(withDefaultValue(false, true)).toBe(false);
|
|
36
|
+
expect(withDefaultValue("", "default")).toBe("");
|
|
30
37
|
});
|
|
31
38
|
|
|
32
39
|
it("retorna el valor por defecto si el valor es null o undefined", () => {
|
|
@@ -35,135 +42,202 @@ describe("withDefaultValue", () => {
|
|
|
35
42
|
});
|
|
36
43
|
});
|
|
37
44
|
|
|
45
|
+
// ── handleParametersToGenerateData ───────────────────────────────────────────
|
|
46
|
+
|
|
38
47
|
describe("handleParametersToGenerateData", () => {
|
|
39
|
-
it("maneja
|
|
40
|
-
const
|
|
41
|
-
{ name: "
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
it("maneja tipo string", () => {
|
|
49
|
+
const result = handleParametersToGenerateData(
|
|
50
|
+
[{ name: "p1", value: "v1", type: "string" }],
|
|
51
|
+
[],
|
|
52
|
+
);
|
|
53
|
+
expect(result.parameters).toContainEqual({ name: "p1", type: "string", defaultValue: "v1" });
|
|
54
|
+
expect(result.environment).toContainEqual({ varName: "p1", param: "p1" });
|
|
55
|
+
});
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
expect(result.parameters[1].defaultValue).toBe(42);
|
|
55
|
-
expect(result.parameters[2].defaultValue).toBe(true);
|
|
57
|
+
it("maneja tipo number", () => {
|
|
58
|
+
const result = handleParametersToGenerateData(
|
|
59
|
+
[{ name: "p1", value: "42", type: "number" }],
|
|
60
|
+
[],
|
|
61
|
+
);
|
|
62
|
+
expect(result.parameters[0].defaultValue).toBe(42);
|
|
63
|
+
expect(result.environment).toContainEqual({ varName: "p1", param: "p1" });
|
|
56
64
|
});
|
|
57
65
|
|
|
58
|
-
it("maneja
|
|
59
|
-
const
|
|
60
|
-
|
|
66
|
+
it("maneja tipo boolean con string 'true'", () => {
|
|
67
|
+
const result = handleParametersToGenerateData(
|
|
68
|
+
[{ name: "p1", value: "true", type: "boolean" }],
|
|
69
|
+
[],
|
|
70
|
+
);
|
|
71
|
+
expect(result.parameters[0].defaultValue).toBe(true);
|
|
72
|
+
});
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
});
|
|
74
|
+
it("maneja tipo bool con string 'false'", () => {
|
|
75
|
+
const result = handleParametersToGenerateData(
|
|
76
|
+
[{ name: "p1", value: "false", type: "bool" }],
|
|
77
|
+
[],
|
|
78
|
+
);
|
|
79
|
+
expect(result.parameters[0].defaultValue).toBe(false);
|
|
69
80
|
});
|
|
70
81
|
|
|
71
|
-
it("maneja
|
|
72
|
-
const
|
|
73
|
-
{ name: "
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
it("maneja tipo bool con valor boolean true", () => {
|
|
83
|
+
const result = handleParametersToGenerateData(
|
|
84
|
+
[{ name: "p1", value: true as any, type: "bool" }],
|
|
85
|
+
[],
|
|
86
|
+
);
|
|
87
|
+
expect(result.parameters[0].defaultValue).toBe(true);
|
|
88
|
+
});
|
|
76
89
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
90
|
+
it("maneja tipo secret", () => {
|
|
91
|
+
const result = handleParametersToGenerateData(
|
|
92
|
+
[{ name: "sec1", value: "secretValue", type: "secret" }],
|
|
93
|
+
[],
|
|
94
|
+
);
|
|
95
|
+
expect(result.environment).toContainEqual({ varName: "sec1", secret: "sec1" });
|
|
96
|
+
expect(result.resources).toContainEqual({ secret: { name: "secretValue" } });
|
|
97
|
+
expect(result.parameters).toHaveLength(0);
|
|
81
98
|
});
|
|
82
99
|
|
|
83
|
-
it("maneja
|
|
84
|
-
const
|
|
85
|
-
{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
100
|
+
it("maneja tipo file", () => {
|
|
101
|
+
const result = handleParametersToGenerateData(
|
|
102
|
+
[{ name: "myconfig", value: "/path/to/file", type: "file" }],
|
|
103
|
+
[],
|
|
104
|
+
);
|
|
105
|
+
expect(result.parameters[0].name).toBe("CONFIG_FILE_0");
|
|
106
|
+
expect(result.parameters[0].type).toBe("string");
|
|
107
|
+
expect(result.parameters[0].defaultValue).toBe("/path/to/file");
|
|
108
|
+
expect(result.fileSystem).toContainEqual({ path: "myconfig", param: "CONFIG_FILE_0" });
|
|
109
|
+
});
|
|
93
110
|
|
|
111
|
+
it("maneja tipo fileContent con content", () => {
|
|
112
|
+
const result = handleParametersToGenerateData(
|
|
113
|
+
[{ name: "f1", value: "/custom/path", type: "fileContent", content: "contenido" }],
|
|
114
|
+
[],
|
|
115
|
+
);
|
|
94
116
|
expect(result.parameters[0].defaultValue).toBe("contenido");
|
|
95
117
|
expect(result.fileSystem[0].path).toBe("/custom/path");
|
|
118
|
+
expect(result.fileSystem[0].param).toBe("CONFIG_FILE_0");
|
|
96
119
|
});
|
|
97
120
|
|
|
98
|
-
it("maneja
|
|
99
|
-
const
|
|
100
|
-
|
|
121
|
+
it("maneja tipo fileContent sin content (usa value)", () => {
|
|
122
|
+
const result = handleParametersToGenerateData(
|
|
123
|
+
[{ name: "f1", value: "/some/path", type: "fileContent" }],
|
|
124
|
+
[],
|
|
125
|
+
);
|
|
126
|
+
expect(result.parameters[0].defaultValue).toBe("/some/path");
|
|
127
|
+
expect(result.fileSystem[0].path).toBe("/some/path");
|
|
128
|
+
});
|
|
101
129
|
|
|
130
|
+
it("maneja tipo volume sin content (usa value)", () => {
|
|
131
|
+
const result = handleParametersToGenerateData(
|
|
132
|
+
[{ name: "vol1", value: "/data", type: "volume" }],
|
|
133
|
+
[],
|
|
134
|
+
);
|
|
102
135
|
expect(result.resources).toContainEqual({ volume: { name: "vol1" } });
|
|
103
|
-
expect(result.fileSystem).toContainEqual({
|
|
104
|
-
path: "/data",
|
|
105
|
-
resourceVolume: "vol1",
|
|
106
|
-
});
|
|
136
|
+
expect(result.fileSystem).toContainEqual({ path: "/data", resourceVolume: "vol1" });
|
|
107
137
|
});
|
|
108
138
|
|
|
109
|
-
it("maneja
|
|
110
|
-
const
|
|
111
|
-
{ name: "
|
|
112
|
-
|
|
113
|
-
|
|
139
|
+
it("maneja tipo volume con content", () => {
|
|
140
|
+
const result = handleParametersToGenerateData(
|
|
141
|
+
[{ name: "vol1", value: "/data", type: "volume", content: "/mount/path" }],
|
|
142
|
+
[],
|
|
143
|
+
);
|
|
144
|
+
expect(result.resources).toContainEqual({ volume: { name: "vol1" } });
|
|
145
|
+
expect(result.fileSystem).toContainEqual({ path: "/mount/path", resourceVolume: "vol1" });
|
|
146
|
+
});
|
|
114
147
|
|
|
148
|
+
it("maneja tipo serviceUrl", () => {
|
|
149
|
+
const result = handleParametersToGenerateData(
|
|
150
|
+
[{ name: "url1", value: "https://api.example.com", type: "serviceUrl" }],
|
|
151
|
+
[],
|
|
152
|
+
);
|
|
115
153
|
expect(result.parameters[0].defaultValue).toBe("https://api.example.com");
|
|
154
|
+
expect(result.environment).toContainEqual({ varName: "url1", param: "url1" });
|
|
116
155
|
});
|
|
117
156
|
|
|
118
|
-
it("maneja tipos desconocidos", () => {
|
|
119
|
-
const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
157
|
+
it("maneja tipos desconocidos con warn", () => {
|
|
158
|
+
const consoleSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
159
|
+
const result = handleParametersToGenerateData(
|
|
160
|
+
[{ name: "p1", value: "v1", type: "unknown" }],
|
|
161
|
+
[],
|
|
162
|
+
);
|
|
124
163
|
expect(consoleSpy).toHaveBeenCalledWith("Unknown parameter type: unknown");
|
|
164
|
+
expect(result.parameters[0].type).toBe("string");
|
|
125
165
|
consoleSpy.mockRestore();
|
|
126
166
|
});
|
|
127
167
|
|
|
128
|
-
it("maneja
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
168
|
+
it("maneja tipos desconocidos con value undefined", () => {
|
|
169
|
+
const consoleSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
170
|
+
handleParametersToGenerateData(
|
|
171
|
+
[{ name: "p1", value: undefined as any, type: "unknown" }],
|
|
172
|
+
[],
|
|
173
|
+
);
|
|
174
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
175
|
+
consoleSpy.mockRestore();
|
|
176
|
+
});
|
|
133
177
|
|
|
178
|
+
it("maneja formResources de tipo secret", () => {
|
|
179
|
+
const result = handleParametersToGenerateData([], [
|
|
180
|
+
{ name: "res1", type: "secret", value: "secretValue" },
|
|
181
|
+
]);
|
|
134
182
|
expect(result.resources).toContainEqual({ secret: { name: "res1" } });
|
|
183
|
+
expect(result.environment).toContainEqual({ varName: "res1", secret: "res1" });
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("maneja formResources de tipo volume", () => {
|
|
187
|
+
const result = handleParametersToGenerateData([], [
|
|
188
|
+
{ name: "vol1", type: "volume", key: "/mnt/data" },
|
|
189
|
+
]);
|
|
190
|
+
expect(result.resources).toContainEqual({ volume: { name: "vol1" } });
|
|
191
|
+
expect(result.fileSystem).toContainEqual({ path: "/mnt/data", resourceVolume: "vol1" });
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("maneja formResources de tipo volume sin key", () => {
|
|
195
|
+
const result = handleParametersToGenerateData([], [
|
|
196
|
+
{ name: "vol1", type: "volume" },
|
|
197
|
+
]);
|
|
198
|
+
expect(result.fileSystem[0].path).toBe("");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("maneja formResources de tipo desconocido con warn", () => {
|
|
202
|
+
const consoleSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
203
|
+
handleParametersToGenerateData([], [{ name: "r1", type: "unknown" }]);
|
|
204
|
+
expect(consoleSpy).toHaveBeenCalledWith("Unknown resource type: unknown");
|
|
205
|
+
consoleSpy.mockRestore();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("maneja múltiples parámetros a la vez", () => {
|
|
209
|
+
const result = handleParametersToGenerateData(
|
|
210
|
+
[
|
|
211
|
+
{ name: "s1", value: "v1", type: "string" },
|
|
212
|
+
{ name: "n1", value: "10", type: "number" },
|
|
213
|
+
{ name: "b1", value: "true", type: "boolean" },
|
|
214
|
+
],
|
|
215
|
+
[],
|
|
216
|
+
);
|
|
217
|
+
expect(result.parameters).toHaveLength(3);
|
|
218
|
+
expect(result.environment).toHaveLength(3);
|
|
135
219
|
});
|
|
136
220
|
});
|
|
137
221
|
|
|
222
|
+
// ── transformServiceToForm ────────────────────────────────────────────────────
|
|
223
|
+
|
|
138
224
|
describe("transformServiceToForm", () => {
|
|
139
225
|
const createMockService = (overrides = {}): Service => ({
|
|
140
|
-
id: "
|
|
226
|
+
id: "s1",
|
|
141
227
|
tenant: "tenant1",
|
|
142
228
|
account: "account1",
|
|
143
229
|
environment: "env1",
|
|
144
|
-
name: "
|
|
230
|
+
name: "TestService",
|
|
145
231
|
logo: "",
|
|
146
232
|
description: "",
|
|
147
233
|
revisions: [],
|
|
148
|
-
status: {
|
|
149
|
-
|
|
150
|
-
timestamp: "2024-01-01",
|
|
151
|
-
args: [],
|
|
152
|
-
code: "200",
|
|
153
|
-
},
|
|
154
|
-
role: [{ name: "servicio1", instances: [] }],
|
|
234
|
+
status: { message: "running", timestamp: "2024-01-01", args: [], code: "200" },
|
|
235
|
+
role: [{ name: "s1", instances: [] }],
|
|
155
236
|
links: [],
|
|
156
237
|
resources: [],
|
|
157
238
|
parameters: [],
|
|
158
239
|
usage: {
|
|
159
|
-
current: {
|
|
160
|
-
cpu: 2,
|
|
161
|
-
memory: 512,
|
|
162
|
-
storage: 100,
|
|
163
|
-
volatileStorage: 10,
|
|
164
|
-
nonReplicatedStorage: 10,
|
|
165
|
-
persistentStorage: 20,
|
|
166
|
-
},
|
|
240
|
+
current: { cpu: 2, memory: 512, storage: 100, volatileStorage: 10, nonReplicatedStorage: 10, persistentStorage: 20 },
|
|
167
241
|
limit: {
|
|
168
242
|
cpu: { max: 4, min: 1 },
|
|
169
243
|
memory: { max: 1024, min: 256 },
|
|
@@ -174,14 +248,11 @@ describe("transformServiceToForm", () => {
|
|
|
174
248
|
},
|
|
175
249
|
cost: 100,
|
|
176
250
|
},
|
|
177
|
-
|
|
178
|
-
maxReplicas: 3,
|
|
179
|
-
lastDeployed: "",
|
|
180
|
-
project: "project1",
|
|
251
|
+
project: "proj1",
|
|
181
252
|
registry: "docker.io",
|
|
182
|
-
imageName: "
|
|
183
|
-
entrypoint: "/
|
|
184
|
-
cmd: "
|
|
253
|
+
imageName: "myImage",
|
|
254
|
+
entrypoint: "/start",
|
|
255
|
+
cmd: "run",
|
|
185
256
|
serverChannels: [],
|
|
186
257
|
clientChannels: [],
|
|
187
258
|
duplexChannels: [],
|
|
@@ -189,231 +260,427 @@ describe("transformServiceToForm", () => {
|
|
|
189
260
|
...overrides,
|
|
190
261
|
});
|
|
191
262
|
|
|
192
|
-
it("transforma
|
|
193
|
-
const
|
|
194
|
-
clientChannels: [{ name: "clientChannel1", from: "", to: "" }],
|
|
195
|
-
serverChannels: [
|
|
196
|
-
{
|
|
197
|
-
name: "serverChannel1",
|
|
198
|
-
from: "",
|
|
199
|
-
to: "",
|
|
200
|
-
port: 8080,
|
|
201
|
-
isPublic: true,
|
|
202
|
-
protocol: "http",
|
|
203
|
-
portNum: 80,
|
|
204
|
-
},
|
|
205
|
-
],
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const form = transformServiceToForm(service);
|
|
209
|
-
|
|
263
|
+
it("transforma servicio básico correctamente", () => {
|
|
264
|
+
const form = transformServiceToForm(createMockService());
|
|
210
265
|
expect(form.tenantId).toBe("tenant1");
|
|
211
266
|
expect(form.accountId).toBe("account1");
|
|
212
|
-
expect(form.
|
|
267
|
+
expect(form.environmentId).toBe("env1");
|
|
268
|
+
expect(form.serviceId).toBe("TestService");
|
|
213
269
|
expect(form.cpuRequirements).toBe(4);
|
|
214
270
|
expect(form.memoryRequirements).toBe(1024);
|
|
215
271
|
expect(form.registryUrl).toBe("docker.io");
|
|
216
|
-
expect(form.imageTag).toBe("
|
|
217
|
-
expect(form.
|
|
272
|
+
expect(form.imageTag).toBe("myImage");
|
|
273
|
+
expect(form.defaultExecutable).toEqual({ cmd: "run", entryPoint: "/start" });
|
|
274
|
+
expect(form.clientChannels).toEqual([]);
|
|
275
|
+
expect(form.channels).toEqual([]);
|
|
276
|
+
expect(form.clientChannelsExtra).toEqual([]);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("transforma serverChannels con protocolo http a HTTPS", () => {
|
|
280
|
+
const service = createMockService({
|
|
281
|
+
serverChannels: [{ name: "web", port: 8080, isPublic: true, protocol: "http", portNum: 80 }],
|
|
282
|
+
});
|
|
283
|
+
const form = transformServiceToForm(service);
|
|
218
284
|
expect(form.channels[0].protocol).toBe("HTTPS");
|
|
285
|
+
expect(form.channels[0].containerPort).toBe(8080);
|
|
286
|
+
expect(form.channels[0].isPublic).toBe(true);
|
|
287
|
+
expect(form.channels[0].publicPort).toBe("80");
|
|
219
288
|
});
|
|
220
289
|
|
|
221
|
-
it("
|
|
290
|
+
it("transforma serverChannels con protocolo tcp a TCP", () => {
|
|
222
291
|
const service = createMockService({
|
|
223
|
-
serverChannels: [
|
|
224
|
-
{
|
|
225
|
-
name: "tcpChannel",
|
|
226
|
-
from: "",
|
|
227
|
-
to: "",
|
|
228
|
-
protocol: "tcp",
|
|
229
|
-
},
|
|
230
|
-
],
|
|
292
|
+
serverChannels: [{ name: "tcp1", protocol: "tcp" }],
|
|
231
293
|
});
|
|
294
|
+
const form = transformServiceToForm(service);
|
|
295
|
+
expect(form.channels[0].protocol).toBe("TCP");
|
|
296
|
+
});
|
|
232
297
|
|
|
298
|
+
it("transforma serverChannels con protocolo desconocido a TCP", () => {
|
|
299
|
+
const service = createMockService({
|
|
300
|
+
serverChannels: [{ name: "other", protocol: "grpc" }],
|
|
301
|
+
});
|
|
233
302
|
const form = transformServiceToForm(service);
|
|
234
303
|
expect(form.channels[0].protocol).toBe("TCP");
|
|
235
304
|
});
|
|
236
305
|
|
|
237
|
-
it("
|
|
306
|
+
it("transforma clientChannels y duplexChannels", () => {
|
|
307
|
+
const service = createMockService({
|
|
308
|
+
clientChannels: [{ name: "clientCh" }],
|
|
309
|
+
duplexChannels: [{ name: "duplexCh" }],
|
|
310
|
+
});
|
|
311
|
+
const form = transformServiceToForm(service);
|
|
312
|
+
expect(form.clientChannels).toEqual([{ name: "clientCh" }]);
|
|
313
|
+
expect(form.clientChannelsExtra).toEqual([{ name: "duplexCh" }]);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("transforma resources de tipo volume al fileSystem", () => {
|
|
238
317
|
const service = createMockService({
|
|
239
|
-
|
|
240
|
-
{
|
|
241
|
-
|
|
242
|
-
instances: [],
|
|
243
|
-
scalling: {
|
|
244
|
-
cpu: { up: "80", down: "20" },
|
|
245
|
-
memory: { up: "80", down: "20" },
|
|
246
|
-
instances: { max: 5, min: 1 },
|
|
247
|
-
histeresys: "10",
|
|
248
|
-
},
|
|
249
|
-
},
|
|
318
|
+
resources: [
|
|
319
|
+
{ name: "vol1", type: "volume", key: "/data" },
|
|
320
|
+
{ name: "sec1", type: "secret", value: "mySecret" },
|
|
250
321
|
],
|
|
251
322
|
});
|
|
323
|
+
const form = transformServiceToForm(service);
|
|
324
|
+
expect(form.fileSystem).toContainEqual({ path: "/data", resourceVolume: "vol1" });
|
|
325
|
+
expect(form.config.resources).toHaveLength(2);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("transforma parameters", () => {
|
|
329
|
+
const service = createMockService({
|
|
330
|
+
parameters: [{ name: "env", value: "prod", type: "string" }],
|
|
331
|
+
});
|
|
332
|
+
const form = transformServiceToForm(service);
|
|
333
|
+
expect(form.config.parameters).toContainEqual({ name: "env", value: "prod", type: "string" });
|
|
334
|
+
});
|
|
252
335
|
|
|
336
|
+
it("transforma scaling configuration", () => {
|
|
337
|
+
const service = createMockService({
|
|
338
|
+
role: [{
|
|
339
|
+
name: "s1",
|
|
340
|
+
instances: [],
|
|
341
|
+
scalling: { cpu: { up: "80", down: "20" }, memory: { up: "80", down: "20" }, instances: { max: 5, min: 1 }, histeresys: "10" },
|
|
342
|
+
}],
|
|
343
|
+
});
|
|
253
344
|
const form = transformServiceToForm(service);
|
|
254
345
|
expect(form.scaling).toBeDefined();
|
|
255
346
|
expect(form.scaling?.instances.max).toBe(5);
|
|
256
347
|
});
|
|
348
|
+
|
|
349
|
+
it("transforma certificateResource, withMtls y caResource en channels", () => {
|
|
350
|
+
const service = createMockService({
|
|
351
|
+
serverChannels: [{ name: "web", protocol: "http", certificateResource: "mycert", withMtls: true, caResource: "myca" }],
|
|
352
|
+
});
|
|
353
|
+
const form = transformServiceToForm(service);
|
|
354
|
+
expect(form.channels[0].certificateResource).toBe("mycert");
|
|
355
|
+
expect(form.channels[0].withMtls).toBe(true);
|
|
356
|
+
expect(form.channels[0].caResource).toBe("myca");
|
|
357
|
+
});
|
|
257
358
|
});
|
|
258
359
|
|
|
360
|
+
// ── generateServiceSpec ───────────────────────────────────────────────────────
|
|
361
|
+
|
|
259
362
|
describe("generateServiceSpec", () => {
|
|
260
|
-
const createMockForm = (overrides = {}): ServiceSpecForm => ({
|
|
363
|
+
const createMockForm = (overrides: Partial<ServiceSpecForm> = {}): ServiceSpecForm => ({
|
|
261
364
|
tenantId: "tenant1",
|
|
262
365
|
accountId: "account1",
|
|
263
366
|
environmentId: "env1",
|
|
264
|
-
serviceId: "
|
|
367
|
+
serviceId: "svc1",
|
|
265
368
|
cpuRequirements: 2,
|
|
266
369
|
memoryRequirements: 512,
|
|
267
370
|
registryUrl: "docker.io",
|
|
268
|
-
imageTag: "
|
|
371
|
+
imageTag: "myImage",
|
|
269
372
|
clientChannels: [],
|
|
270
373
|
channels: [],
|
|
271
374
|
defaultExecutable: {},
|
|
272
|
-
config: {
|
|
273
|
-
parameters: [],
|
|
274
|
-
resources: [],
|
|
275
|
-
},
|
|
375
|
+
config: { parameters: [], resources: [] },
|
|
276
376
|
...overrides,
|
|
277
377
|
});
|
|
278
378
|
|
|
279
|
-
it("genera
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
379
|
+
it("genera spec básico sin channels ni recursos", async () => {
|
|
380
|
+
const spec = await generateServiceSpec(createMockForm());
|
|
381
|
+
expect(spec.type).toBe("service");
|
|
382
|
+
expect(spec.subtype).toBe("service_with_local_component");
|
|
383
|
+
expect(spec.tenantId).toBe("tenant1");
|
|
384
|
+
expect(spec.deploymentConfig.scale.detail["svc1"]).toBe(1);
|
|
385
|
+
expect(spec.local_components["svc1"]).toBeDefined();
|
|
386
|
+
});
|
|
285
387
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
expect(
|
|
289
|
-
expect(
|
|
290
|
-
"
|
|
291
|
-
|
|
388
|
+
it("genera spec con clientChannels", async () => {
|
|
389
|
+
const spec = await generateServiceSpec(createMockForm({ clientChannels: [{ name: "ch1" }] }));
|
|
390
|
+
expect(spec.local_components["svc1"].channels.client).toContain("ch1");
|
|
391
|
+
expect(spec.topology).toContainEqual(
|
|
392
|
+
expect.objectContaining({ clientRole: "svc1", clientChannel: "ch1", serverRole: "self" }),
|
|
393
|
+
);
|
|
292
394
|
});
|
|
293
395
|
|
|
294
|
-
it("genera inbound
|
|
396
|
+
it("genera inbound role para canal HTTPS público sin certificateResource (cert por defecto)", async () => {
|
|
295
397
|
const form = createMockForm({
|
|
296
|
-
channels: [
|
|
297
|
-
{
|
|
298
|
-
channelName: "https1",
|
|
299
|
-
containerPort: 443,
|
|
300
|
-
isPublic: true,
|
|
301
|
-
protocol: "HTTPS",
|
|
302
|
-
domain: "example.com",
|
|
303
|
-
},
|
|
304
|
-
],
|
|
398
|
+
channels: [{ channelName: "web", containerPort: 443, isPublic: true, protocol: "HTTPS", domain: "example.com" }],
|
|
305
399
|
});
|
|
400
|
+
const spec = await generateServiceSpec(form);
|
|
401
|
+
const inbound = spec.roles.find((r) => r.name === "web_inbound");
|
|
402
|
+
expect(inbound).toBeDefined();
|
|
403
|
+
expect(inbound?.artifact.config.parameters).toContainEqual({ name: "type", value: "https", type: "string" });
|
|
404
|
+
expect(spec.config.resources).toContainEqual({ certificate: { name: "main_inbound_servercert" } });
|
|
405
|
+
const certDeploy = spec.deploymentConfig.resources.find((r: any) => r.certificate?.name === "main_inbound_servercert");
|
|
406
|
+
expect(certDeploy).toBeDefined();
|
|
407
|
+
});
|
|
306
408
|
|
|
307
|
-
|
|
409
|
+
it("genera inbound role para canal HTTPS público con certificateResource personalizado", async () => {
|
|
410
|
+
const form = createMockForm({
|
|
411
|
+
channels: [{ channelName: "web", containerPort: 443, isPublic: true, protocol: "HTTPS", domain: "example.com", certificateResource: "my-cert" }],
|
|
412
|
+
});
|
|
413
|
+
const spec = await generateServiceSpec(form);
|
|
414
|
+
const certConfig = spec.config.resources.find((r: any) => r.certificate?.name === "web_cert");
|
|
415
|
+
expect(certConfig).toBeDefined();
|
|
416
|
+
const certDeploy = spec.deploymentConfig.resources.find((r: any) => r.certificate?.name === "web_cert");
|
|
417
|
+
expect(certDeploy).toBeDefined();
|
|
418
|
+
expect((certDeploy as any).certificate.resource).toBe("my-cert");
|
|
419
|
+
});
|
|
308
420
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
value: "https",
|
|
316
|
-
type: "string",
|
|
421
|
+
it("genera inbound role para canal HTTPS con mTLS y caResource", async () => {
|
|
422
|
+
const form = createMockForm({
|
|
423
|
+
channels: [{
|
|
424
|
+
channelName: "web", containerPort: 443, isPublic: true, protocol: "HTTPS",
|
|
425
|
+
domain: "example.com", certificateResource: "my-cert", withMtls: true, caResource: "my-ca",
|
|
426
|
+
}],
|
|
317
427
|
});
|
|
428
|
+
const spec = await generateServiceSpec(form);
|
|
429
|
+
const caConfig = spec.config.resources.find((r: any) => r.ca?.name === "web_ca");
|
|
430
|
+
expect(caConfig).toBeDefined();
|
|
431
|
+
const caDeploy = spec.deploymentConfig.resources.find((r: any) => r.ca?.name === "web_ca");
|
|
432
|
+
expect(caDeploy).toBeDefined();
|
|
318
433
|
});
|
|
319
434
|
|
|
320
|
-
it("genera inbound
|
|
435
|
+
it("genera inbound role para canal TCP público", async () => {
|
|
321
436
|
const form = createMockForm({
|
|
322
|
-
channels: [
|
|
323
|
-
{
|
|
324
|
-
channelName: "tcp1",
|
|
325
|
-
containerPort: 9000,
|
|
326
|
-
isPublic: true,
|
|
327
|
-
protocol: "TCP",
|
|
328
|
-
publicPort: "9000",
|
|
329
|
-
},
|
|
330
|
-
],
|
|
437
|
+
channels: [{ channelName: "tcp1", containerPort: 9000, isPublic: true, protocol: "TCP", publicPort: "9000" }],
|
|
331
438
|
});
|
|
439
|
+
const spec = await generateServiceSpec(form);
|
|
440
|
+
const inbound = spec.roles.find((r) => r.name === "tcp1_inbound");
|
|
441
|
+
expect(inbound).toBeDefined();
|
|
442
|
+
expect(inbound?.artifact.config.parameters).toContainEqual({ name: "type", value: "tcp", type: "string" });
|
|
443
|
+
const portDeploy = spec.deploymentConfig.resources.find((r: any) => r.port?.name === "tcp1_port");
|
|
444
|
+
expect(portDeploy).toBeDefined();
|
|
445
|
+
});
|
|
332
446
|
|
|
333
|
-
|
|
447
|
+
it("canal con containerPort 0 no añade port en channels.server", async () => {
|
|
448
|
+
const form = createMockForm({
|
|
449
|
+
channels: [{ channelName: "web", containerPort: 0, isPublic: false, protocol: "HTTPS" }],
|
|
450
|
+
});
|
|
451
|
+
const spec = await generateServiceSpec(form);
|
|
452
|
+
const ch = spec.channels.server.find((s: any) => s.name === "web");
|
|
453
|
+
expect(ch).toEqual({ name: "web" });
|
|
454
|
+
});
|
|
334
455
|
|
|
335
|
-
|
|
336
|
-
|
|
456
|
+
it("genera topology para canal público", async () => {
|
|
457
|
+
const form = createMockForm({
|
|
458
|
+
channels: [{ channelName: "web", containerPort: 443, isPublic: true, protocol: "HTTPS" }],
|
|
459
|
+
});
|
|
460
|
+
const spec = await generateServiceSpec(form);
|
|
461
|
+
expect(spec.topology).toContainEqual(
|
|
462
|
+
expect.objectContaining({ clientRole: "web_inbound", clientChannel: "inbound" }),
|
|
463
|
+
);
|
|
464
|
+
expect(spec.topology).toContainEqual(
|
|
465
|
+
expect.objectContaining({ clientRole: "self", clientChannel: "web" }),
|
|
337
466
|
);
|
|
338
|
-
expect(inboundRole).toBeDefined();
|
|
339
467
|
});
|
|
340
468
|
|
|
341
|
-
it("maneja
|
|
342
|
-
const
|
|
469
|
+
it("maneja recursos de tipo secret en config", async () => {
|
|
470
|
+
const form = createMockForm({
|
|
471
|
+
config: { parameters: [], resources: [{ name: "sec1", type: "secret", value: "my-secret" }] },
|
|
472
|
+
});
|
|
473
|
+
const spec = await generateServiceSpec(form);
|
|
474
|
+
expect(spec.config.resources).toContainEqual({ secret: { name: "sec1" } });
|
|
475
|
+
const secDeploy = spec.deploymentConfig.resources.find((r: any) => r.secret?.name === "sec1");
|
|
476
|
+
expect(secDeploy).toBeDefined();
|
|
477
|
+
});
|
|
343
478
|
|
|
479
|
+
it("maneja volumen sin size (referencia externa)", async () => {
|
|
344
480
|
const form = createMockForm({
|
|
345
|
-
config: {
|
|
346
|
-
parameters: [],
|
|
347
|
-
resources: [
|
|
348
|
-
{ name: "vol1", type: "volume", size: 5, kind: "volatile" },
|
|
349
|
-
{ name: "vol2", type: "volume", size: 10, kind: "persistent" },
|
|
350
|
-
{ name: "vol3", type: "volume", size: 15, kind: "nonreplicated" },
|
|
351
|
-
],
|
|
352
|
-
},
|
|
481
|
+
config: { parameters: [], resources: [{ name: "vol1", type: "volume", value: "existing-vol" }] },
|
|
353
482
|
});
|
|
483
|
+
const spec = await generateServiceSpec(form);
|
|
484
|
+
const volDeploy = spec.deploymentConfig.resources.find((r: any) => r.volume?.name === "vol1");
|
|
485
|
+
expect(volDeploy).toBeDefined();
|
|
486
|
+
expect((volDeploy as any).volume.resource).toBe("existing-vol");
|
|
487
|
+
});
|
|
354
488
|
|
|
355
|
-
|
|
489
|
+
it("maneja volúmenes con type volatile", async () => {
|
|
490
|
+
const form = createMockForm({
|
|
491
|
+
config: { parameters: [], resources: [{ name: "vol1", type: "volume", size: 5, kind: "volatile" } as any] },
|
|
492
|
+
});
|
|
493
|
+
const spec = await generateServiceSpec(form);
|
|
494
|
+
const volDeploy = spec.deploymentConfig.resources.find((r: any) => r.volume?.volume?.type === "volatile");
|
|
495
|
+
expect(volDeploy).toBeDefined();
|
|
496
|
+
expect((volDeploy as any).volume.volume.size).toBe(5000);
|
|
497
|
+
});
|
|
356
498
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
499
|
+
it("maneja volúmenes con type persistent", async () => {
|
|
500
|
+
const form = createMockForm({
|
|
501
|
+
config: { parameters: [], resources: [{ name: "vol2", type: "volume", size: 10, kind: "persistent" } as any] },
|
|
502
|
+
});
|
|
503
|
+
const spec = await generateServiceSpec(form);
|
|
504
|
+
const volDeploy = spec.deploymentConfig.resources.find((r: any) => r.volume?.volume?.type === "persistent");
|
|
505
|
+
expect(volDeploy).toBeDefined();
|
|
506
|
+
});
|
|
361
507
|
|
|
362
|
-
|
|
508
|
+
it("maneja volúmenes con type nonreplicated (default)", async () => {
|
|
509
|
+
const form = createMockForm({
|
|
510
|
+
config: { parameters: [], resources: [{ name: "vol3", type: "volume", size: 15, kind: "nonreplicated" } as any] },
|
|
511
|
+
});
|
|
512
|
+
const spec = await generateServiceSpec(form);
|
|
513
|
+
const volDeploy = spec.deploymentConfig.resources.find((r: any) => r.volume?.volume?.type === "nonreplicated");
|
|
514
|
+
expect(volDeploy).toBeDefined();
|
|
363
515
|
});
|
|
364
516
|
|
|
365
|
-
it("maneja
|
|
517
|
+
it("maneja volúmenes sin kind (usa nonreplicated por defecto)", async () => {
|
|
366
518
|
const form = createMockForm({
|
|
367
|
-
|
|
368
|
-
cpu: { up: "80", down: "20" },
|
|
369
|
-
memory: { up: "80", down: "20" },
|
|
370
|
-
instances: { min: 1, max: 5 },
|
|
371
|
-
histeresys: "10",
|
|
372
|
-
},
|
|
519
|
+
config: { parameters: [], resources: [{ name: "vol4", type: "volume", size: 20 } as any] },
|
|
373
520
|
});
|
|
521
|
+
const spec = await generateServiceSpec(form);
|
|
522
|
+
const volDeploy = spec.deploymentConfig.resources.find((r: any) => r.volume?.volume?.type === "nonreplicated");
|
|
523
|
+
expect(volDeploy).toBeDefined();
|
|
524
|
+
});
|
|
374
525
|
|
|
375
|
-
|
|
526
|
+
it("genera scaling configuration", async () => {
|
|
527
|
+
const form = createMockForm({
|
|
528
|
+
scaling: { cpu: { up: "80", down: "20" }, memory: { up: "80", down: "20" }, instances: { min: 1, max: 5 }, histeresys: "10" },
|
|
529
|
+
});
|
|
530
|
+
const spec = await generateServiceSpec(form);
|
|
531
|
+
expect(spec.deploymentConfig.meta.scaling).toBeDefined();
|
|
532
|
+
expect(spec.deploymentConfig.meta.scaling.simple["svc1"].max_replicas).toBe(5);
|
|
533
|
+
expect(spec.deploymentConfig.meta.scaling.simple["svc1"].hysteresis).toBe(10);
|
|
534
|
+
});
|
|
376
535
|
|
|
377
|
-
|
|
378
|
-
|
|
536
|
+
it("sin scaling: meta es objeto vacío", async () => {
|
|
537
|
+
const spec = await generateServiceSpec(createMockForm());
|
|
538
|
+
expect(spec.deploymentConfig.meta).toEqual({});
|
|
379
539
|
});
|
|
380
540
|
|
|
381
541
|
it("genera spec con marketplace item de tipo service", async () => {
|
|
382
542
|
const form = createMockForm();
|
|
383
|
-
const
|
|
384
|
-
type: "service"
|
|
543
|
+
const mktItem = {
|
|
544
|
+
type: "service",
|
|
385
545
|
domain: "kumori.systems",
|
|
386
|
-
module: "marketplace/
|
|
546
|
+
module: "marketplace/svc",
|
|
387
547
|
version: "1.2.3",
|
|
388
|
-
serviceName: "
|
|
389
|
-
deploymentData: { name: "
|
|
548
|
+
serviceName: "MktSvc",
|
|
549
|
+
deploymentData: { name: "mktRole" },
|
|
390
550
|
roles: ["role1", "role2"],
|
|
391
551
|
};
|
|
552
|
+
const spec = await generateServiceSpec(form, mktItem as any);
|
|
553
|
+
const role = spec.roles.find((r) => r.name === "mktRole");
|
|
554
|
+
expect(role?.artifact.artifactKind).toBe("service");
|
|
555
|
+
expect(role?.artifact.moduleVersion).toEqual([1, 2, 3]);
|
|
556
|
+
expect(spec.local_components).toEqual({});
|
|
557
|
+
});
|
|
392
558
|
|
|
393
|
-
|
|
559
|
+
it("genera spec con marketplace item de tipo component", async () => {
|
|
560
|
+
const form = createMockForm();
|
|
561
|
+
const mktItem = {
|
|
562
|
+
type: "component",
|
|
563
|
+
domain: "kumori.systems",
|
|
564
|
+
module: "marketplace/comp",
|
|
565
|
+
version: "2.0.0",
|
|
566
|
+
serviceName: "MktComp",
|
|
567
|
+
deploymentData: { name: "compRole" },
|
|
568
|
+
roles: [],
|
|
569
|
+
};
|
|
570
|
+
const spec = await generateServiceSpec(form, mktItem as any);
|
|
571
|
+
const role = spec.roles.find((r) => r.name === "compRole");
|
|
572
|
+
expect(role?.artifact.artifactKind).toBe("component");
|
|
573
|
+
expect(spec.local_components).toEqual({});
|
|
574
|
+
});
|
|
394
575
|
|
|
395
|
-
|
|
396
|
-
|
|
576
|
+
it("genera spec con parámetros de tipo string en roles", async () => {
|
|
577
|
+
const form = createMockForm({
|
|
578
|
+
config: {
|
|
579
|
+
parameters: [{ name: "KEY", value: "val", type: "string" }],
|
|
580
|
+
resources: [],
|
|
581
|
+
},
|
|
582
|
+
});
|
|
583
|
+
const spec = await generateServiceSpec(form);
|
|
584
|
+
const mainRole = spec.roles.find((r) => r.name === "svc1");
|
|
585
|
+
expect(mainRole?.artifact.config.parameters).toContainEqual(
|
|
586
|
+
expect.objectContaining({ name: "KEY", type: "string" }),
|
|
397
587
|
);
|
|
398
|
-
|
|
399
|
-
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it("genera spec con parámetros de tipo boolean en roles", async () => {
|
|
591
|
+
const form = createMockForm({
|
|
592
|
+
config: {
|
|
593
|
+
parameters: [{ name: "FLAG", value: "true", type: "boolean" }],
|
|
594
|
+
resources: [],
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
const spec = await generateServiceSpec(form);
|
|
598
|
+
const params = spec.deploymentConfig.parameters;
|
|
599
|
+
expect(params).toContainEqual(expect.objectContaining({ name: "FLAG", type: "bool" }));
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it("genera spec con parámetros de tipo number en deploymentConfig", async () => {
|
|
603
|
+
const form = createMockForm({
|
|
604
|
+
config: {
|
|
605
|
+
parameters: [{ name: "COUNT", value: "5", type: "number" }],
|
|
606
|
+
resources: [],
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
const spec = await generateServiceSpec(form);
|
|
610
|
+
const params = spec.deploymentConfig.parameters;
|
|
611
|
+
expect(params).toContainEqual(expect.objectContaining({ name: "COUNT", type: "number" }));
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it("genera spec con parámetros de tipo fileContent en deploymentConfig", async () => {
|
|
615
|
+
const form = createMockForm({
|
|
616
|
+
config: {
|
|
617
|
+
parameters: [{ name: "CFG", value: "v", type: "fileContent", content: "file-content" }],
|
|
618
|
+
resources: [],
|
|
619
|
+
},
|
|
620
|
+
});
|
|
621
|
+
const spec = await generateServiceSpec(form);
|
|
622
|
+
const params = spec.deploymentConfig.parameters;
|
|
623
|
+
expect(params).toContainEqual(expect.objectContaining({ type: "string", value: "file-content" }));
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
it("genera spec con volatile volume en parameters (volatileVolumeResources)", async () => {
|
|
627
|
+
const form = createMockForm({
|
|
628
|
+
config: {
|
|
629
|
+
parameters: [{ name: "tempVol", value: "/tmp", type: "volume", size: 2 }],
|
|
630
|
+
resources: [],
|
|
631
|
+
},
|
|
632
|
+
});
|
|
633
|
+
const spec = await generateServiceSpec(form);
|
|
634
|
+
expect(spec.config.resources).toContainEqual({ volume: { name: "tempVol" } });
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it("genera spec con marketplaceItem y volatile volumes (llama createVolume)", async () => {
|
|
638
|
+
const { createVolume } = await import("../api/resources-api-service");
|
|
639
|
+
(createVolume as jest.Mock).mockResolvedValue({});
|
|
640
|
+
const form = createMockForm({
|
|
641
|
+
config: {
|
|
642
|
+
parameters: [],
|
|
643
|
+
resources: [{ name: "vol1", type: "volume", size: 3, kind: "volatile" } as any],
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
const mktItem = {
|
|
647
|
+
type: "service",
|
|
648
|
+
domain: "kumori",
|
|
649
|
+
module: "mod",
|
|
650
|
+
version: "1.0.0",
|
|
651
|
+
serviceName: "S",
|
|
652
|
+
deploymentData: { name: "r1" },
|
|
653
|
+
roles: [],
|
|
654
|
+
};
|
|
655
|
+
await generateServiceSpec(form, mktItem as any);
|
|
656
|
+
expect(createVolume).toHaveBeenCalled();
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it("lanza error si createVolume falla con marketplaceItem", async () => {
|
|
660
|
+
const { createVolume } = await import("../api/resources-api-service");
|
|
661
|
+
(createVolume as jest.Mock).mockRejectedValue(new Error("volume-error"));
|
|
662
|
+
const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => {});
|
|
663
|
+
const form = createMockForm({
|
|
664
|
+
config: {
|
|
665
|
+
parameters: [],
|
|
666
|
+
resources: [{ name: "volFail", type: "volume", size: 3, kind: "volatile" } as any],
|
|
667
|
+
},
|
|
668
|
+
});
|
|
669
|
+
const mktItem: any = { type: "service", domain: "k", module: "m", version: "1.0.0", serviceName: "S", deploymentData: { name: "r" }, roles: [] };
|
|
670
|
+
await expect(generateServiceSpec(form, mktItem)).rejects.toThrow("volume-error");
|
|
671
|
+
consoleSpy.mockRestore();
|
|
400
672
|
});
|
|
401
673
|
});
|
|
402
674
|
|
|
403
|
-
|
|
675
|
+
// ── deployServiceHelper ───────────────────────────────────────────────────────
|
|
676
|
+
|
|
677
|
+
describe("deployServiceHelper", () => {
|
|
404
678
|
const mockClick = jest.fn();
|
|
405
|
-
|
|
406
|
-
beforeAll(() => {
|
|
407
|
-
(global as any).URL = {
|
|
408
|
-
createObjectURL: jest.fn(() => "blob:mock-url"),
|
|
409
|
-
};
|
|
410
679
|
|
|
680
|
+
beforeAll(() => {
|
|
681
|
+
(global as any).URL = { createObjectURL: jest.fn(() => "blob:mock-url") };
|
|
411
682
|
(global as any).document = {
|
|
412
|
-
createElement: jest.fn(() => ({
|
|
413
|
-
click: mockClick,
|
|
414
|
-
target: "",
|
|
415
|
-
href: "",
|
|
416
|
-
})),
|
|
683
|
+
createElement: jest.fn(() => ({ click: mockClick, target: "", href: "" })),
|
|
417
684
|
};
|
|
418
685
|
});
|
|
419
686
|
|
|
@@ -422,33 +689,21 @@ describe("deployService", () => {
|
|
|
422
689
|
});
|
|
423
690
|
|
|
424
691
|
const createMockService = (overrides = {}): Service => ({
|
|
425
|
-
id: "
|
|
692
|
+
id: "s1",
|
|
426
693
|
tenant: "tenant1",
|
|
427
694
|
account: "account1",
|
|
428
695
|
environment: "env1",
|
|
429
|
-
name: "
|
|
696
|
+
name: "TestService",
|
|
430
697
|
logo: "",
|
|
431
698
|
description: "",
|
|
432
699
|
revisions: [],
|
|
433
|
-
status: {
|
|
434
|
-
|
|
435
|
-
timestamp: "2024-01-01",
|
|
436
|
-
args: [],
|
|
437
|
-
code: "200",
|
|
438
|
-
},
|
|
439
|
-
role: [{ name: "servicio1", instances: [] }],
|
|
700
|
+
status: { message: "running", timestamp: "2024-01-01", args: [], code: "200" },
|
|
701
|
+
role: [{ name: "s1", instances: [] }],
|
|
440
702
|
links: [],
|
|
441
703
|
resources: [],
|
|
442
704
|
parameters: [],
|
|
443
705
|
usage: {
|
|
444
|
-
current: {
|
|
445
|
-
cpu: 2,
|
|
446
|
-
memory: 512,
|
|
447
|
-
storage: 100,
|
|
448
|
-
volatileStorage: 10,
|
|
449
|
-
nonReplicatedStorage: 10,
|
|
450
|
-
persistentStorage: 20,
|
|
451
|
-
},
|
|
706
|
+
current: { cpu: 2, memory: 512, storage: 100, volatileStorage: 10, nonReplicatedStorage: 10, persistentStorage: 20 },
|
|
452
707
|
limit: {
|
|
453
708
|
cpu: { max: 4, min: 1 },
|
|
454
709
|
memory: { max: 1024, min: 256 },
|
|
@@ -459,14 +714,11 @@ describe("deployService", () => {
|
|
|
459
714
|
},
|
|
460
715
|
cost: 100,
|
|
461
716
|
},
|
|
462
|
-
|
|
463
|
-
maxReplicas: 3,
|
|
464
|
-
lastDeployed: "",
|
|
465
|
-
project: "project1",
|
|
717
|
+
project: "proj1",
|
|
466
718
|
registry: "docker.io",
|
|
467
|
-
imageName: "
|
|
468
|
-
entrypoint: "/
|
|
469
|
-
cmd: "
|
|
719
|
+
imageName: "myImage",
|
|
720
|
+
entrypoint: "/start",
|
|
721
|
+
cmd: "run",
|
|
470
722
|
serverChannels: [],
|
|
471
723
|
clientChannels: [],
|
|
472
724
|
duplexChannels: [],
|
|
@@ -475,24 +727,18 @@ describe("deployService", () => {
|
|
|
475
727
|
});
|
|
476
728
|
|
|
477
729
|
it("retorna FormData con bundle y meta cuando download es false", async () => {
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
const formData = await deployService(service);
|
|
481
|
-
|
|
730
|
+
const formData = await deployService(createMockService({ download: false }));
|
|
482
731
|
expect(formData.get("bundle")).toBeTruthy();
|
|
483
732
|
expect(formData.get("meta")).toBeTruthy();
|
|
733
|
+
expect(formData.get("labels")).toBeTruthy();
|
|
484
734
|
expect(formData.get("comment")).toBe(" ");
|
|
485
|
-
|
|
486
735
|
const meta = JSON.parse(formData.get("meta") as string);
|
|
487
736
|
expect(meta.targetAccount).toBe("account1");
|
|
488
737
|
expect(meta.targetEnvironment).toBe("env1");
|
|
489
738
|
});
|
|
490
739
|
|
|
491
740
|
it("retorna FormData vacío y ejecuta descarga cuando download es true", async () => {
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
const formData = await deployService(service);
|
|
495
|
-
|
|
741
|
+
const formData = await deployService(createMockService({ download: true }));
|
|
496
742
|
expect((global as any).URL.createObjectURL).toHaveBeenCalled();
|
|
497
743
|
expect((global as any).document.createElement).toHaveBeenCalledWith("a");
|
|
498
744
|
expect(mockClick).toHaveBeenCalled();
|
|
@@ -500,19 +746,177 @@ describe("deployService", () => {
|
|
|
500
746
|
});
|
|
501
747
|
|
|
502
748
|
it("maneja deployment con marketplace item", async () => {
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
type: "service" as const,
|
|
749
|
+
const mktItem = {
|
|
750
|
+
type: "service",
|
|
506
751
|
domain: "kumori.systems",
|
|
507
|
-
module: "marketplace/
|
|
752
|
+
module: "marketplace/svc",
|
|
508
753
|
version: "1.0.0",
|
|
509
|
-
serviceName: "
|
|
754
|
+
serviceName: "TestSvc",
|
|
510
755
|
deploymentData: { name: "testRole" },
|
|
511
756
|
roles: ["role1"],
|
|
512
757
|
};
|
|
758
|
+
const formData = await deployService(createMockService(), mktItem as any);
|
|
759
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
it("incluye project en labels cuando está definido", async () => {
|
|
763
|
+
const formData = await deployService(createMockService({ project: "myProject" }));
|
|
764
|
+
const labels = JSON.parse(formData.get("labels") as string);
|
|
765
|
+
expect(labels.project).toBe("myProject");
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
it("maneja servicio con canales HTTPS públicos", async () => {
|
|
769
|
+
const service = createMockService({
|
|
770
|
+
serverChannels: [{ name: "web", protocol: "http", isPublic: true, portNum: 443 }],
|
|
771
|
+
});
|
|
772
|
+
const formData = await deployService(service);
|
|
773
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
it("maneja servicio con canales TCP públicos", async () => {
|
|
777
|
+
const service = createMockService({
|
|
778
|
+
serverChannels: [{ name: "tcp1", protocol: "tcp", isPublic: true, portNum: 9000 }],
|
|
779
|
+
});
|
|
780
|
+
const formData = await deployService(service);
|
|
781
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
it("maneja servicio con recursos secret y volume", async () => {
|
|
785
|
+
const service = createMockService({
|
|
786
|
+
resources: [
|
|
787
|
+
{ name: "sec1", type: "secret", value: "mySecret" },
|
|
788
|
+
{ name: "vol1", type: "volume", value: "myVol" },
|
|
789
|
+
],
|
|
790
|
+
});
|
|
791
|
+
const formData = await deployService(service);
|
|
792
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
it("maneja parámetros de tipo boolean en generateServiceSpecDSL", async () => {
|
|
796
|
+
const service = createMockService({
|
|
797
|
+
parameters: [{ name: "FLAG", value: true, type: "boolean" }],
|
|
798
|
+
});
|
|
799
|
+
const formData = await deployService(service);
|
|
800
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
it("maneja parámetros de tipo bool en generateServiceSpecDSL", async () => {
|
|
804
|
+
const service = createMockService({
|
|
805
|
+
parameters: [{ name: "FLAG2", value: "true", type: "bool" }],
|
|
806
|
+
});
|
|
807
|
+
const formData = await deployService(service);
|
|
808
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
it("maneja parámetros de tipo number en generateServiceSpecDSL", async () => {
|
|
812
|
+
const service = createMockService({
|
|
813
|
+
parameters: [{ name: "COUNT", value: 5, type: "number" }],
|
|
814
|
+
});
|
|
815
|
+
const formData = await deployService(service);
|
|
816
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
it("maneja parámetros de tipo file en generateServiceSpecDSL", async () => {
|
|
820
|
+
const service = createMockService({
|
|
821
|
+
parameters: [{ name: "/etc/config.conf", value: "/path/to/file", type: "file" }],
|
|
822
|
+
});
|
|
823
|
+
const formData = await deployService(service);
|
|
824
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
it("maneja parámetros de tipo string en generateServiceSpecDSL", async () => {
|
|
828
|
+
const service = createMockService({
|
|
829
|
+
parameters: [{ name: "KEY", value: "val", type: "string" }],
|
|
830
|
+
});
|
|
831
|
+
const formData = await deployService(service);
|
|
832
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
it("maneja parámetros de tipo secret en generateServiceSpecDSL", async () => {
|
|
836
|
+
const service = createMockService({
|
|
837
|
+
parameters: [{ name: "SEC", value: "secRef", type: "secret" }],
|
|
838
|
+
});
|
|
839
|
+
const formData = await deployService(service);
|
|
840
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
it("maneja resources de tipo volume sin size en generateServiceSpecDSL", async () => {
|
|
844
|
+
const service = createMockService({
|
|
845
|
+
resources: [{ name: "vol1", type: "volume", value: "existing-vol" }],
|
|
846
|
+
});
|
|
847
|
+
const formData = await deployService(service);
|
|
848
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
it("maneja resources de tipo volume con size persistent en generateServiceSpecDSL", async () => {
|
|
852
|
+
const service = createMockService({
|
|
853
|
+
resources: [{ name: "vol1", type: "volume", maxItems: 10, kind: "persistent" } as any],
|
|
854
|
+
});
|
|
855
|
+
const formData = await deployService(service);
|
|
856
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
857
|
+
});
|
|
513
858
|
|
|
514
|
-
|
|
859
|
+
it("maneja resources de tipo volume con size volatile en generateServiceSpecDSL", async () => {
|
|
860
|
+
const service = createMockService({
|
|
861
|
+
resources: [{ name: "vol2", type: "volume", maxItems: 5, kind: "volatile" } as any],
|
|
862
|
+
});
|
|
863
|
+
const formData = await deployService(service);
|
|
864
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
it("maneja canal HTTPS público sin certificateResource en generateServiceSpecDSL", async () => {
|
|
868
|
+
const service = createMockService({
|
|
869
|
+
serverChannels: [{ name: "web", protocol: "http", isPublic: true, portNum: 443 }],
|
|
870
|
+
});
|
|
871
|
+
const formData = await deployService(service);
|
|
872
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
it("maneja canal HTTPS público con certificateResource en generateServiceSpecDSL", async () => {
|
|
876
|
+
const service = createMockService({
|
|
877
|
+
serverChannels: [{ name: "web", protocol: "http", isPublic: true, certificateResource: "my-cert" }],
|
|
878
|
+
});
|
|
879
|
+
const formData = await deployService(service);
|
|
880
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
881
|
+
});
|
|
515
882
|
|
|
883
|
+
it("maneja canal HTTPS con mTLS en generateServiceSpecDSL", async () => {
|
|
884
|
+
const service = createMockService({
|
|
885
|
+
serverChannels: [{ name: "web", protocol: "http", isPublic: true, withMtls: true, caResource: "my-ca", certificateResource: "my-cert" }],
|
|
886
|
+
});
|
|
887
|
+
const formData = await deployService(service);
|
|
516
888
|
expect(formData.get("bundle")).toBeTruthy();
|
|
517
889
|
});
|
|
518
|
-
|
|
890
|
+
|
|
891
|
+
it("maneja canal TCP público en generateServiceSpecDSL", async () => {
|
|
892
|
+
const service = createMockService({
|
|
893
|
+
serverChannels: [{ name: "tcp1", protocol: "tcp", isPublic: true }],
|
|
894
|
+
});
|
|
895
|
+
const formData = await deployService(service);
|
|
896
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
it("maneja clientChannels en generateServiceSpecDSL", async () => {
|
|
900
|
+
const service = createMockService({
|
|
901
|
+
clientChannels: [{ name: "clientCh" }],
|
|
902
|
+
});
|
|
903
|
+
const formData = await deployService(service);
|
|
904
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
it("maneja marketplace item con package (hasMarketplacePackage)", async () => {
|
|
908
|
+
const mktItem = {
|
|
909
|
+
type: "service",
|
|
910
|
+
domain: "kumori.systems",
|
|
911
|
+
module: "marketplace/svc",
|
|
912
|
+
version: "2.1.0",
|
|
913
|
+
serviceName: "PkgSvc",
|
|
914
|
+
artifact: "my-artifact",
|
|
915
|
+
package: "s3://my-bucket/pkg.zip",
|
|
916
|
+
deploymentData: { name: "pkgRole" },
|
|
917
|
+
roles: ["role1"],
|
|
918
|
+
};
|
|
919
|
+
const formData = await deployService(createMockService(), mktItem as any);
|
|
920
|
+
expect(formData.get("bundle")).toBeTruthy();
|
|
921
|
+
});
|
|
922
|
+
});
|