From 0608c4ecaac6f34ef50879661fa189755a85c7d8 Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Sun, 28 May 2023 23:31:53 +0200 Subject: [PATCH] Added tests for astronomical data. Let's merge this for now as 0.0.7 --- astroinfo_test.go | 141 ++++++++++++++++++++++++++++++++++++++++++++++ datatype.go | 17 ++---- datatype_test.go | 37 ++++++++++++ datetime.go | 8 ++- 4 files changed, 189 insertions(+), 14 deletions(-) create mode 100644 astroinfo_test.go create mode 100644 datatype_test.go diff --git a/astroinfo_test.go b/astroinfo_test.go new file mode 100644 index 0000000..98b7997 --- /dev/null +++ b/astroinfo_test.go @@ -0,0 +1,141 @@ +// SPDX-FileCopyrightText: 2023 Winni Neessen +// +// SPDX-License-Identifier: MIT +package meteologix + +import ( + "testing" + "time" +) + +func TestClient_AstronomicalInfoByCoordinates(t *testing.T) { + la := 52.5067296 + lo := 13.2599306 + loc, err := time.LoadLocation("Europe/Berlin") + if err != nil { + t.Errorf("failed to load time location data for Europe/Berlin: %s", err) + return + } + rt := time.Date(2023, 5, 28, 15, 8, 33, 0, loc) + nfmt := time.Date(2023, 6, 4, 5, 43, 56, 0, loc) + nnmt := time.Date(2023, 6, 18, 6, 39, 10, 0, loc) + c := New(withMockAPI()) + ai, err := c.AstronomicalInfoByCoordinates(la, lo) + if err != nil { + t.Errorf("failed to fetch astronomical information: %s", err) + return + } + if ai.Latitude != la { + t.Errorf("AstronomicalInfoByCoordinates failed, expected lat: %f, got: %f", la, + ai.Latitude) + } + if ai.Longitude != lo { + t.Errorf("AstronomicalInfoByCoordinates failed, expected lon: %f, got: %f", lo, + ai.Longitude) + } + if ai.Run.UnixMilli() != rt.UnixMilli() { + t.Errorf("AstronomicalInfoByCoordinates failed, expected run time: %s, got: %s", + rt.String(), ai.Run.String()) + } + if ai.TimeZone != loc.String() { + t.Errorf("AstronomicalInfoByCoordinates failed, expected time zone: %s, got: %s", + loc.String(), ai.TimeZone) + } + if ai.NextFullMoon.UnixMilli() != nfmt.UnixMilli() { + t.Errorf("AstronomicalInfoByCoordinates failed, expected next full moon: %s, got: %s", + nfmt.String(), ai.NextFullMoon.String()) + } + if ai.NextNewMoon.UnixMilli() != nnmt.UnixMilli() { + t.Errorf("AstronomicalInfoByCoordinates failed, expected next new moon: %s, got: %s", + nnmt.String(), ai.NextNewMoon.String()) + } +} + +func TestClient_AstronomicalInfoByLocation(t *testing.T) { + la := 52.5067296 + lo := 13.2599306 + loc, err := time.LoadLocation("Europe/Berlin") + if err != nil { + t.Errorf("failed to load time location data for Europe/Berlin: %s", err) + return + } + rt := time.Date(2023, 5, 28, 15, 8, 33, 0, loc) + nfmt := time.Date(2023, 6, 4, 5, 43, 56, 0, loc) + nnmt := time.Date(2023, 6, 18, 6, 39, 10, 0, loc) + c := New(withMockAPI()) + ai, err := c.AstronomicalInfoByLocation("Berlin, Germany") + if err != nil { + t.Errorf("failed to fetch astronomical information: %s", err) + return + } + if ai.Latitude != la { + t.Errorf("AstronomicalInfoByCoordinates failed, expected lat: %f, got: %f", la, + ai.Latitude) + } + if ai.Longitude != lo { + t.Errorf("AstronomicalInfoByCoordinates failed, expected lon: %f, got: %f", lo, + ai.Longitude) + } + if ai.Run.UnixMilli() != rt.UnixMilli() { + t.Errorf("AstronomicalInfoByCoordinates failed, expected run time: %s, got: %s", + rt.String(), ai.Run.String()) + } + if ai.TimeZone != loc.String() { + t.Errorf("AstronomicalInfoByCoordinates failed, expected time zone: %s, got: %s", + loc.String(), ai.TimeZone) + } + if ai.NextFullMoon.UnixMilli() != nfmt.UnixMilli() { + t.Errorf("AstronomicalInfoByCoordinates failed, expected next full moon: %s, got: %s", + nfmt.String(), ai.NextFullMoon.String()) + } + if ai.NextNewMoon.UnixMilli() != nnmt.UnixMilli() { + t.Errorf("AstronomicalInfoByCoordinates failed, expected next new moon: %s, got: %s", + nnmt.String(), ai.NextNewMoon.String()) + } +} + +func TestAstronomicalInfo_SunsetByDateString(t *testing.T) { + la := 52.5067296 + lo := 13.2599306 + loc, err := time.LoadLocation("Europe/Berlin") + if err != nil { + t.Errorf("failed to load time location data for Europe/Berlin: %s", err) + return + } + ti := time.Date(2023, 5, 28, 21, 16, 37, 0, loc) + c := New(withMockAPI()) + ai, err := c.AstronomicalInfoByCoordinates(la, lo) + if err != nil { + t.Errorf("failed to fetch astronomical information: %s", err) + return + } + if !ai.SunsetByTime(ti).IsAvailable() { + t.Errorf("SunsetByTime failed, expected entry, but got 'not available'") + return + } + if ai.SunsetByTime(ti).Value().UnixMilli() != ti.UnixMilli() { + t.Errorf("SunsetByTime failed, expected sunset: %s, got: %s", + ti.String(), ai.SunsetByTime(ti).Value().String()) + } + if !ai.SunsetByDateString(ti.Format(DateFormat)).IsAvailable() { + t.Errorf("SunsetByDateString failed, expected entry, but got 'not available'") + return + } + if ai.SunsetByTime(ti).String() != ti.String() { + t.Errorf("SunsetByTime failed, expected sunset: %s, got: %s", + ti.String(), ai.SunsetByTime(ti).String()) + } + if ai.SunsetByDateString(ti.Format(DateFormat)).Value().UnixMilli() != ti.UnixMilli() { + t.Errorf("SunsetByDateString failed, expected sunset: %s, got: %s", + ti.String(), ai.SunsetByDateString(ti.Format(DateFormat)).Value().String()) + } + ti = time.Time{} + if ai.SunsetByTime(ti).IsAvailable() { + t.Errorf("SunsetByTime failed, expected no entry, but got: %s", + ai.SunsetByTime(ti).Value().String()) + } + if !ai.SunsetByTime(ti).Value().IsZero() { + t.Errorf("SunsetByTime failed, expected no entry, but got: %s", + ai.SunsetByTime(ti).Value().String()) + } +} diff --git a/datatype.go b/datatype.go index 6f0fdb8..a89887d 100644 --- a/datatype.go +++ b/datatype.go @@ -16,7 +16,7 @@ const DataUnavailable = "Data unavailable" // DateFormat is the parsing format that is used for datetime strings // that only hold the date but no time -const DateFormat = "2006-02-01" +const DateFormat = "2006-01-02" // Enum for different Fieldname values const ( @@ -120,7 +120,8 @@ type WeatherData struct { // of an Observation type Fieldname int -// UnmarshalJSON interprets the API datestamp and converts it into a time.Time type +// UnmarshalJSON interprets the API datestamp and converts it into a +// time.Time type func (a *APIDate) UnmarshalJSON(s []byte) error { d := string(s) d = strings.ReplaceAll(d, `"`, ``) @@ -128,17 +129,7 @@ func (a *APIDate) UnmarshalJSON(s []byte) error { return nil } - var pd time.Time - var err error - switch len(d) { - case 10: - pd, err = time.Parse(DateFormat, d) - case 20: - pd, err = time.Parse(time.RFC3339, d) - default: - return fmt.Errorf("failed to parse JSON string as API date: " + - "unknown input date format") - } + pd, err := time.Parse(DateFormat, d) if err != nil { return fmt.Errorf("failed to parse JSON string as APIDate string: %w", err) } diff --git a/datatype_test.go b/datatype_test.go new file mode 100644 index 0000000..d2e427a --- /dev/null +++ b/datatype_test.go @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 Winni Neessen +// +// SPDX-License-Identifier: MIT +package meteologix + +import ( + "encoding/json" + "testing" +) + +func TestAPIDate_UnmarshalJSON(t *testing.T) { + type x struct { + Date APIDate `json:"date"` + } + okd := []byte(`{"date":"2023-05-28"}`) + nokd := []byte(`{"date":"2023-05-32"}`) + null := []byte(`{"date":null}`) + var d x + if err := json.Unmarshal(okd, &d); err != nil { + t.Errorf("APIDate_UnmarshalJSON failed: %s", err) + } + if d.Date.Format(DateFormat) != "2023-05-28" { + t.Errorf("APIDate_UnmarshalJSON failed, expected: %s, but got: %s", + "2023-05-28", d.Date.String()) + } + if err := json.Unmarshal(nokd, &d); err == nil { + t.Errorf("APIDate_UnmarshalJSON was supposed to fail, but didn't") + } + d = x{} + if err := json.Unmarshal(null, &d); err != nil { + t.Errorf("APIDate_UnmarshalJSON failed: %s", err) + } + if !d.Date.IsZero() { + t.Errorf("APIDate_UnmarshalJSON with null was supposed to be empty, but got: %s", + d.Date.String()) + } +} diff --git a/datetime.go b/datetime.go index fa783a2..ca06fcc 100644 --- a/datetime.go +++ b/datetime.go @@ -23,7 +23,13 @@ func (dt DateTime) IsAvailable() bool { // Value will return time.Time with a zero value instead. func (dt DateTime) Value() time.Time { if dt.na { - return time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC) + return time.Time{} } return dt.dv } + +// String satisfies the fmt.Stringer interface for the DateTime +// type +func (dt DateTime) String() string { + return dt.dv.String() +}