Checkpoint
Some checks failed
Test and build / Test and lint (push) Failing after 36s
Test and build / Build collector (push) Failing after 43s
Test and build / Build receiver (push) Failing after 42s

This commit is contained in:
2026-03-05 15:38:18 +01:00
parent 3106b2cf45
commit 13afa08e8a
108 changed files with 19509 additions and 729 deletions

370
server/server_test.go Normal file
View File

@@ -0,0 +1,370 @@
package server
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
"git.maze.io/ham/hamview/schema"
)
// setupTestServer creates an Echo instance with routes configured and an in-memory database
func setupTestServer(t *testing.T) (*echo.Echo, *Server) {
t.Helper()
logger := logrus.New()
logger.SetLevel(logrus.WarnLevel)
// Initialize in-memory SQLite database
if err := schema.Open("sqlite3", ":memory:"); err != nil {
t.Fatalf("Failed to open test database: %v", err)
}
// Create server instance
server := &Server{
listen: "127.0.0.1:0",
logger: logger,
}
// Setup Echo with routes
e := echo.New()
e.HideBanner = true
setupRoutes(server, e)
return e, server
}
// teardownTestServer cleans up test resources
func teardownTestServer(t *testing.T) {
t.Helper()
// Close database connection if needed
// schema.Close() // Add this method to schema package if needed
}
// TestRadiosEndpoints tests all radio-related endpoints
func TestRadiosEndpoints(t *testing.T) {
e, _ := setupTestServer(t)
defer teardownTestServer(t)
t.Run("GET /api/v1/radios", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/radios", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, rec.Code)
}
var radios []*schema.Radio
if err := json.Unmarshal(rec.Body.Bytes(), &radios); err != nil {
t.Errorf("Failed to unmarshal response: %v", err)
}
})
t.Run("GET /api/v1/radios/:protocol", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/radios/aprs", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, rec.Code)
}
var radios []*schema.Radio
if err := json.Unmarshal(rec.Body.Bytes(), &radios); err != nil {
t.Errorf("Failed to unmarshal response: %v", err)
}
})
}
// TestAPRSEndpoints tests all APRS-related endpoints
func TestAPRSEndpoints(t *testing.T) {
e, _ := setupTestServer(t)
defer teardownTestServer(t)
t.Run("GET /api/v1/aprs/packets", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/aprs/packets", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, rec.Code)
}
var packets []*schema.APRSPacket
if err := json.Unmarshal(rec.Body.Bytes(), &packets); err != nil {
t.Errorf("Failed to unmarshal response: %v", err)
}
})
t.Run("GET /api/v1/aprs/packets filters", func(t *testing.T) {
testCases := []string{
"?src=OE1ABC",
"?dst=APRS",
"?limit=200",
}
for _, query := range testCases {
req := httptest.NewRequest(http.MethodGet, "/api/v1/aprs/packets"+query, nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK && rec.Code != http.StatusInternalServerError {
t.Errorf("Expected status %d or %d, got %d", http.StatusOK, http.StatusInternalServerError, rec.Code)
}
}
})
}
// TestMeshCoreEndpoints tests all MeshCore-related endpoints
func TestMeshCoreEndpoints(t *testing.T) {
e, _ := setupTestServer(t)
defer teardownTestServer(t)
t.Run("GET /api/v1/meshcore", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/meshcore", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, rec.Code)
}
var stats map[string]any
if err := json.Unmarshal(rec.Body.Bytes(), &stats); err != nil {
t.Errorf("Failed to unmarshal response: %v", err)
}
})
t.Run("GET /api/v1/meshcore/groups", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/meshcore/groups", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, rec.Code)
}
var groups []any
if err := json.Unmarshal(rec.Body.Bytes(), &groups); err != nil {
t.Errorf("Failed to unmarshal response: %v", err)
}
})
t.Run("GET /api/v1/meshcore/nodes", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/meshcore/nodes", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, rec.Code)
}
var nodes []*schema.MeshCoreNode
if err := json.Unmarshal(rec.Body.Bytes(), &nodes); err != nil {
t.Errorf("Failed to unmarshal response: %v", err)
}
})
t.Run("GET /api/v1/meshcore/nodes?type=chat", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/meshcore/nodes?type=chat", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, rec.Code)
}
var nodes []*schema.MeshCoreNode
if err := json.Unmarshal(rec.Body.Bytes(), &nodes); err != nil {
t.Errorf("Failed to unmarshal response: %v", err)
}
})
t.Run("GET /api/v1/meshcore/packets", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/meshcore/packets", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, rec.Code)
}
var packets []*schema.MeshCorePacket
if err := json.Unmarshal(rec.Body.Bytes(), &packets); err != nil {
t.Errorf("Failed to unmarshal response: %v", err)
}
})
}
// TestMeshCoreNodesCloseTo tests the close-to endpoint
func TestMeshCoreNodesCloseTo(t *testing.T) {
e, _ := setupTestServer(t)
defer teardownTestServer(t)
// First, insert a test node if needed
// This is a placeholder - you'll need actual test data setup
t.Run("GET /api/v1/meshcore/nodes/close-to/:publickey", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/meshcore/nodes/close-to/test_key", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
// May return 404 or error if no data exists, which is fine for skeleton test
if rec.Code != http.StatusOK && rec.Code != http.StatusNotFound && rec.Code != http.StatusInternalServerError {
t.Errorf("Expected status %d, %d, or %d, got %d", http.StatusOK, http.StatusNotFound, http.StatusInternalServerError, rec.Code)
}
})
t.Run("GET /api/v1/meshcore/nodes/close-to/:publickey with radius", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/meshcore/nodes/close-to/test_key?radius=50000", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
// May return 404 or error if no data exists, which is fine for skeleton test
if rec.Code != http.StatusOK && rec.Code != http.StatusNotFound && rec.Code != http.StatusInternalServerError {
t.Errorf("Expected status %d, %d, or %d, got %d", http.StatusOK, http.StatusNotFound, http.StatusInternalServerError, rec.Code)
}
})
}
// TestMeshCorePacketsWithFilters tests packet endpoint with various query parameters
func TestMeshCorePacketsWithFilters(t *testing.T) {
e, _ := setupTestServer(t)
defer teardownTestServer(t)
testCases := []struct {
name string
queryParam string
}{
{"With hash", "?hash=test_hash"},
{"With type", "?type=1"},
{"With type and channel_hash", "?type=1&channel_hash=test_channel"},
{"No filters", ""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/meshcore/packets"+tc.queryParam, nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK && rec.Code != http.StatusInternalServerError {
t.Errorf("Expected status %d or %d, got %d", http.StatusOK, http.StatusInternalServerError, rec.Code)
}
})
}
}
// BenchmarkGetRadios benchmarks the radios endpoint
func BenchmarkGetRadios(b *testing.B) {
logger := logrus.New()
logger.SetLevel(logrus.ErrorLevel)
if err := schema.Open("sqlite3", ":memory:"); err != nil {
b.Fatalf("Failed to open test database: %v", err)
}
server := &Server{
listen: "127.0.0.1:0",
logger: logger,
}
e := echo.New()
e.HideBanner = true
setupRoutes(server, e)
req := httptest.NewRequest(http.MethodGet, "/api/v1/radios", nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
}
}
// BenchmarkGetMeshCorePackets benchmarks the packets endpoint
func BenchmarkGetMeshCorePackets(b *testing.B) {
logger := logrus.New()
logger.SetLevel(logrus.ErrorLevel)
if err := schema.Open("sqlite3", ":memory:"); err != nil {
b.Fatalf("Failed to open test database: %v", err)
}
server := &Server{
listen: "127.0.0.1:0",
logger: logger,
}
e := echo.New()
e.HideBanner = true
setupRoutes(server, e)
req := httptest.NewRequest(http.MethodGet, "/api/v1/meshcore/packets", nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
}
}
// Example test showing how to populate test data
func TestWithTestData(t *testing.T) {
e, _ := setupTestServer(t)
defer teardownTestServer(t)
// Example: Insert test data
ctx := context.Background()
// Example radio insertion (adapt based on your schema package)
// radio := &schema.Radio{
// ID: "test-radio-1",
// Protocol: "aprs",
// Name: "Test Radio",
// }
// if err := schema.InsertRadio(ctx, radio); err != nil {
// t.Fatalf("Failed to insert test radio: %v", err)
// }
t.Run("GET /api/v1/radios with data", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/radios", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, rec.Code)
}
var radios []*schema.Radio
if err := json.Unmarshal(rec.Body.Bytes(), &radios); err != nil {
t.Errorf("Failed to unmarshal response: %v", err)
}
// Add assertions about the data
// if len(radios) != 1 {
// t.Errorf("Expected 1 radio, got %d", len(radios))
// }
})
_ = ctx // Use ctx to avoid unused variable error
}