@platform-mesh/portal-server-lib 0.5.44 → 0.5.45
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/dist/portal-options/auth-config-provider.d.ts +5 -3
- package/dist/portal-options/auth-config-provider.js +28 -10
- package/dist/portal-options/auth-config-provider.js.map +1 -1
- package/dist/portal-options/models/k8s.d.ts +25 -0
- package/dist/portal-options/models/k8s.js +2 -0
- package/dist/portal-options/models/k8s.js.map +1 -0
- package/dist/portal-options/service-providers/kubernetes-service-providers.service.d.ts +1 -1
- package/dist/portal-options/service-providers/kubernetes-service-providers.service.js +15 -32
- package/dist/portal-options/service-providers/kubernetes-service-providers.service.js.map +1 -1
- package/dist/portal-options/services/kcp-k8s.service.d.ts +18 -5
- package/dist/portal-options/services/kcp-k8s.service.js +106 -11
- package/dist/portal-options/services/kcp-k8s.service.js.map +1 -1
- package/package.json +1 -1
- package/src/portal-options/auth-config-provider.spec.ts +480 -58
- package/src/portal-options/auth-config-provider.ts +40 -11
- package/src/portal-options/models/k8s.ts +27 -0
- package/src/portal-options/pm-portal-context.service.spec.ts +4 -0
- package/src/portal-options/service-providers/kubernetes-service-providers.service.spec.ts +500 -203
- package/src/portal-options/service-providers/kubernetes-service-providers.service.ts +30 -40
- package/src/portal-options/services/kcp-k8s.service.spec.ts +502 -89
- package/src/portal-options/services/kcp-k8s.service.ts +147 -13
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { K8sRequestContext, K8sResourceDescriptor } from '../models/k8s.js';
|
|
1
2
|
import { KcpKubernetesService } from '../services/kcp-k8s.service.js';
|
|
2
3
|
import { KubernetesServiceProvidersService } from './kubernetes-service-providers.service.js';
|
|
3
4
|
import { welcomeNodeConfig } from './models/welcome-node-config.js';
|
|
5
|
+
import { Test, TestingModule } from '@nestjs/testing';
|
|
6
|
+
import { ContentConfiguration } from '@openmfp/portal-server-lib';
|
|
4
7
|
import { mock } from 'jest-mock-extended';
|
|
5
8
|
|
|
6
9
|
const listClusterCustomObject = jest.fn();
|
|
@@ -25,194 +28,482 @@ jest.mock('@kubernetes/client-node', () => {
|
|
|
25
28
|
});
|
|
26
29
|
|
|
27
30
|
jest.mock('@kubernetes/client-node/dist/gen/middleware.js', () => ({
|
|
28
|
-
PromiseMiddlewareWrapper: class {
|
|
29
|
-
pre?: (ctx: any) => Promise<any> | any;
|
|
30
|
-
post?: (ctx: any) => Promise<any> | any;
|
|
31
|
-
constructor(opts: any) {
|
|
32
|
-
this.pre = opts.pre;
|
|
33
|
-
this.post = opts.post;
|
|
34
|
-
}
|
|
35
|
-
},
|
|
31
|
+
PromiseMiddlewareWrapper: class {},
|
|
36
32
|
}));
|
|
37
33
|
|
|
38
34
|
describe('KubernetesServiceProvidersService', () => {
|
|
35
|
+
let service: KubernetesServiceProvidersService;
|
|
39
36
|
let kcpKubernetesServiceMock: jest.Mocked<KcpKubernetesService>;
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
const mockToken = 'test-token-123';
|
|
39
|
+
const mockEntities = ['test-entity'];
|
|
40
|
+
const mockContext: K8sRequestContext = {
|
|
41
|
+
organization: 'test-org',
|
|
42
|
+
isSubDomain: true,
|
|
43
|
+
} as K8sRequestContext;
|
|
44
|
+
|
|
45
|
+
beforeEach(async () => {
|
|
46
|
+
kcpKubernetesServiceMock = mock<KcpKubernetesService>();
|
|
47
|
+
|
|
48
|
+
const module: TestingModule = await Test.createTestingModule({
|
|
49
|
+
providers: [
|
|
50
|
+
KubernetesServiceProvidersService,
|
|
51
|
+
{
|
|
52
|
+
provide: KcpKubernetesService,
|
|
53
|
+
useValue: kcpKubernetesServiceMock,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
}).compile();
|
|
57
|
+
|
|
58
|
+
service = module.get<KubernetesServiceProvidersService>(
|
|
59
|
+
KubernetesServiceProvidersService,
|
|
46
60
|
);
|
|
47
|
-
kcpKubernetesServiceMock.getKcpK8sApiClient.mockReturnValue({
|
|
48
|
-
listClusterCustomObject,
|
|
49
|
-
} as any);
|
|
50
61
|
});
|
|
51
62
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
).rejects.toThrow('Token is required');
|
|
59
|
-
});
|
|
63
|
+
describe('getServiceProviders', () => {
|
|
64
|
+
it('should throw error when token is missing', async () => {
|
|
65
|
+
await expect(
|
|
66
|
+
service.getServiceProviders('', mockEntities, mockContext),
|
|
67
|
+
).rejects.toThrow('Token is required');
|
|
68
|
+
});
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
organization: undefined,
|
|
67
|
-
isSubDomain: true,
|
|
68
|
-
}),
|
|
69
|
-
).rejects.toThrow('Context with organization is required');
|
|
70
|
-
});
|
|
70
|
+
it('should throw error when token is null', async () => {
|
|
71
|
+
await expect(
|
|
72
|
+
service.getServiceProviders(null as any, mockEntities, mockContext),
|
|
73
|
+
).rejects.toThrow('Token is required');
|
|
74
|
+
});
|
|
71
75
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
it('should return welcome node config when not subdomain', async () => {
|
|
77
|
+
const context = { ...mockContext, isSubDomain: false };
|
|
78
|
+
|
|
79
|
+
const result = await service.getServiceProviders(
|
|
80
|
+
mockToken,
|
|
81
|
+
mockEntities,
|
|
82
|
+
context,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
expect(result).toEqual(welcomeNodeConfig);
|
|
86
|
+
expect(
|
|
87
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace,
|
|
88
|
+
).not.toHaveBeenCalled();
|
|
77
89
|
});
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
it('should throw error when organization is null', async () => {
|
|
92
|
+
const context = { isSubDomain: true, organization: null } as any;
|
|
81
93
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
},
|
|
87
|
-
);
|
|
94
|
+
await expect(
|
|
95
|
+
service.getServiceProviders(mockToken, mockEntities, context),
|
|
96
|
+
).rejects.toThrow('Context with organization is required');
|
|
97
|
+
});
|
|
88
98
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
it('should return empty array when no items in response', async () => {
|
|
100
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
101
|
+
{
|
|
102
|
+
items: null,
|
|
103
|
+
} as any,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const result = await service.getServiceProviders(
|
|
107
|
+
mockToken,
|
|
108
|
+
mockEntities,
|
|
109
|
+
mockContext,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
expect(result).toEqual({ rawServiceProviders: [] });
|
|
93
113
|
});
|
|
94
|
-
expect(res.rawServiceProviders).toEqual([]);
|
|
95
|
-
});
|
|
96
114
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
it('should return empty array when items is undefined', async () => {
|
|
116
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
117
|
+
{} as any,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const result = await service.getServiceProviders(
|
|
121
|
+
mockToken,
|
|
122
|
+
mockEntities,
|
|
123
|
+
mockContext,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
expect(result).toEqual({ rawServiceProviders: [] });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should parse and return content configurations', async () => {
|
|
130
|
+
const mockContentConfig: ContentConfiguration = {
|
|
131
|
+
url: 'https://test.com/config',
|
|
132
|
+
name: 'Test Config',
|
|
133
|
+
} as ContentConfiguration;
|
|
134
|
+
|
|
135
|
+
const mockResponse = {
|
|
114
136
|
items: [
|
|
115
137
|
{
|
|
116
|
-
status: {
|
|
138
|
+
status: {
|
|
139
|
+
configurationResult: JSON.stringify(mockContentConfig),
|
|
140
|
+
},
|
|
117
141
|
spec: {
|
|
118
|
-
remoteConfiguration: {
|
|
142
|
+
remoteConfiguration: {
|
|
143
|
+
url: 'https://fallback.com',
|
|
144
|
+
},
|
|
119
145
|
},
|
|
120
146
|
},
|
|
121
147
|
],
|
|
122
148
|
};
|
|
149
|
+
|
|
150
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
151
|
+
mockResponse as any,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const result = await service.getServiceProviders(
|
|
155
|
+
mockToken,
|
|
156
|
+
mockEntities,
|
|
157
|
+
mockContext,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
expect(result.rawServiceProviders).toHaveLength(1);
|
|
161
|
+
expect(result.rawServiceProviders[0].name).toBe('platform-mesh-system');
|
|
162
|
+
expect(result.rawServiceProviders[0].contentConfiguration).toHaveLength(
|
|
163
|
+
1,
|
|
164
|
+
);
|
|
165
|
+
expect(result.rawServiceProviders[0].contentConfiguration[0].url).toBe(
|
|
166
|
+
'https://test.com/config',
|
|
167
|
+
);
|
|
123
168
|
});
|
|
124
|
-
kcpKubernetesServiceMock.getKcpVirtualWorkspaceUrl.mockReturnValue(
|
|
125
|
-
new URL(
|
|
126
|
-
'https://k8s.example.com/services/contentconfigurations/clusters/root:orgs:acme:a1',
|
|
127
|
-
),
|
|
128
|
-
);
|
|
129
169
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
170
|
+
it('should use fallback url when content configuration has no url', async () => {
|
|
171
|
+
const mockContentConfig: ContentConfiguration = {
|
|
172
|
+
name: 'Test Config',
|
|
173
|
+
} as ContentConfiguration;
|
|
174
|
+
|
|
175
|
+
const mockResponse = {
|
|
176
|
+
items: [
|
|
177
|
+
{
|
|
178
|
+
status: {
|
|
179
|
+
configurationResult: JSON.stringify(mockContentConfig),
|
|
180
|
+
},
|
|
181
|
+
spec: {
|
|
182
|
+
remoteConfiguration: {
|
|
183
|
+
url: 'https://fallback.com',
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
191
|
+
mockResponse as any,
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const result = await service.getServiceProviders(
|
|
195
|
+
mockToken,
|
|
196
|
+
mockEntities,
|
|
197
|
+
mockContext,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
expect(result.rawServiceProviders[0].contentConfiguration[0].url).toBe(
|
|
201
|
+
'https://fallback.com',
|
|
202
|
+
);
|
|
135
203
|
});
|
|
136
204
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
205
|
+
it('should filter out items without configurationResult', async () => {
|
|
206
|
+
const mockContentConfig: ContentConfiguration = {
|
|
207
|
+
url: 'https://test.com/config',
|
|
208
|
+
} as ContentConfiguration;
|
|
141
209
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
210
|
+
const mockResponse = {
|
|
211
|
+
items: [
|
|
212
|
+
{
|
|
213
|
+
status: {
|
|
214
|
+
configurationResult: JSON.stringify(mockContentConfig),
|
|
215
|
+
},
|
|
216
|
+
spec: {},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
status: {
|
|
220
|
+
configurationResult: null,
|
|
221
|
+
},
|
|
222
|
+
spec: {},
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
status: {},
|
|
226
|
+
spec: {},
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
};
|
|
150
230
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
231
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
232
|
+
mockResponse as any,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const result = await service.getServiceProviders(
|
|
236
|
+
mockToken,
|
|
237
|
+
mockEntities,
|
|
238
|
+
mockContext,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
expect(result.rawServiceProviders[0].contentConfiguration).toHaveLength(
|
|
242
|
+
1,
|
|
243
|
+
);
|
|
163
244
|
});
|
|
164
245
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
246
|
+
it('should handle multiple content configurations', async () => {
|
|
247
|
+
const mockConfig1: ContentConfiguration = {
|
|
248
|
+
url: 'https://test1.com',
|
|
249
|
+
} as ContentConfiguration;
|
|
250
|
+
const mockConfig2: ContentConfiguration = {
|
|
251
|
+
url: 'https://test2.com',
|
|
252
|
+
} as ContentConfiguration;
|
|
171
253
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
254
|
+
const mockResponse = {
|
|
255
|
+
items: [
|
|
256
|
+
{
|
|
257
|
+
status: {
|
|
258
|
+
configurationResult: JSON.stringify(mockConfig1),
|
|
259
|
+
},
|
|
260
|
+
spec: {},
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
status: {
|
|
264
|
+
configurationResult: JSON.stringify(mockConfig2),
|
|
265
|
+
},
|
|
266
|
+
spec: {},
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
272
|
+
mockResponse as any,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
const result = await service.getServiceProviders(
|
|
276
|
+
mockToken,
|
|
277
|
+
mockEntities,
|
|
278
|
+
mockContext,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
expect(result.rawServiceProviders[0].contentConfiguration).toHaveLength(
|
|
282
|
+
2,
|
|
283
|
+
);
|
|
284
|
+
expect(result.rawServiceProviders[0].contentConfiguration[0].url).toBe(
|
|
285
|
+
'https://test1.com',
|
|
286
|
+
);
|
|
287
|
+
expect(result.rawServiceProviders[0].contentConfiguration[1].url).toBe(
|
|
288
|
+
'https://test2.com',
|
|
289
|
+
);
|
|
176
290
|
});
|
|
177
291
|
|
|
178
|
-
|
|
292
|
+
it('should use main entity when entities array is empty', async () => {
|
|
293
|
+
const mockResponse = { items: [] };
|
|
294
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
295
|
+
mockResponse as any,
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
await service.getServiceProviders(mockToken, [], mockContext);
|
|
299
|
+
|
|
300
|
+
expect(
|
|
301
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace,
|
|
302
|
+
).toHaveBeenCalledWith(
|
|
303
|
+
expect.objectContaining({
|
|
304
|
+
labelSelector: 'ui.platform-mesh.io/entity=main',
|
|
305
|
+
}),
|
|
306
|
+
mockContext,
|
|
307
|
+
mockToken,
|
|
308
|
+
);
|
|
309
|
+
});
|
|
179
310
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
311
|
+
it('should use main entity when entities is null', async () => {
|
|
312
|
+
const mockResponse = { items: [] };
|
|
313
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
314
|
+
mockResponse as any,
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
await service.getServiceProviders(mockToken, null as any, mockContext);
|
|
318
|
+
|
|
319
|
+
expect(
|
|
320
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace,
|
|
321
|
+
).toHaveBeenCalledWith(
|
|
322
|
+
expect.objectContaining({
|
|
323
|
+
labelSelector: 'ui.platform-mesh.io/entity=main',
|
|
324
|
+
}),
|
|
325
|
+
mockContext,
|
|
326
|
+
mockToken,
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should use first entity from array', async () => {
|
|
331
|
+
const mockResponse = { items: [] };
|
|
332
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
333
|
+
mockResponse as any,
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
await service.getServiceProviders(
|
|
337
|
+
mockToken,
|
|
338
|
+
['entity1', 'entity2'],
|
|
339
|
+
mockContext,
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
expect(
|
|
343
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace,
|
|
344
|
+
).toHaveBeenCalledWith(
|
|
345
|
+
expect.objectContaining({
|
|
346
|
+
labelSelector: 'ui.platform-mesh.io/entity=entity1',
|
|
347
|
+
}),
|
|
348
|
+
mockContext,
|
|
349
|
+
mockToken,
|
|
350
|
+
);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('should call kubernetes service with correct GVR', async () => {
|
|
354
|
+
const mockResponse = { items: [] };
|
|
355
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
356
|
+
mockResponse as any,
|
|
357
|
+
);
|
|
192
358
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
359
|
+
await service.getServiceProviders(mockToken, mockEntities, mockContext);
|
|
360
|
+
|
|
361
|
+
const expectedGvr: K8sResourceDescriptor = {
|
|
362
|
+
group: 'ui.platform-mesh.io',
|
|
363
|
+
version: 'v1alpha1',
|
|
364
|
+
plural: 'contentconfigurations',
|
|
365
|
+
labelSelector: 'ui.platform-mesh.io/entity=test-entity',
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
expect(
|
|
369
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace,
|
|
370
|
+
).toHaveBeenCalledWith(expectedGvr, mockContext, mockToken);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('should retry once on 429 error', async () => {
|
|
374
|
+
const error = { code: 429 };
|
|
375
|
+
const mockResponse = { items: [] };
|
|
376
|
+
|
|
377
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace
|
|
378
|
+
.mockRejectedValueOnce(error)
|
|
379
|
+
.mockResolvedValueOnce(mockResponse as any);
|
|
380
|
+
|
|
381
|
+
jest.spyOn(console, 'error').mockImplementation();
|
|
382
|
+
jest.spyOn(console, 'log').mockImplementation();
|
|
383
|
+
jest.spyOn(global, 'setTimeout');
|
|
384
|
+
|
|
385
|
+
await service.getServiceProviders(mockToken, mockEntities, mockContext);
|
|
386
|
+
|
|
387
|
+
expect(
|
|
388
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace,
|
|
389
|
+
).toHaveBeenCalledTimes(2);
|
|
390
|
+
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 1000);
|
|
391
|
+
|
|
392
|
+
jest.restoreAllMocks();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('should retry once on statusCode 429 error', async () => {
|
|
396
|
+
const error = { statusCode: 429 };
|
|
397
|
+
const mockResponse = { items: [] };
|
|
398
|
+
|
|
399
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace
|
|
400
|
+
.mockRejectedValueOnce(error)
|
|
401
|
+
.mockResolvedValueOnce(mockResponse as any);
|
|
402
|
+
|
|
403
|
+
jest.spyOn(console, 'error').mockImplementation();
|
|
404
|
+
jest.spyOn(console, 'log').mockImplementation();
|
|
405
|
+
|
|
406
|
+
await service.getServiceProviders(mockToken, mockEntities, mockContext);
|
|
407
|
+
|
|
408
|
+
expect(
|
|
409
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace,
|
|
410
|
+
).toHaveBeenCalledTimes(2);
|
|
411
|
+
|
|
412
|
+
jest.restoreAllMocks();
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('should log error on kubernetes service failure', async () => {
|
|
416
|
+
const error = new Error('Kubernetes error');
|
|
417
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockRejectedValue(
|
|
418
|
+
error,
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
422
|
+
|
|
423
|
+
await service.getServiceProviders(mockToken, mockEntities, mockContext);
|
|
424
|
+
|
|
425
|
+
expect(consoleSpy).toHaveBeenCalledWith(error);
|
|
426
|
+
|
|
427
|
+
consoleSpy.mockRestore();
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('should not retry on non-429 errors', async () => {
|
|
431
|
+
const error = { code: 500 };
|
|
432
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockRejectedValue(
|
|
433
|
+
error,
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
jest.spyOn(console, 'error').mockImplementation();
|
|
437
|
+
|
|
438
|
+
await service.getServiceProviders(mockToken, mockEntities, mockContext);
|
|
439
|
+
|
|
440
|
+
expect(
|
|
441
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace,
|
|
442
|
+
).toHaveBeenCalledTimes(1);
|
|
443
|
+
|
|
444
|
+
jest.restoreAllMocks();
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('should return result after successful retry', async () => {
|
|
448
|
+
const error = { code: 429 };
|
|
449
|
+
const mockContentConfig: ContentConfiguration = {
|
|
450
|
+
url: 'https://test.com',
|
|
451
|
+
} as ContentConfiguration;
|
|
452
|
+
const mockResponse = {
|
|
453
|
+
items: [
|
|
454
|
+
{
|
|
455
|
+
status: {
|
|
456
|
+
configurationResult: JSON.stringify(mockContentConfig),
|
|
457
|
+
},
|
|
458
|
+
spec: {},
|
|
459
|
+
},
|
|
460
|
+
],
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace
|
|
464
|
+
.mockRejectedValueOnce(error)
|
|
465
|
+
.mockResolvedValueOnce(mockResponse as any);
|
|
466
|
+
|
|
467
|
+
jest.spyOn(console, 'error').mockImplementation();
|
|
468
|
+
jest.spyOn(console, 'log').mockImplementation();
|
|
469
|
+
|
|
470
|
+
const result = await service.getServiceProviders(
|
|
471
|
+
mockToken,
|
|
472
|
+
mockEntities,
|
|
473
|
+
mockContext,
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
expect(result.rawServiceProviders[0].contentConfiguration).toHaveLength(
|
|
477
|
+
1,
|
|
478
|
+
);
|
|
479
|
+
expect(result.rawServiceProviders[0].contentConfiguration[0].url).toBe(
|
|
480
|
+
'https://test.com',
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
jest.restoreAllMocks();
|
|
484
|
+
});
|
|
196
485
|
});
|
|
197
486
|
|
|
198
487
|
it('should apply processContentConfigurationForAccountHierarchy when accountPath is provided', async () => {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
488
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
489
|
+
{
|
|
490
|
+
items: [
|
|
491
|
+
{
|
|
492
|
+
status: {
|
|
493
|
+
configurationResult: JSON.stringify({
|
|
494
|
+
name: 'test-config',
|
|
495
|
+
luigiConfigFragment: {
|
|
496
|
+
data: {
|
|
497
|
+
nodes: [{ entityType: 'core_platform-mesh_io_account' }],
|
|
498
|
+
},
|
|
208
499
|
},
|
|
209
|
-
},
|
|
210
|
-
}
|
|
500
|
+
}),
|
|
501
|
+
},
|
|
502
|
+
spec: { remoteConfiguration: { url: 'http://example.com' } },
|
|
211
503
|
},
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
});
|
|
504
|
+
],
|
|
505
|
+
},
|
|
506
|
+
);
|
|
216
507
|
|
|
217
508
|
const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock);
|
|
218
509
|
const res = await svc.getServiceProviders('token', ['main'], {
|
|
@@ -228,23 +519,25 @@ describe('KubernetesServiceProvidersService', () => {
|
|
|
228
519
|
});
|
|
229
520
|
|
|
230
521
|
it('should apply processContentConfigurationForAccountHierarchy with multi-level accountPath', async () => {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
522
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
523
|
+
{
|
|
524
|
+
items: [
|
|
525
|
+
{
|
|
526
|
+
status: {
|
|
527
|
+
configurationResult: JSON.stringify({
|
|
528
|
+
name: 'test-config',
|
|
529
|
+
luigiConfigFragment: {
|
|
530
|
+
data: {
|
|
531
|
+
nodes: [{ entityType: 'core_platform-mesh_io_account' }],
|
|
532
|
+
},
|
|
240
533
|
},
|
|
241
|
-
},
|
|
242
|
-
}
|
|
534
|
+
}),
|
|
535
|
+
},
|
|
536
|
+
spec: { remoteConfiguration: { url: 'http://example.com' } },
|
|
243
537
|
},
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
});
|
|
538
|
+
],
|
|
539
|
+
},
|
|
540
|
+
);
|
|
248
541
|
|
|
249
542
|
const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock);
|
|
250
543
|
const res = await svc.getServiceProviders('token', ['main'], {
|
|
@@ -262,34 +555,36 @@ describe('KubernetesServiceProvidersService', () => {
|
|
|
262
555
|
});
|
|
263
556
|
|
|
264
557
|
it('should update account children nodes for accounts configuration with accountPath', async () => {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
558
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
559
|
+
{
|
|
560
|
+
items: [
|
|
561
|
+
{
|
|
562
|
+
status: {
|
|
563
|
+
configurationResult: JSON.stringify({
|
|
564
|
+
name: 'accounts',
|
|
565
|
+
luigiConfigFragment: {
|
|
566
|
+
data: {
|
|
567
|
+
nodes: [
|
|
568
|
+
{
|
|
569
|
+
entityType: 'core_platform-mesh_io_account',
|
|
570
|
+
children: [
|
|
571
|
+
{
|
|
572
|
+
defineEntity: { id: 'old-id' },
|
|
573
|
+
context: {},
|
|
574
|
+
pathSegment: 'old-path',
|
|
575
|
+
},
|
|
576
|
+
],
|
|
577
|
+
},
|
|
578
|
+
],
|
|
579
|
+
},
|
|
285
580
|
},
|
|
286
|
-
},
|
|
287
|
-
}
|
|
581
|
+
}),
|
|
582
|
+
},
|
|
583
|
+
spec: { remoteConfiguration: { url: 'http://example.com' } },
|
|
288
584
|
},
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
});
|
|
585
|
+
],
|
|
586
|
+
},
|
|
587
|
+
);
|
|
293
588
|
|
|
294
589
|
const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock);
|
|
295
590
|
const res = await svc.getServiceProviders('token', ['main'], {
|
|
@@ -307,23 +602,25 @@ describe('KubernetesServiceProvidersService', () => {
|
|
|
307
602
|
});
|
|
308
603
|
|
|
309
604
|
it('should not apply processContentConfigurationForAccountHierarchy when accountPath is not provided', async () => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
605
|
+
kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
|
|
606
|
+
{
|
|
607
|
+
items: [
|
|
608
|
+
{
|
|
609
|
+
status: {
|
|
610
|
+
configurationResult: JSON.stringify({
|
|
611
|
+
name: 'test-config',
|
|
612
|
+
luigiConfigFragment: {
|
|
613
|
+
data: {
|
|
614
|
+
nodes: [{ entityType: 'core_platform-mesh_io_account' }],
|
|
615
|
+
},
|
|
319
616
|
},
|
|
320
|
-
},
|
|
321
|
-
}
|
|
617
|
+
}),
|
|
618
|
+
},
|
|
619
|
+
spec: { remoteConfiguration: { url: 'http://example.com' } },
|
|
322
620
|
},
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
});
|
|
621
|
+
],
|
|
622
|
+
},
|
|
623
|
+
);
|
|
327
624
|
|
|
328
625
|
const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock);
|
|
329
626
|
const res = await svc.getServiceProviders('token', ['main'], {
|