mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-23 12:15:42 +00:00
Add unit tests for agentVersion, proxmoxVersion, resourceSearchMatch, vmwareDisplay utils
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
compareAgentVersions,
|
||||
formatAgentVersionDisplay,
|
||||
parseAgentVersion,
|
||||
} from '@/utils/agentVersion';
|
||||
|
||||
describe('agentVersion', () => {
|
||||
describe('parseAgentVersion', () => {
|
||||
it.each([
|
||||
['null', null, null],
|
||||
['undefined', undefined, null],
|
||||
['empty string', '', null],
|
||||
['whitespace only', ' ', null],
|
||||
])('returns null for %s', (_label, input, expected) => {
|
||||
expect(parseAgentVersion(input)).toEqual(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['plain x.y.z', '1.2.3', { major: 1, minor: 2, patch: 3, prerelease: [] }],
|
||||
['lowercase v prefix', 'v1.2.3', { major: 1, minor: 2, patch: 3, prerelease: [] }],
|
||||
['uppercase V prefix', 'V1.2.3', { major: 1, minor: 2, patch: 3, prerelease: [] }],
|
||||
['whitespace padded', ' 1.2.3 ', { major: 1, minor: 2, patch: 3, prerelease: [] }],
|
||||
['with prerelease', '1.2.3-rc.5', { major: 1, minor: 2, patch: 3, prerelease: ['rc', '5'] }],
|
||||
['with prerelease + build', '1.2.3-rc.5+git.1', { major: 1, minor: 2, patch: 3, prerelease: ['rc', '5'] }],
|
||||
['build metadata only', '1.2.3+git.151.gabcdef', { major: 1, minor: 2, patch: 3, prerelease: [] }],
|
||||
['v + prerelease + build', 'v1.2.3-rc.5+build.2', { major: 1, minor: 2, patch: 3, prerelease: ['rc', '5'] }],
|
||||
['leading zeros', '01.02.03', { major: 1, minor: 2, patch: 3, prerelease: [] }],
|
||||
['extra dotted segments (first 3 used)', '1.2.3.4.5', { major: 1, minor: 2, patch: 3, prerelease: [] }],
|
||||
['trailing dash with empty prerelease', '1.2.3-', { major: 1, minor: 2, patch: 3, prerelease: [] }],
|
||||
])('parses %s', (_label, input, expected) => {
|
||||
expect(parseAgentVersion(input)).toEqual(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['two parts', '1.2'],
|
||||
['one part', '1'],
|
||||
['non-numeric major', 'a.b.c'],
|
||||
['non-numeric patch', '1.2.x'],
|
||||
['leading dash (negative)', '-1.2.3'],
|
||||
['only dots', '...'],
|
||||
])('returns null for malformed %s', (_label, input) => {
|
||||
expect(parseAgentVersion(input)).toBeNull();
|
||||
});
|
||||
|
||||
it('is lenient about a numeric-looking prefix in a part', () => {
|
||||
// parseInt('1e2', 10) === 1; this documents current lenient behavior.
|
||||
expect(parseAgentVersion('1e2.0.0')).toEqual({
|
||||
major: 1,
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
prerelease: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('compareAgentVersions', () => {
|
||||
it('returns null when either side is unparseable', () => {
|
||||
expect(compareAgentVersions(null, '1.0.0')).toBeNull();
|
||||
expect(compareAgentVersions('1.0.0', null)).toBeNull();
|
||||
expect(compareAgentVersions('garbage', '1.0.0')).toBeNull();
|
||||
expect(compareAgentVersions('1.0.0', '1.0')).toBeNull();
|
||||
expect(compareAgentVersions(undefined, undefined)).toBeNull();
|
||||
});
|
||||
|
||||
it.each([
|
||||
['equal stable', '1.0.0', '1.0.0', 0],
|
||||
['major lower', '1.0.0', '2.0.0', -1],
|
||||
['major higher', '2.0.0', '1.0.0', 1],
|
||||
['minor lower', '1.1.0', '1.2.0', -1],
|
||||
['minor higher', '1.2.0', '1.1.0', 1],
|
||||
['patch lower', '1.0.1', '1.0.2', -1],
|
||||
['patch higher', '1.0.2', '1.0.1', 1],
|
||||
['v prefix ignored', 'v1.2.3', '1.2.3', 0],
|
||||
])('compares core precedence: %s', (_label, a, b, expected) => {
|
||||
expect(compareAgentVersions(a, b)).toBe(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['stable > prerelease', '1.0.0', '1.0.0-rc.1', 1],
|
||||
['prerelease < stable', '1.0.0-rc.1', '1.0.0', -1],
|
||||
['numeric prerelease lower', '1.0.0-rc.1', '1.0.0-rc.2', -1],
|
||||
['numeric prerelease higher', '1.0.0-rc.2', '1.0.0-rc.1', 1],
|
||||
['equal prerelease', '1.0.0-rc.1', '1.0.0-rc.1', 0],
|
||||
['shorter prerelease set lower', '1.0.0-alpha', '1.0.0-alpha.1', -1],
|
||||
['longer prerelease set higher', '1.0.0-alpha.1', '1.0.0-alpha', 1],
|
||||
['lexical alpha lower', '1.0.0-alpha', '1.0.0-beta', -1],
|
||||
['lexical alpha higher', '1.0.0-beta', '1.0.0-alpha', 1],
|
||||
['numeric identifier below alpha', '1.0.0-1', '1.0.0-alpha', -1],
|
||||
['alpha above numeric identifier', '1.0.0-alpha', '1.0.0-1', 1],
|
||||
['build metadata ignored', '1.0.0+a', '1.0.0+b', 0],
|
||||
])('applies semver prerelease rules: %s', (_label, a, b, expected) => {
|
||||
expect(compareAgentVersions(a, b)).toBe(expected);
|
||||
});
|
||||
|
||||
it('compares real-world agent version ranges in ascending order', () => {
|
||||
const sorted = ['6.0.0-rc.5', '6.0.0', '6.1.0', '6.1.1'].sort(
|
||||
(x, y) => (compareAgentVersions(x, y) ?? 0),
|
||||
);
|
||||
expect(sorted).toEqual(['6.0.0-rc.5', '6.0.0', '6.1.0', '6.1.1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatAgentVersionDisplay', () => {
|
||||
it.each([
|
||||
['null', null, ''],
|
||||
['undefined', undefined, ''],
|
||||
['empty', '', ''],
|
||||
['malformed (too few parts)', '1.2', ''],
|
||||
['plain', '1.2.3', 'v1.2.3'],
|
||||
['lowercase v prefix', 'v1.2.3', 'v1.2.3'],
|
||||
['uppercase V prefix', 'V1.2.3', 'v1.2.3'],
|
||||
['prerelease', '1.2.3-rc.5', 'v1.2.3-rc.5'],
|
||||
['strips build metadata', '1.2.3+git.151', 'v1.2.3'],
|
||||
['prerelease + build', '1.2.3-rc.5+build', 'v1.2.3-rc.5'],
|
||||
['trims whitespace', ' 1.2.3 ', 'v1.2.3'],
|
||||
['normalizes extra segments', '1.2.3.4.5', 'v1.2.3'],
|
||||
['normalizes leading zeros', '01.02.03', 'v1.2.3'],
|
||||
])('formats %s', (_label, input, expected) => {
|
||||
expect(formatAgentVersionDisplay(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { formatProxmoxVersion } from '@/utils/proxmoxVersion';
|
||||
|
||||
describe('proxmoxVersion', () => {
|
||||
describe('formatProxmoxVersion', () => {
|
||||
it.each([
|
||||
['null', null, ''],
|
||||
['undefined', undefined, ''],
|
||||
['empty string', '', ''],
|
||||
['whitespace only', ' ', ''],
|
||||
['unknown (lowercase)', 'unknown', ''],
|
||||
['UNKNOWN (uppercase)', 'UNKNOWN', ''],
|
||||
['padded unknown', ' unknown ', ''],
|
||||
['Unknown (mixed case)', 'Unknown', ''],
|
||||
])('returns empty for %s', (_label, input, expected) => {
|
||||
expect(formatProxmoxVersion(input)).toBe(expected);
|
||||
});
|
||||
|
||||
describe('pve-manager extraction', () => {
|
||||
it.each([
|
||||
['plain', 'pve-manager/8.0.4', '8.0.4'],
|
||||
['uppercase prefix', 'PVE-Manager/8.0.4', '8.0.4'],
|
||||
['stops at slash', 'pve-manager/8.0/9', '8.0'],
|
||||
['stops at space', 'pve-manager/8.0.4 running', '8.0.4'],
|
||||
['with suffix', 'pve-manager/8.0.4-1', '8.0.4-1'],
|
||||
])('extracts version for %s', (_label, input, expected) => {
|
||||
expect(formatProxmoxVersion(input)).toBe(expected);
|
||||
});
|
||||
|
||||
it('falls through when pve-manager has no capturable value', () => {
|
||||
expect(formatProxmoxVersion('pve-manager/')).toBe('pve-manager/');
|
||||
});
|
||||
});
|
||||
|
||||
describe('numeric dotted-version fallback', () => {
|
||||
it.each([
|
||||
['plain dotted', '8.0.4', '8.0.4'],
|
||||
['embedded in text', 'version 8.0.4 here', '8.0.4'],
|
||||
['two segments', '8.0', '8.0'],
|
||||
['with hyphen suffix', '8.0.4-1', '8.0.4-1'],
|
||||
['with plus suffix', '8.0.4+build', '8.0.4+build'],
|
||||
])('extracts %s', (_label, input, expected) => {
|
||||
expect(formatProxmoxVersion(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('last-resort passthrough', () => {
|
||||
it.each([
|
||||
['bare single number has no dotted version', '8', '8'],
|
||||
['garbage token', 'garbage', 'garbage'],
|
||||
['preserves original case', 'Garbage-Text', 'Garbage-Text'],
|
||||
['number with trailing text', '8x', '8x'],
|
||||
])('returns input trimmed for %s', (_label, input, expected) => {
|
||||
expect(formatProxmoxVersion(input)).toBe(expected);
|
||||
});
|
||||
|
||||
it('trims surrounding whitespace on passthrough', () => {
|
||||
expect(formatProxmoxVersion(' custom-build ')).toBe('custom-build');
|
||||
});
|
||||
});
|
||||
|
||||
describe('precedence', () => {
|
||||
it('prefers the pve-manager capture over the numeric fallback', () => {
|
||||
expect(formatProxmoxVersion('pve-manager/7.4.3 and 99.99.99')).toBe('7.4.3');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,265 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import type { Resource } from '@/types/resource';
|
||||
import { resourceMatchesSearch } from '@/utils/resourceSearchMatch';
|
||||
|
||||
const makeResource = (overrides: Partial<Resource> = {}): Resource =>
|
||||
({
|
||||
id: 'res-1',
|
||||
type: 'agent',
|
||||
name: 'res-1',
|
||||
displayName: 'Resource One',
|
||||
platformId: 'res-1',
|
||||
platformType: 'agent',
|
||||
sourceType: 'agent',
|
||||
status: 'online',
|
||||
lastSeen: Date.now(),
|
||||
...overrides,
|
||||
}) as Resource;
|
||||
|
||||
describe('resourceMatchesSearch', () => {
|
||||
describe('empty / nullish terms match everything', () => {
|
||||
it.each([
|
||||
['null', null],
|
||||
['undefined', undefined],
|
||||
['empty string', ''],
|
||||
['only spaces', ' '],
|
||||
['tab + newline', '\t\n'],
|
||||
])('returns true for %s', (_label, term) => {
|
||||
expect(resourceMatchesSearch(makeResource(), term)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for an empty term even when resource has no candidates', () => {
|
||||
const empty = makeResource({ id: '', name: '', displayName: '' });
|
||||
expect(resourceMatchesSearch(empty, '')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('case-insensitive substring matching on core identity fields', () => {
|
||||
it.each([
|
||||
['id', { id: 'prod-web-01' }, 'web'],
|
||||
['name', { name: 'prod-web-01' }, 'WEB'],
|
||||
['displayName', { displayName: 'Tower Server' }, 'tower'],
|
||||
['parentName', { parentName: 'cluster-alpha' }, 'alpha'],
|
||||
['agent.hostname', { agent: { hostname: 'node-a.local' } }, 'node-a'],
|
||||
['identity.hostname', { identity: { hostname: 'box.internal' } }, 'box'],
|
||||
])('matches via %s', (_label, overrides, term) => {
|
||||
const resource = makeResource(overrides as Partial<Resource>);
|
||||
expect(resourceMatchesSearch(resource, term)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when no candidate contains the term', () => {
|
||||
expect(resourceMatchesSearch(makeResource({ displayName: 'isolated' }), 'web')).toBe(false);
|
||||
});
|
||||
|
||||
it('matches a partial substring, not just whole values', () => {
|
||||
expect(resourceMatchesSearch(makeResource({ displayName: 'hyper-tower' }), 'tower')).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('canonicalIdentity fields', () => {
|
||||
it.each([
|
||||
['displayName', { canonicalIdentity: { displayName: 'Canonical Box' } }, 'canonical'],
|
||||
['hostname', { canonicalIdentity: { hostname: 'canon.local' } }, 'canon'],
|
||||
['primaryId', { canonicalIdentity: { primaryId: 'node:instance-1' } }, 'instance-1'],
|
||||
])('matches canonicalIdentity.%s', (_label, overrides, term) => {
|
||||
expect(resourceMatchesSearch(makeResource(overrides as Partial<Resource>), term)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches any alias in canonicalIdentity.aliases', () => {
|
||||
const resource = makeResource({
|
||||
canonicalIdentity: { aliases: ['first', 'second-alias', 'third'] },
|
||||
});
|
||||
expect(resourceMatchesSearch(resource, 'second-alias')).toBe(true);
|
||||
expect(resourceMatchesSearch(resource, 'THIRD')).toBe(true);
|
||||
expect(resourceMatchesSearch(resource, 'nope')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('docker fields', () => {
|
||||
it.each([
|
||||
['hostname', { docker: { hostname: 'docker-host' } }, 'docker-host'],
|
||||
['image', { docker: { image: 'nginx:latest' } }, 'nginx'],
|
||||
['imageId', { docker: { imageId: 'sha256:abc123' } }, 'abc123'],
|
||||
['volumeName', { docker: { volumeName: 'data-vol' } }, 'data-vol'],
|
||||
['networkId', { docker: { networkId: 'net-99' } }, 'net-99'],
|
||||
['driver', { docker: { driver: 'overlay2' } }, 'overlay'],
|
||||
['serviceName', { docker: { serviceName: 'swarm-svc' } }, 'swarm'],
|
||||
['taskId', { docker: { taskId: 'task-7' } }, 'task-7'],
|
||||
])('matches docker.%s', (_label, overrides, term) => {
|
||||
expect(resourceMatchesSearch(makeResource(overrides as Partial<Resource>), term)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches any entry in docker.repoTags', () => {
|
||||
const resource = makeResource({ docker: { repoTags: ['redis:7', 'postgres:16'] } });
|
||||
expect(resourceMatchesSearch(resource, 'postgres')).toBe(true);
|
||||
expect(resourceMatchesSearch(resource, 'redis:7')).toBe(true);
|
||||
});
|
||||
|
||||
it('matches any entry in docker.repoDigests', () => {
|
||||
const resource = makeResource({
|
||||
docker: { repoDigests: ['nginx@sha256:aaa', 'redis@sha256:bbb'] },
|
||||
});
|
||||
expect(resourceMatchesSearch(resource, 'sha256:bbb')).toBe(true);
|
||||
});
|
||||
|
||||
it('handles empty repoTags / repoDigests arrays without matching', () => {
|
||||
const resource = makeResource({ docker: { repoTags: [], repoDigests: [] } });
|
||||
expect(resourceMatchesSearch(resource, 'anything')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('kubernetes fields', () => {
|
||||
const k8sBase = {
|
||||
clusterName: 'prod-cluster',
|
||||
nodeName: 'k8s-node-1',
|
||||
namespace: 'kube-system',
|
||||
resourceKind: 'Deployment',
|
||||
serviceType: 'ClusterIP',
|
||||
serviceName: 'api-service',
|
||||
clusterIp: '10.0.0.5',
|
||||
storageClass: 'fast-ssd',
|
||||
provisioner: 'pd.csi.storage',
|
||||
volumeBindingMode: 'WaitForFirstConsumer',
|
||||
addressType: 'Endpointslice',
|
||||
phase: 'Bound',
|
||||
secretType: 'Opaque',
|
||||
minAvailable: '2',
|
||||
maxUnavailable: '25%',
|
||||
targetKind: 'Deployment',
|
||||
targetName: 'api-target',
|
||||
reason: 'BackOff',
|
||||
involvedName: 'involved-pod',
|
||||
volumeName: 'pvc-1234',
|
||||
};
|
||||
|
||||
it.each([
|
||||
['clusterName', 'prod'],
|
||||
['nodeName', 'node-1'],
|
||||
['namespace', 'kube'],
|
||||
['resourceKind', 'deploy'],
|
||||
['serviceType', 'clusterip'],
|
||||
['serviceName', 'api'],
|
||||
['clusterIp', '10.0.0.5'],
|
||||
['storageClass', 'ssd'],
|
||||
['provisioner', 'csi'],
|
||||
['volumeBindingMode', 'first'],
|
||||
['addressType', 'slice'],
|
||||
['phase', 'bound'],
|
||||
['secretType', 'opaque'],
|
||||
['minAvailable', '2'],
|
||||
['maxUnavailable', '25%'],
|
||||
['targetKind', 'deploy'],
|
||||
['targetName', 'target'],
|
||||
['reason', 'backoff'],
|
||||
['involvedName', 'involved'],
|
||||
['volumeName', 'pvc-1234'],
|
||||
])('matches kubernetes.%s', (field, term) => {
|
||||
const resource = makeResource({
|
||||
kubernetes: { [field]: (k8sBase as Record<string, unknown>)[field] } as never,
|
||||
});
|
||||
expect(resourceMatchesSearch(resource, term)).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['externalIps', ['203.0.113.5', '198.51.100.2'], '198.51.100.2'],
|
||||
['hosts', ['host-a.example', 'host-b.example'], 'host-b'],
|
||||
['addresses', ['1.1.1.1', '2.2.2.2'], '2.2.2.2'],
|
||||
['policyTypes', ['Ingress', 'Egress'], 'egress'],
|
||||
['parameterKeys', ['fsType', 'mode'], 'fstype'],
|
||||
['dataKeys', ['config.json', 'data'], 'config'],
|
||||
['binaryDataKeys', ['bin1', 'bin2'], 'bin2'],
|
||||
['imagePullSecrets', ['regcred', 'ghcr'], 'ghcr'],
|
||||
['limitTypes', ['Container', 'Pod'], 'container'],
|
||||
['metricTypes', ['Resource', 'External'], 'resource'],
|
||||
['accessModes', ['ReadWriteOnce', 'ReadOnlyMany'], 'readonly'],
|
||||
])('matches any entry in kubernetes.%s', (field, values, term) => {
|
||||
const resource = makeResource({ kubernetes: { [field]: values } as never });
|
||||
expect(resourceMatchesSearch(resource, term)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches object keys from kubernetes.hard', () => {
|
||||
const resource = makeResource({ kubernetes: { hard: { 'cpu': '4', 'memory': '8Gi' } } });
|
||||
expect(resourceMatchesSearch(resource, 'memory')).toBe(true);
|
||||
expect(resourceMatchesSearch(resource, 'cpu')).toBe(true);
|
||||
});
|
||||
|
||||
it('matches object keys from kubernetes.used', () => {
|
||||
const resource = makeResource({ kubernetes: { used: { 'pods': '10' } } });
|
||||
expect(resourceMatchesSearch(resource, 'pods')).toBe(true);
|
||||
});
|
||||
|
||||
it('handles empty arrays and empty maps without matching', () => {
|
||||
const resource = makeResource({
|
||||
kubernetes: {
|
||||
externalIps: [],
|
||||
hosts: [],
|
||||
addresses: [],
|
||||
hard: {},
|
||||
used: {},
|
||||
} as never,
|
||||
});
|
||||
expect(resourceMatchesSearch(resource, 'whatever')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('platform metadata fields', () => {
|
||||
it.each([
|
||||
['vmware.runtimeHostName', { vmware: { runtimeHostName: 'esxi-01' } }, 'esxi'],
|
||||
['vmware.clusterName', { vmware: { clusterName: 'vc-cluster' } }, 'vc-cluster'],
|
||||
['vmware.datacenterName', { vmware: { datacenterName: 'DC-East' } }, 'east'],
|
||||
['proxmox.node', { proxmox: { node: 'pve1' } }, 'pve1'],
|
||||
['proxmox.nodeName', { proxmox: { nodeName: 'node-a' } }, 'node-a'],
|
||||
['proxmox.clusterName', { proxmox: { clusterName: 'pmx-cluster' } }, 'pmx'],
|
||||
])('matches %s', (_label, overrides, term) => {
|
||||
expect(resourceMatchesSearch(makeResource(overrides as Partial<Resource>), term)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tags', () => {
|
||||
it('matches any tag', () => {
|
||||
const resource = makeResource({ tags: ['env:prod', 'tier:frontend'] });
|
||||
expect(resourceMatchesSearch(resource, 'frontend')).toBe(true);
|
||||
expect(resourceMatchesSearch(resource, 'env:prod')).toBe(true);
|
||||
});
|
||||
|
||||
it('does not match when tags are absent', () => {
|
||||
expect(resourceMatchesSearch(makeResource(), 'env:prod')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('whitespace handling and filtering', () => {
|
||||
it('trims the search term before matching', () => {
|
||||
expect(
|
||||
resourceMatchesSearch(makeResource({ displayName: 'tower' }), ' tower '),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('trims candidate values before matching', () => {
|
||||
expect(
|
||||
resourceMatchesSearch(makeResource({ displayName: ' padded-host ' }), 'padded-host'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('ignores candidates that are empty after trimming', () => {
|
||||
const resource = makeResource({ id: ' ', name: '', displayName: ' ' });
|
||||
expect(resourceMatchesSearch(resource, '')).toBe(true);
|
||||
expect(resourceMatchesSearch(resource, 'anything')).toBe(false);
|
||||
});
|
||||
|
||||
it('does not match a term against whitespace-only candidates', () => {
|
||||
const resource = makeResource({ displayName: ' ' });
|
||||
expect(resourceMatchesSearch(resource, 'x')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resource with no populated metadata', () => {
|
||||
it('matches empty term but not real terms', () => {
|
||||
const resource = makeResource({ id: '', name: '', displayName: '' });
|
||||
expect(resourceMatchesSearch(resource, '')).toBe(true);
|
||||
expect(resourceMatchesSearch(resource, 'nonsense')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import type { ResourceVMwareMeta } from '@/types/resource';
|
||||
import { formatVmwareClusterServices } from '@/utils/vmwareDisplay';
|
||||
|
||||
describe('vmwareDisplay', () => {
|
||||
describe('formatVmwareClusterServices', () => {
|
||||
it('returns empty string when meta is undefined', () => {
|
||||
expect(formatVmwareClusterServices(undefined)).toBe('');
|
||||
});
|
||||
|
||||
it('returns empty string when neither flag is set', () => {
|
||||
expect(formatVmwareClusterServices({})).toBe('');
|
||||
});
|
||||
|
||||
it.each([
|
||||
['HA enabled', { clusterHaEnabled: true }, 'HA enabled'],
|
||||
['HA disabled', { clusterHaEnabled: false }, 'HA disabled'],
|
||||
['DRS enabled', { clusterDrsEnabled: true }, 'DRS enabled'],
|
||||
['DRS disabled', { clusterDrsEnabled: false }, 'DRS disabled'],
|
||||
])('renders a single flag for %s', (_label, meta, expected) => {
|
||||
expect(formatVmwareClusterServices(meta as ResourceVMwareMeta)).toBe(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['both enabled', { clusterHaEnabled: true, clusterDrsEnabled: true }, 'HA enabled · DRS enabled'],
|
||||
['both disabled', { clusterHaEnabled: false, clusterDrsEnabled: false }, 'HA disabled · DRS disabled'],
|
||||
['HA enabled DRS disabled', { clusterHaEnabled: true, clusterDrsEnabled: false }, 'HA enabled · DRS disabled'],
|
||||
['HA disabled DRS enabled', { clusterHaEnabled: false, clusterDrsEnabled: true }, 'HA disabled · DRS enabled'],
|
||||
])('joins both flags with " · " for %s', (_label, meta, expected) => {
|
||||
expect(formatVmwareClusterServices(meta as ResourceVMwareMeta)).toBe(expected);
|
||||
});
|
||||
|
||||
it('ignores unrelated fields on the meta object', () => {
|
||||
expect(
|
||||
formatVmwareClusterServices({
|
||||
clusterName: 'vc-1',
|
||||
clusterHaEnabled: true,
|
||||
} as ResourceVMwareMeta),
|
||||
).toBe('HA enabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user