Cover the last cold arms in seven frontend modules

Seven modules from the current source drop still had a measured branch
residue their own specs never reached. Counts are uncovered branches,
module scoped, existing specs versus existing plus new.

- resourceDetailMappers 13 -> 0, absent nested metadata, empty arrays and
  the unknown-discriminator default arm.
- infrastructureAgentUpdateCommandsModel 9 -> 0 on top of three prior
  coverage files.
- stackedMemoryBarModel 9 -> 1, zero and missing totals, over-total and
  negative segments, the clamping boundaries.
- api/vmware 9 -> 0 and api/nodes 8 -> 0, request building and response
  handling arms rather than mocked-fetch echoes. api/nodes also covers
  createHostAgentInstallToken, which had never been called.
- nodeModalModel 8 -> 0, blank fields and the validation rejection arms.
- utils/workloads 8 -> 0.

No source file is modified. Adversarial review returned no rejects and
flagged ten near-duplicate cases across the vmware and workloads files;
nine were deleted and the module coverage re-measured as identical, proving
they carried nothing. The tenth was restored because re-measuring showed it
did carry a branch.

PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT=test-only branch coverage, no source or contract change
This commit is contained in:
rcourtman
2026-07-24 23:06:13 +01:00
parent 25b2df07a0
commit 4274befe55
7 changed files with 1414 additions and 0 deletions
@@ -0,0 +1,187 @@
import { describe, expect, it, vi, beforeEach } from 'vitest';
import { NodesAPI } from '../nodes';
import type { ProxmoxSetupCommandResponse } from '../nodes';
import type { APITokenRecord } from '@/types/api';
import { apiFetch, apiFetchJSON } from '@/utils/apiClient';
vi.mock('@/utils/apiClient', () => ({
apiFetch: vi.fn(),
apiFetchJSON: vi.fn(),
}));
describe('NodesAPI — branch coverage (createHostAgentInstallToken)', () => {
beforeEach(() => {
vi.clearAllMocks();
});
const validRecord: APITokenRecord = {
id: 'tok-host-1',
name: 'host-agent',
prefix: 'hos_',
suffix: 'AB12',
createdAt: '2026-07-24T00:00:00Z',
};
it('mints a host agent install token, trims it, and echoes the record when a name is supplied', async () => {
vi.mocked(apiFetchJSON).mockResolvedValueOnce({
token: ' host-token-xyz ',
record: validRecord,
});
const result = await NodesAPI.createHostAgentInstallToken({
enableCommands: true,
name: 'render-east',
});
expect(apiFetchJSON).toHaveBeenCalledWith(
'/api/agent-install-command',
expect.objectContaining({
method: 'POST',
body: JSON.stringify({
type: 'host',
enableCommands: true,
name: 'render-east',
}),
}),
);
expect(result).toEqual({ token: 'host-token-xyz', record: validRecord });
});
it('omits the name field from the request body entirely when no name is supplied', async () => {
vi.mocked(apiFetchJSON).mockResolvedValueOnce({
token: 'host-token-xyz',
record: validRecord,
});
await NodesAPI.createHostAgentInstallToken({ enableCommands: false });
const [, options] = vi.mocked(apiFetchJSON).mock.calls[0]!;
const body = JSON.parse(options!.body as string);
expect(body).toEqual({ type: 'host', enableCommands: false });
expect(body).not.toHaveProperty('name');
});
it('rejects with the contract error when the token is blank (short-circuits before checking record)', async () => {
vi.mocked(apiFetchJSON).mockResolvedValueOnce({
token: ' ',
record: validRecord,
});
await expect(NodesAPI.createHostAgentInstallToken({ enableCommands: true })).rejects.toThrow(
'Invalid host agent install token response',
);
});
it('rejects with the contract error when the record is absent even though the token is valid', async () => {
vi.mocked(apiFetchJSON).mockResolvedValueOnce({
token: 'host-token-xyz',
record: null,
});
await expect(NodesAPI.createHostAgentInstallToken({ enableCommands: true })).rejects.toThrow(
'Invalid host agent install token response',
);
});
});
describe('NodesAPI — branch coverage (normalizeProxmoxSetupCommandResponse validation arms)', () => {
beforeEach(() => {
vi.clearAllMocks();
});
const validPveResponse = () => ({
type: 'pve',
host: 'https://pve.example:8006',
url: 'https://pulse.example/api/setup-script?type=pve',
downloadURL: 'https://pulse.example/api/setup-script?type=pve&setup_token=setup-token-123',
scriptFileName: 'pulse-setup-pve.sh',
command: 'curl pve ...',
commandWithEnv: 'curl env pve ...',
setupToken: 'setup-token-123',
tokenHint: 'set…123',
expires: 1_900_000_000,
});
it('rejects when host is blank', async () => {
vi.mocked(apiFetchJSON).mockResolvedValueOnce({ ...validPveResponse(), host: '' });
await expect(
NodesAPI.getProxmoxSetupCommand({ type: 'pve', host: 'pve.example', backupPerms: true }),
).rejects.toThrow('Invalid Proxmox setup response host');
});
it('rejects when url is blank', async () => {
vi.mocked(apiFetchJSON).mockResolvedValueOnce({ ...validPveResponse(), url: ' ' });
await expect(
NodesAPI.getProxmoxSetupCommand({ type: 'pve', host: 'pve.example', backupPerms: true }),
).rejects.toThrow('Invalid Proxmox setup response URL');
});
it('rejects when downloadURL is blank', async () => {
vi.mocked(apiFetchJSON).mockResolvedValueOnce({ ...validPveResponse(), downloadURL: '' });
await expect(
NodesAPI.getProxmoxSetupCommand({ type: 'pve', host: 'pve.example', backupPerms: true }),
).rejects.toThrow('Invalid Proxmox setup response downloadURL');
});
it('rejects when command is blank', async () => {
vi.mocked(apiFetchJSON).mockResolvedValueOnce({ ...validPveResponse(), command: '' });
await expect(
NodesAPI.getProxmoxSetupCommand({ type: 'pve', host: 'pve.example', backupPerms: true }),
).rejects.toThrow('Invalid Proxmox setup response command');
});
it('rejects when setupToken is blank', async () => {
vi.mocked(apiFetchJSON).mockResolvedValueOnce({ ...validPveResponse(), setupToken: '' });
await expect(
NodesAPI.getProxmoxSetupCommand({ type: 'pve', host: 'pve.example', backupPerms: true }),
).rejects.toThrow('Invalid Proxmox setup response setup token');
});
});
describe('NodesAPI — branch coverage (downloadProxmoxSetupScript failure arms)', () => {
beforeEach(() => {
vi.clearAllMocks();
});
const pveBootstrap = (): ProxmoxSetupCommandResponse => ({
type: 'pve',
host: 'https://pve.example:8006',
url: 'https://pulse.example/base/api/setup-script?type=pve',
downloadURL: 'https://pulse.example/base/api/setup-script?type=pve&setup_token=setup-token-123',
scriptFileName: 'pulse-setup-pve.sh',
command: 'curl pve ...',
commandWithEnv: 'curl env pve ...',
commandWithoutEnv: 'curl bare pve ...',
expires: 1_900_000_000,
tokenHint: 'set…123',
});
it('rejects when the fetch response is not ok (HTTP failure)', async () => {
vi.mocked(apiFetch).mockResolvedValueOnce(new Response('', { status: 502 }));
await expect(NodesAPI.downloadProxmoxSetupScript(pveBootstrap())).rejects.toThrow(
'Failed to fetch setup script',
);
});
it('rejects when the script body is empty/whitespace after content-type and filename checks pass', async () => {
vi.mocked(apiFetch).mockResolvedValueOnce(
new Response(' ', {
status: 200,
headers: {
'Content-Type': 'text/x-shellscript; charset=utf-8',
'Content-Disposition': 'attachment; filename="pulse-setup-pve.sh"',
},
}),
);
await expect(NodesAPI.downloadProxmoxSetupScript(pveBootstrap())).rejects.toThrow(
'Empty Proxmox setup script response',
);
});
});
@@ -0,0 +1,148 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { apiFetchJSON } from '@/utils/apiClient';
import { VMwareAPI } from '@/api/vmware';
vi.mock('@/utils/apiClient', () => ({
apiFetchJSON: vi.fn(),
}));
const mockedApiFetchJSON = vi.mocked(apiFetchJSON);
describe('VMwareAPI.listConnections — branch coverage (response-shape arms)', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('coerces a non-array response to an empty list via the arrayOrUndefined(...) ?? [] fallback', async () => {
// Backend contract occasionally yields a bare object / null envelope instead of an array.
// The `?? []` arm must yield [] without throwing.
mockedApiFetchJSON.mockResolvedValueOnce(null as never);
await expect(VMwareAPI.listConnections()).resolves.toEqual([]);
expect(mockedApiFetchJSON).toHaveBeenCalledWith('/api/vmware/connections');
});
it('normalizes a poll.lastError object through the object-guard fall-through of normalizeVMwareConnectionPollError', async () => {
// Existing specs only ever pass `poll` WITHOUT a lastError, so the `(!error || typeof error !== 'object')`
// guard always returned early. Supplying a real object exercises the fall-through that builds the trimmed error.
mockedApiFetchJSON.mockResolvedValueOnce([
{
id: 'conn-1',
host: 'vcsa.lab.local',
insecureSkipVerify: false,
enabled: true,
poll: {
intervalSeconds: 90,
lastAttemptAt: ' 2026-04-01T10:00:00Z ',
lastSuccessAt: ' 2026-04-01T09:59:00Z ',
consecutiveFailures: 3,
lastError: {
at: ' 2026-04-01T10:00:01Z ',
message: ' connection refused ',
category: ' transport ',
},
},
},
] as never);
const [connection] = await VMwareAPI.listConnections();
expect(connection.poll).toEqual({
intervalSeconds: 90,
lastAttemptAt: '2026-04-01T10:00:00Z',
lastSuccessAt: '2026-04-01T09:59:00Z',
consecutiveFailures: 3,
lastError: {
at: '2026-04-01T10:00:01Z',
message: 'connection refused',
category: 'transport',
},
});
});
it('defaults every observed host/vm/datastore/network count to 0 when the raw values are non-finite or absent', async () => {
// finiteNumberOrUndefined(...) returns undefined for strings/NaN/Infinity/missing values, so each
// `?? 0` fallback must fire independently for hosts, vms, datastores and networks.
mockedApiFetchJSON.mockResolvedValueOnce([
{
id: 'conn-1',
host: 'vcsa.lab.local',
insecureSkipVerify: false,
enabled: true,
observed: {
collectedAt: ' 2026-04-01T00:00:00Z ',
hosts: 'not-a-number',
vms: NaN,
datastores: Infinity,
// networks intentionally absent
viRelease: ' 8.0.3 ',
degraded: true,
issueCount: 2,
},
},
] as never);
const [connection] = await VMwareAPI.listConnections();
expect(connection.observed).toMatchObject({
collectedAt: '2026-04-01T00:00:00Z',
hosts: 0,
vms: 0,
datastores: 0,
networks: 0,
viRelease: '8.0.3',
degraded: true,
issueCount: 2,
});
});
it('falls back to an empty name when the raw connection omits name (optionalTrimmedString(...) ?? "" arm)', async () => {
mockedApiFetchJSON.mockResolvedValueOnce([
{
id: ' conn-2 ',
host: ' esxi-02.lab.local ',
insecureSkipVerify: true,
enabled: false,
// name intentionally absent
},
] as never);
const [connection] = await VMwareAPI.listConnections();
expect(connection).toMatchObject({
id: 'conn-2',
name: '',
host: 'esxi-02.lab.local',
});
});
});
describe('VMwareAPI.testSavedConnection — branch coverage (input-absent arm)', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('omits the request body entirely when input is undefined (the `: {}` arm of the input ternary)', async () => {
mockedApiFetchJSON.mockResolvedValueOnce({ success: true } as never);
await expect(VMwareAPI.testSavedConnection('conn/1')).resolves.toEqual({
success: true,
hosts: 0,
vms: 0,
datastores: 0,
networks: 0,
viRelease: undefined,
degraded: false,
issueCount: 0,
issues: [],
});
expect(mockedApiFetchJSON).toHaveBeenCalledWith('/api/vmware/connections/conn%2F1/test', {
method: 'POST',
});
// Belt-and-braces: confirm no `body` key was attached by the spread.
const [, options] = mockedApiFetchJSON.mock.calls[0]!;
expect(options).toEqual({ method: 'POST' });
});
});
@@ -0,0 +1,249 @@
import { describe, expect, it } from 'vitest';
import {
buildTemperatureRows,
toAgentFromResource,
toNodeFromProxmox,
type AgentPlatformData,
} from '@/components/Infrastructure/resourceDetailMappers';
import type { HostSensorSummary } from '@/types/api';
import type { Resource } from '@/types/resource';
/**
* Branch-coverage additions for resourceDetailMappers.ts targeting the arms
* left uncovered by the existing happy-path and prior branchcov suites.
*
* Every assertion is made against observable return values of the exported
* entry points (toAgentFromResource, toNodeFromProxmox, buildTemperatureRows).
* The module-private helpers they delegate to (getPreferredHostLabel,
* getPreferredResourceHostname, getPreferredInfrastructureDisplayName,
* formatPowerSensorLabel, formatFanSensorLabel, formatGPUStatsValue,
* formatFanRPM) are driven exclusively through those exports and never
* re-implemented or imported directly.
*/
const baseResource = (overrides: Partial<Resource> = {}): Resource =>
({
id: 'resource:host:hash-1',
type: 'agent',
name: 'tower',
displayName: 'Tower',
platformId: 'tower',
platformType: 'proxmox-pve',
sourceType: 'hybrid',
status: 'online',
lastSeen: 1_700_000_000_000,
cpu: { current: 12 },
memory: { current: 0.25, total: 1024, used: 256, free: 768 },
disk: { current: 0.25, total: 2048, used: 512, free: 1536 },
platformData: {
proxmox: { nodeName: 'pve-node-1' },
agent: { agentId: 'agent-canonical' },
},
...overrides,
}) as unknown as Resource;
describe('toAgentFromResource — uncovered arms', () => {
it('returns null when neither an explicit agent nor platformData.agent exists', () => {
// Drives the `if (!agent) return null` consequent (branch at line 289).
const resource = baseResource({
platformData: { proxmox: { nodeName: 'pve-node-1' } },
});
expect(toAgentFromResource(resource)).toBeNull();
});
it('returns null when platformData is absent entirely', () => {
expect(toAgentFromResource(baseResource({ platformData: undefined }))).toBeNull();
});
it('reads proxmox.cpuInfo.cores when cpuInfo is present (non-short-circuit optional chain)', () => {
// Drives the `cpuInfo?.cores` access arm (branch at line 291) by giving
// proxmox a populated cpuInfo, and confirms the value flows through the
// cpuCount `.find` predicate (`value > 0` evaluates true).
const resource = baseResource({
platformData: {
proxmox: { cpuInfo: { cores: 4 } },
agent: { agentId: 'agent-canonical' },
},
});
expect(toAgentFromResource(resource)?.cpuCount).toBe(4);
});
it('treats a non-positive cpuCount as not-usable and falls through to undefined', () => {
// cpuCount === 0 is a number so `typeof value === 'number'` is true, but
// `value > 0` is false — drives the false arm of the `.find` predicate's
// `&&` (branch at line 297). With no other positive candidate, cpuCount
// resolves to undefined.
const resource = baseResource({
platformData: {
proxmox: {},
agent: { agentId: 'agent-canonical', cpuCount: 0 },
},
});
expect(toAgentFromResource(resource)?.cpuCount).toBeUndefined();
});
it('accepts a positive cpuCount as the first usable candidate', () => {
const resource = baseResource({
platformData: {
proxmox: { cpuInfo: { cores: 99 } },
agent: { agentId: 'agent-canonical', cpuCount: 8 },
},
});
// First array element wins over the proxmox fallback.
expect(toAgentFromResource(resource)?.cpuCount).toBe(8);
});
it('falls back to getPreferredHostLabel when agent.hostname is absent', () => {
// Drives the `agent.hostname ?? getPreferredHostLabel(resource)` RHS
// (branch at line 299). resource.name remains set so the host label
// resolves to "tower" via getPreferredResourceHostname.
const resource = baseResource({
platformData: {
proxmox: {},
agent: { agentId: 'agent-canonical' },
},
});
expect(toAgentFromResource(resource)?.hostname).toBe('tower');
});
it('falls through hostname and displayName to resource.id when no identity source resolves', () => {
// Drives BOTH `||` short-circuit RHS arms in getPreferredHostLabel
// (branches at lines 188 and 189): no hostname, no displayName, no name,
// no platformId, no identity — so getPreferredResourceHostname is falsy
// (line 188 RHS evaluated) and getPreferredInfrastructureDisplayName also
// bottoms out at the empty resource.id (line 189 RHS evaluated).
//
// NOTE: the line-189 arm is only reachable when resource.id is empty
// (getPreferredInfrastructureDisplayName ultimately returns resource.id),
// i.e. a degenerate identity-less resource. It is reported here, not faked.
const resource = {
id: '',
type: 'agent',
status: 'online',
lastSeen: 1_700_000_000_000,
platformData: { proxmox: {} },
} as unknown as Resource;
const node = toNodeFromProxmox(resource);
expect(node?.name).toBe('');
expect(node?.host).toBe('');
});
it('uses resource.id for the agent id when no actionable agent id resolves', () => {
// Drives the `getActionableAgentIdFromResource(resource) || resource.id`
// RHS (branch at line 300). The resource carries no agent id in any form
// and no discoveryTarget.
const resource = baseResource({
platformData: { proxmox: { nodeName: 'pve-node-1' } },
});
const explicitAgent = { hostname: 'h' } as AgentPlatformData;
expect(toAgentFromResource(resource, explicitAgent)?.id).toBe('resource:host:hash-1');
});
it('defaults osName to "Unknown" when the agent omits it', () => {
const resource = baseResource();
const explicitAgent = {} as AgentPlatformData;
expect(toAgentFromResource(resource, explicitAgent)?.osName).toBe('Unknown');
});
it('defaults kernelVersion to "Unknown" when the agent omits it', () => {
const resource = baseResource();
const explicitAgent = {} as AgentPlatformData;
expect(toAgentFromResource(resource, explicitAgent)?.kernelVersion).toBe('Unknown');
});
it('resolves getPreferredHostLabel via resource.displayName when every hostname source is empty', () => {
// Drives the line-188 `||` RHS (getPreferredResourceHostname falsy) while
// keeping getPreferredInfrastructureDisplayName truthy via displayName, so
// the host label resolves to the displayName without touching resource.id.
const resource = baseResource({
name: undefined,
platformId: undefined,
identity: undefined,
displayName: 'Display Only',
platformData: { proxmox: {} },
});
const explicitAgent = {} as AgentPlatformData;
expect(toAgentFromResource(resource, explicitAgent)?.hostname).toBe('Display Only');
});
});
describe('buildTemperatureRows — uncovered sensor-label arms', () => {
const findRow = (sensors: HostSensorSummary, label: string) =>
buildTemperatureRows(sensors).find((r) => r.label === label);
it('does not append " Power" when the sensor name already carries a power keyword', () => {
// Drives the consequent of the power-keyword ternary in
// formatPowerSensorLabel (branch at line 342). "cpu_power" title-cases to
// "CPU Power", which already matches \b(power)\b, so the label is returned
// verbatim instead of becoming "CPU Power Power".
expect(findRow({ powerWatts: { cpu_power: 82.4 } }, 'CPU Power')?.value).toBe('82.4 W');
expect(
buildTemperatureRows({ powerWatts: { cpu_power: 82.4 } }).some(
(r) => r.label === 'CPU Power Power',
),
).toBe(false);
});
it('does not append " Power" for a "watts" keyword either', () => {
expect(findRow({ powerWatts: { psu_watts: 1500 } }, 'Psu Watts')?.value).toBe('1,500 W');
});
it('appends " Fan" when the fan sensor name lacks the fan keyword', () => {
// Drives the alternate of the fan-keyword ternary in formatFanSensorLabel
// (branch at line 347). "blower" has no \bfan\b match, so the suffix is
// added, yielding "Blower Fan".
expect(findRow({ fanRpm: { blower: 1000 } }, 'Blower Fan')?.value).toBe('1,000 RPM');
});
it('defaults GPU memoryUsedBytes to 0 when it is absent while memoryTotalBytes is valid', () => {
// Drives the alternate of the `used` ternary inside formatGPUStatsValue
// (branch at line 371): memoryTotalBytes is a positive finite number but
// memoryUsedBytes is undefined, so `used` falls back to 0 and is formatted
// as "0 B".
const rows = buildTemperatureRows({
gpu: [
{
id: '0',
name: 'Card',
memoryTotalBytes: 1024 * 1024 * 1024,
},
],
} as HostSensorSummary);
const gpuRow = rows.find((r) => r.label === 'GPU 0');
expect(gpuRow?.value).toBe('Card · 0 B / 1.00 GB');
});
it('defaults GPU memoryUsedBytes to 0 when it is a non-finite value', () => {
const rows = buildTemperatureRows({
gpu: [
{
id: '0',
name: 'Card',
memoryTotalBytes: 1024 * 1024 * 1024,
memoryUsedBytes: Number.NaN,
},
],
} as HostSensorSummary);
expect(rows.find((r) => r.label === 'GPU 0')?.value).toBe('Card · 0 B / 1.00 GB');
});
it('drops a fan row whose RPM is non-finite (formatFanRPM returns empty and is filtered)', () => {
// Drives the `if (!Number.isFinite(value)) return ''` consequent in
// formatFanRPM (branch at line 385); the empty value is then removed by
// the downstream `.filter(([, value]) => value)`.
expect(buildTemperatureRows({ fanRpm: { dead: Number.NaN } })).toEqual([]);
expect(buildTemperatureRows({ fanRpm: { dead: Number.POSITIVE_INFINITY } })).toEqual([]);
});
});
@@ -0,0 +1,251 @@
import { describe, expect, it } from 'vitest';
import type { Connection } from '@/api/connections';
import type { AgentFleetAgentDiagnostic, AgentFleetDiagnosticReason } from '@/api/agentDiagnostics';
import {
collectInfrastructureAgentDoctorTargets,
formatInfrastructureAgentDoctorReport,
getInfrastructureAgentDoctorUninstallHandoff,
} from '../infrastructureAgentUpdateCommandsModel';
import type {
InfrastructureAgentDoctorOptions,
InfrastructureAgentDoctorTarget,
} from '../infrastructureAgentUpdateCommandsModel';
// ---- Fixtures ---------------------------------------------------------------
// Mirrors the sibling infrastructureAgentUpdateCommandsModel.branchcov0722am.test.ts
// factories. This file targets the final nine branch arms V8 still reported as
// zero-hit after the five sibling suites ran. Every arm is driven through the
// exported orchestrators (collectInfrastructureAgentDoctorTargets /
// formatInfrastructureAgentDoctorReport / getInfrastructureAgentDoctorUninstallHandoff).
const agentConnection = (overrides: Partial<Connection> = {}): Connection => ({
id: 'agent:host-1',
type: 'agent',
name: 'host-1',
address: 'host-1.lab',
state: 'active',
stateReason: '',
enabled: true,
surfaces: ['host'],
scope: { host: true },
lastSeen: '2026-07-24T09:00:00Z',
lastError: null,
source: 'agent',
agentVersion: '6.2.0',
expectedAgentVersion: '6.2.0',
agentUpdateAvailable: false,
agentIdentity: { hostname: 'host-1', platform: 'linux', architecture: 'amd64' },
capabilities: { supportsPause: false, supportsScope: false, supportsTest: false },
...overrides,
});
const diagnostic = (
overrides: Partial<AgentFleetAgentDiagnostic> = {},
): AgentFleetAgentDiagnostic => ({
connectionId: 'agent:host-1',
rowKey: 'host-1',
id: 'host-1',
agentId: 'host-1',
name: 'host-1',
hostname: 'host-1',
types: ['host'],
status: 'warning',
version: '6.2.0',
profileId: 'profile-linux',
profileName: 'Linux servers',
profileVersion: 4,
deployedProfileVersion: 3,
reasons: [],
repairActions: [],
...overrides,
});
const runDoctor = (
agents: readonly Connection[],
options: Partial<InfrastructureAgentDoctorOptions> = {},
) =>
collectInfrastructureAgentDoctorTargets({
rows: [],
connections: agents,
diagnosticsAvailable: false,
...options,
});
const runOrphans = (
diagnostics: readonly AgentFleetAgentDiagnostic[],
options: Partial<InfrastructureAgentDoctorOptions> = {},
) =>
runDoctor([], {
diagnostics,
diagnosticsAvailable: true,
...options,
});
// Minimal hand-built doctor target for the pure report formatter (which does
// not re-derive fields, only reads them).
const doctorTarget = (
overrides: Partial<InfrastructureAgentDoctorTarget> = {},
): InfrastructureAgentDoctorTarget =>
({
key: 'agent:host-1',
connectionId: 'agent:host-1',
displayName: 'host-1',
contextLabel: 'Machine',
installFlags: [],
status: 'critical',
reasons: [],
evidence: [],
needsUpdate: false,
commandPlatform: null,
source: 'ledger-fallback',
...overrides,
}) as InfrastructureAgentDoctorTarget;
// ---- ledgerFallbackReasons: command_channel_disconnected via the && arm -------
// The sibling suites only ever drive this reason through the
// `remoteControl === 'disconnected'` short-circuit of the `||`, so the
// `(commandPolicy.status === 'blocked' && commandsEnabled)` right operand is
// never evaluated. Driving it with remoteControl !== 'disconnected' exercises
// both operands of the `&&`, and varying commandPolicy.reason exercises both
// arms of the evidence ternary on line 346.
describe('collectInfrastructureAgentDoctorTargets (command_channel_disconnected && arm)', () => {
it('emits the reason via commandPolicy blocked + commandsEnabled with reason evidence', () => {
// remoteControl 'enabled' forces the || past its left operand so the &&
// (status === 'blocked' && commandsEnabled) is fully evaluated and true.
const [target] = runDoctor([
agentConnection({
agentIdentity: { hostname: 'host-1', platform: 'linux', commandsEnabled: true },
fleet: {
versionDrift: 'current',
remoteControl: 'enabled',
commandPolicy: { status: 'blocked', reason: 'policy disabled by admin' },
} as Connection['fleet'],
}),
]);
expect(target?.status).toBe('critical');
expect(
target?.reasons.find((reason) => reason.code === 'command_channel_disconnected')?.evidence,
).toEqual(['policy disabled by admin']);
});
it('emits the reason with empty evidence when commandPolicy has no reason', () => {
// Same && arm, but commandPolicy.reason absent -> the `reason ? [reason] : []`
// ternary takes its [] alternate (the only previously-unhit arm of line 346).
const [target] = runDoctor([
agentConnection({
agentIdentity: { hostname: 'host-1', platform: 'linux', commandsEnabled: true },
fleet: {
versionDrift: 'current',
remoteControl: 'enabled',
commandPolicy: { status: 'blocked' },
} as Connection['fleet'],
}),
]);
expect(
target?.reasons.find((reason) => reason.code === 'command_channel_disconnected')?.evidence,
).toEqual([]);
});
});
// ---- getInfrastructureAgentDoctorUninstallHandoff: identity agentId fallback --
describe('getInfrastructureAgentDoctorUninstallHandoff (agentId || undefined arm)', () => {
it('resolves identity.agentId to undefined when both agentId and id are blank', () => {
// `agentId?.trim() || id?.trim() || undefined` only reaches its final
// `|| undefined` operand when both agentId and id trim to empty. A removed
// diagnostic with name/hostname present still renders and resolves a known
// platform, so the handoff is non-null and carries the undefined agentId.
const [target] = runOrphans([
diagnostic({
connectionId: 'agent:gone',
rowKey: 'gone-row',
id: '',
agentId: undefined,
name: 'gone',
hostname: 'gone-host',
platform: 'debian',
status: 'removed',
}),
]);
const handoff = getInfrastructureAgentDoctorUninstallHandoff(target!);
expect(handoff).not.toBeNull();
expect(handoff?.identity.agentId).toBeUndefined();
expect(handoff?.identity.hostname).toBe('gone-host');
expect(handoff?.commands.map((command) => command.platform)).toEqual(['linux']);
});
});
// ---- doctorReportLastSeen: numeric + non-finite arms ------------------------
// The sibling formatter test only passes a parseable ISO string, so the
// `typeof value === 'number'` consequent and the `!Number.isFinite(timestamp)`
// return arms of doctorReportLastSeen are never taken.
describe('formatInfrastructureAgentDoctorReport (doctorReportLastSeen arms)', () => {
it('formats a numeric lastSeen directly as an ISO timestamp', () => {
// typeof value === 'number' -> timestamp = value (the consequent arm).
const ms = Date.parse('2025-01-22T09:00:00Z');
const report = formatInfrastructureAgentDoctorReport([doctorTarget({ lastSeen: ms })]);
expect(report).toContain('Last seen 2025-01-22T09:00:00.000Z');
});
it('passes a non-parseable string lastSeen through verbatim', () => {
// Date.parse('not-a-date') -> NaN -> !isFinite -> typeof === 'string' arm.
const report = formatInfrastructureAgentDoctorReport([
doctorTarget({ lastSeen: 'not-a-date' }),
]);
expect(report).toContain('Last seen not-a-date');
});
it('omits Last seen when lastSeen is a non-finite number', () => {
// A NaN number passes the typeof === 'number' filter, fails isFinite, and is
// not a string -> doctorReportLastSeen returns undefined (the alternate arm).
const report = formatInfrastructureAgentDoctorReport([doctorTarget({ lastSeen: Number.NaN })]);
expect(report).not.toContain('Last seen');
});
});
// ---- formatInfrastructureAgentDoctorReport: header + profile + evidence ------
describe('formatInfrastructureAgentDoctorReport (header / profile / reason-evidence arms)', () => {
it('renders the header with no status breakdown for an empty target list', () => {
// No targets -> every status count is zero -> countParts empty -> the
// `countParts.length > 0 ? ... : ''` ternary takes its '' alternate.
expect(formatInfrastructureAgentDoctorReport([])).toBe('Pulse Agent Doctor report (0 agents)');
});
it('renders the profile label without a version parenthetical when profileVersionLabel is absent', () => {
// profileLabel present but profileVersionLabel undefined -> the
// `profileVersionLabel ? \` (${...})\` : ''` ternary takes its '' alternate.
const report = formatInfrastructureAgentDoctorReport([
doctorTarget({ status: 'critical', profileLabel: 'default', profileVersionLabel: undefined }),
]);
expect(report.split('\n')).toContain(' Profile default');
expect(report).not.toContain('Profile default (');
});
it('renders a reason whose evidence is absent without any evidence sublines', () => {
// `reason.evidence ?? []` -> when evidence is nullish the [] operand wins and
// no 4-space-indented evidence line is emitted for that reason.
const reasonWithoutEvidence: AgentFleetDiagnosticReason = {
code: 'agent_stale',
severity: 'critical',
message: 'No heartbeat.',
evidence: undefined,
};
const report = formatInfrastructureAgentDoctorReport([
doctorTarget({ status: 'critical', reasons: [reasonWithoutEvidence], evidence: [] }),
]);
expect(report).toContain(' - No heartbeat.');
// No 4-space-indented evidence line should appear anywhere in the report.
expect(report).not.toMatch(/\n {4}\S/);
});
});
@@ -0,0 +1,162 @@
import { describe, expect, it } from 'vitest';
import type { ClusterEndpoint } from '@/types/nodes';
import {
applyClusterNodeDisplayNamesLocally,
buildClusterNodeDisplayNameOverridesPayload,
} from '../nodeModalModel';
// Same fixture-builder shape as the sibling nodeModalModel.test.ts suite, but
// parameterised over the two optional fields the display-name helpers branch on
// (`nodeIdentity` + `displayName`) so each uncovered arm can be driven in
// isolation. `omitIdentity: true` yields a member whose nodeIdentity is absent
// (undefined), exercising the `!nodeIdentity` guards.
const endpoint = (
nodeName: string,
fields: { nodeIdentity?: string; displayName?: string; omitIdentity?: boolean } = {},
): ClusterEndpoint => ({
nodeId: `node/${nodeName}`,
nodeIdentity: fields.omitIdentity ? undefined : (fields.nodeIdentity ?? `cluster-${nodeName}`),
nodeName,
host: `https://${nodeName}.local:8006`,
ip: '10.0.0.1',
online: true,
lastSeen: '',
...(fields.displayName !== undefined ? { displayName: fields.displayName } : {}),
});
// ---- buildClusterNodeDisplayNameOverridesPayload ----------------------------
//
// The sibling suites only feed this helper non-empty endpoints whose identities
// are all present in the form map and whose values all differ from the saved
// names. The tests below drive the remaining arms:
// * `if (!endpoints?.length) return undefined` — empty + undefined endpoints.
// * `if (!nodeIdentity) return []` — a member with an absent identity.
// * `if (value === undefined || value.trim() === (endpoint.displayName ?? ''))`
// — both the `value === undefined` short-circuit (identity absent from the
// form map) and the equality-true arm (form value trims to the saved name,
// including the `(undefined ?? '')` fallback for a member with no saved
// display name).
// * `changed.length > 0 ? changed : undefined` — the falsy (`: undefined`)
// arm reached when every member is filtered out.
describe('buildClusterNodeDisplayNameOverridesPayload', () => {
it('returns undefined for empty or undefined endpoints (early return)', () => {
expect(
buildClusterNodeDisplayNameOverridesPayload([], { 'cluster-pve1': 'Compute' }),
).toBeUndefined();
expect(
buildClusterNodeDisplayNameOverridesPayload(undefined, { 'cluster-pve1': 'Compute' }),
).toBeUndefined();
});
it('drops members that have no nodeIdentity while still emitting changed siblings', () => {
const endpoints = [
endpoint('pve1', { omitIdentity: true, displayName: 'Lonely' }),
endpoint('pve2', { displayName: 'Old' }),
];
const payload = buildClusterNodeDisplayNameOverridesPayload(endpoints, {
// Present but unreachable: pve1 has no identity, so this entry is never
// matched against it.
'cluster-pve1': 'Whatever',
'cluster-pve2': 'New',
});
expect(payload).toEqual([{ nodeIdentity: 'cluster-pve2', displayName: 'New' }]);
});
it('skips members whose identity has no form entry (value === undefined short-circuit)', () => {
const endpoints = [
endpoint('pve1', { displayName: 'Alpha' }), // 'cluster-pve1' absent from the map
endpoint('pve2', { displayName: 'Beta' }), // 'cluster-pve2' present + changed
];
const payload = buildClusterNodeDisplayNameOverridesPayload(endpoints, {
'cluster-pve2': 'Beta2',
});
expect(payload).toEqual([{ nodeIdentity: 'cluster-pve2', displayName: 'Beta2' }]);
});
it('treats a form value that trims to the saved display name as unchanged', () => {
const payload = buildClusterNodeDisplayNameOverridesPayload(
[endpoint('pve1', { displayName: 'Compute' })],
{ 'cluster-pve1': ' Compute ' },
);
expect(payload).toBeUndefined();
});
it('treats an empty form value as unchanged when no display name is saved (?? "" fallback)', () => {
// saved displayName is absent -> (undefined ?? '') === ''; form '' trims to
// '' which equals it, so the member is filtered and the result collapses to
// undefined via the `changed.length > 0 ? changed : undefined` falsy arm.
const payload = buildClusterNodeDisplayNameOverridesPayload([endpoint('pve1')], {
'cluster-pve1': '',
});
expect(payload).toBeUndefined();
});
});
// ---- applyClusterNodeDisplayNamesLocally ------------------------------------
//
// Remaining arms:
// * `if (!endpoints?.length || !overrides?.length) return endpoints` — every
// falsy operand (empty/undefined endpoints, empty/undefined overrides),
// asserting the SAME reference is handed back untouched.
// * `if (!endpoint.nodeIdentity) return endpoint` — a member with an absent
// identity is returned by reference even when an override targets the
// identity string it would have had.
// * `const value = byIdentity.get(...); if (value === undefined) return endpoint`
// — no override targets this member's identity.
// * `displayName: value || undefined` — the falsy arm where an empty-string
// override clears the saved display name.
describe('applyClusterNodeDisplayNamesLocally', () => {
it('returns the input by reference when there is nothing to apply', () => {
const endpoints = [endpoint('pve1')];
// overrides empty or undefined -> endpoints handed back untouched
expect(applyClusterNodeDisplayNamesLocally(endpoints, [])).toBe(endpoints);
expect(applyClusterNodeDisplayNamesLocally(endpoints, undefined)).toBe(endpoints);
// endpoints empty or undefined -> early return of that same value
expect(
applyClusterNodeDisplayNamesLocally([], [{ nodeIdentity: 'cluster-pve1', displayName: 'X' }]),
).toEqual([]);
expect(
applyClusterNodeDisplayNamesLocally(undefined, [
{ nodeIdentity: 'cluster-pve1', displayName: 'X' },
]),
).toBeUndefined();
});
it('leaves members without a nodeIdentity untouched (returns the same endpoint)', () => {
const endpoints = [
endpoint('pve1', { omitIdentity: true, displayName: 'Kept' }),
endpoint('pve2', { displayName: 'Old' }),
];
const result = applyClusterNodeDisplayNamesLocally(endpoints, [
{ nodeIdentity: 'cluster-pve1', displayName: 'ShouldNotApply' },
{ nodeIdentity: 'cluster-pve2', displayName: 'New' },
]);
// pve1 short-circuits on `!nodeIdentity`: same object, display name intact.
expect(result?.[0]).toBe(endpoints[0]);
expect(result?.[0].displayName).toBe('Kept');
// pve2 is matched and updated.
expect(result?.[1].displayName).toBe('New');
});
it('leaves a member untouched when no override targets its identity', () => {
const endpoints = [endpoint('pve1', { displayName: 'Original' })];
const result = applyClusterNodeDisplayNamesLocally(endpoints, [
{ nodeIdentity: 'cluster-somebody-else', displayName: 'Ignored' },
]);
expect(result?.[0]).toBe(endpoints[0]);
expect(result?.[0].displayName).toBe('Original');
});
it('clears the display name when the override value is an empty string (value || undefined)', () => {
const endpoints = [endpoint('pve1', { displayName: 'OldName' })];
const result = applyClusterNodeDisplayNamesLocally(endpoints, [
{ nodeIdentity: 'cluster-pve1', displayName: '' },
]);
expect(result?.[0].displayName).toBeUndefined();
// a fresh object is produced (spread), not the original reference
expect(result?.[0]).not.toBe(endpoints[0]);
expect(result?.[0].nodeName).toBe('pve1');
});
});
@@ -0,0 +1,197 @@
import { describe, expect, it } from 'vitest';
import type { AnomalyReport } from '@/types/aiIntelligence';
import type { StackedMemoryBarProps } from '../stackedMemoryBarModel';
import { buildStackedMemoryBarPresentation } from '../stackedMemoryBarModel';
// The four module-private helpers (getEffectiveCache, getUtilizationPercent,
// getSegments, getTooltipRows) are exercised through the exported
// buildStackedMemoryBarPresentation. The happy-path spec
// (stackedMemoryBarModel.test.ts) covers total > 0 with used/cache/balloon
// combinations but never drives: total <= 0 (the division guard that falls
// through to percentOnly), any swap input, or any anomaly input. Those nine
// branches are the sole focus below — every assertion checks a concrete
// observable value rather than a recomputed source expression.
const GiB = 1024 ** 3;
// Normal-band RGBA for memory (default thresholds warning 75 / critical 85);
// a 70% utilization sits below warning -> 'normal' severity color.
const NORMAL_RGBA = 'rgba(34, 197, 94, 0.6)';
const makeProps = (overrides: Partial<StackedMemoryBarProps> = {}): StackedMemoryBarProps => ({
used: 0,
total: 0,
...overrides,
});
const makeAnomaly = (overrides: Partial<AnomalyReport> = {}): AnomalyReport => ({
resource_id: 'r1',
resource_name: 'resource-1',
resource_type: 'guest',
metric: 'memory',
current_value: 30,
baseline_mean: 10,
baseline_std_dev: 1,
z_score: 20,
severity: 'high',
description: 'Memory spike',
...overrides,
});
describe('stackedMemoryBarModel (branch coverage 0724pm)', () => {
describe('getUtilizationPercent — total <= 0 division guard (percentOnly fallback)', () => {
it('uses percentOnly when total <= 0 and percentOnly is finite', () => {
// Branch 8: false arm of `if (props.total > 0)` -> percentOnly path.
// Math.max(0, Math.min(70, 100)) === 70.
const p = buildStackedMemoryBarPresentation(makeProps({ total: 0, percentOnly: 70 }), 400);
expect(p.displayPercentValue).toBe(70);
expect(p.displayLabel).toBe('70%');
});
it('clamps percentOnly > 100 down to 100', () => {
// Math.max(0, Math.min(150, 100)) === 100 — the upper clamp arm.
const p = buildStackedMemoryBarPresentation(makeProps({ total: 0, percentOnly: 150 }), 400);
expect(p.displayPercentValue).toBe(100);
expect(p.displayLabel).toBe('100%');
});
it('clamps percentOnly < 0 up to 0', () => {
// Math.max(0, Math.min(-5, 100)) === 0 — the lower clamp arm. The 0%
// utilization then propagates to getSegments (no segment) and to the
// tooltip else arm.
const p = buildStackedMemoryBarPresentation(makeProps({ total: 0, percentOnly: -5 }), 400);
expect(p.displayPercentValue).toBe(0);
expect(p.segments).toEqual([]);
});
it('returns 0 when total <= 0 and percentOnly is NaN (not finite)', () => {
// Number.isFinite(NaN) === false -> final `return 0` arm.
const p = buildStackedMemoryBarPresentation(
makeProps({ total: 0, percentOnly: Number.NaN }),
400,
);
expect(p.displayPercentValue).toBe(0);
});
it('returns 0 when total <= 0 and percentOnly is absent', () => {
// percentOnly undefined -> Number.isFinite(undefined) === false -> 0.
const p = buildStackedMemoryBarPresentation(makeProps({ total: 0 }), 400);
expect(p.displayPercentValue).toBe(0);
expect(p.displayLabel).toBe('0%');
});
});
describe('getSegments — total <= 0 path', () => {
it('emits a single Utilization segment when total <= 0 and utilization > 0', () => {
// Branch 12: true arm of `if (props.total <= 0)`; utilizationPercent 70
// exceeds the inner `<= 0` guard -> single Utilization segment colored
// via the default memory thresholds (70 < 75 -> normal).
const p = buildStackedMemoryBarPresentation(makeProps({ total: 0, percentOnly: 70 }), 400);
expect(p.segments).toHaveLength(1);
expect(p.segments[0]).toStrictEqual({
color: NORMAL_RGBA,
label: 'Utilization',
leftPercent: 0,
widthPercent: 70,
});
});
it('emits no segments when total <= 0 and utilization <= 0', () => {
// Branch 12 entered; inner `if (utilizationPercent <= 0)` true -> [].
const p = buildStackedMemoryBarPresentation(makeProps({ total: 0 }), 400);
expect(p.segments).toEqual([]);
});
});
describe('getTooltipRows — else arm (unavailable=false AND total <= 0)', () => {
it('renders a single Utilization tooltip row carrying the percent-only label', () => {
// Branch 32: the final else arm (unavailable false, total <= 0) pushes a
// Utilization row whose value is the formatted displayLabel, not a byte
// reading — distinct from both the unavailable and total > 0 arms.
const p = buildStackedMemoryBarPresentation(makeProps({ total: 0, percentOnly: 70 }), 400);
expect(p.tooltipRows).toEqual([
{
borderTop: true,
label: 'Utilization',
labelClass: 'text-blue-300',
value: '70%',
},
]);
});
});
describe('swap integration (showSwapBar, swapBarPercent, Swap tooltip row)', () => {
it('enables the swap bar, reports the swap ratio, and adds a Swap tooltip row', () => {
// Branches 41 (showSwapBar && true), 42 (swapTotal && >0 true),
// 43 (ternary ? arm -> Math.min computation):
// swapBarPercent = Math.min((2 GiB / 8 GiB) * 100, 100) === 25.
// Branch 33: tooltip Swap row pushed because total > 0 && hasSwap.
const p = buildStackedMemoryBarPresentation(
makeProps({ used: 4 * GiB, total: 16 * GiB, swapUsed: 2 * GiB, swapTotal: 8 * GiB }),
400,
);
expect(p.showSwapBar).toBe(true);
expect(p.swapBarPercent).toBe(25);
const swap = p.tooltipRows.find((row) => row.label === 'Swap');
expect(swap).toBeDefined();
expect(swap?.borderTop).toBe(true);
expect(swap?.labelClass).toBe('text-amber-400');
expect(swap?.value).toBe('2.00 GB / 8.00 GB');
});
it('clamps swapBarPercent to 100 when swapUsed exceeds swapTotal', () => {
// Branch 43: Math.min((16 GiB / 8 GiB) * 100, 100) === Math.min(200, 100).
const p = buildStackedMemoryBarPresentation(
makeProps({ used: 4 * GiB, total: 16 * GiB, swapUsed: 16 * GiB, swapTotal: 8 * GiB }),
400,
);
expect(p.swapBarPercent).toBe(100);
expect(p.showSwapBar).toBe(true);
});
it('defaults swapUsed to 0 when absent (Swap row still shown, swap bar suppressed)', () => {
// Drives the `|| 0` fallback arms for swapUsed at line 246 (tooltip
// value), 278 (showSwapBar), and 281 (swapBarPercent). swapTotal > 0
// keeps the Swap tooltip row visible (hasSwap true); swapUsed absent ->
// showSwapBar false, swapBarPercent 0, tooltip reads '0 B / 8.00 GB'.
const p = buildStackedMemoryBarPresentation(
makeProps({ used: 4 * GiB, total: 16 * GiB, swapTotal: 8 * GiB }),
400,
);
expect(p.showSwapBar).toBe(false);
expect(p.swapBarPercent).toBe(0);
const swap = p.tooltipRows.find((row) => row.label === 'Swap');
expect(swap).toBeDefined();
expect(swap?.value).toBe('0 B / 8.00 GB');
});
});
describe('anomaly threading (anomalyClass, anomalyDescription, anomalyRatio)', () => {
it('maps a known severity to its anomaly class and surfaces the description', () => {
// Branches 39 (props.anomaly truthy -> ANOMALY_SEVERITY_CLASS[severity])
// and 40 (props.anomaly?.description non-null/undefined access).
// severity 'high' -> 'text-orange-400'; ratio 30/10 = 3.0 -> '3.0x'.
const p = buildStackedMemoryBarPresentation(
makeProps({ total: 16 * GiB, anomaly: makeAnomaly() }),
400,
);
expect(p.anomalyClass).toBe('text-orange-400');
expect(p.anomalyDescription).toBe('Memory spike');
expect(p.anomalyRatio).toBe('3.0x');
});
it('falls back to the default anomaly class for an unknown severity', () => {
// Branch 39 true arm + the ?? 'text-yellow-400' fallback:
// ANOMALY_SEVERITY_CLASS has no 'weird' key -> undefined ?? fallback.
const p = buildStackedMemoryBarPresentation(
makeProps({ total: 16 * GiB, anomaly: makeAnomaly({ severity: 'weird' }) }),
400,
);
expect(p.anomalyClass).toBe('text-yellow-400');
// description is still threaded through the optional-chain true arm.
expect(p.anomalyDescription).toBe('Memory spike');
});
});
});
@@ -0,0 +1,220 @@
import { describe, expect, it } from 'vitest';
import {
buildCanonicalNodeScopedWorkloadId,
buildKubernetesWorkloadMetadataId,
getCanonicalWorkloadIdForResource,
getWorkloadMetadataId,
getWorkloadMetadataIdCandidates,
isDockerManagedAppContainer,
} from '@/utils/workloads';
import type { WorkloadGuest } from '@/types/workloads';
// `type`, `platformType`, and `containerRuntime` are optional on WorkloadGuest
// (`platformType`/`containerRuntime` via `?`, `type` via the VM | Container
// base). isDockerManagedAppContainer reads them defensively with `|| ''`, so
// the malformed-input cases below opt out of the contract intentionally.
type DockerManagedAppContainerInput = Pick<
WorkloadGuest,
'workloadType' | 'type' | 'platformType' | 'containerRuntime'
>;
// Covers the type/runtime fallback arms of isDockerManagedAppContainer that the
// happy-path specs never reach (existing specs only exercise the platformType
// 'docker' -> true and platformType 'truenas' -> false early returns).
describe('isDockerManagedAppContainer fallback signals', () => {
it('returns true from the raw type fallback when type is docker and platform type is unset', () => {
expect(
isDockerManagedAppContainer({
workloadType: 'app-container',
type: 'docker',
platformType: undefined,
containerRuntime: undefined,
} as unknown as DockerManagedAppContainerInput),
).toBe(true);
});
it('returns true from the container runtime fallback when runtime is docker', () => {
expect(
isDockerManagedAppContainer({
workloadType: 'app-container',
type: 'app-container',
platformType: '',
containerRuntime: 'docker',
}),
).toBe(true);
});
it('returns true from the container runtime fallback when runtime is podman', () => {
expect(
isDockerManagedAppContainer({
workloadType: 'app-container',
type: 'app-container',
platformType: '',
containerRuntime: 'podman',
}),
).toBe(true);
});
it('returns false when no docker signal matches across type, platform, or runtime', () => {
expect(
isDockerManagedAppContainer({
workloadType: 'app-container',
type: 'app-container',
platformType: '',
containerRuntime: 'containerd',
}),
).toBe(false);
});
it('returns false when type is undefined and runtime is unset', () => {
expect(
isDockerManagedAppContainer({
workloadType: 'app-container',
type: undefined,
platformType: undefined,
containerRuntime: undefined,
} as unknown as DockerManagedAppContainerInput),
).toBe(false);
});
it('is case-insensitive across the type and runtime fallbacks', () => {
expect(
isDockerManagedAppContainer({
workloadType: 'app-container',
type: ' Docker ',
platformType: ' Unraid ',
containerRuntime: '',
}),
).toBe(true);
expect(
isDockerManagedAppContainer({
workloadType: 'app-container',
type: 'app-container',
platformType: '',
containerRuntime: 'PODMAN',
}),
).toBe(true);
});
});
// Covers the kind-coercion ternary in buildKubernetesWorkloadMetadataId. The
// happy-path specs only use the default kind ('pod'); the deployment/service
// arms and the falsy-kind guard are uncovered.
describe('buildKubernetesWorkloadMetadataId kind coercion', () => {
const base = {
kubernetesClusterId: 'cluster-a',
namespace: 'payments',
name: 'checkout',
};
it('maps k8s-deployment kind to the deployment segment', () => {
expect(buildKubernetesWorkloadMetadataId({ ...base, kind: 'k8s-deployment' })).toBe(
'k8s-workload:cluster-a:deployment:payments:checkout',
);
});
it('maps deployment kind to the deployment segment', () => {
expect(buildKubernetesWorkloadMetadataId({ ...base, kind: 'deployment' })).toBe(
'k8s-workload:cluster-a:deployment:payments:checkout',
);
});
it('maps k8s-service kind to the service segment', () => {
expect(buildKubernetesWorkloadMetadataId({ ...base, kind: 'k8s-service' })).toBe(
'k8s-workload:cluster-a:service:payments:checkout',
);
});
it('maps service kind to the service segment', () => {
expect(buildKubernetesWorkloadMetadataId({ ...base, kind: 'service' })).toBe(
'k8s-workload:cluster-a:service:payments:checkout',
);
});
it('is case-insensitive when matching the kind token', () => {
expect(buildKubernetesWorkloadMetadataId({ ...base, kind: 'Deployment' })).toBe(
'k8s-workload:cluster-a:deployment:payments:checkout',
);
expect(buildKubernetesWorkloadMetadataId({ ...base, kind: ' K8S-Service ' })).toBe(
'k8s-workload:cluster-a:service:payments:checkout',
);
});
it('returns null when kind is empty (no workload kind segment)', () => {
expect(buildKubernetesWorkloadMetadataId({ ...base, kind: '' })).toBeNull();
});
});
// Covers the empty-candidates fallback (`candidates[0] || getCanonicalWorkloadId`)
// in getWorkloadMetadataId. The happy-path specs always resolve at least one
// candidate, so the `||` fallback is never exercised.
describe('getWorkloadMetadataId empty-candidates fallback', () => {
const baseGuest = {
id: '',
name: '',
workloadType: 'app-container' as const,
type: 'app-container',
platformType: 'truenas',
instance: '',
node: '',
vmid: 0,
};
it('resolves zero metadata id candidates for a non-docker-managed app-container with a blank id', () => {
expect(getWorkloadMetadataIdCandidates(baseGuest)).toEqual([]);
});
it('falls back to the canonical workload id (the blank id) when no candidates resolve', () => {
expect(getWorkloadMetadataId(baseGuest)).toBe('');
});
});
// Covers the non-finite vmid arm (`Number.isFinite(vmid) ? Number(vmid) : 0`)
// in buildCanonicalNodeScopedWorkloadId. The happy-path specs only pass numeric
// vmids, so the `: 0` coercion (which then short-circuits to null) is cold.
describe('buildCanonicalNodeScopedWorkloadId non-finite vmid coercion', () => {
it('coerces a null vmid to 0 and returns null', () => {
expect(
buildCanonicalNodeScopedWorkloadId({ instance: 'homelab', node: 'pve1', vmid: null }),
).toBeNull();
});
});
// Covers the proxmox.nodeName fallback in getCanonicalWorkloadIdForResource.
// The happy-path specs populate proxmox.node, so the `node || nodeName` fallback
// to nodeName is never exercised.
describe('getCanonicalWorkloadIdForResource proxmox nodeName fallback', () => {
it('uses proxmox.nodeName when proxmox.node is absent', () => {
expect(
getCanonicalWorkloadIdForResource({
id: 'vm-fallback',
type: 'vm',
clusterId: 'Core Fabric',
proxmox: { nodeName: 'pve3', vmid: 112 },
} as any),
).toBe('Core Fabric:pve3:112');
});
it('prefers proxmox.node over proxmox.nodeName when both are present', () => {
expect(
getCanonicalWorkloadIdForResource({
id: 'lxc-fallback',
type: 'system-container',
clusterId: 'Core Fabric',
proxmox: { node: 'pve1', nodeName: 'pve3', vmid: 200 },
} as any),
).toBe('Core Fabric:pve1:200');
});
it('falls back to the resource id when only nodeName is present but it is blank', () => {
expect(
getCanonicalWorkloadIdForResource({
id: 'vm-orphan',
type: 'vm',
clusterId: 'Core Fabric',
proxmox: { nodeName: ' ', vmid: 112 },
} as any),
).toBe('vm-orphan');
});
});