package moon import ( "errors" "math" "testing" "time" "b612.me/astro/basic" ) func TestOccultationCommonInputsValidate(t *testing.T) { if err := (Observer{Longitude: 121.5, Latitude: 31.2, Height: 10}).Validate(); err != nil { t.Fatalf("valid observer rejected: %v", err) } if err := (OccultationSearchOptions{SafetyMarginArcsec: 2, MaxEvents: 3}).Validate(); err != nil { t.Fatalf("valid options rejected: %v", err) } if err := (OccultationSearchOptions{}).Validate(); err != nil { t.Fatalf("zero options rejected: %v", err) } if err := (OccultationSearchOptions{MaxStep: 250 * time.Millisecond}).Validate(); err != nil { t.Fatalf("minimum search step rejected: %v", err) } invalid := []error{ (Observer{Latitude: 91}).Validate(), (Observer{Longitude: math.NaN()}).Validate(), (OccultationSearchOptions{SafetyMarginArcsec: -1}).Validate(), (OccultationSearchOptions{MaxEvents: -1}).Validate(), (OccultationSearchOptions{MaxStep: 100 * time.Millisecond}).Validate(), } for index, err := range invalid { if !errors.Is(err, ErrInvalidOccultationInput) { t.Fatalf("invalid input %d error = %v, want ErrInvalidOccultationInput", index, err) } } } func TestStarCoordinateValidate(t *testing.T) { valid := StarCoordinate{ ID: "star", RA: 101.28715533, Dec: -16.71611586, Epoch: time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), Frame: CoordinateFrameICRS, ProperMotionRACosDecMasPerYear: 10, ProperMotionDecMasPerYear: -20, ParallaxMas: 7, } if err := valid.Validate(); err != nil { t.Fatalf("valid star coordinate rejected: %v", err) } tests := []struct { name string modify func(*StarCoordinate) }{ {"RA=360", func(s *StarCoordinate) { s.RA = 360 }}, {"Dec out of range", func(s *StarCoordinate) { s.Dec = -91 }}, {"missing epoch", func(s *StarCoordinate) { s.Epoch = time.Time{} }}, {"unsupported frame", func(s *StarCoordinate) { s.Frame = CoordinateFrame("galactic") }}, {"non-finite proper motion", func(s *StarCoordinate) { s.ProperMotionDecMasPerYear = math.NaN() }}, {"negative parallax", func(s *StarCoordinate) { s.ParallaxMas = -1 }}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { candidate := valid tc.modify(&candidate) if err := candidate.Validate(); !errors.Is(err, ErrInvalidOccultationInput) { t.Fatalf("StarCoordinate.Validate() error = %v, want ErrInvalidOccultationInput", err) } }) } } func TestStarCoordinateFromStarData(t *testing.T) { data, err := basic.StarDataByChinese("进贤增九") if err != nil { t.Fatalf("load HR 4799: %v", err) } data.Pc = 50 coordinate, err := StarCoordinateFromStarData(data) if err != nil { t.Fatalf("StarCoordinateFromStarData() error = %v", err) } if coordinate.ID != "进贤增九" { t.Fatalf("ID = %q, want %q", coordinate.ID, "进贤增九") } if coordinate.RA != data.Ra || coordinate.Dec != data.Dec { t.Fatalf("RA/Dec = %.12f %.12f, want %.12f %.12f", coordinate.RA, coordinate.Dec, data.Ra, data.Dec) } if coordinate.Frame != CoordinateFrameJ2000 || !coordinate.Epoch.Equal(time.Date(2000, time.January, 1, 12, 0, 0, 0, time.UTC)) { t.Fatalf("frame/epoch = %q %v, want J2000", coordinate.Frame, coordinate.Epoch) } if coordinate.ProperMotionRACosDecMasPerYear != data.PmRA*1000 || coordinate.ProperMotionDecMasPerYear != data.PmDec*1000 { t.Fatalf("proper motion = %.3f %.3f mas/year", coordinate.ProperMotionRACosDecMasPerYear, coordinate.ProperMotionDecMasPerYear) } if coordinate.ParallaxMas != 20 { t.Fatalf("parallax = %.6f mas, want 20", coordinate.ParallaxMas) } } func TestStarCoordinateFromStarDataRejectsInvalidData(t *testing.T) { tests := []struct { name string data StarData }{ {name: "zero value"}, {name: "non-finite distance", data: StarData{InnerStarData: basic.InnerStarData{HR: 1, Ra: 10, Dec: 20, Pc: math.NaN()}}}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if _, err := StarCoordinateFromStarData(tc.data); !errors.Is(err, ErrInvalidOccultationInput) { t.Fatalf("StarCoordinateFromStarData() error = %v, want ErrInvalidOccultationInput", err) } }) } }