From 06284c68acb2f62a7800f7ee57aa88d0556fcd0e Mon Sep 17 00:00:00 2001 From: Jan Christophersen Date: Sat, 31 Oct 2020 09:54:29 +0100 Subject: [PATCH 01/11] Expose time.Ticker for manual cleanup --- client.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index 3129faa..43566ef 100644 --- a/client.go +++ b/client.go @@ -30,6 +30,8 @@ type Client struct { throttle <-chan time.Time testMode bool ctx context.Context + + ticker *time.Ticker } type logger interface { @@ -39,12 +41,15 @@ type logger interface { // NewClient is a constructor for the Client. It takes the key and token credentials // of a Trello member to authenticate and authorise requests with. func NewClient(key, token string) *Client { + ticker := time.NewTicker(time.Second / 8) + return &Client{ Client: http.DefaultClient, BaseURL: DefaultBaseURL, Key: key, Token: token, - throttle: time.Tick(time.Second / 8), // Actually 10/second, but we're extra cautious + throttle: ticker.C, // Actually 10/second, but we're extra cautious + ticker: ticker, testMode: false, ctx: context.Background(), } @@ -65,6 +70,11 @@ func (c *Client) Throttle() { } } +// Cleanup stops the throttling ticker +func (c *Client) Cleanup() { + c.ticker.Stop() +} + // Get takes a path, Arguments, and a target interface (e.g. Board or Card). // It runs a GET request on the Trello API endpoint and the path and uses the // Arguments as URL parameters. Then it returns either the target interface From 32c3e55ab739839bedca29834824932966a6939f Mon Sep 17 00:00:00 2001 From: Jan Christophersen Date: Sat, 31 Oct 2020 09:55:18 +0100 Subject: [PATCH 02/11] Update go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index b88317f..e103049 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/adlio/trello +module github.com/Rukenshia/trello go 1.12 From b9279bf379c496ade0dcedeb1bd5a5cc337ebe5c Mon Sep 17 00:00:00 2001 From: Jan Christophersen Date: Sat, 31 Oct 2020 10:07:50 +0100 Subject: [PATCH 03/11] Use rate package for rate limiting --- client.go | 18 +++++------------- go.mod | 5 ++++- go.sum | 2 ++ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/client.go b/client.go index 43566ef..a0bbf5e 100644 --- a/client.go +++ b/client.go @@ -14,6 +14,7 @@ import ( "time" "github.com/pkg/errors" + "golang.org/x/time/rate" ) // DefaultBaseURL is the default API base url used by Client to send requests to Trello. @@ -27,11 +28,9 @@ type Client struct { BaseURL string Key string Token string - throttle <-chan time.Time + throttle *rate.Limiter testMode bool ctx context.Context - - ticker *time.Ticker } type logger interface { @@ -41,15 +40,13 @@ type logger interface { // NewClient is a constructor for the Client. It takes the key and token credentials // of a Trello member to authenticate and authorise requests with. func NewClient(key, token string) *Client { - ticker := time.NewTicker(time.Second / 8) - + limit := rate.Every(time.Second / 8) return &Client{ Client: http.DefaultClient, BaseURL: DefaultBaseURL, Key: key, Token: token, - throttle: ticker.C, // Actually 10/second, but we're extra cautious - ticker: ticker, + throttle: rate.NewLimiter(limit, 1), testMode: false, ctx: context.Background(), } @@ -66,15 +63,10 @@ func (c *Client) WithContext(ctx context.Context) *Client { // Throttle starts receiving throttles from throttle channel each ticker period. func (c *Client) Throttle() { if !c.testMode { - <-c.throttle + c.throttle.Wait(c.ctx) } } -// Cleanup stops the throttling ticker -func (c *Client) Cleanup() { - c.ticker.Stop() -} - // Get takes a path, Arguments, and a target interface (e.g. Board or Card). // It runs a GET request on the Trello API endpoint and the path and uses the // Arguments as URL parameters. Then it returns either the target interface diff --git a/go.mod b/go.mod index e103049..62ae697 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module github.com/Rukenshia/trello go 1.12 -require github.com/pkg/errors v0.8.1 +require ( + github.com/pkg/errors v0.8.1 + golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e +) diff --git a/go.sum b/go.sum index f29ab35..b3b14f0 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,4 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 42972acb58f95d76fb1e068ad7a9dcbc5178493c Mon Sep 17 00:00:00 2001 From: Jan Christophersen Date: Wed, 14 Apr 2021 19:08:54 +0200 Subject: [PATCH 04/11] Add "start" field to trello.Card --- card.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/card.go b/card.go index b00f19a..c1bb1fe 100644 --- a/card.go +++ b/card.go @@ -29,6 +29,7 @@ type Card struct { ShortURL string `json:"shortUrl"` URL string `json:"url"` Desc string `json:"desc"` + Start *time.Time `json:"start"` Due *time.Time `json:"due"` DueComplete bool `json:"dueComplete"` Closed bool `json:"closed"` @@ -272,6 +273,9 @@ func (c *Client) CreateCard(card *Card, extraArgs ...Arguments) error { if card.Due != nil { args["due"] = card.Due.Format(time.RFC3339) } + if card.Start != nil { + args["start"] = card.Start.Format(time.RFC3339) + } args.flatten(extraArgs) err := c.Post(path, args, &card) @@ -293,6 +297,9 @@ func (l *List) AddCard(card *Card, extraArgs ...Arguments) error { if card.Due != nil { args["due"] = card.Due.Format(time.RFC3339) } + if card.Start != nil { + args["start"] = card.Start.Format(time.RFC3339) + } args.flatten(extraArgs) From fba5d59bb3c090081b9e1683e62722ce1d3c85e8 Mon Sep 17 00:00:00 2001 From: Jan Christophersen Date: Sat, 17 Apr 2021 08:05:12 +0200 Subject: [PATCH 05/11] Add tests for card start/due on CreateCard --- card_test.go | 19 ++++++++++++++++++- mock-response_test.go | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/card_test.go b/card_test.go index 524c814..135d423 100644 --- a/card_test.go +++ b/card_test.go @@ -6,6 +6,8 @@ package trello import ( + "fmt" + "net/http" "testing" "time" ) @@ -117,16 +119,31 @@ func TestBoardContainsCopyOfCard(t *testing.T) { func TestCreateCard(t *testing.T) { c := testClient() - server := mockResponse("cards", "card-create.json") + server := mockResponseWithRequestValidator(t, func(r *http.Request) error { + due := r.URL.Query().Get("due") + + if _, err := time.Parse(time.RFC3339, due); err != nil { + return fmt.Errorf("Expected due to be in RFC3339 format, but value was '%v'", due) + } + + start := r.URL.Query().Get("start") + + if _, err := time.Parse(time.RFC3339, start); err != nil { + return fmt.Errorf("Expected start to be in RFC3339 format, but value was '%v'", start) + } + return nil + }, "cards", "card-create.json") defer server.Close() c.BaseURL = server.URL dueDate := time.Now().AddDate(0, 0, 3) + startDate := time.Now().AddDate(0, 0, 2) card := Card{ Name: "Test Card Create", Desc: "What its about", Due: &dueDate, + Start: &startDate, IDList: "57f03a06b5ff33a63c8be316", IDLabels: []string{"label1", "label2"}, } diff --git a/mock-response_test.go b/mock-response_test.go index 4cd25b3..7767733 100644 --- a/mock-response_test.go +++ b/mock-response_test.go @@ -15,6 +15,7 @@ import ( "os" "path/filepath" "strings" + "testing" ) func mockResponse(paths ...string) *httptest.Server { @@ -30,6 +31,25 @@ func mockResponse(paths ...string) *httptest.Server { })) } +func mockResponseWithRequestValidator(t *testing.T, validatorFn func(r *http.Request) error, paths ...string) *httptest.Server { + parts := []string{".", "testdata"} + filename := filepath.Join(append(parts, paths...)...) + + mockData, err := ioutil.ReadFile(filename) + if err != nil { + log.Fatal(err) + } + return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if err := validatorFn(r); err != nil { + t.Errorf("Request validation failed: %v", err) + t.FailNow() + return + } + + rw.Write(mockData) + })) +} + func mockDynamicPathResponse() *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { From dab0f63951e799a37aed7e8c22c57eea940c4341 Mon Sep 17 00:00:00 2001 From: Jan Christophersen Date: Sat, 17 Apr 2021 08:17:37 +0200 Subject: [PATCH 06/11] Add start/due test to AddCardToList --- card_test.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/card_test.go b/card_test.go index 135d423..f47c553 100644 --- a/card_test.go +++ b/card_test.go @@ -173,15 +173,30 @@ func TestCreateCard(t *testing.T) { func TestAddCardToList(t *testing.T) { l := testList(t) - server := mockResponse("cards", "card-posted-to-bottom-of-list.json") + server := mockResponseWithRequestValidator(t, func(r *http.Request) error { + due := r.URL.Query().Get("due") + + if _, err := time.Parse(time.RFC3339, due); err != nil { + return fmt.Errorf("Expected due to be in RFC3339 format, but value was '%v'", due) + } + + start := r.URL.Query().Get("start") + + if _, err := time.Parse(time.RFC3339, start); err != nil { + return fmt.Errorf("Expected start to be in RFC3339 format, but value was '%v'", start) + } + return nil + }, "cards", "card-posted-to-bottom-of-list.json") defer server.Close() l.client.BaseURL = server.URL - dueDate := time.Now().AddDate(0, 0, 1) + dueDate := time.Now().AddDate(0, 0, 2) + startDate := time.Now().AddDate(0, 0, 1) card := Card{ Name: "Test Card POSTed to List", Desc: "This is its description.", Due: &dueDate, + Start: &startDate, IDLabels: []string{"label1", "label2"}, } From e257dc7778c96ec1db53b1b59b67a2e8b43199c2 Mon Sep 17 00:00:00 2001 From: Aaron Longwell Date: Fri, 25 Mar 2022 16:23:18 -0700 Subject: [PATCH 07/11] Remove obsoloete TODO.txt --- TODO.txt | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 TODO.txt diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index 5161e28..0000000 --- a/TODO.txt +++ /dev/null @@ -1,2 +0,0 @@ -- Create List -- Reorder Cards in List From fe9f4e63c27829d1030ebdf5d2a359318abcbd16 Mon Sep 17 00:00:00 2001 From: Aaron Longwell Date: Mon, 28 Mar 2022 14:50:00 -0700 Subject: [PATCH 08/11] Restore adlio/trello module name --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index f65970a..a33a592 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/Rukenshia/trello +module github.com/adlio/trello go 1.13 From c967f19f4e00bb9646d0a4a671c7c86dc8911840 Mon Sep 17 00:00:00 2001 From: Aaron Longwell Date: Mon, 28 Mar 2022 15:58:14 -0700 Subject: [PATCH 09/11] Create MockResponder This type will eventually replace all occurrences of mockResponse, mockDynamicResponse. For now its used only on card_test.go and eliminates only the mockResponseWithRequestValidator(). --- mock-responder_test.go | 153 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 mock-responder_test.go diff --git a/mock-responder_test.go b/mock-responder_test.go new file mode 100644 index 0000000..51c371e --- /dev/null +++ b/mock-responder_test.go @@ -0,0 +1,153 @@ +package trello + +import ( + "crypto/md5" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" +) + +// MockResponder is a thin wrapper around the httptest.Server. It adds the +// ability to specify a file or directory of files to use as mock responses, +// and provides the AsertRequest method to add test assertions that requests +// are being made correctly. Just like with httptest.Server, the caller should +// defer a call to .Close() to shutdown the server when all requests complete. +// MockResponders should be created via the NewMockResponder() constructor. +// +type MockResponder interface { + Close() + URL() string + AssertRequest(func(t *testing.T, r *http.Request)) +} + +type mockResponder struct { + t *testing.T + + // server will be nil until .URL() is called the first time + server *httptest.Server + + // requestAssertions is a list of functions which is called on + // each HTTP request before finding and returning the mock response content. + // They should be used to make assertions on the contents of the HTTP + // request being made against this MockResponder + requestAssertions []func(t *testing.T, r *http.Request) + + // mockPath holds the results of filepath.Join on the provided path parts. + // The constructor verifies the existence of the path, so this will always + // hold a valid path to either a mock file, or a directory of many mocks + mockPath string + + // useDynamicPaths is set to true when mockPath is a directory. It triggers + // code which determies the mock file from the path of incoming HTTP + // requests + useDynamicPaths bool +} + +// NewMockResponder creates a new MockResponder instance around the provided +// test case. The mockPath is the relative filesystem path under ./testdata/ +// where the mock response JSON can be found. +// +// If mockPath describes the path to a *file*, then that file +// will be used for ALL requests. If the path is a directory, then the mock +// response will be built dynamically from the path of the request (e.g. +// GET /subdir/folder/file will return the file at subdir/folder/file.json, +// assuming it exists). This latter mode is described as "dynamic paths". When +// requests arrive with querystring arguments, the dynamic path builder will +// compute an MD5 hash of the arguments and include that as a suffix of the +// mock file path. +// +// If no mockPath is provided, then the MockResponder will run in dynamic path +// mode from the root of the testdata/ directory. +// +// The caller is expected to defer a call .Close() after NewMockResponder(). +// +func NewMockResponder(t *testing.T, mockPath ...string) MockResponder { + r := &mockResponder{t: t} + + // Verify a valid path was provided + r.mockPath = filepath.Join(append([]string{".", "testdata"}, mockPath...)...) + fi, err := os.Stat(r.mockPath) + if err != nil { + log.Fatalf("invalid mock path %v: %s", mockPath, err) + } + + // If the provided mockPath points to a directory, then + // we'll figure out the ultimate path dynamically as requests occur. + r.useDynamicPaths = fi.IsDir() + + return r +} + +// AssertRequest adds a new function to be run on each HTTP request the mock +// responder recveives. Its intended use is to make test assertions on the +// content of the request +func (mr *mockResponder) AssertRequest(ra func(t *testing.T, r *http.Request)) { + mr.requestAssertions = append(mr.requestAssertions, ra) +} + +// Close wraps the *httptest.Server's Close method +func (mr *mockResponder) Close() { + if mr.server != nil { + mr.server.Close() + } +} + +// URL is equivalent to the *httptest.Server property of the same name, but it +// is responsible for *creating* the *httptest.Server. This function should +// be called after all customization (including calls to AssertRequest) is +// complete. +// +func (mr *mockResponder) URL() string { + if mr.server != nil { + mr.t.Error("URL() should only be called once, after completing configuration") + } + mr.server = httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + for _, assertion := range mr.requestAssertions { + assertion(mr.t, r) + } + mr.mockHandler(rw, r) + })) + return mr.server.URL +} + +// mockHandler is the http.HandlerFunc for the httptest.Server inside the +// mockResponder. When the mockPath points to a single file, it simply returns +// that file in the HTTP response. Otherwise it dynamically determines the +// path of the mock file to use and returns that if the file is found... +// otherwise it responds with an error instructing the user where to put their +// mock file. +// +func (mr *mockResponder) mockHandler(rw http.ResponseWriter, r *http.Request) { + var filename string + if mr.useDynamicPaths { + parts := []string{mr.mockPath} + parts = append(parts, strings.Split(strings.TrimPrefix(r.URL.Path, "/"), "/")...) + queryStringPart := strings.Replace(r.URL.RawQuery, "key=user&token=pass", "", -1) + if queryStringPart != "" { + parts[len(parts)-1] = fmt.Sprintf("%s-%x", parts[len(parts)-1], md5.Sum([]byte(queryStringPart))) + } + + filename = filepath.Join(parts...) + if !strings.HasSuffix(filename, ".json") { + filename = filename + ".json" + } + if _, err := os.Stat(filename); err != nil { + http.Error(rw, fmt.Sprintf("%s doesn't exist or couldn't be read. Create it with the mock you'd like to use.\n Args were: %s", filename, queryStringPart), http.StatusNotFound) + return + } + } else { + filename = mr.mockPath + } + + mockData, err := ioutil.ReadFile(filename) + if err != nil { + log.Fatal(err) + } + rw.Write(mockData) +} From a78a5daaa7f4a8242754b08e5d3b6c3112fb05ee Mon Sep 17 00:00:00 2001 From: Aaron Longwell Date: Mon, 28 Mar 2022 15:59:06 -0700 Subject: [PATCH 10/11] Replace mockResponseWithRequestValidator with MockResponder --- card_test.go | 30 +++++++++++++----------------- mock-response_test.go | 20 -------------------- 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/card_test.go b/card_test.go index f47c553..d4e3b1b 100644 --- a/card_test.go +++ b/card_test.go @@ -119,23 +119,21 @@ func TestBoardContainsCopyOfCard(t *testing.T) { func TestCreateCard(t *testing.T) { c := testClient() - server := mockResponseWithRequestValidator(t, func(r *http.Request) error { + server := NewMockResponder(t, "cards", "card-create.json") + defer server.Close() + server.AssertRequest(func(t *testing.T, r *http.Request) { due := r.URL.Query().Get("due") - if _, err := time.Parse(time.RFC3339, due); err != nil { - return fmt.Errorf("Expected due to be in RFC3339 format, but value was '%v'", due) + t.Errorf("Expected due to be in RFC3339 format, but value was '%v'", due) } start := r.URL.Query().Get("start") - if _, err := time.Parse(time.RFC3339, start); err != nil { - return fmt.Errorf("Expected start to be in RFC3339 format, but value was '%v'", start) + t.Errorf("Expected start to be in RFC3339 format, but value was '%v'", start) } - return nil - }, "cards", "card-create.json") - defer server.Close() + }) - c.BaseURL = server.URL + c.BaseURL = server.URL() dueDate := time.Now().AddDate(0, 0, 3) startDate := time.Now().AddDate(0, 0, 2) @@ -173,22 +171,20 @@ func TestCreateCard(t *testing.T) { func TestAddCardToList(t *testing.T) { l := testList(t) - server := mockResponseWithRequestValidator(t, func(r *http.Request) error { + server := NewMockResponder(t, "cards", "card-posted-to-bottom-of-list.json") + server.AssertRequest(func(t *testing.T, r *http.Request) { due := r.URL.Query().Get("due") - if _, err := time.Parse(time.RFC3339, due); err != nil { - return fmt.Errorf("Expected due to be in RFC3339 format, but value was '%v'", due) + t.Errorf("Expected due to be in RFC3339 format, but value was '%v'", due) } start := r.URL.Query().Get("start") - if _, err := time.Parse(time.RFC3339, start); err != nil { - return fmt.Errorf("Expected start to be in RFC3339 format, but value was '%v'", start) + t.Errorf("Expected start to be in RFC3339 format, but value was '%v'", start) } - return nil - }, "cards", "card-posted-to-bottom-of-list.json") + }) defer server.Close() - l.client.BaseURL = server.URL + l.client.BaseURL = server.URL() dueDate := time.Now().AddDate(0, 0, 2) startDate := time.Now().AddDate(0, 0, 1) diff --git a/mock-response_test.go b/mock-response_test.go index 7767733..4cd25b3 100644 --- a/mock-response_test.go +++ b/mock-response_test.go @@ -15,7 +15,6 @@ import ( "os" "path/filepath" "strings" - "testing" ) func mockResponse(paths ...string) *httptest.Server { @@ -31,25 +30,6 @@ func mockResponse(paths ...string) *httptest.Server { })) } -func mockResponseWithRequestValidator(t *testing.T, validatorFn func(r *http.Request) error, paths ...string) *httptest.Server { - parts := []string{".", "testdata"} - filename := filepath.Join(append(parts, paths...)...) - - mockData, err := ioutil.ReadFile(filename) - if err != nil { - log.Fatal(err) - } - return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - if err := validatorFn(r); err != nil { - t.Errorf("Request validation failed: %v", err) - t.FailNow() - return - } - - rw.Write(mockData) - })) -} - func mockDynamicPathResponse() *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { From 08501d63a12dfcb10fe4175d5f25a14d541a36b5 Mon Sep 17 00:00:00 2001 From: Aaron Longwell Date: Mon, 28 Mar 2022 15:59:23 -0700 Subject: [PATCH 11/11] Use MockResponder for all Card tests --- card_test.go | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/card_test.go b/card_test.go index d4e3b1b..5fb9af8 100644 --- a/card_test.go +++ b/card_test.go @@ -6,7 +6,6 @@ package trello import ( - "fmt" "net/http" "testing" "time" @@ -27,10 +26,10 @@ func TestCardCreatedAt(t *testing.T) { func TestGetCardsOnBoard(t *testing.T) { board := testBoard(t) - server := mockDynamicPathResponse() + server := NewMockResponder(t) defer server.Close() - board.client.BaseURL = server.URL + board.client.BaseURL = server.URL() cards, err := board.GetCards(Defaults()) if err != nil { t.Fatal(err) @@ -43,9 +42,9 @@ func TestGetCardsOnBoard(t *testing.T) { func TestGetCardsInList(t *testing.T) { list := testList(t) - server := mockResponse("cards", "list-cards-api-example.json") + server := NewMockResponder(t, "cards", "list-cards-api-example.json") defer server.Close() - list.client.BaseURL = server.URL + list.client.BaseURL = server.URL() cards, err := list.GetCards(Defaults()) if err != nil { @@ -59,9 +58,9 @@ func TestGetCardsInList(t *testing.T) { func TestCardsCustomFields(t *testing.T) { list := testList(t) - server := mockResponse("cards", "list-cards-api-example.json") + server := NewMockResponder(t, "cards", "list-cards-api-example.json") defer server.Close() - list.client.BaseURL = server.URL + list.client.BaseURL = server.URL() cards, err := list.GetCards(Defaults()) if err != nil { @@ -96,10 +95,10 @@ func TestCardsCustomFields(t *testing.T) { func TestBoardContainsCopyOfCard(t *testing.T) { board := testBoard(t) - server := mockResponse("actions", "board-actions-copyCard.json") + server := NewMockResponder(t, "actions", "board-actions-copyCard.json") defer server.Close() - board.client.BaseURL = server.URL + board.client.BaseURL = server.URL() firstResult, err := board.ContainsCopyOfCard("57f50c552b96e3fffe588aad", Defaults()) if err != nil { t.Error(err) @@ -221,16 +220,16 @@ func TestAddCardToList(t *testing.T) { func TestArchiveUnarchive(t *testing.T) { c := testCard(t) - server := mockResponse("cards", "card-archived.json") - c.client.BaseURL = server.URL + server := NewMockResponder(t, "cards", "card-archived.json") + c.client.BaseURL = server.URL() c.Archive() if c.Closed == false { t.Errorf("Card should have been archived.") } server.Close() - server = mockResponse("cards", "card-unarchived.json") - c.client.BaseURL = server.URL + server = NewMockResponder(t, "cards", "card-unarchived.json") + c.client.BaseURL = server.URL() c.Unarchive() if c.Closed == true { t.Errorf("Card should have been unarchived.") @@ -241,9 +240,9 @@ func TestArchiveUnarchive(t *testing.T) { func TestCopyCardToList(t *testing.T) { c := testCard(t) - server := mockResponse("cards", "card-copied.json") + server := NewMockResponder(t, "cards", "card-copied.json") defer server.Close() - c.client.BaseURL = server.URL + c.client.BaseURL = server.URL() newCard, err := c.CopyToList("57f03a022cd45c863ca581f1", Defaults()) if err != nil { @@ -262,9 +261,9 @@ func TestCopyCardToList(t *testing.T) { func TestGetParentCard(t *testing.T) { c := testCard(t) - server := mockDynamicPathResponse() + server := NewMockResponder(t) defer server.Close() - c.client.BaseURL = server.URL + c.client.BaseURL = server.URL() parent, err := c.GetParentCard(Defaults()) if err != nil { @@ -293,10 +292,10 @@ func TestGetAncestorCards(t *testing.T) { func TestAddMemberIdToCard(t *testing.T) { c := testCard(t) - server := mockResponse("cards", "card-add-member-response.json") + server := NewMockResponder(t, "cards", "card-add-member-response.json") defer server.Close() - c.client.BaseURL = server.URL + c.client.BaseURL = server.URL() member, err := c.AddMemberID("testmemberid") if err != nil { t.Error(err) @@ -311,10 +310,10 @@ func TestAddMemberIdToCard(t *testing.T) { func TestAddURLAttachmentToCard(t *testing.T) { c := testCard(t) - server := mockResponse("cards", "url-attachments.json") + server := NewMockResponder(t, "cards", "url-attachments.json") defer server.Close() - c.client.BaseURL = server.URL + c.client.BaseURL = server.URL() attachment := Attachment{ Name: "Test", URL: "https://github.com/test", @@ -341,10 +340,10 @@ func TestCardSetClient(t *testing.T) { // func testCard(t *testing.T) *Card { c := testClient() - server := mockResponse("cards", "card-api-example.json") + server := NewMockResponder(t, "cards", "card-api-example.json") defer server.Close() - c.BaseURL = server.URL + c.BaseURL = server.URL() card, err := c.GetCard("4eea503", Defaults()) if err != nil { t.Fatal(err)