github.com/polygon-io/client-go@v1.16.4/rest/iter/iter_test.go (about) 1 package iter_test 2 3 import ( 4 "context" 5 "encoding/json" 6 "net/http" 7 "testing" 8 9 "github.com/jarcoal/httpmock" 10 "github.com/stretchr/testify/assert" 11 12 "github.com/polygon-io/client-go/rest/client" 13 "github.com/polygon-io/client-go/rest/iter" 14 "github.com/polygon-io/client-go/rest/models" 15 ) 16 17 const ( 18 listResourcePath = "/resource/{ticker}" 19 ) 20 21 type Client struct { 22 client.Client 23 } 24 25 type ListResourceResponse struct { 26 models.BaseResponse 27 Results []Resource `json:"results,omitempty"` 28 } 29 30 type Resource struct { 31 Price string `json:"price"` 32 } 33 34 type ListResourceParams struct { 35 Ticker string `validate:"required" path:"ticker"` 36 37 Timestamp *string `query:"timestamp"` 38 } 39 40 func (c *Client) ListResource(ctx context.Context, params *ListResourceParams, options ...models.RequestOption) *iter.Iter[Resource] { 41 return iter.NewIter(ctx, listResourcePath, params, func(uri string) (iter.ListResponse, []Resource, error) { 42 res := &ListResourceResponse{} 43 err := c.CallURL(ctx, http.MethodGet, uri, res, options...) 44 return res, res.Results, err 45 }) 46 } 47 48 func TestListResource(t *testing.T) { 49 c := Client{Client: client.New("API_KEY")} 50 51 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 52 defer httpmock.DeactivateAndReset() 53 54 resource1 := Resource{Price: "price1"} 55 registerResponder(200, "https://api.polygon.io/resource/ticker1", ListResourceResponse{ 56 BaseResponse: models.BaseResponse{ 57 Status: "OK", 58 RequestID: "req1", 59 Count: 1, 60 PaginationHooks: models.PaginationHooks{ 61 NextURL: "https://api.polygon.io/resource/ticker1?cursor=NEXT", 62 }, 63 }, 64 Results: []Resource{resource1}, 65 }) 66 67 resource2 := Resource{Price: "price2"} 68 resource3 := Resource{Price: "price3"} 69 registerResponder(200, "https://api.polygon.io/resource/ticker1?cursor=NEXT", ListResourceResponse{ 70 BaseResponse: models.BaseResponse{ 71 Status: "OK", 72 RequestID: "req2", 73 Count: 2, 74 PaginationHooks: models.PaginationHooks{ 75 NextURL: "https://api.polygon.io/resource/ticker1?cursor=NEXTER", 76 }, 77 }, 78 Results: []Resource{resource2, resource3}, 79 }) 80 81 registerResponder(200, "https://api.polygon.io/resource/ticker1?cursor=NEXTER", ListResourceResponse{ 82 BaseResponse: models.BaseResponse{ 83 Status: "OK", 84 RequestID: "req3", 85 Count: 0, 86 PaginationHooks: models.PaginationHooks{ 87 NextURL: "https://api.polygon.io/resource/ticker1?cursor=NEXTER", 88 }, 89 }, 90 }) 91 92 it := c.ListResource(context.Background(), &ListResourceParams{ 93 Ticker: "ticker1", 94 }) 95 96 // verify the first page 97 assert.Nil(t, it.Err()) 98 assert.NotNil(t, it.Item()) 99 // verify the first and second quotes 100 assert.True(t, it.Next()) 101 assert.Nil(t, it.Err()) 102 assert.Equal(t, resource1, it.Item()) 103 104 // verify the second page 105 assert.True(t, it.Next()) 106 assert.Nil(t, it.Err()) 107 assert.Equal(t, resource2, it.Item()) 108 assert.True(t, it.Next()) 109 assert.Nil(t, it.Err()) 110 assert.Equal(t, resource3, it.Item()) 111 112 // verify the third page (end of list) 113 assert.False(t, it.Next()) // this should be false since the third page has no results 114 assert.Nil(t, it.Err()) 115 } 116 117 func TestListResourceError(t *testing.T) { 118 c := Client{Client: client.New("API_KEY")} 119 120 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 121 defer httpmock.DeactivateAndReset() 122 123 baseRes := models.BaseResponse{ 124 Status: "NOT FOUND", 125 RequestID: "req1", 126 ErrorMessage: "resource not found", 127 } 128 expectedErr := models.ErrorResponse{ 129 StatusCode: 404, 130 BaseResponse: baseRes, 131 } 132 registerResponder(404, "https://api.polygon.io/resource/ticker1", ListResourceResponse{ 133 BaseResponse: baseRes, 134 }) 135 136 it := c.ListResource(context.Background(), &ListResourceParams{ 137 Ticker: "ticker1", 138 }) 139 140 // it.Next() should return false and the error should not be nil 141 assert.NotNil(t, it.Err()) 142 assert.False(t, it.Next()) 143 assert.NotNil(t, it.Err()) 144 assert.Equal(t, expectedErr.Error(), it.Err().Error()) 145 146 // subsequent calls to it.Next() should be false, item should be not nil, page should be an empty response 147 assert.False(t, it.Next()) 148 assert.NotNil(t, it.Item()) 149 } 150 151 func TestListResourceEncodeError(t *testing.T) { 152 c := Client{Client: client.New("API_KEY")} 153 154 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 155 defer httpmock.DeactivateAndReset() 156 157 it := c.ListResource(context.Background(), nil) 158 159 // it.Next() should return false and the error should not be nil 160 assert.NotNil(t, it.Err()) 161 assert.False(t, it.Next()) 162 assert.NotNil(t, it.Err()) 163 164 // subsequent calls to it.Next() should be false, item should be not nil, page should be an empty response 165 assert.False(t, it.Next()) 166 assert.NotNil(t, it.Item()) 167 } 168 169 func registerResponder(status int, url string, res ListResourceResponse) { 170 httpmock.RegisterResponder("GET", url, 171 func(req *http.Request) (*http.Response, error) { 172 b, err := json.Marshal(res) 173 if err != nil { 174 return nil, err 175 } 176 resp := httpmock.NewStringResponse(status, string(b)) 177 resp.Header.Add("Content-Type", "application/json") 178 return resp, nil 179 }, 180 ) 181 }