mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
On the release that adds the ledger the table is empty, so on a host that has been uploading nightly for months every account read Never, under a sentence saying no copy had ever reached the bucket. That sentence is untrue on exactly the morning an operator opens the new card for the first time, and it is a claim the host cannot make: what is empty is the record, not the bucket. The run now records every account it walks, not only the ones an object came of. A walk that produced nothing moves the attempt instant and leaves the success columns alone, so the two cases separate: no row at all is no record yet, a row without a success is never. Never now means what the word says — a run looked at this account and has never put anything there. The card names the last check on the never row, so the state is a fact with a timestamp rather than an assertion. Runbook and all seven bundles carry the same two sentences. The integration file proves both arms against real Postgres, including that a failed run leaves the previous good copy on the row.
113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
/**
|
|
* The off-host card paints what the response says, including the case an
|
|
* operator most needs stated out loud: this host is not sending anything
|
|
* anywhere. A card that renders an empty list on an unconfigured host reads
|
|
* as "no problems".
|
|
*/
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
import type { BackupsList } from "@/types/backups";
|
|
|
|
const queryResult = vi.hoisted(() => ({
|
|
current: {
|
|
data: null as BackupsList | null,
|
|
isLoading: false,
|
|
isError: false,
|
|
},
|
|
}));
|
|
|
|
vi.mock("@tanstack/react-query", () => ({
|
|
useQuery: () => ({ ...queryResult.current, refetch: vi.fn() }),
|
|
}));
|
|
|
|
import { I18nProvider } from "@/lib/i18n/context";
|
|
import { OffhostBackupsSection } from "../offhost-backups-section";
|
|
|
|
function render(data: BackupsList["offhost"] | null): string {
|
|
queryResult.current = {
|
|
data:
|
|
data === null
|
|
? null
|
|
: ({
|
|
rows: [],
|
|
retentionDays: 90,
|
|
schedule: null,
|
|
offhost: data,
|
|
} as unknown as BackupsList),
|
|
isLoading: false,
|
|
isError: false,
|
|
};
|
|
return renderToStaticMarkup(
|
|
<I18nProvider initialLocale="en">
|
|
<OffhostBackupsSection />
|
|
</I18nProvider>,
|
|
);
|
|
}
|
|
|
|
describe("<OffhostBackupsSection>", () => {
|
|
it("says off-host backup is not configured rather than showing an empty list", () => {
|
|
const html = render({ configured: false, periodHours: 24, rows: [] });
|
|
expect(html).toContain("Off-host backup is not configured");
|
|
expect(html).not.toContain('data-slot="offhost-backup-rows"');
|
|
});
|
|
|
|
it("paints one row per account with its verdict", () => {
|
|
const html = render({
|
|
configured: true,
|
|
periodHours: 24,
|
|
rows: [
|
|
{
|
|
userId: "u1",
|
|
username: "account-one",
|
|
lastAttemptAt: "2026-09-10T06:00:00.000Z",
|
|
lastSuccessAt: "2026-09-10T06:00:00.000Z",
|
|
sizeBytes: 4096,
|
|
ageHours: 3,
|
|
freshness: "fresh",
|
|
},
|
|
{
|
|
userId: "u2",
|
|
username: "account-two",
|
|
lastAttemptAt: "2026-09-06T06:00:00.000Z",
|
|
lastSuccessAt: "2026-09-06T06:00:00.000Z",
|
|
sizeBytes: 2048,
|
|
ageHours: 99,
|
|
freshness: "stale",
|
|
},
|
|
{
|
|
userId: "u3",
|
|
username: "account-three",
|
|
lastAttemptAt: "2026-09-10T02:30:00.000Z",
|
|
lastSuccessAt: null,
|
|
sizeBytes: null,
|
|
ageHours: null,
|
|
freshness: "never",
|
|
},
|
|
{
|
|
userId: "u4",
|
|
username: "account-four",
|
|
lastAttemptAt: null,
|
|
lastSuccessAt: null,
|
|
sizeBytes: null,
|
|
ageHours: null,
|
|
freshness: "unknown",
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(html).toContain('data-offhost-freshness="fresh"');
|
|
expect(html).toContain('data-offhost-freshness="stale"');
|
|
expect(html).toContain('data-offhost-freshness="never"');
|
|
expect(html).toContain('data-offhost-freshness="unknown"');
|
|
expect(html).toContain("account-two");
|
|
// The account a run walked and found nothing for says that, with the
|
|
// instant it was checked.
|
|
expect(html).toContain("Nothing has landed for this account");
|
|
// The account no run has recorded says THAT — not "never", which would
|
|
// claim the bucket is empty on the strength of an empty ledger.
|
|
expect(html).toContain("No nightly run has recorded this account yet");
|
|
// Never the bucket's own coordinates.
|
|
expect(html).not.toMatch(/secret|access[- ]key/i);
|
|
});
|
|
});
|