migueleliasweb / go-github-mock

A library to aid unittesting code that uses Golang's Github SDK
MIT License
98 stars 24 forks source link

Question: How to mock pagination? #2

Closed tangrufus closed 3 years ago

tangrufus commented 3 years ago

How to mock pagination?

Use case

This is a slightly modified version of the pagination example taken from https://github.com/google/go-github/blob/54ec837fb63dc356426c665bddb5a5b4354e621d/README.md#pagination

func ListAllReposByOrg(ctx context.Context, org string, client *github.Client) ([]*github.Repository, error) {
    opt := &github.RepositoryListByOrgOptions{
        ListOptions: github.ListOptions{PerPage: 2},
    }

    var allRepos []*github.Repository
    for {
        repos, resp, err := client.Repositories.ListByOrg(ctx, org, opt)
        if err != nil {
            return nil, err
        }
        allRepos = append(allRepos, repos...)
        if resp.NextPage == 0 {
            break
        }
        opt.Page = resp.NextPage
    }

    return allRepos, nil
}

Using responsesFIFO I can mock different results for subsequent requests. But, how to mock resp.NextPage?

mockedHTTPClient := mock.NewMockedHTTPClient(
        mock.WithRequestMatch(
            mock.GetOrgsReposByOrg,
            [][]byte{
                mock.MustMarshal([]github.Repository{
                    {
                        Name: github.String("repo-A-on-first-page"),
                    },
                    {
                        Name: github.String("repo-B-on-first-page"),
                    },
                }),
                mock.MustMarshal([]github.Repository{
                    {
                        Name: github.String("repo-C-on-second-page"),
                    },
                    {
                        Name: github.String("repo-D-on-second-page"),
                    },
                }),
            },
        ),
    )

Thanks!

tangrufus commented 3 years ago

For those who needs a temporary solution. Here is an ulgy workaround:

mockedHTTPClient := mock.NewMockedHTTPClient(
        mock.WithRequestMatchHandler(
            mock.GetOrgsReposByOrg,
            http.HandlerFunc(func(w http.ResponseWriter, res *http.Request) {

                if res.URL.Query().Get("page") == "" { // Assume page 1.
                    // Must add headers before w.Write.
                    w.Header().Add("Link", `<?page=2>; rel="next"`)

                    w.Write(mock.MustMarshal([]github.Repository{
                        {
                            Name: github.String("repo-A-on-first-page"),
                        },
                        {
                            Name: github.String("repo-B-on-first-page"),
                        },
                    }))
                }

                if res.URL.Query().Get("page") == "2" { // Last page.
                    w.Write(mock.MustMarshal([]github.Repository{
                        {
                            Name: github.String("repo-C-on-second-page"),
                        },
                        {
                            Name: github.String("repo-D-on-second-page"),
                        },
                    }))
                }
            }),
        ),
    )
migueleliasweb commented 3 years ago

Hi @TangRufus ,

I would expect your first example to work as the path of the request itself is the same between pages. I ran your code locally and it seems only the first request is being returned for some reason.

I will debug this a bit deeper and will let you know.

tangrufus commented 3 years ago

Thanks!

Hope this helps: I found that the Link header is important to make resp.NextPage works.

w.Header().Add("Link", `<?page=2>; rel="next"`)
migueleliasweb commented 3 years ago

@TangRufus have a look at https://github.com/migueleliasweb/go-github-mock/pull/3. I've added a new option called WithRequestMatchPages that should catter for the cases where full pagination support is needed. If that solves your usecase I can polish it a bit more then merge.

Thanks for the example above!

tangrufus commented 3 years ago

mock.WithRequestMatchPages at 1e92357 (#3) works perfectly! Thanks!!!!!!

migueleliasweb commented 3 years ago

Cool, I will just write some docs and improve the testing so I can create a new release. šŸ‘

migueleliasweb commented 3 years ago

@TangRufus you can use the new release now: https://github.com/migueleliasweb/go-github-mock/releases/tag/v0.0.2

tangrufus commented 3 years ago

Upgraded to v0.0.2. Works like a charm. Thank you!