Skip to content

Commit d4c0fd7

Browse files
committed
tests
1 parent 3470cb2 commit d4c0fd7

2 files changed

Lines changed: 284 additions & 0 deletions

File tree

src/openshift/test/actions/DeploymentLabelAction/DeploymentLabelActionModal.test.tsx

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,140 @@ describe('DeploymentLabelActionModal', () => {
220220
expect(screen.getByText('DEPLOYMENT_ACTION_QUICK_REGISTER')).toBeInTheDocument();
221221
});
222222
});
223+
224+
it('should include callback port label when set in wizard', async () => {
225+
renderModal(mockDeploymentWithoutLabels);
226+
227+
const select = screen.getByLabelText('Cryostat Instance Selection');
228+
await userEvent.selectOptions(select, '0');
229+
await waitFor(() => {
230+
expect(select).toHaveValue('0');
231+
});
232+
233+
// Navigate through wizard steps to Agent Configuration
234+
let nextButton = screen.queryByRole('button', { name: /next/i });
235+
while (nextButton) {
236+
await userEvent.click(nextButton);
237+
// Wait a bit for the step to change
238+
await new Promise((resolve) => setTimeout(resolve, 100));
239+
240+
// Check if we're on the Agent Configuration step by looking for the callback port field
241+
const callbackPortInput = screen.queryByRole('spinbutton', { name: '' });
242+
if (callbackPortInput) {
243+
// Found the callback port field, set its value
244+
await userEvent.clear(callbackPortInput);
245+
await userEvent.type(callbackPortInput, '8080');
246+
break;
247+
}
248+
249+
nextButton = screen.queryByRole('button', { name: /next/i });
250+
}
251+
252+
// Continue to finish
253+
nextButton = screen.queryByRole('button', { name: /next/i });
254+
while (nextButton) {
255+
await userEvent.click(nextButton);
256+
nextButton = screen.queryByRole('button', { name: /next/i });
257+
}
258+
259+
const finishButton = screen.queryByRole('button', { name: /finish|submit/i });
260+
if (finishButton) {
261+
await userEvent.click(finishButton);
262+
expect(k8sPatchResource).toHaveBeenCalled();
263+
264+
const callArgs = (k8sPatchResource as jest.Mock).mock.calls[0][0];
265+
const patches = callArgs.patches;
266+
267+
// Verify callback port patch is included
268+
expect(patches).toEqual(
269+
expect.arrayContaining([
270+
{
271+
op: 'replace',
272+
path: '/spec/template/metadata/labels/cryostat.io~1callback-port',
273+
value: '8080',
274+
},
275+
]),
276+
);
277+
}
278+
});
279+
280+
it('should NOT include callback port label in Quick Register', async () => {
281+
renderModal(mockDeploymentWithoutLabels);
282+
283+
const select = screen.getByLabelText('Cryostat Instance Selection');
284+
await userEvent.selectOptions(select, '0');
285+
await waitFor(() => {
286+
expect(select).toHaveValue('0');
287+
});
288+
289+
// Click Quick Register button
290+
const quickRegisterButton = screen.getByText('DEPLOYMENT_ACTION_QUICK_REGISTER');
291+
await userEvent.click(quickRegisterButton);
292+
293+
await waitFor(() => {
294+
expect(k8sPatchResource).toHaveBeenCalled();
295+
});
296+
297+
const callArgs = (k8sPatchResource as jest.Mock).mock.calls[0][0];
298+
const patches = callArgs.patches;
299+
300+
// Verify callback port patch is NOT included
301+
const hasCallbackPortPatch = patches.some((p) => p.path?.includes('callback-port'));
302+
expect(hasCallbackPortPatch).toBe(false);
303+
});
304+
305+
it('should remove callback port label when deployment has it and user selects empty option', async () => {
306+
const deploymentWithCallbackPort = {
307+
...mockDeploymentWithLabels,
308+
spec: {
309+
...mockDeploymentWithLabels.spec,
310+
template: {
311+
...mockDeploymentWithLabels.spec?.template,
312+
metadata: {
313+
...mockDeploymentWithLabels.spec?.template?.metadata,
314+
labels: {
315+
...mockDeploymentWithLabels.spec?.template?.metadata?.labels,
316+
'cryostat.io/callback-port': '8080',
317+
},
318+
},
319+
},
320+
},
321+
};
322+
323+
renderModal(deploymentWithCallbackPort);
324+
325+
const selectElement = screen.getByLabelText('Cryostat Instance Selection');
326+
expect(selectElement).toHaveValue('0');
327+
328+
await userEvent.selectOptions(selectElement, '-1');
329+
await waitFor(() => {
330+
expect(selectElement).toHaveValue('-1');
331+
});
332+
333+
const nextButtons = screen.queryAllByRole('button', { name: /next/i });
334+
if (nextButtons.length > 0) {
335+
for (const button of nextButtons) {
336+
await userEvent.click(button);
337+
}
338+
}
339+
340+
const finishButton = screen.queryByRole('button', { name: /finish|submit/i });
341+
if (finishButton) {
342+
await userEvent.click(finishButton);
343+
expect(k8sPatchResource).toHaveBeenCalled();
344+
345+
const callArgs = (k8sPatchResource as jest.Mock).mock.calls[0][0];
346+
const patches = callArgs.patches;
347+
348+
// Verify callback port removal patch is included
349+
expect(patches).toEqual(
350+
expect.arrayContaining([
351+
{
352+
op: 'remove',
353+
path: '/spec/template/metadata/labels/cryostat.io~1callback-port',
354+
},
355+
]),
356+
);
357+
}
358+
});
223359
});

src/openshift/test/actions/DeploymentLabelAction/utils.test.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import {
1919
LOG_LEVELS,
2020
getAgentConfig,
2121
formatAgentConfig,
22+
formatDurationForLabel,
23+
formatByteSizeForLabel,
24+
parseDuration,
2225
} from '@console-plugin/actions/DeploymentLabelAction/utils';
2326

2427
describe('envVarUtils', () => {
@@ -239,4 +242,149 @@ describe('envVarUtils', () => {
239242
expect(result).toBe('None');
240243
});
241244
});
245+
246+
describe('parseDuration', () => {
247+
it('should parse duration with hours unit', () => {
248+
expect(parseDuration('2h', 0)).toBe(2 * 60 * 60 * 1000);
249+
});
250+
251+
it('should parse duration with minutes unit', () => {
252+
expect(parseDuration('15m', 0)).toBe(15 * 60 * 1000);
253+
});
254+
255+
it('should parse duration with seconds unit', () => {
256+
expect(parseDuration('30s', 0)).toBe(30 * 1000);
257+
});
258+
259+
it('should parse duration with milliseconds unit', () => {
260+
expect(parseDuration('500ms', 0)).toBe(500);
261+
});
262+
263+
it('should parse duration without unit as milliseconds', () => {
264+
expect(parseDuration('1000', 0)).toBe(1000);
265+
});
266+
267+
it('should return default value for undefined duration', () => {
268+
expect(parseDuration(undefined, 12345)).toBe(12345);
269+
});
270+
271+
it('should return default value for invalid duration format', () => {
272+
expect(parseDuration('invalid', 12345)).toBe(12345);
273+
});
274+
275+
it('should return default value for empty string', () => {
276+
expect(parseDuration('', 12345)).toBe(12345);
277+
});
278+
});
279+
280+
describe('formatDurationForLabel', () => {
281+
it('should format duration in hours when value divides evenly', () => {
282+
expect(formatDurationForLabel(3600000)).toBe('1h');
283+
expect(formatDurationForLabel(7200000)).toBe('2h');
284+
expect(formatDurationForLabel(10800000)).toBe('3h');
285+
});
286+
287+
it('should format duration in minutes when value divides evenly', () => {
288+
expect(formatDurationForLabel(60000)).toBe('1m');
289+
expect(formatDurationForLabel(900000)).toBe('15m');
290+
expect(formatDurationForLabel(1800000)).toBe('30m');
291+
});
292+
293+
it('should format duration in seconds when value divides evenly', () => {
294+
expect(formatDurationForLabel(1000)).toBe('1s');
295+
expect(formatDurationForLabel(5000)).toBe('5s');
296+
expect(formatDurationForLabel(30000)).toBe('30s');
297+
});
298+
299+
it('should format duration in milliseconds when value does not divide evenly', () => {
300+
expect(formatDurationForLabel(1500)).toBe('1500ms');
301+
expect(formatDurationForLabel(999)).toBe('999ms');
302+
expect(formatDurationForLabel(12345)).toBe('12345ms');
303+
});
304+
305+
it('should prefer larger units over smaller units', () => {
306+
// 1 hour should be formatted as 1h, not 60m
307+
expect(formatDurationForLabel(3600000)).toBe('1h');
308+
// 1 minute should be formatted as 1m, not 60s
309+
expect(formatDurationForLabel(60000)).toBe('1m');
310+
});
311+
312+
it('should handle zero duration', () => {
313+
expect(formatDurationForLabel(0)).toBe('0ms');
314+
});
315+
316+
it('should handle very large durations', () => {
317+
expect(formatDurationForLabel(86400000)).toBe('24h'); // 24 hours
318+
});
319+
320+
it('should format default harvester period correctly', () => {
321+
expect(formatDurationForLabel(900000)).toBe('15m');
322+
});
323+
324+
it('should format default harvester exit max age correctly', () => {
325+
expect(formatDurationForLabel(300000)).toBe('5m');
326+
});
327+
});
328+
329+
describe('formatByteSizeForLabel', () => {
330+
it('should format size in Gi when value divides evenly', () => {
331+
expect(formatByteSizeForLabel(1073741824)).toBe('1Gi');
332+
expect(formatByteSizeForLabel(2147483648)).toBe('2Gi');
333+
expect(formatByteSizeForLabel(5368709120)).toBe('5Gi');
334+
});
335+
336+
it('should format size in Mi when value divides evenly', () => {
337+
expect(formatByteSizeForLabel(1048576)).toBe('1Mi');
338+
expect(formatByteSizeForLabel(20971520)).toBe('20Mi');
339+
expect(formatByteSizeForLabel(52428800)).toBe('50Mi');
340+
});
341+
342+
it('should format size in Ki when value divides evenly', () => {
343+
expect(formatByteSizeForLabel(1024)).toBe('1Ki');
344+
expect(formatByteSizeForLabel(2048)).toBe('2Ki');
345+
expect(formatByteSizeForLabel(10240)).toBe('10Ki');
346+
});
347+
348+
it('should format size in bytes when value does not divide evenly', () => {
349+
expect(formatByteSizeForLabel(500)).toBe('500');
350+
expect(formatByteSizeForLabel(1023)).toBe('1023');
351+
expect(formatByteSizeForLabel(1025)).toBe('1025');
352+
});
353+
354+
it('should prefer larger units over smaller units', () => {
355+
// 1 GiB should be formatted as 1Gi, not 1024Mi
356+
expect(formatByteSizeForLabel(1073741824)).toBe('1Gi');
357+
// 1 MiB should be formatted as 1Mi, not 1024Ki
358+
expect(formatByteSizeForLabel(1048576)).toBe('1Mi');
359+
});
360+
361+
it('should handle zero size', () => {
362+
expect(formatByteSizeForLabel(0)).toBe('0');
363+
});
364+
365+
it('should handle very large sizes', () => {
366+
expect(formatByteSizeForLabel(10737418240)).toBe('10Gi'); // 10 GiB
367+
});
368+
369+
it('should format default harvester exit max size correctly', () => {
370+
expect(formatByteSizeForLabel(20971520)).toBe('20Mi');
371+
});
372+
373+
it('should use Kubernetes resource format (no B suffix)', () => {
374+
// Verify the format matches k8s resource quantities
375+
expect(formatByteSizeForLabel(1073741824)).not.toContain('B');
376+
expect(formatByteSizeForLabel(1048576)).not.toContain('B');
377+
expect(formatByteSizeForLabel(1024)).not.toContain('B');
378+
});
379+
380+
it('should handle fractional MiB that do not divide evenly', () => {
381+
// 1.5 MiB in bytes (1572864) should not format as Mi
382+
expect(formatByteSizeForLabel(1572864)).toBe('1536Ki');
383+
});
384+
385+
it('should handle fractional GiB that do not divide evenly', () => {
386+
// 1.5 GiB in bytes (1610612736) should not format as Gi
387+
expect(formatByteSizeForLabel(1610612736)).toBe('1536Mi');
388+
});
389+
});
242390
});

0 commit comments

Comments
 (0)