Centralize dashboard guest identity helpers

This commit is contained in:
rcourtman
2026-03-19 05:55:28 +00:00
parent 70c764f202
commit 7a514154ab
4 changed files with 16 additions and 13 deletions
@@ -2,6 +2,7 @@ import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
import { render, screen, fireEvent, cleanup } from '@solidjs/testing-library';
import type { WorkloadGuest } from '@/types/workloads';
import type { Memory, Disk, GuestNetworkInterface } from '@/types/api';
import { getCanonicalWorkloadId } from '@/utils/workloads';
// ── Mocks ──────────────────────────────────────────────────────────────
@@ -507,19 +508,23 @@ describe('GuestDrawer', () => {
// ── WebInterfaceUrlField ──
describe('WebInterfaceUrlField', () => {
it('passes correct metadataId using guest.id', () => {
it('passes canonical metadataId using shared workload identity', () => {
render(() => <GuestDrawer guest={makeGuest({ id: 'my-guest-id' })} onClose={vi.fn()} />);
expect(screen.getByTestId('url-id').textContent).toBe('my-guest-id');
expect(screen.getByTestId('url-id').textContent).toBe(
getCanonicalWorkloadId(makeGuest({ id: 'my-guest-id' })),
);
});
it('builds fallback id from instance:node:vmid when id is empty', () => {
it('builds canonical id from instance:node:vmid when id is empty', () => {
render(() => (
<GuestDrawer
guest={makeGuest({ id: '', instance: 'pve', node: 'n1', vmid: 200 })}
onClose={vi.fn()}
/>
));
expect(screen.getByTestId('url-id').textContent).toBe('pve:n1:200');
expect(screen.getByTestId('url-id').textContent).toBe(
getCanonicalWorkloadId(makeGuest({ id: '', instance: 'pve', node: 'n1', vmid: 200 })),
);
});
it('labels app-container guests as "container"', () => {
@@ -6,6 +6,7 @@ import { DiskList } from './DiskList';
import { DiscoveryTab } from '../Discovery/DiscoveryTab';
import {
getDiscoveryResourceTypeForWorkload,
getCanonicalWorkloadId,
getWebInterfaceTargetLabelForWorkload,
resolveWorkloadType,
} from '@/utils/workloads';
@@ -28,10 +29,7 @@ interface GuestDrawerProps {
export const GuestDrawer: Component<GuestDrawerProps> = (props) => {
const navigate = useNavigate();
const guestId = () => {
if (props.guest.id) return props.guest.id;
return `${props.guest.instance}:${props.guest.node}:${props.guest.vmid}`;
};
const guestId = () => getCanonicalWorkloadId(props.guest);
const infrastructureHref = () => buildInfrastructureHrefForWorkload(props.guest);
const isVM = (guest: Guest): boolean => {
@@ -110,10 +110,6 @@ const getOutlierEmphasis = (value: number, stats: IODistributionStats): IOEmphas
const GROUPED_FIRST_CELL_INDENT = 'pl-3 sm:pl-5 lg:pl-8';
const DEFAULT_FIRST_CELL_INDENT = 'pl-2 sm:pl-3';
const buildGuestId = (guest: Guest) => {
return getCanonicalWorkloadId(guest);
};
// Type guard for VM vs Container
const isVM = (guest: Guest): guest is VM => {
return resolveWorkloadType(guest) === 'vm';
@@ -771,7 +767,7 @@ interface GuestRowProps {
export function GuestRow(props: GuestRowProps) {
const navigate = useNavigate();
const guestId = createMemo(() => buildGuestId(props.guest));
const guestId = createMemo(() => getCanonicalWorkloadId(props.guest));
const infrastructureHref = createMemo(() => buildInfrastructureHrefForWorkload(props.guest));
// Use breakpoint hook directly for responsive behavior
@@ -316,6 +316,10 @@ describe('frontend resource type boundaries', () => {
expect(dashboardSource).toContain('normalizeWorkloadViewModeParam');
expect(dashboardSource).not.toContain('function normalizeViewModeParam');
expect(dashboardSource).not.toContain('workloadSummaryGuestId');
expect(guestRowSource).toContain('getCanonicalWorkloadId');
expect(guestRowSource).not.toContain('buildGuestId');
expect(guestDrawerSource).toContain('getCanonicalWorkloadId');
expect(guestDrawerSource).not.toContain('const guestId = () => {');
expect(emptyStateSource).toContain('getEmptyStatePresentation');
expect(emptyStateSource).not.toContain('const iconBgClass: Record<EmptyStateTone, string> =');
expect(emptyStateSource).not.toContain(