test(web): guard PBS identity rows across snapshot refresh (#1723)

The Backups drawer renders Discovery and Metrics Target from the merged host resource, so a snapshot-driven correlation change would silently flip those labels. Add discoveryTarget to the retained real-browser fixture and assert both identity rows stay present and byte-identical across reordered, timer-driven snapshots and datastore switching. The guard passes on the current source; it does not reproduce the reporter's live target-spelling flicker.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-09-22 23:49:13 +01:00
parent 50dc7f3e17
commit 2545fb8cba
2 changed files with 37 additions and 0 deletions
@@ -39,6 +39,7 @@ const pbs = {
},
// The PBS service target: correct for the service, but it has no host series.
metricsTarget: { resourceType: 'agent', resourceId: 'pbs-1' },
discoveryTarget: { resourceType: 'agent', resourceId: 'pbs-1' },
platformData: {
sources: ['pbs'],
pbs: { instanceId: 'proxback', hostname: 'proxback-vm', datastoreCount: 2 },
@@ -60,6 +61,7 @@ const guest = {
memory: { current: 16.9, total: 8000, used: 1352, free: 6648 },
agent: sharedAgent,
metricsTarget: { resourceType: 'vm', resourceId: 'proxmox:100' },
discoveryTarget: { resourceType: 'agent', resourceId: 'proxback-vm' },
platformData: { sources: ['proxmox', 'agent'], agent: sharedAgent },
} as unknown as Resource;
@@ -76,6 +78,7 @@ const standalone = {
lastSeen: Date.now(),
agent: sharedAgent,
metricsTarget: { resourceType: 'agent', resourceId: 'agent-proxback' },
discoveryTarget: { resourceType: 'agent', resourceId: 'proxback-vm' },
platformData: { sources: ['agent', 'pbs'], agent: sharedAgent },
} as unknown as Resource;
@@ -38,6 +38,23 @@ const launchOptions = {
const failures = [];
const topologies = ['pbs-only', 'side-by-side', 'guest'];
const wrong = 'agent/pbs-1';
// Reporter avsdev-cw sees the Identity card's "Discovery" and "Metrics
// Target" rows flick between a service id and the correlated host id while
// the table sits idle. Read the rendered rows so a snapshot-driven identity
// change fails the check instead of only being visible to a human.
const readIdentityRows = (detail) =>
detail
.locator('[data-testid="resource-identity-section"] tr')
.evaluateAll((rows) =>
rows
.map((row) => {
const cells = row.querySelectorAll('td');
return cells.length >= 2
? [cells[0].textContent.trim(), cells[1].textContent.trim()]
: null;
})
.filter((row) => row && row[0]),
);
try {
await server.listen();
browser = await chromium.launch(launchOptions);
@@ -170,6 +187,16 @@ const launchOptions = {
.locator('[data-testid="guest-history-plot"] path')
.count();
assert.ok(pathsBefore > 0);
const identityBefore = await readIdentityRows(detail);
const identityLabels = identityBefore.map(([label]) => label);
assert.ok(
identityLabels.includes('Discovery'),
'Discovery identity row missing from the drawer',
);
assert.ok(
identityLabels.includes('Metrics Target'),
'Metrics Target identity row missing from the drawer',
);
await page
.getByRole('button', { name: 'Schedule automatic snapshot' })
.click();
@@ -205,9 +232,16 @@ const launchOptions = {
.count(),
pathsBefore,
);
const identityAfter = await readIdentityRows(detail);
assert.deepEqual(
identityAfter,
identityBefore,
'Identity rows changed after a refreshed snapshot',
);
observations.push({
datastore,
paths: pathsBefore,
identity: identityAfter,
refreshPreservedHistory: true,
});
}