Files
beacon-server/db/observer_window_test.go
T
MrAlders0n ff8758b932 fix(db): read observer activity raw rows back to the rollup watermark
Hourly activity read raw rows only 3h back, so a longer rollup backlog left
hours drawn as zero. The raw tail now starts at the newest complete rollup
hour, capped at the 24h raw holdback, and the response reports rolledUntil
and rawFrom so clients can show anything still uncovered.
2026-10-02 19:03:46 -04:00

58 lines
2.3 KiB
Go

// Copyright 2026 Beacon Contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
package db
import (
"context"
"testing"
"time"
sqlc "github.com/MeshCore-Beacon/beacon-server/db/sqlc"
mockdb "github.com/MeshCore-Beacon/beacon-server/db/sqlc/mock"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"go.uber.org/mock/gomock"
)
func TestObserverActivityLiveAndFixedWindow(t *testing.T) {
for _, interval := range []time.Duration{15 * time.Minute, time.Hour, 6 * time.Hour} {
for _, fixed := range []bool{false, true} {
t.Run(interval.String()+"/"+map[bool]string{false: "live", true: "fixed"}[fixed], func(t *testing.T) {
mock := mockdb.NewMockQuerier(gomock.NewController(t))
id := uuid.New()
mock.EXPECT().GetObserverByID(gomock.Any(), id).Return(sqlc.Observer{}, nil)
mock.EXPECT().GetObserverActivityLiveSummary(gomock.Any(), gomock.Any()).Return(sqlc.GetObserverActivityLiveSummaryRow{}, nil)
if interval < time.Hour {
mock.EXPECT().GetObserverActivityRaw(gomock.Any(), gomock.Any()).Return(nil, nil)
mock.EXPECT().GetObserverActivityRawPayloadTypes(gomock.Any(), gomock.Any()).Return(nil, nil)
} else {
mock.EXPECT().GetLatestCompleteRollupHour(gomock.Any()).Return(pgtype.Timestamptz{}, nil)
mock.EXPECT().GetObserverActivityHourly(gomock.Any(), gomock.Any()).Return(nil, nil)
mock.EXPECT().GetObserverActivityHourlyPayloadTypes(gomock.Any(), gomock.Any()).Return(nil, nil)
}
before := time.Now().UTC()
var until time.Time
if fixed {
until = before.Add(-24*time.Hour + 123*time.Millisecond)
}
got, err := (&Store{q: mock}).GetObserverActivity(context.Background(), id, 48*time.Hour, interval, until)
if err != nil {
t.Fatal(err)
}
if fixed {
if got.WindowEnd != until.Truncate(interval).UnixMilli() {
t.Fatalf("fixed end was not aligned: %d", got.WindowEnd)
}
} else if got.WindowEnd < before.UnixMilli() || got.WindowEnd > time.Now().UnixMilli() {
t.Fatalf("live end dropped the partial bucket: %d", got.WindowEnd)
}
// Freshness is always live, even when the activity window is historical.
if got.Summary.LastCompleteHourEnd != time.UnixMilli(got.GeneratedAt).UTC().Truncate(time.Hour).UnixMilli() {
t.Fatal("freshness was anchored to the historical activity window")
}
})
}
}
}