Repository: nicklaw5/helix
Pull request #160 (2022-12-18 16:52:02)

Add the ability to pass context into client
I started making my package use context and realized helix had no way of passing in the context. The best way would be to make every call pass in a context, and have a `GetStreamers(opts)` call `GetStreamersWithContext(opts)` and propagate context all the way down to `Client.newRequest` but the alternative is passing the context into the client options, which affects less code.


Patch:
diff --git a/docs/README.md b/docs/README.md
index 43e1f33..0e6c397 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -90,6 +90,17 @@ if err != nil {
 }
 ```
 
+You also have the option to pass a context to it using:
+
+```go
+client, err := helix.NewClientWithContext(ctx, &helix.Options{
+    ClientID: "your-client-id",
+})
+if err != nil {
+    // handle error
+}
+```
+
 If you'd like to pass in your own `http.Client`, you can do so like this:
 
 ```go
diff --git a/helix.go b/helix.go
index 942b302..e0ed1f4 100644
--- a/helix.go
+++ b/helix.go
@@ -2,6 +2,7 @@ package helix
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -28,6 +29,7 @@ type HTTPClient interface {
 
 type Client struct {
 	mu           sync.RWMutex
+	ctx          context.Context
 	opts         *Options
 	lastResponse *Response
 }
@@ -109,6 +111,10 @@ type Pagination struct {
 // NewClient returns a new Twitch Helix API client. It returns an
 // if clientID is an empty string. It is concurrency safe.
 func NewClient(options *Options) (*Client, error) {
+	return NewClientWithContext(context.Background(), options)
+}
+
+func NewClientWithContext(ctx context.Context, options *Options) (*Client, error) {
 	if options.ClientID == "" {
 		return nil, errors.New("A client ID was not provided but is required")
 	}
@@ -122,6 +128,7 @@ func NewClient(options *Options) (*Client, error) {
 	}
 
 	client := &Client{
+		ctx:  ctx,
 		opts: options,
 	}
 
@@ -267,7 +274,7 @@ func (c *Client) newRequest(method, path string, data interface{}, hasJSONBody b
 }
 
 func (c *Client) newStandardRequest(method, url string, data interface{}) (*http.Request, error) {
-	req, err := http.NewRequest(method, url, nil)
+	req, err := http.NewRequestWithContext(c.ctx, method, url, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -294,7 +301,7 @@ func (c *Client) newJSONRequest(method, url string, data interface{}) (*http.Req
 
 	buf := bytes.NewBuffer(b)
 
-	req, err := http.NewRequest(method, url, buf)
+	req, err := http.NewRequestWithContext(c.ctx, method, url, buf)
 	if err != nil {
 		return nil, err
 	}


Test patch:
diff --git a/ads_test.go b/ads_test.go
index ab6d20c..9255c2c 100644
--- a/ads_test.go
+++ b/ads_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -96,6 +97,7 @@ func TestClient_StartCommercial(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.StartCommercial(&StartCommercialParams{})
diff --git a/analytics_test.go b/analytics_test.go
index eef85d3..753dbbf 100644
--- a/analytics_test.go
+++ b/analytics_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -81,6 +82,7 @@ func TestGetExtensionAnalytics(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetExtensionAnalytics(&ExtensionAnalyticsParams{})
@@ -165,6 +167,7 @@ func TestGetGameAnalytics(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetGameAnalytics(&GameAnalyticsParams{})
diff --git a/authentication_test.go b/authentication_test.go
index fabe78a..30b1a22 100644
--- a/authentication_test.go
+++ b/authentication_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -138,6 +139,7 @@ func TestRequestAppAccessToken(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.RequestAppAccessToken([]string{"some-scope"})
@@ -287,6 +289,7 @@ func TestRequestUserAccessToken(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.RequestUserAccessToken("valid-auth-code")
@@ -430,6 +433,7 @@ func TestRefreshUserAccessToken(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.RefreshUserAccessToken("refresh-token")
@@ -517,6 +521,7 @@ func TestRevokeUserAccessToken(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.RevokeUserAccessToken("access-token")
@@ -619,6 +624,7 @@ func TestValidateToken(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, _, err := c.ValidateToken("access-token")
diff --git a/bits_test.go b/bits_test.go
index dd6e5f8..3e686f6 100644
--- a/bits_test.go
+++ b/bits_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 	"time"
@@ -114,6 +115,7 @@ func TestClient_GetBitsLeaderboard(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetBitsLeaderboard(&BitsLeaderboardParams{})
@@ -628,6 +630,7 @@ func TestClient_GetCheermotes(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetCheermotes(&CheermotesParams{})
diff --git a/channels_editors_test.go b/channels_editors_test.go
index b695e82..29d48aa 100644
--- a/channels_editors_test.go
+++ b/channels_editors_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -61,6 +62,7 @@ func TestGetChannelEditors(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetChannelEditors(&ChannelEditorsParams{})
diff --git a/channels_points_test.go b/channels_points_test.go
index e5d53f0..0844cac 100644
--- a/channels_points_test.go
+++ b/channels_points_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -63,6 +64,7 @@ func TestCreateCustomReward(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.CreateCustomReward(&ChannelCustomRewardsParams{})
@@ -134,6 +136,7 @@ func TestUpdateCustomReward(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.UpdateCustomReward(&UpdateChannelCustomRewardsParams{})
@@ -203,6 +206,7 @@ func TestDeleteCustomRewards(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.DeleteCustomRewards(&DeleteCustomRewardsParams{})
@@ -271,6 +275,7 @@ func TestGetCustomRewards(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetCustomRewards(&GetCustomRewardsParams{})
diff --git a/channels_test.go b/channels_test.go
index ae93981..a1a041b 100644
--- a/channels_test.go
+++ b/channels_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -120,6 +121,7 @@ func TestSearchChannels(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.SearchChannels(&SearchChannelsParams{})
@@ -273,6 +275,7 @@ func TestGetChannelInformation(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetChannelInformation(&GetChannelInformationParams{})
@@ -366,6 +369,7 @@ func TestEditChannelInformation(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.EditChannelInformation(&EditChannelInformationParams{})
diff --git a/charity_test.go b/charity_test.go
index fba3d7d..1d0cf1a 100644
--- a/charity_test.go
+++ b/charity_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -73,6 +74,7 @@ func TestGetCharityCampaigns(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetCharityCampaigns(&CharityCampaignsParams{})
@@ -153,6 +155,7 @@ func TestGetCharityDonations(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetCharityDonations(&CharityDonationParams{})
diff --git a/chat_test.go b/chat_test.go
index bffb46c..c8b5a76 100644
--- a/chat_test.go
+++ b/chat_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -137,6 +138,7 @@ func TestGetChannelChatBadges(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetChannelChatBadges(&GetChatBadgeParams{})
@@ -208,6 +210,7 @@ func TestGetGlobalChatBadges(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetGlobalChatBadges()
@@ -282,6 +285,7 @@ func TestGetChannelEmotes(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetChannelEmotes(&GetChannelEmotesParams{})
@@ -353,6 +357,7 @@ func TestGetGlobalEmotes(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetGlobalEmotes()
@@ -456,6 +461,7 @@ func TestGetEmoteSets(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetEmoteSets(&GetEmoteSetsParams{})
@@ -530,6 +536,7 @@ func TestSendChatAnnouncement(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.SendChatAnnouncement(&SendChatAnnouncementParams{})
@@ -680,6 +687,7 @@ func TestGetChatSettings(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetChatSettings(&GetChatSettingsParams{BroadcasterID: "123"})
diff --git a/clips_test.go b/clips_test.go
index 5a3368a..39b3671 100644
--- a/clips_test.go
+++ b/clips_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -89,6 +90,7 @@ func TestGetClips(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetClips(&ClipsParams{})
@@ -189,6 +191,7 @@ func TestCreateClip(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.CreateClip(&CreateClipParams{})
diff --git a/drops_test.go b/drops_test.go
index e487fad..1ffefdb 100644
--- a/drops_test.go
+++ b/drops_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"strings"
 	"testing"
@@ -93,6 +94,7 @@ func TestGetDropsEntitlements(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetDropsEntitlements(&GetDropEntitlementsParams{})
@@ -202,6 +204,7 @@ func TestUpdateDropsEntitlements(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.UpdateDropsEntitlements(&UpdateDropsEntitlementsParams{})
diff --git a/entitlement_codes_test.go b/entitlement_codes_test.go
index 0347c68..aaedb05 100644
--- a/entitlement_codes_test.go
+++ b/entitlement_codes_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -69,6 +70,7 @@ func TestClient_GetEntitlementCodeStatus(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetEntitlementCodeStatus(&CodesParams{})
diff --git a/entitlement_grants_test.go b/entitlement_grants_test.go
index b2d44cb..fa5d537 100644
--- a/entitlement_grants_test.go
+++ b/entitlement_grants_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"strings"
 	"testing"
@@ -111,6 +112,7 @@ func TestCreateEntitlementsUploadURL(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.CreateEntitlementsUploadURL("manifestID", "entitlementType")
diff --git a/eventsub_test.go b/eventsub_test.go
index 3e24787..b86168c 100644
--- a/eventsub_test.go
+++ b/eventsub_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -81,6 +82,7 @@ func TestGetEventSubSubscriptions(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetEventSubSubscriptions(&EventSubSubscriptionsParams{})
@@ -173,6 +175,7 @@ func TestRemoveEventSubSubscriptions(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.RemoveEventSubSubscription("832389eb-0d0b-41f8-b564-da039f6c4c75")
diff --git a/games_test.go b/games_test.go
index cf29819..af56d93 100644
--- a/games_test.go
+++ b/games_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -63,6 +64,7 @@ func TestGetGames(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetGames(&GamesParams{})
@@ -177,6 +179,7 @@ func TestGetTopGames(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetTopGames(&TopGamesParams{})
diff --git a/helix_test.go b/helix_test.go
index 7bd3345..8a8c2ef 100644
--- a/helix_test.go
+++ b/helix_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"errors"
 	"net/http"
 	"net/http/httptest"
@@ -24,7 +25,10 @@ func (mtc *mockHTTPClient) Do(req *http.Request) (*http.Response, error) {
 
 func newMockClient(options *Options, mockHandler http.HandlerFunc) *Client {
 	options.HTTPClient = &mockHTTPClient{mockHandler}
-	return &Client{opts: options}
+	return &Client{
+		opts: options,
+		ctx:  context.Background(),
+	}
 }
 
 func newMockHandler(statusCode int, json string, headers map[string]string) http.HandlerFunc {
@@ -158,6 +162,122 @@ func TestNewClientDefault(t *testing.T) {
 	}
 }
 
+func TestNewClientHasContext(t *testing.T) {
+	t.Parallel()
+
+	contextExists := false
+	mockClient := &mockHTTPClient{func(w http.ResponseWriter, r *http.Request) {
+		contextExists = r.Context() != nil
+	}}
+
+	c, err := NewClient(&Options{
+		ClientID:   "test",
+		HTTPClient: mockClient,
+	})
+	if err != nil {
+		t.Errorf("could not create client: %v", err)
+		return
+	}
+
+	_, err = c.GetStreams(nil)
+
+	if err != nil {
+		t.Errorf("Did not expect error, got \"%s\"", err)
+	}
+
+	if !contextExists {
+		t.Error("Expected context to be passed into request, got nil context")
+	}
+}
+
+func TestNewClientWithContext(t *testing.T) {
+	t.Parallel()
+
+	type CtxKey string
+	ctxKey := CtxKey("test")
+
+	contextExists := false
+	mockClient := &mockHTTPClient{func(w http.ResponseWriter, r *http.Request) {
+		ctx := r.Context()
+		contextExists = ctx != nil && ctx.Value(ctxKey) != nil
+	}}
+
+	ctx := context.WithValue(context.Background(), ctxKey, 0)
+	c, err := NewClientWithContext(ctx, &Options{
+		ClientID:   "test",
+		HTTPClient: mockClient,
+	})
+	if err != nil {
+		t.Errorf("could not create client with context: %v", err)
+		return
+	}
+
+	_, err = c.GetStreams(nil)
+
+	if err != nil {
+		t.Errorf("Did not expect error, got \"%s\"", err)
+	}
+
+	if !contextExists {
+		t.Error("Expected context to be passed into request, got nil context")
+	}
+}
+
+func TestNewClientWithContextCancel(t *testing.T) {
+	t.Parallel()
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	wasCanceled := false
+	mockClient := &mockHTTPClient{func(w http.ResponseWriter, r *http.Request) {
+		<-r.Context().Done()
+		wasCanceled = true
+	}}
+
+	c, err := NewClientWithContext(ctx, &Options{
+		ClientID:   "test",
+		HTTPClient: mockClient,
+	})
+	if err != nil {
+		t.Errorf("Could not create client with context: %v", err)
+		return
+	}
+
+	cancel()
+	_, err = c.GetStreams(nil)
+
+	if err != nil {
+		t.Errorf("Did not expect error, got \"%s\"", err)
+	}
+
+	if !wasCanceled {
+		t.Error("Context was not cancelled")
+	}
+}
+
+func TestNoContext(t *testing.T) {
+	t.Parallel()
+
+	contextExists := false
+	handlerFunc := func(w http.ResponseWriter, r *http.Request) {
+		if r.Context() != nil {
+			contextExists = true
+		}
+	}
+
+	c := newMockClient(&Options{}, handlerFunc)
+	_, err := c.GetStreams(nil)
+
+	if err != nil {
+		t.Errorf("Did not expect error, got \"%s\"", err)
+	}
+
+	if !contextExists {
+		t.Error("Expected context to be passed into request, got nil context")
+	}
+}
+
 func TestRatelimitCallback(t *testing.T) {
 	t.Parallel()
 
@@ -355,6 +475,7 @@ func TestFailedHTTPClientDoRequest(t *testing.T) {
 
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetUsers(&UsersParams{
@@ -365,7 +486,7 @@ func TestFailedHTTPClientDoRequest(t *testing.T) {
 	}
 
 	if err.Error() != "Failed to execute API request: Oops, that's bad :(" {
-		t.Error("expected error does match return error")
+		t.Errorf("expected error does match return error: %v", err)
 	}
 }
 
diff --git a/hype_train_test.go b/hype_train_test.go
index dfeddbc..bf76eb1 100644
--- a/hype_train_test.go
+++ b/hype_train_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -75,6 +76,7 @@ func TestGetHypeTrainEvents(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetHypeTrainEvents(&HypeTrainEventsParams{})
diff --git a/moderation_automod_test.go b/moderation_automod_test.go
index 316478f..ebd99bc 100644
--- a/moderation_automod_test.go
+++ b/moderation_automod_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -71,6 +72,7 @@ func TestModerateHeldMessage(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.ModerateHeldMessage(&HeldMessageModerationParams{})
diff --git a/moderation_test.go b/moderation_test.go
index a6b8bcd..cbfea00 100644
--- a/moderation_test.go
+++ b/moderation_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 	"time"
@@ -82,6 +83,7 @@ func TestGetBannedUsers(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetBannedUsers(&BannedUsersParams{})
@@ -192,6 +194,7 @@ func TestBanUser(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.BanUser(&BanUserParams{})
@@ -262,6 +265,7 @@ func TestUnbanUser(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.UnbanUser(&UnbanUserParams{})
@@ -364,6 +368,7 @@ func TestGetBlockedTerms(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetBlockedTerms(&BlockedTermsParams{BroadcasterID: "1234", ModeratorID: "1234"})
@@ -458,6 +463,7 @@ func TestAddBlockedTerm(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.AddBlockedTerm(&AddBlockedTermParams{BroadcasterID: "1234", ModeratorID: "1234", Text: "test"})
@@ -528,6 +534,7 @@ func TestRemoveBlockedTerm(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.RemoveBlockedTerm(&RemoveBlockedTermParams{BroadcasterID: "1234", ModeratorID: "1234", ID: "test"})
diff --git a/polls_test.go b/polls_test.go
index b1f67c8..0376703 100644
--- a/polls_test.go
+++ b/polls_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -67,6 +68,7 @@ func TestGetPolls(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetPolls(&PollsParams{})
@@ -149,6 +151,7 @@ func TestCreatePoll(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.CreatePoll(&CreatePollParams{})
@@ -227,6 +230,7 @@ func TestEndPoll(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.EndPoll(&EndPollParams{})
diff --git a/predictions_test.go b/predictions_test.go
index 96eaf53..17f519e 100644
--- a/predictions_test.go
+++ b/predictions_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -67,6 +68,7 @@ func TestGetPredictions(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetPredictions(&PredictionsParams{})
@@ -149,6 +151,7 @@ func TestCreatePrediction(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.CreatePrediction(&CreatePredictionParams{})
@@ -228,6 +231,7 @@ func TestEndPrediction(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.EndPrediction(&EndPredictionParams{})
diff --git a/stream_markers_test.go b/stream_markers_test.go
index c7ec3c9..5a0685d 100644
--- a/stream_markers_test.go
+++ b/stream_markers_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -71,6 +72,7 @@ func TestGetStreamMarkers(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetStreamMarkers(&StreamMarkersParams{})
@@ -149,6 +151,7 @@ func TestCreateStreamMarker(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.CreateStreamMarker(&CreateStreamMarkerParams{})
diff --git a/streams_test.go b/streams_test.go
index a0f4e58..ffa13ed 100644
--- a/streams_test.go
+++ b/streams_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -66,6 +67,7 @@ func TestGetStreams(t *testing.T) {
 
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetStreams(&StreamsParams{
@@ -137,6 +139,7 @@ func TestGetFollowedStreams(t *testing.T) {
 
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetFollowedStream(&FollowedStreamsParams{
@@ -215,6 +218,7 @@ func TestGetStreamKeys(t *testing.T) {
 
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetStreamKey(&StreamKeyParams{})
diff --git a/subscriptions_test.go b/subscriptions_test.go
index 53967b3..001d7bf 100644
--- a/subscriptions_test.go
+++ b/subscriptions_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -79,6 +80,7 @@ func TestGetSubscriptions(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetSubscriptions(&SubscriptionsParams{})
@@ -156,6 +158,7 @@ func TestChechUserSubscription(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.CheckUserSubscription(&UserSubscriptionsParams{})
diff --git a/user_extension_test.go b/user_extension_test.go
index 840fc40..5a08ed1 100644
--- a/user_extension_test.go
+++ b/user_extension_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -46,6 +47,7 @@ func TestGetUserExtensions(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetUserExtensions()
@@ -102,6 +104,7 @@ func TestGetUserActiveExtensions(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetUserActiveExtensions(nil)
@@ -176,6 +179,7 @@ func TestUpdateUserExtensions(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.UpdateUserExtensions(&UpdateUserExtensionsPayload{})
diff --git a/users_test.go b/users_test.go
index 9e085ff..e307d0e 100644
--- a/users_test.go
+++ b/users_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -84,6 +85,7 @@ func TestGetUsers(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetUsers(&UsersParams{})
@@ -164,6 +166,7 @@ func TestUpdateUser(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.UpdateUser(&UpdateUserParams{})
@@ -254,6 +257,7 @@ func TestGetUsersFollows(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetUsersFollows(&UsersFollowsParams{})
@@ -358,6 +362,7 @@ func TestGetUsersBlocked(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetUsersBlocked(&UsersBlockedParams{})
@@ -440,6 +445,7 @@ func TestBlockUser(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.BlockUser(&BlockUserParams{})
@@ -518,6 +524,7 @@ func TestUnblockUser(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.UnblockUser(&UnblockUserParams{})
diff --git a/videos_test.go b/videos_test.go
index cbf9c55..e3d9844 100644
--- a/videos_test.go
+++ b/videos_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -79,6 +80,7 @@ func TestGetVideos(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetVideos(&VideosParams{})
@@ -153,6 +155,7 @@ func TestDeleteVideos(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.DeleteVideos(&DeleteVideosParams{})
diff --git a/webhooks_test.go b/webhooks_test.go
index d59e8df..5f56feb 100644
--- a/webhooks_test.go
+++ b/webhooks_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"reflect"
 	"regexp"
@@ -75,6 +76,7 @@ func TestGetWebhookSubscriptions(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.GetWebhookSubscriptions(&WebhookSubscriptionsParams{})
@@ -152,6 +154,7 @@ func TestPostWebhookSubscriptions(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.PostWebhookSubscription(&WebhookSubscriptionPayload{})
diff --git a/whispers_test.go b/whispers_test.go
index d6a4526..a73437e 100644
--- a/whispers_test.go
+++ b/whispers_test.go
@@ -1,6 +1,7 @@
 package helix
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -62,6 +63,7 @@ func TestSendUserWhisper(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.SendUserWhisper(&SendUserWhisperParams{})