Files

311 lines
10 KiB
Go

// Copyright 2026 Beacon Contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
package handlers
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/MeshCore-Beacon/beacon-server/internal/api"
"github.com/go-chi/chi/v5"
)
func TestGetStatsObservations_InvalidSince(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/observations", getStatsObservations(stubReader{}))
req := httptest.NewRequest(http.MethodGet, "/stats/observations?since=notanint", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestGetStatsPayloadBreakdown_InvalidSince(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/payload-breakdown", getStatsPayloadBreakdown(stubReader{}))
req := httptest.NewRequest(http.MethodGet, "/stats/payload-breakdown?since=notanint", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestGetStatsTopNodes_InvalidLimit(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-nodes", getStatsTopNodes(stubReader{}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-nodes?limit=notanint", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestGetStatsTopObservers_InvalidSince(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-observers", getStatsTopObservers(stubReader{}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-observers?since=notanint", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestGetStatsTopObservers_InvalidLimit(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-observers", getStatsTopObservers(stubReader{}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-observers?limit=notanint", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestGetStatsOverview_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/overview", getStatsOverview(stubReader{
getStatsOverview: func(_ context.Context, _ []string) (*api.StatsOverview, error) {
return &api.StatsOverview{TotalPackets: 100}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/overview", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}
func TestGetStatsObservations_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/observations", getStatsObservations(stubReader{
getStatsObservations: func(_ context.Context, _ []string, _ time.Time) ([]api.ObservationPoint, error) {
return []api.ObservationPoint{{IATA: "YVR", ObservationCount: 10}}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/observations", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}
func TestGetStatsPayloadBreakdown_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/payload-breakdown", getStatsPayloadBreakdown(stubReader{
getStatsPayloadBreakdown: func(_ context.Context, _ []string, _ time.Time) ([]api.PayloadBreakdownItem, error) {
return []api.PayloadBreakdownItem{{PayloadType: 4, Count: 100}}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/payload-breakdown", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}
func TestGetStatsTopNodes_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-nodes", getStatsTopNodes(stubReader{
getStatsTopNodes: func(_ context.Context, _ []string, _ int32) ([]api.TopNode, error) {
return []api.TopNode{{IATA: "YVR", ObservationCount: 50}}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-nodes", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}
func TestGetStatsTopObservers_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-observers", getStatsTopObservers(stubReader{
getStatsTopObservers: func(_ context.Context, _ []string, _ time.Time, _ int32) ([]api.TopObserver, error) {
return []api.TopObserver{{IATA: "YVR", ObservationCount: 20}}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-observers", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}
func TestGetStatsTopAdvertisers_InvalidSince(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-advertisers", getStatsTopAdvertisers(stubReader{}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-advertisers?since=notanint", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestGetStatsTopAdvertisers_InvalidLimit(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-advertisers", getStatsTopAdvertisers(stubReader{}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-advertisers?limit=notanint", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestGetStatsTopAdvertisers_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-advertisers", getStatsTopAdvertisers(stubReader{
getStatsTopAdvertisers: func(_ context.Context, _ []string, _ time.Time, _ int32) ([]api.TopAdvertiser, error) {
return []api.TopAdvertiser{{IATA: "YVR", AdvertCount: 5}}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-advertisers", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}
func TestGetStatsClockDrift_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/clock-drift", getStatsClockDrift(stubReader{
getStatsClockDrift: func(_ context.Context, _ []string, _ int32) ([]api.ClockDriftEntry, error) {
return []api.ClockDriftEntry{{ClockDriftSeconds: -600}}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/clock-drift", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}
func TestGetStatsClockDrift_MultiIATA_PassedThrough(t *testing.T) {
var gotIATAs []string
r := chi.NewRouter()
r.Get("/stats/clock-drift", getStatsClockDrift(stubReader{
getStatsClockDrift: func(_ context.Context, iatas []string, _ int32) ([]api.ClockDriftEntry, error) {
gotIATAs = iatas
return nil, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/clock-drift?iatas=YVR,YYJ", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
if len(gotIATAs) != 2 || gotIATAs[0] != "YVR" || gotIATAs[1] != "YYJ" {
t.Errorf("expected [YVR YYJ], got %v", gotIATAs)
}
}
func TestGetStatsClockDrift_InvalidLimit(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/clock-drift", getStatsClockDrift(stubReader{}))
req := httptest.NewRequest(http.MethodGet, "/stats/clock-drift?limit=notanint", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestGetStatsTopTalkers_InvalidSince(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-talkers", getStatsTopTalkers(stubReader{}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-talkers?since=notanint", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestGetStatsTopTalkers_InvalidLimit(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-talkers", getStatsTopTalkers(stubReader{}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-talkers?limit=notanint", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestGetStatsTopTalkers_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/top-talkers", getStatsTopTalkers(stubReader{
getStatsTopTalkers: func(_ context.Context, _ []string, _ time.Time, _ int32) ([]api.TopTalker, error) {
return []api.TopTalker{{SenderName: "Robbie", MessageCount: 3}}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/top-talkers", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}
func TestGetStatsRadioPresets_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/radio-presets", getStatsRadioPresets(stubReader{
getRadioPresets: func(_ context.Context, _ string, _ []string) ([]api.RadioPreset, error) {
return []api.RadioPreset{{Preset: "915.0,125,7", IATA: "YVR"}}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/radio-presets", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}
func TestGetStatsScopes_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/scopes", getStatsScopes(stubReader{
getScopeStats: func(_ context.Context) ([]api.ScopeStats, error) {
return []api.ScopeStats{{Name: "#bc", PacketCount: 100}}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/scopes", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}
func TestGetStatsNodeTypes_OK(t *testing.T) {
r := chi.NewRouter()
r.Get("/stats/node-types", getStatsNodeTypes(stubReader{
getStatsNodeTypes: func(_ context.Context, _ []string) ([]api.NodeTypeCount, error) {
return []api.NodeTypeCount{{NodeType: 1, Count: 5}}, nil
},
}))
req := httptest.NewRequest(http.MethodGet, "/stats/node-types", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
}