mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-23 20:25:56 +00:00
Remove contract prose and badge helpers for deleted surfaces (#2655)
Stacked on #2649, which carries its commitdcd409f9da. Once #2649 merges, this PR's diff is the single commitbf29201b19. PR 2612 deleted the never-mounted UnifiedResourceTable, andabb6f86aebretired the shared workloads-link helper. The unified-resources and performance-and-scalability contracts still described both as live owners. Each clause is checked against current source and now names the live owner, becomes a conditional rule, or goes: - System identity: the Docker hosts System column (`getDockerHostSystemBadge`, which drops runtime-only labels) and the resource drawer header. - Drawer resource labels: `createPlatformResourceLabelResolver`. These are operator-local names, not governed labels, and a table without a resolver shows raw IDs. - Same-route drawer reveal: `useWorkloadSelectionState`. Platform tables keep drawer state local. - Kubernetes cluster helpers: `agentResources.ts`, shared by `useWorkloads` and the drawer. - Workload platform scoping: `buildWorkloadsRouteSearch`. - Row policy chips and the workload-to-infrastructure href builder: now conditional or recorded as retired. No platform table renders policy chips. Code: with `infrastructureSelectors.ts` gone, `getInfrastructureSystemIdentitySortLabel` has no production caller, and `getInfrastructureSystemTitleBadges` has had none since PR 2612. Both are removed, along with the private helpers only the title helper used. Their tests either drop duplicate assertions or now assert the identity badges directly. Verification: badge suites plus `src/utils`, Docker, Infrastructure and platformPage vitest pass; the only failure is `agentInstallCommand`, which fails on macOS regardless. `tsc --noEmit` is clean, the bundle-size check passes with the same chunk set, and `contract_audit.py --check` reports no errors. A browser pass ran on an embedded production build (receipt `6c79cba8f0c6e7b0.json`) covering the Docker hosts System column render and sort plus the Docker and Machines drawers at 1440px and 390px.
This commit is contained in:
@@ -2600,26 +2600,29 @@ across provider-backed history. Row hover and focus plus top-card isolation on
|
||||
workload surfaces must resolve against the same canonical workload ID even
|
||||
when the backing history is stored under a provider metrics target, so
|
||||
provider-backed VM rows do not silently drop out of summary emphasis.
|
||||
The shared infrastructure table hot path now also treats operator-facing
|
||||
resource identity as a protected boundary: sorting, searching, summary-series
|
||||
matching, and row titles on the infrastructure page must use the canonical
|
||||
local instance identity rather than governed AI-summary text, so performance
|
||||
work cannot “optimize” the table into ambiguous labels that collapse multiple
|
||||
resources into the same visible name.
|
||||
The same protected table path treats the visible system column as
|
||||
identity-first presentation over canonical merged-source data. Sort derivation
|
||||
for that column must use the same displayed system identity as the render path,
|
||||
while the render path may keep full merged-source detail in tooltips. When a
|
||||
row contains both `agent` and a provider/API platform such as Proxmox, the table
|
||||
must render the provider platform as the compact visible badge rather than
|
||||
adding extra Agent badge width or sorting primarily by the telemetry method.
|
||||
When a row is only known through an agent or container runtime, the table must
|
||||
prefer reported OS/appliance identity before falling back to Docker/runtime
|
||||
capability labels.
|
||||
When the displayed system badge already includes the platform version, row title
|
||||
metadata must not spend extra badge/title budget repeating that same platform as
|
||||
an unversioned source; it may keep non-duplicate collection context such as
|
||||
Pulse Agent.
|
||||
Platform-table hot paths treat operator-facing resource identity as a
|
||||
protected boundary: sorting, searching, summary-series matching, and row
|
||||
titles must use the canonical local instance identity (the resource's own name or
|
||||
`getPreferredInfrastructureDisplayName(...)`) rather than governed AI-summary
|
||||
text, so performance work cannot “optimize” a table into ambiguous labels that
|
||||
collapse multiple resources into the same visible name.
|
||||
A visible system column is identity-first presentation over canonical
|
||||
merged-source data. The Docker hosts table's System column is the live
|
||||
instance: `frontend-modern/src/features/docker/DockerHostsTable.tsx` sorts and
|
||||
renders the same `getDockerHostSystemBadge(...)` result, and the render path
|
||||
may keep full merged-source detail in tooltips. When a row contains both
|
||||
`agent` and a provider/API platform such as Proxmox, the system badge must show
|
||||
the provider platform rather than adding extra Agent badge width or sorting
|
||||
primarily by the telemetry method. When a row is only known through an agent or
|
||||
container runtime, the shared resolver prefers reported OS/appliance identity
|
||||
before falling back to Docker/runtime capability labels; the Docker hosts
|
||||
column drops that runtime-only fallback, since every row there is a Docker
|
||||
host, and shows `—` instead.
|
||||
A surface that shows source badges beside a versioned system badge must not
|
||||
spend extra badge/title budget repeating that same platform as an unversioned
|
||||
source; it may keep non-duplicate collection context such as Pulse Agent. The
|
||||
resource drawer header avoids the repeat by showing system identity badges in
|
||||
place of source badges.
|
||||
That derived workload owner now also routes grouped row windowing through
|
||||
`frontend-modern/src/components/Workloads/useGroupedTableWindowing.ts`, which
|
||||
owns row-window thresholds, overscan behavior, reveal-index clamping, and
|
||||
@@ -2996,17 +2999,17 @@ the timeline/history path can resolve through the canonical catalog without addi
|
||||
detail-only lookup branch to the hot-row path.
|
||||
The same detail drawer also uses that resolver for correlation dependency and
|
||||
dependent chips, so the investigation path shows catalog labels in the drawer
|
||||
and keeps the raw ID only when the catalog cannot resolve one, while the AI
|
||||
page keeps its broader no-catalog fallback.
|
||||
That same workloads-link path and the workload projection now also
|
||||
share the canonical cluster-name helpers in the shared agent-resource layer,
|
||||
so route labels, pod grouping, and cluster-name fetch keys keep using the
|
||||
same source of truth instead of rebuilding the `clusterName`/`context`/
|
||||
`clusterId` prefix locally.
|
||||
The infrastructure host-table hot path now also suppresses the default
|
||||
`Internal` + `Cloud Summary` policy pair in row chrome. That baseline posture
|
||||
still belongs to the canonical policy contract, but repeating it on every host
|
||||
burns row-density budget without adding operator-grade signal.
|
||||
and keeps the raw ID only when the catalog cannot resolve one.
|
||||
The workload projection and the resource drawer share the canonical
|
||||
cluster-name helpers in `frontend-modern/src/utils/agentResources.ts`, so pod
|
||||
grouping and the drawer's cluster-name fetch keys keep using the same source
|
||||
of truth instead of rebuilding the `clusterName`/`context`/`clusterId` prefix
|
||||
locally.
|
||||
A platform table that adds policy chips to row chrome must suppress the
|
||||
default `Internal` + `Cloud Summary` policy pair. That baseline posture still
|
||||
belongs to the canonical policy contract, but repeating it on every row burns
|
||||
row-density budget without adding operator-grade signal. No platform table
|
||||
renders policy chips today.
|
||||
The shared node adapter also uses that same cluster-name helper for the
|
||||
infrastructure summary surface, so Proxmox node projections stay aligned with
|
||||
the same canonical cluster label instead of carrying a raw adapter-local
|
||||
|
||||
@@ -3398,10 +3398,14 @@ this health-state projection: it must derive counts only from the resources
|
||||
accessor already available to the summary state, not from a separate API call or
|
||||
a re-projection of websocket state outside the shared summary pipeline.
|
||||
|
||||
`resourceBadgePresentation.ts` now owns the Infrastructure table system
|
||||
identity resolver. That resolver must prefer provider/API platform identity,
|
||||
then reported host OS or appliance identity, before falling back to Docker or
|
||||
other runtime capability labels.
|
||||
`resourceBadgePresentation.ts` owns the canonical system identity resolver,
|
||||
`getInfrastructureSystemIdentityBadges(...)`, which feeds the resource drawer's
|
||||
header badges and, through `getDockerHostSystemBadge(...)` in
|
||||
`frontend-modern/src/features/docker/dockerPageModel.ts` (which drops
|
||||
runtime-only Docker/Podman labels), the Docker hosts table's System column.
|
||||
That resolver must prefer provider/API platform
|
||||
identity, then reported host OS or appliance identity, before falling back to
|
||||
Docker or other runtime capability labels.
|
||||
|
||||
This subsystem now sits under the dedicated core monitoring runtime lane so
|
||||
canonical resource identity, discovery normalization, and platform-runtime
|
||||
@@ -4040,16 +4044,23 @@ across backend clone/merge and frontend transport. The owning resource remains
|
||||
the canonical `app-container`; the TrueNAS facet explains the platform-native
|
||||
app shape, and Docker metadata remains secondary runtime compatibility rather
|
||||
than the source of truth for the TrueNAS overview.
|
||||
That same infrastructure focus boundary now also owns deliberate drawer reveal.
|
||||
When an infrastructure row opens inline detail on the same route, the page must
|
||||
tag that detail with the canonical active resource ID. Direct row toggles that
|
||||
already have the row in view must capture the current `.app-scroll-shell`
|
||||
position through `frontend-modern/src/utils/appShellScrollRestoration.ts`, so
|
||||
the remounted root shell in `frontend-modern/src/App.tsx` can stay anchored,
|
||||
and then still hand off to the shared summary-table/contextual-focus helpers
|
||||
when the opened drawer would otherwise fall below the fold. That reveal must
|
||||
scroll only enough of the infrastructure table to keep the row header plus the
|
||||
start of the detail visible, not leave the drawer clipped and not hard-center
|
||||
Deliberate drawer reveal after a same-route selection write is owned by the
|
||||
Workloads table
|
||||
(`frontend-modern/src/components/Workloads/useWorkloadSelectionState.ts`,
|
||||
governed in `performance-and-scalability`). Platform resource tables open their
|
||||
inline drawer through local component state, most through
|
||||
`createPlatformResourceDetailState(...)` in
|
||||
`frontend-modern/src/features/platformPage/PlatformResourceDetailTableRow.tsx`
|
||||
while the Docker hosts and Proxmox nodes tables keep their own selection
|
||||
signal, and write no route state. An infrastructure table that moves drawer selection
|
||||
into route state must tag the opened detail with the canonical active resource
|
||||
ID; direct toggles of a row already in view must capture the current
|
||||
`.app-scroll-shell` position through
|
||||
`frontend-modern/src/utils/appShellScrollRestoration.ts`, so the remounted root
|
||||
shell in `frontend-modern/src/App.tsx` stays anchored, and then hand off to the
|
||||
shared summary-table/contextual-focus helpers when the opened drawer would fall
|
||||
below the fold. That reveal scrolls only enough to keep the row header plus the
|
||||
start of the detail visible, never leaving the drawer clipped or hard-centering
|
||||
the selected row.
|
||||
That same unified-resource boundary now also owns stored metrics-target
|
||||
continuity for provider-backed resources. When registry rebuild cannot derive a
|
||||
@@ -4684,11 +4695,10 @@ The frontend now also consumes those facet reads through
|
||||
drawer, which keeps the presentation surface aligned with the governed API
|
||||
contract instead of rebuilding the relationship and timeline inline.
|
||||
The canonical routing owner now also lives in
|
||||
`frontend-modern/src/routing/resourceLinks.ts`, including the
|
||||
workload-to-infrastructure href builder used by Workloads row and drawer
|
||||
consumers. Future workload-to-resource navigation changes must extend through
|
||||
that shared routing contract instead of reintroducing workload-local path
|
||||
builders.
|
||||
`frontend-modern/src/routing/resourceLinks.ts`. Its workload-to-infrastructure
|
||||
href builder was retired with the legacy `/infrastructure` route, so future
|
||||
workload-to-resource navigation must extend that shared routing contract
|
||||
instead of reintroducing workload-local path builders.
|
||||
That same shared routing boundary now also owns the canonical Patrol
|
||||
destination used by cross-surface findings and drill-down links. Shared
|
||||
dashboard, alert, and settings referrals may target `/patrol` through
|
||||
@@ -4706,9 +4716,10 @@ resolve the deep-link source from the canonical merged source set before
|
||||
falling back to raw `platformType`, so TrueNAS-backed hybrid systems do not
|
||||
lose their storage context just because agent telemetry is also present.
|
||||
That same routing contract now also owns workload platform scoping without
|
||||
restoring the retired `/workloads` top-level route. Shared workloads links,
|
||||
dashboard URL-sync state, and infrastructure drill-down helpers must preserve
|
||||
`platform=<owned-source-key>` for API-backed workloads such as TrueNAS
|
||||
restoring the retired `/workloads` top-level route. Workloads URL-sync state,
|
||||
and any workload link built through the shared `buildWorkloadsRouteSearch(...)`
|
||||
helper, must preserve `platform=<owned-source-key>` for API-backed workloads
|
||||
such as TrueNAS
|
||||
app-containers instead of collapsing those routes back to generic agent or
|
||||
Docker-only semantics when host telemetry is also present. Runtime-local
|
||||
filters like `agent` or cluster context may still be added as secondary scope,
|
||||
@@ -5106,25 +5117,20 @@ block entirely.
|
||||
The shared frontend resource identity helper now owns that policy-aware
|
||||
display contract, so infrastructure surfaces that ask for the preferred
|
||||
resource label no longer need to re-encode the governed summary boundary by
|
||||
hand. Settings quick-picks, infrastructure selectors, and the connected-
|
||||
infrastructure / monitored-system projections now all stay on that same
|
||||
preferred-label helper instead of carrying a separate raw-name fallback fork.
|
||||
hand. Provider-bound frontend labels, such as Assistant chat resource labels
|
||||
and organization sharing, stay on that same preferred-label helper instead of
|
||||
carrying a separate raw-name fallback fork. Operator-local pickers such as the
|
||||
Settings `ResourcePicker` use `getPreferredInfrastructureDisplayName(...)` on
|
||||
purpose, under the operator-local resource search obligation above.
|
||||
That same helper also owns platform-id redundancy suppression for
|
||||
infrastructure drawers, so surfaces only render platform IDs when they add
|
||||
identity context beyond the canonical display name or hostname instead of
|
||||
repeating the same identifier chrome in both runtime and identity sections.
|
||||
The shared workloads-link helper now also uses that preferred-label helper
|
||||
for Kubernetes-cluster navigation fallbacks, so drawer/table navigation
|
||||
context stays inside the same governed resource-label boundary instead of
|
||||
repeating a raw display-name fork.
|
||||
That same shared workloads-link path now also owns top-level TrueNAS system
|
||||
handoffs to the canonical app-container workloads route, so infrastructure
|
||||
drawers and related-link surfaces do not strand canonical `truenas` resources
|
||||
on infrastructure-only navigation while API-backed apps already exist in the
|
||||
shared workloads model. That same helper must also honor merged
|
||||
`agent`+`truenas` source sets for top-level systems, so hybrid TrueNAS
|
||||
surfaces keep the same workload handoff even when the resource shape presents
|
||||
through the canonical host path instead of a raw `type='truenas'` row.
|
||||
The shared workloads-link helper, which labelled Kubernetes-cluster
|
||||
navigation fallbacks and handed top-level TrueNAS systems to the workloads
|
||||
route, was retired with the drawer's related-links injection described above.
|
||||
New drawer or table links follow the local operator identity rule for
|
||||
infrastructure links and land inside the owning platform page.
|
||||
The resource drawer's Kubernetes namespace and deployment tabs use the
|
||||
canonical cluster-name helper for backend fetch keys, keeping lookup identity
|
||||
separate from the governed display-label contract.
|
||||
@@ -5162,10 +5168,12 @@ cluster name, namespace, or surface-local aliases.
|
||||
The drawer's discovery mapper also reuses that helper for pod fallback agent
|
||||
IDs, so the resource-detail path and the Workloads path stay aligned on the
|
||||
same cluster-name source of truth.
|
||||
The workload projection and workloads-link route helpers also share
|
||||
the same Kubernetes context prefix helper in the shared agent-resource
|
||||
layer, so pod grouping and cluster navigation keep the same cluster-context
|
||||
prefix before any surface-specific display fallback is applied.
|
||||
The workload projection and the resource drawer also share the Kubernetes
|
||||
context helper `getPreferredResourceKubernetesContext(...)` in
|
||||
`frontend-modern/src/utils/agentResources.ts`, directly in `useWorkloads` and
|
||||
through `getPreferredResourceClusterName(...)` in the drawer, so pod grouping
|
||||
and drawer cluster context keep the same cluster-context prefix before any
|
||||
surface-specific display fallback is applied.
|
||||
The unified-resource projection also reuses that same prefix helper for
|
||||
projected Kubernetes `clusterId`, so the shared resource store stays aligned
|
||||
with the Workloads and detail surfaces on the same canonical cluster-context
|
||||
@@ -5428,14 +5436,14 @@ owners instead of rebuilding ad hoc platform-data parsing or action-target
|
||||
fallback logic inside alerts, settings, recovery, AI, or infrastructure-local
|
||||
state owners.
|
||||
Those runtime adapters must preserve the same operator-facing local identity
|
||||
boundary as the infrastructure tables and selectors: node, PBS, and PMG view
|
||||
boundary as the platform tables: node, PBS, and PMG view
|
||||
models keep canonical local instance labels for summary rows, drawers, and
|
||||
settings selectors, while governed summaries remain available for policy/detail
|
||||
surfaces rather than replacing per-instance operator identity.
|
||||
`ResourceFacetSummary` now consumes the shared
|
||||
`frontend-modern/src/utils/resourceChangePresentation.ts` label helper for
|
||||
canonical change kinds, source types, and adapter provenance, so the chip
|
||||
wording stays aligned across table, drawer, and intelligence surfaces.
|
||||
wording stays aligned with the drawer's recent-change cards and timeline.
|
||||
Timeline cards in that drawer surface change metadata when it is present, so
|
||||
the history view preserves the richer provenance already carried by the
|
||||
unified-resource model instead of flattening those fields away.
|
||||
@@ -5445,14 +5453,17 @@ and `frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.ts
|
||||
cards from the Patrol page, resource drawer, and problem-resource dashboard
|
||||
panels, so canonical resource-filter path construction stays owned by the
|
||||
shared summary cards rather than being duplicated per surface.
|
||||
The unified resource table now also supplies a canonical resource-label
|
||||
resolver into the resource drawer, so related-resource timeline chips can use
|
||||
the same governed display labels as the table without adding a new
|
||||
detail-local lookup path.
|
||||
Platform tables supply the resource-label resolver to the resource drawer
|
||||
through `PlatformResourceDetailTableRow`. `createPlatformResourceLabelResolver(...)`
|
||||
in `frontend-modern/src/features/platformPage/PlatformResourceDetailTableRow.tsx`
|
||||
resolves an ID against the page's own resources through
|
||||
`getPreferredInfrastructureDisplayName(...)`, so related-resource timeline
|
||||
chips show the operator-local display name rather than a raw ID, without adding
|
||||
a detail-local lookup path. A table that passes no resolver leaves those chips
|
||||
on the raw resource ID.
|
||||
The resource drawer now also passes that same resolver into the shared
|
||||
correlation summary, so dependency and dependent chips stay on governed
|
||||
labels in the investigation path while the AI summary page keeps its broader
|
||||
raw-ID fallback.
|
||||
correlation summary, its only consumer, so dependency and dependent chips stay
|
||||
on those labels in the investigation path.
|
||||
The same timeline and facet-bundle reads now also accept governed `kind` and
|
||||
`sourceType` filters, plus a governed `sourceAdapter` filter for adapter-level
|
||||
provenance drill-down, so history can narrow by canonical change class and
|
||||
@@ -5701,12 +5712,12 @@ because the legacy backend resource projection leaves `platformType` empty on
|
||||
several canonical resource types; the helper falls back to the resource's
|
||||
`sources` array via the existing source-platform normalization so mock
|
||||
fixtures and live backends produce the same client-side platform grouping.
|
||||
That same boundary also owns the infrastructure table's operator-facing system
|
||||
identity vocabulary. `frontend-modern/src/utils/resourceBadgePresentation.ts`,
|
||||
`frontend-modern/src/components/Infrastructure/resourceBadges.ts`, and the
|
||||
unified resource table sections may preserve full merged-source detail in
|
||||
tooltips and accessibility metadata, but visible table headers, sort keys, and
|
||||
row badges must answer what system the operator is looking at. Provider/API
|
||||
That same boundary also owns the operator-facing system identity vocabulary.
|
||||
`frontend-modern/src/utils/resourceBadgePresentation.ts` and its consumers, the
|
||||
Docker hosts table's System column through `getDockerHostSystemBadge(...)` and
|
||||
the resource drawer header, may preserve full merged-source detail in tooltips
|
||||
and accessibility metadata, but visible headers, sort keys, and badges must
|
||||
answer what system the operator is looking at. Provider/API
|
||||
platforms such as Proxmox, TrueNAS, VMware, and Kubernetes outrank collection
|
||||
methods; reported agent OS or appliance identity such as Unraid or Ubuntu
|
||||
outranks a generic container-runtime capability; and the shared generic source
|
||||
@@ -6028,8 +6039,8 @@ hydration and unsupported filtered queries, but canonical live freshness for
|
||||
supported resource snapshots must flow from websocket `state.resources`
|
||||
instead of layering confirmatory REST refresh loops on top of already-owned
|
||||
resource updates.
|
||||
Shared infrastructure consumers such as the unified resource table and detail
|
||||
drawer must present that owned metadata through shared helpers instead of
|
||||
Shared infrastructure consumers such as platform tables and the resource
|
||||
detail drawer must present that owned metadata through shared helpers instead of
|
||||
reconstructing privacy posture from display names, source types, or other
|
||||
incidental runtime hints.
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"version": 1,
|
||||
"base_sha": "dcd409f9da0a7a7f35a2a4ad5a4354e0b1d3ee44",
|
||||
"verified_at": "2026-10-07T10:52:19Z",
|
||||
"result": "passed",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/utils/resourceBadgePresentation.ts"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/utils/resourceBadgePresentation.ts": "ff0409fed88a69acf8acba49ab66a7fd46147d3ba65e8433f0f758fc50a405ca"
|
||||
},
|
||||
"routes": [
|
||||
"/docker",
|
||||
"/standalone/machines"
|
||||
],
|
||||
"viewports": [
|
||||
{
|
||||
"width": 1440,
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
"width": 390,
|
||||
"height": 844
|
||||
}
|
||||
],
|
||||
"states": [
|
||||
"Embedded production build of this tree on a private demo-mode mock stack (port 7697, fresh data dir)",
|
||||
"Docker hosts table System column shows getInfrastructureSystemIdentityBadges-derived labels: Ubuntu, Alpine, Debian, and LXC with the \"Docker running inside Proxmox LXC <vmid>\" tooltip",
|
||||
"Docker host inline drawer opens with its Overview/History/Manage tabs",
|
||||
"Machines inline drawer header shows the type badge plus the system identity badge (Agent, Ubuntu 24.04 LTS)",
|
||||
"Phone width: Docker hosts table hides the System column, Machines drawer header keeps both badges",
|
||||
"No page errors; only console errors are the pre-existing inline-style CSP reports that PR 2642 fixes on main, which this base predates"
|
||||
],
|
||||
"interactions": [
|
||||
"Clicked the System column header: aria-sort became ascending and rows ordered Alpine, Debian, LXC, LXC, Ubuntu x3, matching the rendered badges",
|
||||
"Expanded the first Docker host row through its disclosure button at both widths",
|
||||
"Expanded the first Machines row through its disclosure button at both widths",
|
||||
"Production bundle-size check passed with the same chunk set"
|
||||
]
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
getInfrastructureSystemIdentityBadges,
|
||||
getInfrastructureSystemIdentitySortLabel,
|
||||
getSourceBadge,
|
||||
getTypeBadge,
|
||||
getUnifiedSourceBadges,
|
||||
} from '@/utils/resourceBadgePresentation';
|
||||
import type { PlatformType, Resource, ResourceType, SourceType } from '@/types/resource';
|
||||
import type { Resource, ResourceType, SourceType } from '@/types/resource';
|
||||
|
||||
const makeResource = (overrides: Partial<Resource>): Resource => ({
|
||||
id: 'resource-1',
|
||||
@@ -485,38 +484,3 @@ describe('getDockerHostOsIdentityBadge (via getInfrastructureSystemIdentityBadge
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInfrastructureSystemIdentitySortLabel — branch coverage', () => {
|
||||
it('returns the first badge label when identity badges exist', () => {
|
||||
const resource = makeResource({
|
||||
type: 'k8s-node',
|
||||
platformType: 'kubernetes',
|
||||
sourceType: 'api',
|
||||
sources: ['kubernetes'],
|
||||
platformData: {
|
||||
sources: ['kubernetes'],
|
||||
kubernetes: { version: '1.28.4' },
|
||||
},
|
||||
});
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('K8s 1.28.4');
|
||||
});
|
||||
|
||||
it('returns empty string when no badges and platformType is falsy', () => {
|
||||
const resource = makeResource({
|
||||
type: 'storage',
|
||||
platformType: '' as unknown as PlatformType,
|
||||
sourceType: 'agent',
|
||||
});
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('');
|
||||
});
|
||||
|
||||
it('returns the platform badge label when no system identity is found', () => {
|
||||
const resource = makeResource({
|
||||
type: 'agent',
|
||||
platformType: 'truenas',
|
||||
sourceType: 'agent',
|
||||
platformData: { sources: ['agent'] },
|
||||
});
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('TrueNAS');
|
||||
});
|
||||
});
|
||||
|
||||
+2
-59
@@ -5,8 +5,6 @@ import {
|
||||
getContainerRuntimeBadgeForRuntime,
|
||||
getInfrastructurePlatformBadges,
|
||||
getInfrastructureSystemIdentityBadges,
|
||||
getInfrastructureSystemIdentitySortLabel,
|
||||
getInfrastructureSystemTitleBadges,
|
||||
getPlatformBadge,
|
||||
getSourceBadge,
|
||||
getTypeBadge,
|
||||
@@ -19,8 +17,8 @@ import {
|
||||
// `getHostIdentityAgentProfile`, `getHostIdentityPlatform`, `getAgentSystemIdentityBadge`,
|
||||
// `getDockerHostOsIdentityBadge`, `getAvailabilitySystemIdentityBadge`,
|
||||
// `getStorageSystemIdentityBadge`, `getStoragePlatformSource`, `getSystemSourceVersion`,
|
||||
// `getVersionedSourceBadge`, `proxmoxLxcDockerBadge`, `proxmoxLxcDockerVmid`,
|
||||
// `badgeIdentityLabels`) are exercised transitively through the exported entry points
|
||||
// `getVersionedSourceBadge`, `proxmoxLxcDockerBadge`, `proxmoxLxcDockerVmid`) are
|
||||
// exercised transitively through the exported entry points
|
||||
// below, asserting on their observable outputs.
|
||||
|
||||
const makeResource = (overrides: Partial<Resource> = {}): Resource =>
|
||||
@@ -211,28 +209,6 @@ describe('dedupeResourceBadges — branch coverage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('badgeIdentityLabels via getInfrastructureSystemTitleBadges — branch coverage', () => {
|
||||
it('treats a system badge with no title as its label-only identity (value ? ... : "" arm)', () => {
|
||||
// badgeIdentityLabels maps [badge.label, badge.title]; when title is undefined
|
||||
// the `(value ? normalizeBadgeIdentityLabel(value) : '')` ternary takes the ""
|
||||
// arm and the entry is dropped by filter(Boolean). The label identity survives,
|
||||
// so a source badge sharing it is deduped away.
|
||||
const result = getInfrastructureSystemTitleBadges(
|
||||
[{ label: 'PVE', classes: 'type-cls' }],
|
||||
[{ label: 'pve', classes: 'type-cls', title: 'pve' }],
|
||||
);
|
||||
expect(result).toEqual([{ label: 'PVE', classes: 'type-cls' }]);
|
||||
});
|
||||
|
||||
it('retains a source badge whose identity is absent from the system identity set', () => {
|
||||
const result = getInfrastructureSystemTitleBadges(
|
||||
[{ label: 'PVE', classes: 'type-cls' }],
|
||||
[{ label: 'K8s', classes: 'type-cls', title: 'K8s' }],
|
||||
);
|
||||
expect(result.map((b) => b.label)).toEqual(['PVE', 'K8s']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('withBadgeVersion via getInfrastructureSystemIdentityBadges — branch coverage', () => {
|
||||
it('strips the Proxmox pve-manager wrapper format down to the dotted version', () => {
|
||||
const badges = getInfrastructureSystemIdentityBadges(
|
||||
@@ -923,36 +899,3 @@ describe('getInfrastructureSystemIdentityBadges — end-to-end branch coverage',
|
||||
expect(getInfrastructureSystemIdentityBadges(resource)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInfrastructureSystemIdentitySortLabel — branch coverage', () => {
|
||||
it('returns the first identity badge label when one resolves', () => {
|
||||
const resource = makeResource({
|
||||
type: 'k8s-node',
|
||||
platformType: 'kubernetes',
|
||||
sourceType: 'api',
|
||||
sources: ['kubernetes'],
|
||||
platformData: { sources: ['kubernetes'], kubernetes: { version: '1.28.4' } },
|
||||
});
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('K8s 1.28.4');
|
||||
});
|
||||
|
||||
it('returns the platform badge label when no system identity is found', () => {
|
||||
const resource = makeResource({
|
||||
type: 'agent',
|
||||
platformType: 'truenas',
|
||||
sourceType: 'agent',
|
||||
platformData: { sources: ['agent'] },
|
||||
});
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('TrueNAS');
|
||||
});
|
||||
|
||||
it('returns "" when no identity, no platform badge, and no platformType resolve', () => {
|
||||
const resource = makeResource({
|
||||
type: 'storage',
|
||||
platformType: '' as Resource['platformType'],
|
||||
sourceType: 'agent',
|
||||
sources: [],
|
||||
});
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
+3
-12
@@ -5,7 +5,6 @@ import {
|
||||
getContainerRuntimeBadgeForRuntime,
|
||||
getInfrastructurePlatformBadges,
|
||||
getInfrastructureSystemIdentityBadges,
|
||||
getInfrastructureSystemIdentitySortLabel,
|
||||
getPlatformBadge,
|
||||
getTypeBadge,
|
||||
getUnifiedSourceBadges,
|
||||
@@ -131,16 +130,8 @@ describe('resourceBadgePresentation — dead-arm reachability characterization (
|
||||
expect(badges.map((b) => b.label)).toEqual(['Docker / Podman']);
|
||||
});
|
||||
|
||||
it('getInfrastructureSystemIdentitySortLabel derives a platform label for an unknown platformType, so the L701 truthy-stop of the `||` chain cannot fire', () => {
|
||||
// Reaching the L701 operand requires the identity badge list to be empty
|
||||
// (badges[0]?.label falsy). But the list is only empty when no identity at
|
||||
// all resolves — which requires platformType to be absent/falsy (a non-empty
|
||||
// platformType always yields a platform badge at L690). With a falsy
|
||||
// platformType, getPlatformBadge returns null, so L701's `?.label` is
|
||||
// always falsy when reached and never stops the chain. A non-empty (even
|
||||
// unknown) platformType instead produces a non-empty identity list, so the
|
||||
// sort label resolves at L700 and L701 is never reached.
|
||||
const label = getInfrastructureSystemIdentitySortLabel(
|
||||
it('getInfrastructureSystemIdentityBadges derives a platform badge for an unknown platformType', () => {
|
||||
const badges = getInfrastructureSystemIdentityBadges(
|
||||
makeResource({
|
||||
type: 'vm',
|
||||
platformType: 'fictional-hypervisor-xyz' as Resource['platformType'],
|
||||
@@ -148,7 +139,7 @@ describe('resourceBadgePresentation — dead-arm reachability characterization (
|
||||
sources: [],
|
||||
}),
|
||||
);
|
||||
expect(label).toBe('Fictional Hypervisor Xyz');
|
||||
expect(badges.map((b) => b.label)).toEqual(['Fictional Hypervisor Xyz']);
|
||||
});
|
||||
|
||||
it('treats a whitespace-only agent.platform as absent, so the `if (!normalized) return null` guards at L271/L287 in the host-identity helpers cannot fire', () => {
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
getContainerRuntimeBadgeForRuntime,
|
||||
getInfrastructurePlatformBadges,
|
||||
getInfrastructureSystemIdentityBadges,
|
||||
getInfrastructureSystemIdentitySortLabel,
|
||||
getPlatformBadge,
|
||||
getTypeBadge,
|
||||
getUnifiedSourceBadges,
|
||||
@@ -498,7 +497,7 @@ describe('getVersionedSourceBadge via getInfrastructureSystemIdentityBadges (bra
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInfrastructureSystemIdentityBadges and getInfrastructureSystemIdentitySortLabel (branch coverage)', () => {
|
||||
describe('getInfrastructureSystemIdentityBadges fallbacks (branch coverage)', () => {
|
||||
it('falls back to the platform badge for a bare agent resource with no other identity', () => {
|
||||
const resource = makeResource({
|
||||
type: 'agent',
|
||||
@@ -507,10 +506,9 @@ describe('getInfrastructureSystemIdentityBadges and getInfrastructureSystemIdent
|
||||
sources: [],
|
||||
});
|
||||
expect(getInfrastructureSystemIdentityBadges(resource).map((b) => b.label)).toEqual(['Agent']);
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('Agent');
|
||||
});
|
||||
|
||||
it('returns an empty badge set and empty sort label when nothing resolves', () => {
|
||||
it('returns an empty badge set when nothing resolves', () => {
|
||||
const resource = makeResource({
|
||||
id: 'lonely',
|
||||
name: 'lonely',
|
||||
@@ -520,6 +518,5 @@ describe('getInfrastructureSystemIdentityBadges and getInfrastructureSystemIdent
|
||||
sources: [],
|
||||
});
|
||||
expect(getInfrastructureSystemIdentityBadges(resource)).toEqual([]);
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,8 +4,6 @@ import {
|
||||
getContainerRuntimeBadgeForRuntime,
|
||||
getInfrastructurePlatformBadges,
|
||||
getInfrastructureSystemIdentityBadges,
|
||||
getInfrastructureSystemIdentitySortLabel,
|
||||
getInfrastructureSystemTitleBadges,
|
||||
getPlatformBadge,
|
||||
getSourceBadge,
|
||||
getTypeBadge,
|
||||
@@ -158,7 +156,6 @@ describe('resourceBadgePresentation', () => {
|
||||
expect(
|
||||
getInfrastructureSystemIdentityBadges(unraidStorage).map((badge) => badge.label),
|
||||
).toEqual(['Unraid']);
|
||||
expect(getInfrastructureSystemIdentitySortLabel(unraidStorage)).toBe('Unraid');
|
||||
|
||||
const pbsDatastore = makeResource({
|
||||
type: 'storage',
|
||||
@@ -278,7 +275,6 @@ describe('resourceBadgePresentation', () => {
|
||||
expect(getInfrastructureSystemIdentityBadges(resource).map((badge) => badge.label)).toEqual([
|
||||
'PVE',
|
||||
]);
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('PVE');
|
||||
});
|
||||
|
||||
it('shows platform versions when the reported version belongs to the platform identity', () => {
|
||||
@@ -319,7 +315,7 @@ describe('resourceBadgePresentation', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('does not repeat the unversioned platform source after a versioned system identity', () => {
|
||||
it('takes the platform version from the agent report when the platform facet has none', () => {
|
||||
const agentDiscoveredPve = makeResource({
|
||||
type: 'agent',
|
||||
platformType: 'proxmox-pve',
|
||||
@@ -343,9 +339,6 @@ describe('resourceBadgePresentation', () => {
|
||||
|
||||
expect(systemBadges.map((badge) => badge.label)).toEqual(['PVE 8.3.3']);
|
||||
expect(sourceBadges.map((badge) => badge.label)).toEqual(['PVE', 'Agent']);
|
||||
expect(
|
||||
getInfrastructureSystemTitleBadges(systemBadges, sourceBadges).map((badge) => badge.label),
|
||||
).toEqual(['PVE 8.3.3', 'Agent']);
|
||||
});
|
||||
|
||||
it('prefers platform-facet version over agent OS version on hybrid-source resources', () => {
|
||||
@@ -391,7 +384,6 @@ describe('resourceBadgePresentation', () => {
|
||||
expect(getInfrastructureSystemIdentityBadges(resource).map((badge) => badge.label)).toEqual([
|
||||
'PVE',
|
||||
]);
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('PVE');
|
||||
});
|
||||
|
||||
it('renders Proxmox LXC Docker hosts with a canonical LXC badge and surfaces the VMID in the tooltip', () => {
|
||||
@@ -449,7 +441,6 @@ describe('resourceBadgePresentation', () => {
|
||||
expect(getInfrastructureSystemIdentityBadges(resource)[0]?.title).toBe(
|
||||
'TCP availability probe power-meter-01.lab.local:1883',
|
||||
);
|
||||
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('TCP');
|
||||
});
|
||||
|
||||
it('uses ICMP identity for ping-only availability endpoints', () => {
|
||||
|
||||
@@ -159,20 +159,6 @@ const withBadgeVersion = (badge: ResourceBadge, version: string): ResourceBadge
|
||||
};
|
||||
};
|
||||
|
||||
const VERSIONED_BADGE_SUFFIX_PATTERN = /\s+v?\d+(?:[._-]\d+)*(?:[-+~:][0-9a-z][0-9a-z._:+~-]*)?$/i;
|
||||
|
||||
const normalizeBadgeIdentityLabel = (value: string): string =>
|
||||
value.trim().toLowerCase().replace(VERSIONED_BADGE_SUFFIX_PATTERN, '').trim();
|
||||
|
||||
const badgeIdentityLabels = (badge: ResourceBadge): string[] =>
|
||||
Array.from(
|
||||
new Set(
|
||||
[badge.label, badge.title]
|
||||
.map((value) => (value ? normalizeBadgeIdentityLabel(value) : ''))
|
||||
.filter(Boolean),
|
||||
),
|
||||
);
|
||||
|
||||
const normalizeUnifiedSourceKeys = (sources?: string[] | null): KnownSourcePlatform[] => {
|
||||
if (!sources || sources.length === 0) return [];
|
||||
const normalized = sources
|
||||
@@ -695,15 +681,6 @@ export function getInfrastructureSystemIdentityBadges(resource: Resource): Resou
|
||||
return getInfrastructurePlatformBadges(rawSources);
|
||||
}
|
||||
|
||||
export function getInfrastructureSystemIdentitySortLabel(resource: Resource): string {
|
||||
return (
|
||||
getInfrastructureSystemIdentityBadges(resource)[0]?.label ||
|
||||
getPlatformBadge(resource.platformType)?.label ||
|
||||
resource.platformType ||
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
export function dedupeResourceBadges(
|
||||
badges: Array<ResourceBadge | null | undefined>,
|
||||
): ResourceBadge[] {
|
||||
@@ -717,19 +694,6 @@ export function dedupeResourceBadges(
|
||||
});
|
||||
}
|
||||
|
||||
export function getInfrastructureSystemTitleBadges(
|
||||
systemBadges: ResourceBadge[],
|
||||
sourceBadges: ResourceBadge[],
|
||||
): ResourceBadge[] {
|
||||
const systemIdentities = new Set(systemBadges.flatMap(badgeIdentityLabels));
|
||||
return dedupeResourceBadges([
|
||||
...systemBadges,
|
||||
...sourceBadges.filter((badge) =>
|
||||
badgeIdentityLabels(badge).every((identity) => !systemIdentities.has(identity)),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
const getContainerRuntimeLabel = (runtime?: string | null): string => {
|
||||
const raw = (runtime || '').trim();
|
||||
const normalized = raw.toLowerCase();
|
||||
|
||||
Reference in New Issue
Block a user