371 lines
10 KiB
Go
371 lines
10 KiB
Go
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
|
|
}
|