Files
wild-web-app/src/components/nodes/NodeForm.unit.test.tsx

393 lines
14 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import type { NodeFormData } from './NodeForm';
import { createMockNode, createMockNodes, createMockHardwareInfo } from '../../test/utils/nodeFormTestUtils';
import type { HardwareInfo } from '../../services/api/types';
function getInitialValues(
initial?: Partial<NodeFormData>,
detection?: HardwareInfo,
nodes?: Array<{ role: string; hostname?: string }>,
hostnamePrefix?: string
): NodeFormData {
let defaultRole: 'controlplane' | 'worker' = 'controlplane';
if (nodes) {
const controlPlaneCount = nodes.filter(n => n.role === 'controlplane').length;
if (controlPlaneCount >= 3) {
defaultRole = 'worker';
}
}
const role = initial?.role || defaultRole;
let defaultHostname = '';
if (!initial?.hostname && nodes && hostnamePrefix !== undefined) {
const prefix = hostnamePrefix || '';
if (role === 'controlplane') {
const controlNumbers = nodes
.filter(n => n.role === 'controlplane')
.map(n => {
const match = n.hostname?.match(new RegExp(`${prefix}control-(\\d+)$`));
return match ? parseInt(match[1], 10) : null;
})
.filter((n): n is number => n !== null);
const nextNumber = controlNumbers.length > 0 ? Math.max(...controlNumbers) + 1 : 1;
defaultHostname = `${prefix}control-${nextNumber}`;
} else {
const workerNumbers = nodes
.filter(n => n.role === 'worker')
.map(n => {
const match = n.hostname?.match(new RegExp(`${prefix}worker-(\\d+)$`));
return match ? parseInt(match[1], 10) : null;
})
.filter((n): n is number => n !== null);
const nextNumber = workerNumbers.length > 0 ? Math.max(...workerNumbers) + 1 : 1;
defaultHostname = `${prefix}worker-${nextNumber}`;
}
}
let defaultDisk = initial?.disk || detection?.selected_disk || '';
if (!defaultDisk && detection?.disks && detection.disks.length > 0) {
defaultDisk = detection.disks[0].path;
}
let defaultInterface = initial?.interface || detection?.interface || '';
if (!defaultInterface && detection?.interfaces && detection.interfaces.length > 0) {
defaultInterface = detection.interfaces[0];
}
return {
hostname: initial?.hostname || defaultHostname,
role,
disk: defaultDisk,
targetIp: initial?.targetIp || '',
currentIp: initial?.currentIp || detection?.ip || '',
interface: defaultInterface,
schematicId: initial?.schematicId || '',
maintenance: initial?.maintenance ?? true,
};
}
describe('getInitialValues', () => {
describe('Role Selection', () => {
it('defaults to controlplane when no nodes exist', () => {
const result = getInitialValues(undefined, undefined, [], 'test-');
expect(result.role).toBe('controlplane');
});
it('defaults to controlplane when fewer than 3 control nodes exist', () => {
const nodes = createMockNodes(2, 'controlplane');
const result = getInitialValues(undefined, undefined, nodes, 'test-');
expect(result.role).toBe('controlplane');
});
it('defaults to worker when 3 or more control nodes exist', () => {
const nodes = createMockNodes(3, 'controlplane');
const result = getInitialValues(undefined, undefined, nodes, 'test-');
expect(result.role).toBe('worker');
});
it('respects explicit role in initial values', () => {
const nodes = createMockNodes(3, 'controlplane');
const result = getInitialValues({ role: 'controlplane' }, undefined, nodes, 'test-');
expect(result.role).toBe('controlplane');
});
});
describe('Hostname Generation', () => {
it('generates first control node hostname', () => {
const result = getInitialValues(undefined, undefined, [], 'test-');
expect(result.hostname).toBe('test-control-1');
});
it('generates second control node hostname', () => {
const nodes = [createMockNode({ hostname: 'test-control-1', role: 'controlplane' })];
const result = getInitialValues(undefined, undefined, nodes, 'test-');
expect(result.hostname).toBe('test-control-2');
});
it('generates third control node hostname', () => {
const nodes = [
createMockNode({ hostname: 'test-control-1', role: 'controlplane' }),
createMockNode({ hostname: 'test-control-2', role: 'controlplane' }),
];
const result = getInitialValues(undefined, undefined, nodes, 'test-');
expect(result.hostname).toBe('test-control-3');
});
it('generates first worker node hostname', () => {
const nodes = createMockNodes(3, 'controlplane');
const result = getInitialValues({ role: 'worker' }, undefined, nodes, 'test-');
expect(result.hostname).toBe('test-worker-1');
});
it('generates second worker node hostname', () => {
const nodes = [
...createMockNodes(3, 'controlplane'),
createMockNode({ hostname: 'test-worker-1', role: 'worker' }),
];
const result = getInitialValues({ role: 'worker' }, undefined, nodes, 'test-');
expect(result.hostname).toBe('test-worker-2');
});
it('handles empty hostname prefix', () => {
const result = getInitialValues(undefined, undefined, [], '');
expect(result.hostname).toBe('control-1');
});
it('handles gaps in hostname numbering for control nodes', () => {
const nodes = [
createMockNode({ hostname: 'test-control-1', role: 'controlplane' }),
createMockNode({ hostname: 'test-control-3', role: 'controlplane' }),
];
const result = getInitialValues(undefined, undefined, nodes, 'test-');
expect(result.hostname).toBe('test-control-4');
});
it('handles gaps in hostname numbering for worker nodes', () => {
const nodes = [
...createMockNodes(3, 'controlplane'),
createMockNode({ hostname: 'test-worker-1', role: 'worker' }),
createMockNode({ hostname: 'test-worker-5', role: 'worker' }),
];
const result = getInitialValues({ role: 'worker' }, undefined, nodes, 'test-');
expect(result.hostname).toBe('test-worker-6');
});
it('preserves initial hostname when provided', () => {
const result = getInitialValues(
{ hostname: 'custom-hostname' },
undefined,
[],
'test-'
);
expect(result.hostname).toBe('custom-hostname');
});
it('does not generate hostname when hostnamePrefix is undefined', () => {
const result = getInitialValues(undefined, undefined, [], undefined);
expect(result.hostname).toBe('');
});
it('does not generate hostname when initial hostname is provided', () => {
const result = getInitialValues(
{ hostname: 'existing-node' },
undefined,
[],
'test-'
);
expect(result.hostname).toBe('existing-node');
});
});
describe('Disk Selection', () => {
it('uses disk from initial values', () => {
const result = getInitialValues(
{ disk: '/dev/nvme0n1' },
createMockHardwareInfo(),
[],
'test-'
);
expect(result.disk).toBe('/dev/nvme0n1');
});
it('uses selected_disk from detection', () => {
const detection = createMockHardwareInfo({ selected_disk: '/dev/sdb' });
const result = getInitialValues(undefined, detection, [], 'test-');
expect(result.disk).toBe('/dev/sdb');
});
it('auto-selects first disk from detection when no selected_disk', () => {
const detection = createMockHardwareInfo({ selected_disk: undefined });
const result = getInitialValues(undefined, detection, [], 'test-');
expect(result.disk).toBe('/dev/sda');
});
it('returns empty string when no disk info available', () => {
const result = getInitialValues(undefined, undefined, [], 'test-');
expect(result.disk).toBe('');
});
it('returns empty string when detection has no disks', () => {
const detection = createMockHardwareInfo({ disks: [], selected_disk: undefined });
const result = getInitialValues(undefined, detection, [], 'test-');
expect(result.disk).toBe('');
});
});
describe('Interface Selection', () => {
it('uses interface from initial values', () => {
const result = getInitialValues(
{ interface: 'eth2' },
createMockHardwareInfo(),
[],
'test-'
);
expect(result.interface).toBe('eth2');
});
it('uses interface from detection', () => {
const detection = createMockHardwareInfo({ interface: 'eth1' });
const result = getInitialValues(undefined, detection, [], 'test-');
expect(result.interface).toBe('eth1');
});
it('auto-selects first interface from detection when no interface set', () => {
const detection = createMockHardwareInfo({ interface: undefined });
const result = getInitialValues(undefined, detection, [], 'test-');
expect(result.interface).toBe('eth0');
});
it('returns empty string when no interface info available', () => {
const result = getInitialValues(undefined, undefined, [], 'test-');
expect(result.interface).toBe('');
});
it('returns empty string when detection has no interfaces', () => {
const detection = createMockHardwareInfo({ interface: undefined, interfaces: [] });
const result = getInitialValues(undefined, detection, [], 'test-');
expect(result.interface).toBe('');
});
});
describe('IP Address Handling', () => {
it('does not auto-fill targetIp', () => {
const result = getInitialValues(undefined, createMockHardwareInfo(), [], 'test-');
expect(result.targetIp).toBe('');
});
it('preserves initial targetIp', () => {
const result = getInitialValues(
{ targetIp: '192.168.1.200' },
createMockHardwareInfo(),
[],
'test-'
);
expect(result.targetIp).toBe('192.168.1.200');
});
it('auto-fills currentIp from detection', () => {
const detection = createMockHardwareInfo({ ip: '192.168.1.75' });
const result = getInitialValues(undefined, detection, [], 'test-');
expect(result.currentIp).toBe('192.168.1.75');
});
it('preserves initial currentIp over detection', () => {
const detection = createMockHardwareInfo({ ip: '192.168.1.75' });
const result = getInitialValues({ currentIp: '192.168.1.80' }, detection, [], 'test-');
expect(result.currentIp).toBe('192.168.1.80');
});
it('returns empty currentIp when no detection', () => {
const result = getInitialValues(undefined, undefined, [], 'test-');
expect(result.currentIp).toBe('');
});
});
describe('SchematicId Handling', () => {
it('uses initial schematicId when provided', () => {
const result = getInitialValues({ schematicId: 'custom-123' }, undefined, [], 'test-');
expect(result.schematicId).toBe('custom-123');
});
it('returns empty string when no initial schematicId', () => {
const result = getInitialValues(undefined, undefined, [], 'test-');
expect(result.schematicId).toBe('');
});
});
describe('Maintenance Mode', () => {
it('defaults to true when not provided', () => {
const result = getInitialValues(undefined, undefined, [], 'test-');
expect(result.maintenance).toBe(true);
});
it('respects explicit true value', () => {
const result = getInitialValues({ maintenance: true }, undefined, [], 'test-');
expect(result.maintenance).toBe(true);
});
it('respects explicit false value', () => {
const result = getInitialValues({ maintenance: false }, undefined, [], 'test-');
expect(result.maintenance).toBe(false);
});
});
describe('Combined Scenarios', () => {
it('handles adding first control node with full detection', () => {
const detection = createMockHardwareInfo();
const result = getInitialValues(undefined, detection, [], 'prod-');
expect(result).toEqual({
hostname: 'prod-control-1',
role: 'controlplane',
disk: '/dev/sda',
targetIp: '',
currentIp: '192.168.1.50',
interface: 'eth0',
schematicId: '',
maintenance: true,
});
});
it('handles configuring existing node (all initial values)', () => {
const initial: Partial<NodeFormData> = {
hostname: 'existing-control-1',
role: 'controlplane',
disk: '/dev/nvme0n1',
targetIp: '192.168.1.105',
currentIp: '192.168.1.60',
interface: 'eth1',
schematicId: 'existing-schematic-456',
maintenance: false,
};
const result = getInitialValues(initial, undefined, [], 'test-');
expect(result).toEqual(initial);
});
it('handles adding second control node with partial detection', () => {
const nodes = [createMockNode({ hostname: 'test-control-1', role: 'controlplane' })];
const detection = createMockHardwareInfo({
interfaces: ['enp0s1'],
interface: 'enp0s1'
});
const result = getInitialValues(undefined, detection, nodes, 'test-');
expect(result.hostname).toBe('test-control-2');
expect(result.role).toBe('controlplane');
expect(result.interface).toBe('enp0s1');
});
it('handles missing detection data', () => {
const result = getInitialValues(undefined, undefined, [], 'test-');
expect(result).toEqual({
hostname: 'test-control-1',
role: 'controlplane',
disk: '',
targetIp: '',
currentIp: '',
interface: '',
schematicId: '',
maintenance: true,
});
});
it('handles partial detection data', () => {
const detection: HardwareInfo = {
ip: '192.168.1.50',
};
const result = getInitialValues(undefined, detection, [], 'test-');
expect(result.currentIp).toBe('192.168.1.50');
expect(result.disk).toBe('');
expect(result.interface).toBe('');
});
});
});