From af1679ad845559894c43c781ac709e1acbe58ddc Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Thu, 23 Mar 2023 14:41:42 -0400 Subject: [PATCH 01/15] add integration tests --- .github/workflows/ci.yaml | 3 + .github/workflows/release.yaml | 3 + Makefile | 4 + internal/integrationtest/integration_test.go | 93 ++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 internal/integrationtest/integration_test.go diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c5d6022..40de333 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -17,3 +17,6 @@ jobs: with: go-version-file: go.mod - run: make + - run: make int-test + env: + MAGIC_SEAWEED_API_KEY: ${{ secrets.MAGIC_SEAWEED_API_KEY }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9e6e97f..0edfd0d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -17,6 +17,9 @@ jobs: with: go-version-file: go.mod - run: make + - run: make int-test + env: + MAGIC_SEAWEED_API_KEY: ${{ secrets.MAGIC_SEAWEED_API_KEY }} release: needs: test diff --git a/Makefile b/Makefile index 319c9c7..cc80c36 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,10 @@ test: vet test-fmt go test -v -coverprofile=coverage.out -race $(SOURCE) .PHONY: test +int-test: vet test-fmt + go test -v -race ./internal/integrationtest +.PHONY: int-test + vet: go vet $(SOURCE) .PHONY: vet diff --git a/internal/integrationtest/integration_test.go b/internal/integrationtest/integration_test.go new file mode 100644 index 0000000..b6640fa --- /dev/null +++ b/internal/integrationtest/integration_test.go @@ -0,0 +1,93 @@ +package integrationtest + +import ( + "os" + "testing" + "time" + + "github.com/mdb/seaweed" +) + +var client *seaweed.Client + +func TestMain(m *testing.M) { + client = seaweed.NewClient(os.Getenv("MAGIC_SEAWEED_API_KEY")) + exitVal := m.Run() + os.Exit(exitVal) +} + +func TestForecast_Integration(t *testing.T) { + resp, err := client.Forecast("391") + if err != nil { + t.Error(err) + } + + if len(resp) == 0 { + t.Error("Forecast returned no forecasts") + } + + if resp[0].LocalTimestamp == 0 { + t.Error("Forecast returned no forecast timestamp") + } +} + +func TestToday_Integration(t *testing.T) { + resp, err := client.Today("391") + if err != nil { + t.Error(err) + } + + if len(resp) == 0 { + t.Error("Today returned no forecasts") + } + + today := time.Now().UTC() + + for _, forecast := range resp { + fd := time.Unix(forecast.LocalTimestamp, 0).UTC() + + if fd.Day() != today.Day() { + t.Errorf("Today returned forecast for '%s'", fd.String()) + } + } +} + +func TestTomorrow_Integration(t *testing.T) { + resp, err := client.Tomorrow("391") + if err != nil { + t.Error(err) + } + + if len(resp) == 0 { + t.Error("API returned no forecasts") + } + + tomorrow := time.Now().UTC().AddDate(0, 0, 1) + + for _, forecast := range resp { + fd := time.Unix(forecast.LocalTimestamp, 0).UTC() + + if fd.Day() != tomorrow.Day() { + t.Errorf("Tomorrow returned forecast for '%s'", fd.String()) + } + } +} + +func TestWeekend_Integration(t *testing.T) { + resp, err := client.Weekend("391") + if err != nil { + t.Error(err) + } + + if len(resp) == 0 { + t.Error("API returned no forecasts") + } + + for _, forecast := range resp { + fd := time.Unix(forecast.LocalTimestamp, 0).UTC().Weekday().String() + + if fd != "Saturday" && fd != "Sunday" { + t.Errorf("Weekend returned forecast for '%s'", fd) + } + } +} From 27fe28f29916dfd54208ba8ff300ef67e28ae96f Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Thu, 23 Mar 2023 14:45:19 -0400 Subject: [PATCH 02/15] 'make' only runs unit tests --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index cc80c36..f95b6df 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -SOURCE=./... +SOURCE=./ VERSION=0.5.0 .DEFAULT_GOAL := test From 4830db755f99bc5436fc5bf40b1934ad01dd851d Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Thu, 23 Mar 2023 15:38:16 -0400 Subject: [PATCH 03/15] handle HTTP 200 errors --- client.go | 40 +++++++++++++++----- client_test.go | 24 +++++++++++- internal/integrationtest/integration_test.go | 13 +++++++ resources.go | 14 +++++++ testdata/error.json | 6 +++ 5 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 testdata/error.json diff --git a/client.go b/client.go index 6076d6c..fb22a94 100644 --- a/client.go +++ b/client.go @@ -2,15 +2,18 @@ package seaweed import ( "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" + "strings" "time" "github.com/sirupsen/logrus" ) // Clock is a clock interface used to report the current time. +// It exists largely to test the Client#Tomorrow method. type Clock interface { Now() time.Time } @@ -41,7 +44,15 @@ func NewClient(APIKey string) *Client { } } -// Forecast fetches the full, multi-day forecast for a given spot. +// Forecast fetches the full, multi-day forecast for a given spot ID. +// +// A spot's ID appears in its URL. For example, Ocean City, NJ's spot ID is 391: +// https://magicseaweed.com/Ocean-City-NJ-Surf-Report/391/ +// +// Note that the Magic Seaweed API may respond with an HTTP status code of 200 +// and a response body reporting an error (see APIError). Forecast attempts to +// handle such instances by returning an error surfacing the response body error +// message. func (c *Client) Forecast(spot string) ([]Forecast, error) { forecasts, err := c.getForecast(spot) if err != nil { @@ -51,7 +62,7 @@ func (c *Client) Forecast(spot string) ([]Forecast, error) { return forecasts, nil } -// Today fetches the today's forecast for a given spot. +// Today fetches the today's forecast for a given spot ID. func (c *Client) Today(spot string) ([]Forecast, error) { today := []Forecast{} now := c.clock.Now().UTC() @@ -69,7 +80,7 @@ func (c *Client) Today(spot string) ([]Forecast, error) { return today, nil } -// Tomorrow fetches tomorrow's forecast for a given spot. +// Tomorrow fetches tomorrow's forecast for a given spot ID. func (c *Client) Tomorrow(spot string) ([]Forecast, error) { tomorrow := []Forecast{} tomorrowD := c.clock.Now().UTC().AddDate(0, 0, 1) @@ -87,7 +98,7 @@ func (c *Client) Tomorrow(spot string) ([]Forecast, error) { return tomorrow, nil } -// Weekend fetches the weekend's forecast for a given spot. +// Weekend fetches the weekend's forecast for a given spot ID. func (c *Client) Weekend(spot string) ([]Forecast, error) { weekendFs := []Forecast{} forecasts, err := c.Forecast(spot) @@ -112,12 +123,23 @@ func (c *Client) getForecast(spotID string) ([]Forecast, error) { return forecasts, err } - err = json.Unmarshal(body, &forecasts) - if err != nil { - return forecasts, err - } + switch { + case strings.Contains(string(body), "error_response"): + var errResp APIError + err = json.Unmarshal(body, &errResp) + if err != nil { + return forecasts, err + } - return forecasts, nil + return forecasts, errors.New(errResp.ErrorResponse.ErrorMsg) + default: + err = json.Unmarshal(body, &forecasts) + if err != nil { + return forecasts, err + } + + return forecasts, nil + } } func (c *Client) get(url string) ([]byte, error) { diff --git a/client_test.go b/client_test.go index 6e7f1f5..1ed6e13 100644 --- a/client_test.go +++ b/client_test.go @@ -15,7 +15,10 @@ import ( "github.com/sirupsen/logrus" ) -var resp string +var ( + resp string + errorResp string +) func TestMain(m *testing.M) { content, err := ioutil.ReadFile("testdata/response.json") @@ -25,6 +28,13 @@ func TestMain(m *testing.M) { resp = string(content) + errContent, err := ioutil.ReadFile("testdata/error.json") + if err != nil { + log.Fatal(err) + } + + errorResp = string(errContent) + exitVal := m.Run() os.Exit(exitVal) } @@ -94,6 +104,18 @@ func TestForecast(t *testing.T) { code: 500, expectForecastCount: 0, expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + }, { + desc: "when the response code is OK but the response body specifies an error", + body: errorResp, + code: 200, + expectForecastCount: 0, + expectError: errors.New("Unable to authenticate request: Ensure your API key is passed correctly. Refer to the API docs."), + }, { + desc: "when the response code is OK and the response body indicates an error, but with unexpected JSON", + body: "error_response{", + code: 200, + expectForecastCount: 0, + expectError: errors.New("invalid character 'e' looking for beginning of value"), }} for _, test := range tests { diff --git a/internal/integrationtest/integration_test.go b/internal/integrationtest/integration_test.go index b6640fa..b865a0c 100644 --- a/internal/integrationtest/integration_test.go +++ b/internal/integrationtest/integration_test.go @@ -31,6 +31,19 @@ func TestForecast_Integration(t *testing.T) { } } +func TestForecast_Integration_error(t *testing.T) { + c := seaweed.NewClient("") + resp, err := c.Forecast("391") + expected := "Unable to authenticate request: Ensure your API key is passed correctly. Refer to the API docs." + if err.Error() != expected { + t.Errorf("expected Forecast to err with '%s'; got '%s'", expected, err.Error()) + } + + if len(resp) > 0 { + t.Error("erroring Forecast returned forecasts") + } +} + func TestToday_Integration(t *testing.T) { resp, err := client.Today("391") if err != nil { diff --git a/resources.go b/resources.go index faa02f5..77f4587 100644 --- a/resources.go +++ b/resources.go @@ -2,6 +2,20 @@ package seaweed import "time" +// APIError represents a Seaweed API error response body. +// +// Note that the Magic Seaweed API may respond with an HTTP status code of 200 +// and a response body reporting an error. +type APIError struct { + ErrorResponse ErrorResponse `json:"error_response"` +} + +// ErrorResponse represents a Seaweed API error response. +type ErrorResponse struct { + Code int `json:"code"` + ErrorMsg string `json:"error_msg"` +} + // Forecast represents a Seaweed API forecast. type Forecast struct { Timestamp int64 `json:"timestamp"` diff --git a/testdata/error.json b/testdata/error.json new file mode 100644 index 0000000..8c8ea6b --- /dev/null +++ b/testdata/error.json @@ -0,0 +1,6 @@ +{ + "error_response": { + "code": 115, + "error_msg": "Unable to authenticate request: Ensure your API key is passed correctly. Refer to the API docs." + } +} From 2d8c769d1111fa5c53d722031743dc18a5364342 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Thu, 23 Mar 2023 16:13:16 -0400 Subject: [PATCH 04/15] ensure API Key is not logged --- client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index fb22a94..eba22b4 100644 --- a/client.go +++ b/client.go @@ -158,9 +158,10 @@ func (c *Client) get(url string) ([]byte, error) { err = fmt.Errorf("%s returned HTTP status code %d", url, resp.StatusCode) } + sanitizedURL := strings.Replace(url, c.APIKey, "", 1) l := c.Logger.WithFields( logrus.Fields{ - "url": url, + "url": sanitizedURL, "http_status": resp.StatusCode, "body": string(body), }) From 1846f0d479925fb5ced2b13f12ad9d26da8714b2 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Thu, 23 Mar 2023 16:16:42 -0400 Subject: [PATCH 05/15] use HTTPS --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index eba22b4..4a8e26d 100644 --- a/client.go +++ b/client.go @@ -116,7 +116,7 @@ func (c *Client) Weekend(spot string) ([]Forecast, error) { } func (c *Client) getForecast(spotID string) ([]Forecast, error) { - url := fmt.Sprintf("http://magicseaweed.com/api/%s/forecast/?spot_id=%s", c.APIKey, spotID) + url := fmt.Sprintf("https://magicseaweed.com/api/%s/forecast/?spot_id=%s", c.APIKey, spotID) forecasts := []Forecast{} body, err := c.get(url) if err != nil { From 7a1748057ec4b0d698c47436dcc36b00a239cbd5 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Thu, 23 Mar 2023 16:16:42 -0400 Subject: [PATCH 06/15] use HTTPS --- client_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client_test.go b/client_test.go index 1ed6e13..c92c22a 100644 --- a/client_test.go +++ b/client_test.go @@ -103,7 +103,7 @@ func TestForecast(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("https://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), }, { desc: "when the response code is OK but the response body specifies an error", body: errorResp, @@ -174,7 +174,7 @@ func TestWeekend(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("https://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { @@ -233,7 +233,7 @@ func TestToday(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("https://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { @@ -292,7 +292,7 @@ func TestTomorrow(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("https://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { From cd582572463aeaaa260729c08bc0d81d9a0540e1 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Thu, 23 Mar 2023 16:39:16 -0400 Subject: [PATCH 07/15] Revert "use HTTPS" This reverts commit 194c944daf1f18b650192d3399daa9603838cec7. --- client.go | 2 +- client_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client.go b/client.go index 4a8e26d..eba22b4 100644 --- a/client.go +++ b/client.go @@ -116,7 +116,7 @@ func (c *Client) Weekend(spot string) ([]Forecast, error) { } func (c *Client) getForecast(spotID string) ([]Forecast, error) { - url := fmt.Sprintf("https://magicseaweed.com/api/%s/forecast/?spot_id=%s", c.APIKey, spotID) + url := fmt.Sprintf("http://magicseaweed.com/api/%s/forecast/?spot_id=%s", c.APIKey, spotID) forecasts := []Forecast{} body, err := c.get(url) if err != nil { diff --git a/client_test.go b/client_test.go index c92c22a..1ed6e13 100644 --- a/client_test.go +++ b/client_test.go @@ -103,7 +103,7 @@ func TestForecast(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("https://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), }, { desc: "when the response code is OK but the response body specifies an error", body: errorResp, @@ -174,7 +174,7 @@ func TestWeekend(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("https://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { @@ -233,7 +233,7 @@ func TestToday(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("https://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { @@ -292,7 +292,7 @@ func TestTomorrow(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("https://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { From ff78a8aaf69ab1c601e3b42b9525a70b044ade6f Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Thu, 23 Mar 2023 16:42:38 -0400 Subject: [PATCH 08/15] ensure API key doesn't appear in errors --- client.go | 5 +++-- client_test.go | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/client.go b/client.go index eba22b4..3533df2 100644 --- a/client.go +++ b/client.go @@ -154,11 +154,12 @@ func (c *Client) get(url string) ([]byte, error) { return nil, err } + sanitizedURL := strings.Replace(url, c.APIKey, "", 1) + if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("%s returned HTTP status code %d", url, resp.StatusCode) + err = fmt.Errorf("%s returned HTTP status code %d", sanitizedURL, resp.StatusCode) } - sanitizedURL := strings.Replace(url, c.APIKey, "", 1) l := c.Logger.WithFields( logrus.Fields{ "url": sanitizedURL, diff --git a/client_test.go b/client_test.go index 1ed6e13..782a5a5 100644 --- a/client_test.go +++ b/client_test.go @@ -103,7 +103,7 @@ func TestForecast(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("http://magicseaweed.com/api//forecast/?spot_id=123 returned HTTP status code 500"), }, { desc: "when the response code is OK but the response body specifies an error", body: errorResp, @@ -174,7 +174,7 @@ func TestWeekend(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("http://magicseaweed.com/api//forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { @@ -233,7 +233,7 @@ func TestToday(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("http://magicseaweed.com/api//forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { @@ -292,7 +292,7 @@ func TestTomorrow(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api/fakeKey/forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("http://magicseaweed.com/api//forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { From 276089547778f86a1e9c3529ffa5fb0ca15206ff Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Fri, 24 Mar 2023 09:01:34 -0400 Subject: [PATCH 09/15] use HTTPS --- README.md | 1 + client.go | 7 +++++-- client_test.go | 9 +++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e2f1189..0edc902 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Use a customized client: ```go client := seaweed.Client{ + BaseURL: "https://magicseaweed.com", APIKey: "YOUR_KEY", HTTPClient: &http.Client{}, // *http.Client Logger: logrus.New(logging.INFO), // *logrus.Logger diff --git a/client.go b/client.go index 3533df2..58d56f0 100644 --- a/client.go +++ b/client.go @@ -28,6 +28,7 @@ func (RealClock) Now() time.Time { // Client represents a seaweed API client type Client struct { + BaseURL string APIKey string HTTPClient *http.Client Logger *logrus.Logger @@ -37,6 +38,7 @@ type Client struct { // NewClient takes an API key and returns a seaweed API client func NewClient(APIKey string) *Client { return &Client{ + "https://magicseaweed.com", APIKey, &http.Client{}, logrus.New(), @@ -116,7 +118,7 @@ func (c *Client) Weekend(spot string) ([]Forecast, error) { } func (c *Client) getForecast(spotID string) ([]Forecast, error) { - url := fmt.Sprintf("http://magicseaweed.com/api/%s/forecast/?spot_id=%s", c.APIKey, spotID) + url := fmt.Sprintf("%s/api/%s/forecast/?spot_id=%s", c.BaseURL, c.APIKey, spotID) forecasts := []Forecast{} body, err := c.get(url) if err != nil { @@ -155,9 +157,10 @@ func (c *Client) get(url string) ([]byte, error) { } sanitizedURL := strings.Replace(url, c.APIKey, "", 1) + sanitizedURL = strings.Replace(sanitizedURL, c.BaseURL, "", 1) if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("%s returned HTTP status code %d", sanitizedURL, resp.StatusCode) + err = fmt.Errorf("GET %s returned HTTP status code %d", sanitizedURL, resp.StatusCode) } l := c.Logger.WithFields( diff --git a/client_test.go b/client_test.go index 782a5a5..10cedb5 100644 --- a/client_test.go +++ b/client_test.go @@ -61,6 +61,7 @@ func testServerAndClient(code int, body string) (*httptest.Server, *Client) { httpClient := &http.Client{Transport: tr} client := &Client{ + server.URL, "fakeKey", httpClient, logrus.New(), @@ -103,7 +104,7 @@ func TestForecast(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api//forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("GET /api//forecast/?spot_id=123 returned HTTP status code 500"), }, { desc: "when the response code is OK but the response body specifies an error", body: errorResp, @@ -174,7 +175,7 @@ func TestWeekend(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api//forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("GET /api//forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { @@ -233,7 +234,7 @@ func TestToday(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api//forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("GET /api//forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { @@ -292,7 +293,7 @@ func TestTomorrow(t *testing.T) { body: resp, code: 500, expectForecastCount: 0, - expectError: errors.New("http://magicseaweed.com/api//forecast/?spot_id=123 returned HTTP status code 500"), + expectError: errors.New("GET /api//forecast/?spot_id=123 returned HTTP status code 500"), }} for _, test := range tests { From 7cbb5cc2a96935c22805371dc335339bfd5f4595 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Fri, 24 Mar 2023 09:05:27 -0400 Subject: [PATCH 10/15] run tests concurrently --- client_test.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/client_test.go b/client_test.go index 10cedb5..32c0508 100644 --- a/client_test.go +++ b/client_test.go @@ -119,8 +119,12 @@ func TestForecast(t *testing.T) { expectError: errors.New("invalid character 'e' looking for beginning of value"), }} - for _, test := range tests { + for i := range tests { + test := tests[i] + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + server, c := testServerAndClient(test.code, test.body) defer server.Close() forecasts, err := c.Forecast("123") @@ -178,8 +182,12 @@ func TestWeekend(t *testing.T) { expectError: errors.New("GET /api//forecast/?spot_id=123 returned HTTP status code 500"), }} - for _, test := range tests { + for i := range tests { + test := tests[i] + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + server, c := testServerAndClient(test.code, test.body) defer server.Close() forecasts, err := c.Weekend("123") @@ -237,8 +245,12 @@ func TestToday(t *testing.T) { expectError: errors.New("GET /api//forecast/?spot_id=123 returned HTTP status code 500"), }} - for _, test := range tests { + for i := range tests { + test := tests[i] + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + server, c := testServerAndClient(test.code, test.body) defer server.Close() forecasts, err := c.Today("123") @@ -296,8 +308,12 @@ func TestTomorrow(t *testing.T) { expectError: errors.New("GET /api//forecast/?spot_id=123 returned HTTP status code 500"), }} - for _, test := range tests { + for i := range tests { + test := tests[i] + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + server, c := testServerAndClient(test.code, test.body) defer server.Close() forecasts, err := c.Tomorrow("123") From 48334f3cc56590f29708d886f8fe6da647a18fc9 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Fri, 24 Mar 2023 09:15:35 -0400 Subject: [PATCH 11/15] improve documentation --- client.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/client.go b/client.go index 58d56f0..a5a930b 100644 --- a/client.go +++ b/client.go @@ -1,3 +1,4 @@ +// Package seaweed provides a Magic Seaweed API client. package seaweed import ( @@ -12,8 +13,10 @@ import ( "github.com/sirupsen/logrus" ) -// Clock is a clock interface used to report the current time. -// It exists largely to test the Client#Tomorrow method. +// Clock is a clock interface used to report the current time such that the +// Client#Today and Client#Tomorrow methods can return the proper forecasts +// relative to the current time. +// It exists largely for testing purposes. type Clock interface { Now() time.Time } @@ -28,11 +31,18 @@ func (RealClock) Now() time.Time { // Client represents a seaweed API client type Client struct { - BaseURL string - APIKey string + // BaseURL is the targeted Magic Seaweed API base URL, such as https://magicseaweed.com. + BaseURL string + // APIKey is a Magic Seaweed API key. + APIKey string + // HTTPClient is a *http.Client. HTTPClient *http.Client - Logger *logrus.Logger - clock Clock + // Logger is a *logrus.Logger. + Logger *logrus.Logger + // clock is a seaweed.Clock used to report the current time/date such that the + // Client#Tomorrow and Client#Today methods can return the proper forecasts + // relative to the current time. + clock Clock } // NewClient takes an API key and returns a seaweed API client From 1a7f1e2007d2c9bd44b032cd414f24a4d773ed85 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Fri, 24 Mar 2023 09:20:09 -0400 Subject: [PATCH 12/15] add badge linking to docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0edc902..b0d77f0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![CI](https://github.com/mdb/seaweed/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/mdb/seaweed/actions/workflows/ci.yaml) +[![CI](https://github.com/mdb/seaweed/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/mdb/seaweed/actions/workflows/ci.yaml) [![PkgGoDev](https://pkg.go.dev/badge/github.com/mdb/seaweed)](https://pkg.go.dev/github.com/mdb/seaweed) # seaweed From e5a244d24db6ad9e2a05c936828eef139eb35fca Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Fri, 24 Mar 2023 09:33:11 -0400 Subject: [PATCH 13/15] add Go Report Card badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b0d77f0..6c73fa3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![CI](https://github.com/mdb/seaweed/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/mdb/seaweed/actions/workflows/ci.yaml) [![PkgGoDev](https://pkg.go.dev/badge/github.com/mdb/seaweed)](https://pkg.go.dev/github.com/mdb/seaweed) +[![CI](https://github.com/mdb/seaweed/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/mdb/seaweed/actions/workflows/ci.yaml) [![PkgGoDev](https://pkg.go.dev/badge/github.com/mdb/seaweed)](https://pkg.go.dev/github.com/mdb/seaweed) [![Go Report Card](https://goreportcard.com/badge/github.com/mdb/seaweed)](https://goreportcard.com/report/github.com/mdb/seaweed) # seaweed From ca15dcbba97a3efe7a720f83be40b9a093acff9c Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Fri, 24 Mar 2023 09:33:39 -0400 Subject: [PATCH 14/15] bump major version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f95b6df..513bb14 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ SOURCE=./ -VERSION=0.5.0 +VERSION=1.0.0 .DEFAULT_GOAL := test From 5409753d5dbfce39d842432ec89640b055b79128 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Fri, 24 Mar 2023 09:52:07 -0400 Subject: [PATCH 15/15] downgrade to 0.5.0 seaweeed is probably not ready for a 1.0.0 release. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 513bb14..b5b6925 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ SOURCE=./ -VERSION=1.0.0 +VERSION=0.6.0 .DEFAULT_GOAL := test