mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-16 11:39:53 +00:00
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.
58 lines
2.3 KiB
Go
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")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|