fix: more summary dashboard bug fixes

This commit is contained in:
garethgeorge
2026-07-08 17:24:37 -07:00
parent 03e69c98dd
commit 222aa1caf7
3 changed files with 37 additions and 7 deletions
+11 -5
View File
@@ -282,10 +282,12 @@ func (a *summaryAcc) finalize(id string, now time.Time, allowedStaleness time.Du
}
// A day is overdue when, at its close (clamped to now for today), the newest
// OK backup is older than the allowed staleness. Days before the first OK
// backup ever are exempt: the scheduler anchors to the first real run. A day
// where a late backup finally lands renders as backed-up, so checking only the
// day's close loses nothing visible.
// OK backup is older than the allowed staleness AND the day had no OK backup
// of its own. Days before the first OK backup ever are exempt: the scheduler
// anchors to the first real run. Requiring the day itself to be empty keeps
// sub-daily schedules honest: an hourly plan whose last run was at noon still
// backed the day up even though midnight is well past the allowed gap, so the
// day must render as backed-up, not overdue.
if allowedStaleness > 0 {
checkpoint := day.AddDate(0, 0, 1)
if checkpoint.After(now) {
@@ -295,7 +297,11 @@ func (a *summaryAcc) finalize(id string, now time.Time, allowedStaleness time.Du
lastOkBackup = okBackupDates[nextOkBackup]
nextOkBackup++
}
bucket.Overdue = !lastOkBackup.IsZero() && checkpoint.Sub(lastOkBackup) > allowedStaleness
// lastOkBackup <= checkpoint <= day+1, so lastOkBackup.Before(day) is
// exactly "the newest OK backup landed on an earlier day" — this day had none.
bucket.Overdue = !lastOkBackup.IsZero() &&
lastOkBackup.Before(day) &&
checkpoint.Sub(lastOkBackup) > allowedStaleness
}
history = append(history, bucket)
+16
View File
@@ -69,6 +69,22 @@ func TestSummaryOverdueFlags(t *testing.T) {
// day closes more than 48h past it.
expected: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
},
{
name: "sub-daily schedule with a backup each day is never overdue",
okBackupDates: []time.Time{at(0, 2), at(1, 2), at(2, 2), at(3, 2), at(4, 2), at(5, 2), at(6, 2), at(7, 2), at(8, 2), at(9, 2)},
allowedGap: 75 * time.Minute, // hourly cadence * 1.25 grace
// Every day has an OK backup at 02:00. Its close (midnight) is ~22h past
// that, far beyond the gap, but a day that backed up is never overdue.
expected: nil,
},
{
name: "sub-daily schedule flags a fully missed day",
okBackupDates: []time.Time{at(0, 2), at(1, 2), at(3, 2), at(4, 2), at(5, 2), at(6, 2), at(7, 2), at(8, 2), at(9, 2)}, // day2 missing
allowedGap: 75 * time.Minute,
// Day 2 has no OK backup of its own and closes far past the allowed gap;
// day 3 recovers with its own backup and stays backed-up.
expected: []int{2},
},
{
name: "gap exactly at allowed staleness is not overdue",
okBackupDates: []time.Time{at(0, 0), at(2, 0), at(4, 0), at(6, 0), at(8, 0)},
+10 -2
View File
@@ -32,6 +32,7 @@ import { toaster } from "../../components/ui/toaster";
import {
BackupProgressEntry,
BackupProgressEntrySchema,
ResticSnapshot,
SnapshotSummary,
} from "../../../gen/ts/v1/restic_pb";
@@ -58,7 +59,7 @@ import {
import { OperationIcon } from "./OperationIcon";
import { LogView } from "../../components/common/LogView";
import { create } from "@bufbuild/protobuf";
import { create, toJsonString } from "@bufbuild/protobuf";
import { useReposByGuid } from "../../app/provider";
import { OperationListView } from "./OperationListView";
import * as m from "../../paraglide/messages";
@@ -737,7 +738,14 @@ const BackupOperationStatus = ({
);
} else {
console.error("GOT UNEXPECTED STATUS: ", status);
return <>{m.op_row_unexpected_status() + JSON.stringify(status)}</>;
// Serialize via the proto helper: BackupProgressEntry carries int64 fields that
// become BigInt, which raw JSON.stringify cannot serialize (it throws).
return (
<>
{m.op_row_unexpected_status() +
toJsonString(BackupProgressEntrySchema, status)}
</>
);
}
};