package utils
import (
"testing"
"time"
)
var testLocationAuckland = struct {
lat float64
lon float64
}{
lat: -36.8485,
lon: 174.7633,
}
func TestCalculateAstronomicalData(t *testing.T) {
tests := []struct {
name string
timestamp string
duration float64
lat, lon float64
wantNoSNight bool wantNoCNight bool }{
{name: "valid moon phase range", timestamp: "2024-06-15T12:00:00Z", duration: 60.0, lat: testLocationAuckland.lat, lon: testLocationAuckland.lon},
{name: "no solar night during daytime", timestamp: "2024-12-15T00:00:00Z", duration: 60.0, lat: testLocationAuckland.lat, lon: testLocationAuckland.lon, wantNoSNight: true, wantNoCNight: true},
{name: "short duration", timestamp: "2024-06-15T10:00:00Z", duration: 30.0, lat: testLocationAuckland.lat, lon: testLocationAuckland.lon},
{name: "long duration", timestamp: "2024-06-15T10:00:00Z", duration: 3600.0, lat: testLocationAuckland.lat, lon: testLocationAuckland.lon},
{name: "midpoint calculation", timestamp: "2024-06-15T10:00:00Z", duration: 7200.0, lat: testLocationAuckland.lat, lon: testLocationAuckland.lon},
{name: "different location", timestamp: "2024-06-15T12:00:00Z", duration: 60.0, lat: testLocationAuckland.lat, lon: testLocationAuckland.lon},
{name: "very short duration", timestamp: "2024-06-15T12:00:00Z", duration: 0.1, lat: testLocationAuckland.lat, lon: testLocationAuckland.lon},
{name: "very long duration", timestamp: "2024-06-15T12:00:00Z", duration: 86400.0, lat: testLocationAuckland.lat, lon: testLocationAuckland.lon},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ts := parseTime(t, tt.timestamp)
result := CalculateAstronomicalData(ts, tt.duration, tt.lat, tt.lon)
if result.MoonPhase < 0 || result.MoonPhase > 1 {
t.Errorf("MoonPhase out of range: got %f, want 0-1", result.MoonPhase)
}
if tt.wantNoSNight && result.SolarNight {
t.Error("Expected SolarNight to be false")
}
if tt.wantNoCNight && result.CivilNight {
t.Error("Expected CivilNight to be false")
}
})
}
}
func TestBooleanLogicValidation(t *testing.T) {
t.Run("should never return invalid values for valid inputs", func(t *testing.T) {
testCases := []string{
"2024-06-15T06:00:00Z", "2024-06-15T12:00:00Z", "2024-06-15T18:00:00Z", "2024-12-15T06:00:00Z", "2024-12-15T12:00:00Z", "2024-12-15T18:00:00Z", }
for _, timestamp := range testCases {
t.Run(timestamp, func(t *testing.T) {
ts := parseTime(t, timestamp)
result := CalculateAstronomicalData(ts, 60, testLocationAuckland.lat, testLocationAuckland.lon)
_ = result.SolarNight
_ = result.CivilNight
if result.MoonPhase < 0 || result.MoonPhase > 1 {
t.Errorf("MoonPhase out of range: got %f, want 0-1", result.MoonPhase)
}
})
}
})
t.Run("should return false for daytime recordings", func(t *testing.T) {
summerMidday := parseTime(t, "2024-12-15T00:30:00Z") duration := 60.0
result := CalculateAstronomicalData(summerMidday, duration, testLocationAuckland.lat, testLocationAuckland.lon)
if result.SolarNight && result.CivilNight {
t.Logf("Note: Both SolarNight and CivilNight are true (may be valid depending on season)")
}
})
t.Run("should return true for nighttime recordings", func(t *testing.T) {
winterMidnight := parseTime(t, "2024-06-15T12:30:00Z") duration := 60.0
result := CalculateAstronomicalData(winterMidnight, duration, testLocationAuckland.lat, testLocationAuckland.lon)
_ = result.SolarNight
_ = result.CivilNight
})
}
func TestCalculateMidpointTime(t *testing.T) {
t.Run("should calculate midpoint correctly", func(t *testing.T) {
startTime := parseTime(t, "2024-06-15T10:00:00Z")
duration := 3600.0
midpoint := CalculateMidpointTime(startTime, duration)
expected := parseTime(t, "2024-06-15T10:30:00Z")
if !midpoint.Equal(expected) {
t.Errorf("Midpoint incorrect: got %v, want %v", midpoint, expected)
}
})
t.Run("should handle short durations", func(t *testing.T) {
startTime := parseTime(t, "2024-06-15T10:00:00Z")
duration := 10.0
midpoint := CalculateMidpointTime(startTime, duration)
expected := parseTime(t, "2024-06-15T10:00:05Z")
if !midpoint.Equal(expected) {
t.Errorf("Midpoint incorrect: got %v, want %v", midpoint, expected)
}
})
}
func parseTime(t *testing.T, s string) time.Time {
t.Helper()
parsed, err := time.Parse(time.RFC3339, s)
if err != nil {
t.Fatalf("Failed to parse time %s: %v", s, err)
}
return parsed
}