Close remaining branch gaps in storage and discovery presentation

Four frontend modules had a small measured residue their existing specs never
reached, including four functions no test called at all. Deltas below are
uncovered counts, module-scoped, existing specs vs existing plus new.

- storagePageState: getActiveStorageNodeOptions was never called; branches
  5 -> 1 uncovered, functions 1 -> 0.
- discoveryPresentation: the three suggested-URL class helpers were never
  called; branches 3 -> 1 uncovered, functions 3 -> 0.
- infrastructureOnboardingPresentation:
  getInfrastructureSourcePickerItemForRouteStep was never called; functions
  1 -> 0.
- storageBarPresentation: branches 7 -> 0 uncovered, so the module is now
  fully branch-covered.

No source file is modified. Adversarial review found no rejects. Three
tautological assertions of the form f() equals f(), one test duplicating an
arm the existing spec already covered, and four cases re-exercising an already
covered arm of a two-arm selector were removed before committing; re-measuring
afterwards produced identical coverage, which confirms they contributed
nothing.

PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT=test-only branch coverage, no source or contract change

Contract-Neutral: test-only branch coverage, no source or contract change
This commit is contained in:
rcourtman
2026-07-23 17:06:47 +01:00
parent 5fde028276
commit c46ce90484
4 changed files with 767 additions and 0 deletions
@@ -0,0 +1,154 @@
import { describe, expect, it } from 'vitest';
import type { Resource } from '@/types/resource';
import {
getActiveStorageNodeOptions,
getStorageFilterGroupBy,
storageResourceMatchesSourceFilter,
syncExpandedStorageGroups,
toStorageHealthFilterValue,
type StoragePageNodeOption,
type StorageStatusFilterValue,
type StorageView,
} from '@/components/Storage/storagePageState';
const makeNodeOption = (overrides: Partial<StoragePageNodeOption> = {}): StoragePageNodeOption => ({
id: 'node-1',
label: 'pve1',
...overrides,
});
const makeFilterableResource = (overrides: Partial<Resource> = {}): Resource =>
({
id: 'node-1',
type: 'agent',
name: 'pve1',
platformType: 'proxmox-pve',
sourceType: 'api',
platformData: { sources: ['proxmox-pve', 'agent'] },
...overrides,
}) as Resource;
describe('storagePageState branch coverage 0723pm', () => {
describe('getActiveStorageNodeOptions', () => {
it('returns the diskNodeOptions array (by reference) when view is "disks"', () => {
const nodes = [makeNodeOption({ id: 'node-1', label: 'pve1' })];
const disks = [makeNodeOption({ id: 'node-2', label: 'pve2' })];
// Reference equality proves the function performs no defensive copy and
// selects the disk set, not the pool set.
expect(getActiveStorageNodeOptions('disks', nodes, disks)).toBe(disks);
});
it('returns the nodeOptions array (by reference) when view is "pools"', () => {
const nodes = [makeNodeOption({ id: 'node-1', label: 'pve1' })];
const disks = [makeNodeOption({ id: 'node-2', label: 'pve2' })];
expect(getActiveStorageNodeOptions('pools', nodes, disks)).toBe(nodes);
});
it('preserves a several-node ordering without sorting', () => {
const disks = [
makeNodeOption({ id: 'c', label: 'gamma' }),
makeNodeOption({ id: 'a', label: 'alpha' }),
makeNodeOption({ id: 'b', label: 'beta' }),
];
const result = getActiveStorageNodeOptions('disks', [], disks);
expect(result.map((node) => node.id)).toEqual(['c', 'a', 'b']);
});
it('does NOT dedupe duplicate ids or labels (returns them as-is)', () => {
// The function is a pure selector: it performs no deduplication.
const disks = [
makeNodeOption({ id: 'dup', label: 'same' }),
makeNodeOption({ id: 'dup', label: 'same' }),
];
const result = getActiveStorageNodeOptions('disks', [], disks);
expect(result).toHaveLength(2);
expect(result.map((node) => node.id)).toEqual(['dup', 'dup']);
});
it('preserves options whose label is missing/empty (no filtering by name)', () => {
const disks = [
makeNodeOption({ id: 'has-name', label: 'pve1' }),
makeNodeOption({ id: 'no-name', label: '' }),
];
const result = getActiveStorageNodeOptions('disks', [], disks);
expect(result.map((node) => node.id)).toEqual(['has-name', 'no-name']);
});
it('treats an unrecognized view string as pools (returns nodeOptions)', () => {
// StorageView is a closed union, but the function only special-cases
// the literal "disks"; every other value resolves to the pool set.
const nodes = [makeNodeOption({ id: 'node-1', label: 'pve1' })];
const disks = [makeNodeOption({ id: 'node-2', label: 'pve2' })];
const result = getActiveStorageNodeOptions('banana' as unknown as StorageView, nodes, disks);
expect(result).toBe(nodes);
});
});
describe('getStorageFilterGroupBy (ternary true arm)', () => {
// Existing coverage only exercised value === 'node', which hits the false
// arm (default). These exercise the true arm: an accepted canonical key is
// returned unchanged.
it.each(['type', 'status', 'none'] as const)(
'returns %s unchanged when it is a recognized group key (true arm)',
(value) => {
expect(getStorageFilterGroupBy(value)).toBe(value);
},
);
});
describe('toStorageHealthFilterValue (value === "all" arm)', () => {
// Existing coverage exercised attention/available and the fallthrough, but
// never the literal "all" early-return.
it('returns "all" when the value is "all"', () => {
const input: StorageStatusFilterValue = 'all';
expect(toStorageHealthFilterValue(input)).toBe('all');
});
});
describe('storageResourceMatchesSourceFilter (sources-is-not-an-array arm)', () => {
it('falls back to the empty sources list when platformData.sources is missing', () => {
// Array.isArray(undefined) === false => the `: []` arm fires, so matching
// must rely on platformType / sourceType only.
const node = makeFilterableResource({ platformData: {} });
expect(storageResourceMatchesSourceFilter(node, 'proxmox-pve')).toBe(true);
expect(storageResourceMatchesSourceFilter(node, 'truenas')).toBe(false);
});
it('falls back to the empty sources list when platformData.sources is a non-array', () => {
const node = makeFilterableResource({
platformData: { sources: 'proxmox-pve' } as unknown as Resource['platformData'],
});
// platformType / sourceType still match even though the bogus sources value is ignored.
expect(storageResourceMatchesSourceFilter(node, 'proxmox-pve')).toBe(true);
expect(storageResourceMatchesSourceFilter(node, 'api')).toBe(true);
expect(storageResourceMatchesSourceFilter(node, 'agent')).toBe(false);
});
it('matches via sourceType alone when platformData is undefined', () => {
const node = makeFilterableResource({
platformData: undefined,
platformType: undefined,
sourceType: 'agent',
});
expect(storageResourceMatchesSourceFilter(node, 'agent')).toBe(true);
expect(storageResourceMatchesSourceFilter(node, 'proxmox-pve')).toBe(false);
});
});
describe('syncExpandedStorageGroups (changed=false arm)', () => {
it('returns the previous set unchanged (same reference) when every key is already present', () => {
// No new keys => `changed` stays false => the `: previous` arm fires and
// the input reference is returned verbatim.
const previous = new Set(['A', 'B']);
const result = syncExpandedStorageGroups(previous, ['A', 'B']);
expect(result).toBe(previous);
expect(result).toEqual(new Set(['A', 'B']));
});
it('returns previous unchanged when allKeys is empty but previous is non-empty', () => {
const previous = new Set(['A']);
const result = syncExpandedStorageGroups(previous, []);
expect(result).toBe(previous);
});
});
});
@@ -0,0 +1,296 @@
import { describe, expect, it } from 'vitest';
import type { ZFSPool } from '@/types/api';
import {
getStorageBarLabel,
getStorageBarUsagePercent,
getStorageBarZfsSummary,
} from '@/features/storageBackups/storageBarPresentation';
// ---------------------------------------------------------------------------
// Fixture builder — mirrors the convention in storageBarPresentation.test.ts
// (which casts a partial with `as any`) but stays fully typed so the pre-push
// tsc stays happy. Only the fields getStorageBarZfsSummary reads need to be
// overridden; everything else defaults to a clean ONLINE pool with no errors.
// ---------------------------------------------------------------------------
const makeZfsPool = (overrides: Partial<ZFSPool> = {}): ZFSPool =>
({
name: 'tank',
state: 'ONLINE',
status: 'Healthy',
scan: 'none',
readErrors: 0,
writeErrors: 0,
checksumErrors: 0,
devices: [],
...overrides,
}) as ZFSPool;
// ===========================================================================
// Branch map for storageBarPresentation.ts
//
// getStorageBarUsagePercent (L36-39)
// L37 `if (total <= 0) return 0`
// true arm : total<=0 -> 0 [UNCOVERED — covered here]
// false arm : total>0 (covered by happy path)
//
// getStorageBarZfsSummary (L61-77)
// L62 `if (!zfsPool) return null`
// true arm : zfsPool missing/null [UNCOVERED — covered here]
// false arm (covered by happy path)
// L64 `zfsPool.scan || ''`
// falsy arm : scan undefined/'' [UNCOVERED — covered here]
// truthy arm (covered by happy path)
// L65 `readErrors>0 || writeErrors>0 || checksumErrors>0`
// readErrors>0 short-circuit only [UNCOVERED — covered here]
// writeErrors>0 arm [UNCOVERED — covered here]
// checksumErrors>0 arm [UNCOVERED — covered here]
// all-false arm [UNCOVERED — covered here]
// L69 `scan.toLowerCase().includes('scrub')`
// true arm [UNCOVERED — covered here]
// false arm (covered by happy path: 'resilver…')
// L70 `scan.toLowerCase().includes('resilver')`
// true arm (covered by happy path)
// false arm [UNCOVERED — covered here]
// L72 `scan && scan !== 'none' ? scan : ''`
// false arm : scan==='none' [UNCOVERED — covered here]
// true arm (covered by happy path)
// L73 `hasErrors ? 'Errors:…' : ''`
// false arm [UNCOVERED — covered here]
// true arm (covered by happy path)
// ===========================================================================
describe('storageBarPresentation branch coverage', () => {
// =========================================================================
// getStorageBarUsagePercent — L37 `if (total <= 0) return 0` true arm
// =========================================================================
describe('getStorageBarUsagePercent — total<=0 guard (L37 true arm)', () => {
it('returns exactly 0 when total is 0 (no NaN, no Infinity)', () => {
expect(getStorageBarUsagePercent(40, 0)).toBe(0);
});
it('returns exactly 0 when total is negative (guard is <=, not <)', () => {
// Locks the boundary: -100 is strictly < 0, proving the guard matches
// the `<= 0` predicate rather than `=== 0`.
expect(getStorageBarUsagePercent(40, -100)).toBe(0);
});
it('still computes a real ratio for total === 1 (proves guard is total<=0, not total<1)', () => {
// 0.5 of 1 byte = 50% — sanity check that small positive totals are NOT
// collapsed to 0 by some other implicit clamp.
expect(getStorageBarUsagePercent(0.5, 1)).toBe(50);
});
});
describe('getStorageBarUsagePercent — boundary ratios', () => {
it('returns exactly 0 when used is 0 (zero percent usage)', () => {
expect(getStorageBarUsagePercent(0, 100)).toBe(0);
});
it('returns a value over 100 when used exceeds total (over-capacity)', () => {
expect(getStorageBarUsagePercent(150, 100)).toBe(150);
});
it('returns a negative ratio when used is negative (no clamp on `used`)', () => {
// Documents current behaviour: only `total` is guarded; `used` flows
// straight into the division, so a malformed negative `used` surfaces
// as a negative percentage rather than 0.
expect(getStorageBarUsagePercent(-10, 100)).toBe(-10);
});
});
// =========================================================================
// getStorageBarLabel — composes the percent guard + formatters; pin the
// observable string for the boundary cases so the format is locked.
// =========================================================================
describe('getStorageBarLabel — observable label for guard + over-100 cases', () => {
it('produces "0% (used/0 B)" when total is 0 (percent short-circuited to 0)', () => {
// percent(40,0)=0 -> formatPercent(0)='0%'; formatBytes(40)='40.0 B'; formatBytes(0)='0 B'.
expect(getStorageBarLabel(40, 0)).toBe('0% (40.0 B/0 B)');
});
it('produces a >100% label when used exceeds total (formatPercent has no clamp either)', () => {
// percent(150,100)=150 -> '150%'; formatBytes(150)='150 B'; formatBytes(100)='100 B'.
expect(getStorageBarLabel(150, 100)).toBe('150% (150 B/100 B)');
});
});
// =========================================================================
// getStorageBarZfsSummary — L62 `if (!zfsPool) return null` true arm
// =========================================================================
describe('getStorageBarZfsSummary — missing pool (L62 true arm)', () => {
it('returns null when called with no argument', () => {
expect(getStorageBarZfsSummary()).toBeNull();
});
it('returns null when called with explicit undefined', () => {
expect(getStorageBarZfsSummary(undefined)).toBeNull();
});
it('returns null when called with null cast through unknown (defensive null guard)', () => {
// ZFSPool is typed as a required interface, so null must be injected
// via `as unknown as ZFSPool` to bypass strict null checks at the call
// site without weakening the source signature.
expect(getStorageBarZfsSummary(null as unknown as ZFSPool)).toBeNull();
});
});
// =========================================================================
// getStorageBarZfsSummary — L64 `zfsPool.scan || ''` falsy arm
// =========================================================================
describe('getStorageBarZfsSummary — scan falsy fallback (L64 falsy arm)', () => {
it('substitutes empty string when scan is undefined (observable via output.scan / substring flags)', () => {
// L64 falsy -> scan becomes '' -> L72 ternary false -> output.scan = ''.
// Also proves the downstream substring checks run against '', so both
// isScrubbing and isResilvering come back false.
const summary = getStorageBarZfsSummary(
makeZfsPool({ scan: undefined } as unknown as ZFSPool),
);
expect(summary).not.toBeNull();
expect(summary!.scan).toBe('');
expect(summary!.isScrubbing).toBe(false);
expect(summary!.isResilvering).toBe(false);
});
it('substitutes empty string when scan is the empty string', () => {
const summary = getStorageBarZfsSummary(makeZfsPool({ scan: '' }));
expect(summary!.scan).toBe('');
});
});
// =========================================================================
// getStorageBarZfsSummary — L65 OR chain (3 arms + all-false arm)
// =========================================================================
describe('getStorageBarZfsSummary — error-count OR chain (L65)', () => {
it('flags hasErrors=true via readErrors only (first OR arm, short-circuit)', () => {
const summary = getStorageBarZfsSummary(
makeZfsPool({ readErrors: 1, writeErrors: 0, checksumErrors: 0 }),
);
expect(summary!.hasErrors).toBe(true);
expect(summary!.errorSummary).toBe('Errors: R:1 W:0 C:0');
});
it('flags hasErrors=true via writeErrors only (second OR arm)', () => {
const summary = getStorageBarZfsSummary(
makeZfsPool({ readErrors: 0, writeErrors: 7, checksumErrors: 0 }),
);
expect(summary!.hasErrors).toBe(true);
expect(summary!.errorSummary).toBe('Errors: R:0 W:7 C:0');
});
it('flags hasErrors=true via checksumErrors only (third OR arm)', () => {
const summary = getStorageBarZfsSummary(
makeZfsPool({ readErrors: 0, writeErrors: 0, checksumErrors: 4 }),
);
expect(summary!.hasErrors).toBe(true);
expect(summary!.errorSummary).toBe('Errors: R:0 W:0 C:4');
});
it('hasErrors=false when all error counts are 0 (OR chain all-false arm + L73 false arm)', () => {
const summary = getStorageBarZfsSummary(
makeZfsPool({ readErrors: 0, writeErrors: 0, checksumErrors: 0 }),
);
expect(summary!.hasErrors).toBe(false);
// L73 false arm: errorSummary collapses to ''.
expect(summary!.errorSummary).toBe('');
});
it('treats negative error counts as no-error (each `> 0` is false)', () => {
// Documents current behaviour: the guards are strictly `> 0`, so a
// malformed negative count does NOT flip hasErrors. Exercises the false
// arm of every comparison in the OR chain simultaneously.
const summary = getStorageBarZfsSummary(
makeZfsPool({ readErrors: -1, writeErrors: -2, checksumErrors: -3 }),
);
expect(summary!.hasErrors).toBe(false);
expect(summary!.errorSummary).toBe('');
});
});
// =========================================================================
// getStorageBarZfsSummary — L69/L70 scan substring arms
// =========================================================================
describe('getStorageBarZfsSummary — scan substring arms (L69/L70)', () => {
it('detects scrub when scan contains "scrub" (L69 true arm) and not resilver (L70 false arm)', () => {
const summary = getStorageBarZfsSummary(makeZfsPool({ scan: 'scrub in progress' }));
expect(summary!.isScrubbing).toBe(true);
expect(summary!.isResilvering).toBe(false);
});
it('detects resilver when scan contains "resilver" (L70 true arm) and not scrub (L69 false arm)', () => {
const summary = getStorageBarZfsSummary(makeZfsPool({ scan: 'resilver completed' }));
expect(summary!.isResilvering).toBe(true);
expect(summary!.isScrubbing).toBe(false);
});
it('detects both substrings when scan mentions scrub and resilver', () => {
const summary = getStorageBarZfsSummary(makeZfsPool({ scan: 'scrub+resilver running' }));
expect(summary!.isScrubbing).toBe(true);
expect(summary!.isResilvering).toBe(true);
});
it('matches neither substring for an unrelated scan (both false arms)', () => {
const summary = getStorageBarZfsSummary(makeZfsPool({ scan: 'idle' }));
expect(summary!.isScrubbing).toBe(false);
expect(summary!.isResilvering).toBe(false);
});
it('matches scrub case-insensitively (proves .toLowerCase() runs on the scan value)', () => {
// 'SCRUB PENDING' -> lowercased 'scrub pending' -> includes('scrub') true.
const summary = getStorageBarZfsSummary(makeZfsPool({ scan: 'SCRUB PENDING' }));
expect(summary!.isScrubbing).toBe(true);
});
});
// =========================================================================
// getStorageBarZfsSummary — L72 `scan && scan !== 'none' ? scan : ''`
// =========================================================================
describe('getStorageBarZfsSummary — scan output ternary (L72)', () => {
it('collapses scan === "none" to empty string (L72 false arm via equality)', () => {
// 'none' is truthy, so the first half of the && is true, but the
// second half (`scan !== 'none'`) is false -> whole && is false -> ''.
const summary = getStorageBarZfsSummary(makeZfsPool({ scan: 'none' }));
expect(summary!.scan).toBe('');
});
it('passes through a non-"none" scan value verbatim (L72 true arm)', () => {
const summary = getStorageBarZfsSummary(makeZfsPool({ scan: 'scrub repaired 0 errors' }));
expect(summary!.scan).toBe('scrub repaired 0 errors');
});
});
// =========================================================================
// Full-shape regression for the clean-pool case — exercises every "false"
// arm of getStorageBarZfsSummary in one assertion so the canonical object
// shape is locked alongside the per-arm tests above.
// =========================================================================
describe('getStorageBarZfsSummary — clean-pool canonical shape', () => {
it('returns the canonical clean-pool summary object', () => {
// Arms exercised simultaneously:
// L62 false (pool present)
// L64 truthy (scan='none' is truthy)
// L65 all-false (all counts 0)
// L69 false, L70 false ('none' contains neither substring)
// L72 false (scan==='none')
// L73 false (hasErrors false)
expect(
getStorageBarZfsSummary(
makeZfsPool({
state: 'ONLINE',
scan: 'none',
readErrors: 0,
writeErrors: 0,
checksumErrors: 0,
}),
),
).toEqual({
hasErrors: false,
isScrubbing: false,
isResilvering: false,
state: 'ONLINE',
scan: '',
errorSummary: '',
});
});
});
});
@@ -0,0 +1,176 @@
import { describe, expect, it } from 'vitest';
import type { ResourceDiscovery } from '@/types/discovery';
import {
getDiscoveryIdentifiedSummary,
getDiscoverySuggestedURLCodeClass,
getDiscoverySuggestedURLFallback,
getDiscoverySuggestedURLHeadingClass,
getDiscoverySuggestedURLTextClass,
} from '@/utils/discoveryPresentation';
// Baseline discovery record carries no meaningful signal: every "unknown" /
// empty value, no ports, paths, facts, url or probe. Mirrors the fixture in
// discoveryPresentation.branchcov.test.ts so behaviour stays consistent.
const makeDiscovery = (overrides: Partial<ResourceDiscovery> = {}): ResourceDiscovery =>
({
id: 'disc-1',
resource_type: 'docker',
resource_id: 'res-1',
target_id: 'agent-1',
hostname: 'host',
service_type: 'unknown',
service_name: 'unknown',
service_version: 'unknown',
category: 'unknown',
cli_access: '',
facts: [],
config_paths: [],
data_paths: [],
log_paths: [],
ports: [],
user_notes: '',
user_secrets: {},
confidence: 0,
ai_reasoning: '',
discovered_at: '',
updated_at: '',
scan_duration: 0,
...overrides,
}) as ResourceDiscovery;
// ---------------------------------------------------------------------------
// Three presentation class helpers are never called by any existing test.
// They take no arguments and have no internal branches, so "all inputs" is a
// single arm each: assert the exact returned class string.
// ---------------------------------------------------------------------------
describe('getDiscoverySuggestedURLHeadingClass (function + branch coverage)', () => {
it('returns the canonical heading class string and takes no inputs', () => {
expect(getDiscoverySuggestedURLHeadingClass()).toBe(
'text-[11px] font-medium uppercase tracking-wide text-blue-800 dark:text-blue-200 mb-1',
);
});
});
describe('getDiscoverySuggestedURLTextClass (function + branch coverage)', () => {
it('returns the canonical body-text class string and takes no inputs', () => {
expect(getDiscoverySuggestedURLTextClass()).toBe('text-blue-700 dark:text-blue-300');
});
});
describe('getDiscoverySuggestedURLCodeClass (function + branch coverage)', () => {
it('returns the canonical mono code class string and takes no inputs', () => {
expect(getDiscoverySuggestedURLCodeClass()).toBe(
'min-w-0 flex-1 rounded bg-blue-100 px-2 py-1.5 text-xs text-blue-800 dark:bg-blue-950 dark:text-blue-100 font-mono break-all',
);
});
});
// ---------------------------------------------------------------------------
// Branch coverage: line 143 `const serviceName = (discovery.service_name || '').trim();`
// The makeDiscovery default ships service_name='unknown' (truthy), so the
// fallback arm only fires for a null/empty name. The record must stay
// meaningful via another signal or getDiscoveryIdentifiedSummary short-circuits
// to null before line 143 executes; a single port supplies that signal here.
// ---------------------------------------------------------------------------
describe('getDiscoveryIdentifiedSummary: (service_name || "") fallback arm (branch coverage)', () => {
// Only genuinely falsy values flip the `||` to its fallback arm. (A
// whitespace-only string is truthy and so never reaches this branch.)
it.each<[string, unknown]>([
['null', null],
['undefined', undefined],
['empty string', ''],
])(
'falls back to "" and labels "Unidentified service" when service_name is %s',
(_label, name) => {
const summary = getDiscoveryIdentifiedSummary(
makeDiscovery({
service_name: name as unknown as string,
ports: [{ port: 80, protocol: 'tcp', process: 'nginx', address: '0.0.0.0' }],
}),
);
expect(summary).not.toBeNull();
// The fallback arm produced '' which isMeaningfulDiscoveryText rejects,
// so the summary surfaces the placeholder name rather than the raw input.
expect(summary?.serviceName).toBe('Unidentified service');
expect(summary?.portCount).toBe(1);
// service_type/version/category stay "unknown" so they are dropped too.
expect(summary?.serviceType).toBeUndefined();
expect(summary?.serviceVersion).toBeUndefined();
expect(summary?.category).toBeUndefined();
},
);
it('keeps the raw service_name when it is a meaningful truthy value (truthy-arm regression guard)', () => {
const summary = getDiscoveryIdentifiedSummary(makeDiscovery({ service_name: 'Nginx' }));
expect(summary?.serviceName).toBe('Nginx');
});
it('treats a whitespace-only service_name as truthy at the ||, then trims it to empty (no-trim-before-|| guard)', () => {
// service_name=' ' is truthy, so the || keeps it; the subsequent .trim()
// then empties it and isMeaningfulDiscoveryText rejects the empty result.
// This documents that the trim happens AFTER the ||, not before.
const summary = getDiscoveryIdentifiedSummary(
makeDiscovery({
service_name: ' ',
ports: [{ port: 80, protocol: 'tcp', process: 'nginx', address: '0.0.0.0' }],
}),
);
expect(summary?.serviceName).toBe('Unidentified service');
});
});
// ---------------------------------------------------------------------------
// Branch coverage: line 302 `description: diagnostic || ''` in
// getDiscoverySuggestedURLFallback. Existing tests only pass a truthy 'diag',
// so exercise every falsy shape the public signature accepts.
// ---------------------------------------------------------------------------
describe('getDiscoverySuggestedURLFallback: (diagnostic || "") fallback arm (branch coverage)', () => {
// Only genuinely falsy values flip the `||` to its fallback arm. (A
// whitespace-only string is truthy — see the dedicated test below.)
it.each<[string, string | null | undefined]>([
['undefined', undefined],
['null', null],
['empty string', ''],
])(
'collapses a %s diagnostic to an empty description while keeping the title',
(_label, diagnostic) => {
expect(getDiscoverySuggestedURLFallback(diagnostic)).toEqual({
title: 'No suggested URL available',
description: '',
});
},
);
// Suspected source finding (reported, not fixed): unlike
// normalizeDiscoverySuggestedUrl / cli_access handling, this fallback does
// NOT trim — a whitespace-only diagnostic survives verbatim because it is
// truthy at the ||.
it('forwards a whitespace-only diagnostic verbatim (documents the missing trim)', () => {
expect(getDiscoverySuggestedURLFallback(' ')).toEqual({
title: 'No suggested URL available',
description: ' ',
});
});
});
// ---------------------------------------------------------------------------
// Suspected source finding (reported, NOT fixed):
// Line 176 `suggestedUrlReasonTitle: suggestedUrlReason.title || undefined`.
// The falsy arm of this || is unreachable through the public API. Reasoning:
// - Line 176 only executes after `getDiscoveryIdentifiedSummary` has passed
// the `if (!discovery) return null` guard at line 141, so discovery is a
// non-null object when `getDiscoverySuggestedURLReason(discovery)` runs.
// - Inside getDiscoverySuggestedURLReason, the only path returning an empty
// title is `if (!discovery) return { text: '', title: '' }`, which cannot
// fire here. For any non-null discovery, `title` resolves to either
// `${label}: ${detail}` or `label`, and `label` always falls back to the
// non-empty default 'Discovery heuristic' from getDiscoveryURLSuggestionSourceLabel.
// - Therefore suggestedUrlReason.title is always a non-empty (truthy) string
// at line 176, and the `|| undefined` short-circuit arm is dead code.
// No test is written for it because faking it would require constructing an
// impossible state. Documented here and in the report.
// ---------------------------------------------------------------------------
@@ -0,0 +1,141 @@
import { describe, expect, it } from 'vitest';
import { getInfrastructureSourcePickerItemForRouteStep } from '@/utils/infrastructureOnboardingPresentation';
// The 6 uncovered branches + the never-called function reported by coverage
// all live in getInfrastructureSourcePickerItemForRouteStep (source lines
// 480-490). That function has exactly three decision points, each with two
// arms:
// 1. `(step || '').trim()` -> `||` truthy arm vs falsy arm
// 2. `if (!normalized)` -> empty (return null) vs non-empty (continue)
// 3. `if (item.routeStep === normalized)` -> match (return) vs miss (loop on)
// This file closes every one of those arms and exercises each of the nine
// route steps the function recognises, plus one it does not.
//
// SOURCE_PICKER_ITEM_ORDER (the only ids the loop can ever return) is, in
// order: pve, pbs, pmg, truenas, vmware, unraid, docker, kubernetes, linux-host.
const RECOGNISED_STEP_EXPECTATIONS = [
{ step: 'pve', id: 'pve', connectionType: 'pve', label: 'Proxmox VE' },
{ step: 'pbs', id: 'pbs', connectionType: 'pbs', label: 'Proxmox Backup Server' },
{ step: 'pmg', id: 'pmg', connectionType: 'pmg', label: 'Proxmox Mail Gateway' },
{ step: 'truenas', id: 'truenas', connectionType: 'truenas', label: 'TrueNAS SCALE' },
{ step: 'vmware', id: 'vmware', connectionType: 'vmware', label: 'VMware vCenter' },
{ step: 'unraid', id: 'unraid', connectionType: 'agent', label: 'Unraid' },
{ step: 'docker', id: 'docker', connectionType: 'agent', label: 'Docker' },
{ step: 'kubernetes', id: 'kubernetes', connectionType: 'agent', label: 'Kubernetes' },
{
step: 'linux-host',
id: 'linux-host',
connectionType: 'agent',
label: 'Linux, macOS, Windows host',
},
] as const;
describe('infrastructureOnboardingPresentation — branch coverage (branchcov0723pm)', () => {
describe('getInfrastructureSourcePickerItemForRouteStep', () => {
describe('`(step || "")` falsy arm + `!normalized` true arm -> null', () => {
// Each of these is falsy, so `(step || "")` collapses to `""`, `.trim()`
// stays `""`, and `if (!normalized) return null` fires.
it('returns null for null', () => {
expect(getInfrastructureSourcePickerItemForRouteStep(null)).toBeNull();
});
it('returns null for undefined', () => {
expect(getInfrastructureSourcePickerItemForRouteStep(undefined)).toBeNull();
});
it('returns null for the empty string', () => {
expect(getInfrastructureSourcePickerItemForRouteStep('')).toBeNull();
});
});
describe('`(step || "")` truthy arm + trim empties it + `!normalized` true arm -> null', () => {
// A whitespace-only string is a TRUTHY string, so it takes the `step`
// side of `||` (not the `""` side), then `.trim()` produces `""` and the
// `!normalized` early return fires. This is the only input shape that
// exercises the truthy `||` arm while still landing on the null return.
it('returns null for a whitespace-only string (trim empties it)', () => {
expect(getInfrastructureSourcePickerItemForRouteStep(' ')).toBeNull();
expect(getInfrastructureSourcePickerItemForRouteStep('\t\n ')).toBeNull();
});
});
describe('recognised route steps (`routeStep === normalized` match arm)', () => {
// Exercises the truthy `||` arm, the `!normalized` false arm (non-empty,
// so the loop runs), and the TRUE arm of the inner equality for the
// matching iteration. Each case asserts the concrete id / connectionType
// / label of the resolved card so we prove WHICH branch matched rather
// than merely that something came back.
it.each(RECOGNISED_STEP_EXPECTATIONS)(
'resolves the "$step" route step to the $label card',
({ step, id, connectionType, label }) => {
const item = getInfrastructureSourcePickerItemForRouteStep(step);
expect(item).not.toBeNull();
expect(item!.id).toBe(id);
expect(item!.routeStep).toBe(step);
expect(item!.connectionType).toBe(connectionType);
expect(item!.label).toBe(label);
},
);
it('returns the Proxmox VE card verbatim for the first ordered step (pve)', () => {
// pve is the FIRST id in SOURCE_PICKER_ITEM_ORDER, so this confirms the
// loop returns on the very first iteration rather than scanning further.
const item = getInfrastructureSourcePickerItemForRouteStep('pve');
expect(item).toMatchObject({
id: 'pve',
connectionType: 'pve',
label: 'Proxmox VE',
sourceStrategy: 'api-agent',
});
});
it('returns the linux-host card for the LAST ordered step', () => {
// linux-host is the LAST id in SOURCE_PICKER_ITEM_ORDER, so the loop
// must scan every earlier id (all missing) before matching — exercising
// the inner `===` FALSE arm repeatedly before the single TRUE arm.
const item = getInfrastructureSourcePickerItemForRouteStep('linux-host');
expect(item).toMatchObject({
id: 'linux-host',
connectionType: 'agent',
label: 'Linux, macOS, Windows host',
});
});
});
describe('whitespace padding (`.trim()` is applied to the input)', () => {
// Still the truthy `||` arm + `!normalized` false arm + match TRUE arm,
// but proves the comparison runs against the TRIMMED value, not the raw
// input. Without the trim, ` pve ` would miss and return null.
it('strips surrounding whitespace before matching a recognised step', () => {
const item = getInfrastructureSourcePickerItemForRouteStep(' pve\t');
expect(item).not.toBeNull();
expect(item!.id).toBe('pve');
expect(item!.routeStep).toBe('pve');
});
});
describe('unrecognised step (inner `===` FALSE on every iteration -> final null)', () => {
// A valid non-empty string that no item's routeStep equals: the loop runs
// to completion, the inner `if` is never true, and control reaches the
// final `return null`. This is the only path that exercises the FALSE arm
// of the inner equality without a subsequent TRUE arm rescuing it.
it('returns null for a completely unknown step', () => {
expect(getInfrastructureSourcePickerItemForRouteStep('hyper-v')).toBeNull();
});
it('returns null for a recognised connection-type that is not itself a route step', () => {
// 'agent' is a connectionType but is NOT a routeStep id (the host path
// is 'linux-host'), so it must miss every item and return null.
expect(getInfrastructureSourcePickerItemForRouteStep('agent')).toBeNull();
});
it('is case-sensitive: an uppercased recognised step does not match', () => {
// The function trims but never lowercases, and every routeStep is
// lowercase, so 'PVE' !== 'pve' and the lookup misses.
expect(getInfrastructureSourcePickerItemForRouteStep('PVE')).toBeNull();
expect(getInfrastructureSourcePickerItemForRouteStep('Docker')).toBeNull();
});
});
});
});