feat(orbitviewer): add satellite orbit viewer with 2D track, 10-day pass prediction, and AR sky view
Build and Push Docker Image / build (push) Successful in 2m31s
Build and Push Docker Image / build (push) Successful in 2m31s
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
package orbitviewer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func setupTestRouter() *gin.Engine {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
api := r.Group("/api/orbit-viewer")
|
||||
RegisterRoutes(api)
|
||||
return r
|
||||
}
|
||||
|
||||
func TestAPISatellites(t *testing.T) {
|
||||
r := setupTestRouter()
|
||||
|
||||
req, _ := http.NewRequest("GET", "/api/orbit-viewer/satellites", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Satellites []*SatelliteCatalogEntry `json:"satellites"`
|
||||
}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("Failed to parse JSON: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Satellites) == 0 {
|
||||
t.Errorf("Expected satellites in catalog, got 0")
|
||||
}
|
||||
|
||||
hasISS := false
|
||||
for _, sat := range resp.Satellites {
|
||||
if sat.ID == "iss" {
|
||||
hasISS = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasISS {
|
||||
t.Errorf("Expected ISS in satellite catalog")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPITrack(t *testing.T) {
|
||||
r := setupTestRouter()
|
||||
|
||||
payload := TrackRequest{
|
||||
SatelliteID: "iss",
|
||||
Observer: &Geodetic{
|
||||
Latitude: 39.9042,
|
||||
Longitude: 116.4074,
|
||||
Altitude: 0.05,
|
||||
},
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/api/orbit-viewer/track", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("Expected status 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var resp TrackResponse
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("Failed to parse JSON: %v", err)
|
||||
}
|
||||
|
||||
if resp.SatelliteID != "iss" {
|
||||
t.Errorf("Expected satellite_id iss, got %s", resp.SatelliteID)
|
||||
}
|
||||
if resp.AltitudeKm < 300 || resp.AltitudeKm > 500 {
|
||||
t.Errorf("Unrealistic ISS altitude: %f km", resp.AltitudeKm)
|
||||
}
|
||||
if len(resp.PastTrack) == 0 || len(resp.FutureTrack) == 0 {
|
||||
t.Errorf("Expected track points in response")
|
||||
}
|
||||
if resp.ObserverLook == nil {
|
||||
t.Errorf("Expected ObserverLook populated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIPasses(t *testing.T) {
|
||||
r := setupTestRouter()
|
||||
|
||||
payload := PassesRequest{
|
||||
SatelliteID: "iss",
|
||||
Observer: Geodetic{
|
||||
Latitude: 37.7749,
|
||||
Longitude: -122.4194,
|
||||
Altitude: 0.03,
|
||||
},
|
||||
Days: 10,
|
||||
MinElevationDeg: 10.0,
|
||||
VisibleOnly: false,
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/api/orbit-viewer/passes", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("Expected status 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Count int `json:"count"`
|
||||
Passes []SatellitePass `json:"passes"`
|
||||
}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("Failed to parse JSON: %v", err)
|
||||
}
|
||||
|
||||
if resp.Count == 0 {
|
||||
t.Errorf("Expected passes for ISS over 10 days, got 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIPosition(t *testing.T) {
|
||||
r := setupTestRouter()
|
||||
|
||||
payload := PositionRequest{
|
||||
SatelliteID: "iss",
|
||||
Observer: Geodetic{
|
||||
Latitude: 51.5074,
|
||||
Longitude: -0.1278,
|
||||
Altitude: 0.02,
|
||||
},
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/api/orbit-viewer/position", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("Expected status 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var resp map[string]interface{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("Failed to parse JSON: %v", err)
|
||||
}
|
||||
|
||||
if _, ok := resp["azimuth"]; !ok {
|
||||
t.Errorf("Missing azimuth in position response")
|
||||
}
|
||||
if _, ok := resp["elevation"]; !ok {
|
||||
t.Errorf("Missing elevation in position response")
|
||||
}
|
||||
if _, ok := resp["range_km"]; !ok {
|
||||
t.Errorf("Missing range_km in position response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPICustomTLE(t *testing.T) {
|
||||
r := setupTestRouter()
|
||||
|
||||
payload := TLEInput{
|
||||
Name: "TEST-CSS",
|
||||
Line1: "1 48274U 21035A 24080.45678901 .00021456 00000-0 31456-3 0 9991",
|
||||
Line2: "2 48274 41.4723 112.3456 0005123 85.2341 274.9812 15.61234567167890",
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/api/orbit-viewer/custom", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("Expected status 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Satellite *SatelliteCatalogEntry `json:"satellite"`
|
||||
}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("Failed to parse JSON: %v", err)
|
||||
}
|
||||
|
||||
if resp.Satellite.NoradID != 48274 {
|
||||
t.Errorf("Expected NORAD ID 48274, got %d", resp.Satellite.NoradID)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user