Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
99 lines
3.2 KiB
Go
99 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// fakeGitHub answers every request with a fixed status, headers and body.
|
|
type fakeGitHub struct {
|
|
status int
|
|
header http.Header
|
|
body string
|
|
calls int
|
|
last *http.Request
|
|
}
|
|
|
|
func (f *fakeGitHub) RoundTrip(r *http.Request) (*http.Response, error) {
|
|
f.calls++
|
|
f.last = r
|
|
return &http.Response{
|
|
StatusCode: f.status,
|
|
Status: http.StatusText(f.status),
|
|
Header: f.header,
|
|
Body: io.NopCloser(strings.NewReader(f.body)),
|
|
Request: r,
|
|
}, nil
|
|
}
|
|
|
|
func newFake(status int, body string) (*GitHub, *fakeGitHub) {
|
|
f := &fakeGitHub{status: status, body: body, header: http.Header{
|
|
"X-Ratelimit-Limit": {"10"},
|
|
"X-Ratelimit-Remaining": {"9"},
|
|
"X-Ratelimit-Reset": {"1790851447"},
|
|
"X-Ratelimit-Resource": {"search"},
|
|
}}
|
|
gh := NewGitHub("https://api.github.test", "secret", time.Minute)
|
|
gh.client = &http.Client{Transport: f}
|
|
return gh, f
|
|
}
|
|
|
|
const searchBody = `{"total_count": 14, "incomplete_results": false, "items": [{
|
|
"full_name": "mcuadros/go-syslog", "name": "go-syslog", "html_url": "https://github.com/mcuadros/go-syslog",
|
|
"description": "Syslog server library for go", "language": "Go", "topics": null,
|
|
"stargazers_count": 540, "forks_count": 150, "open_issues_count": 20, "archived": false, "fork": false,
|
|
"created_at": "2014-09-01T10:00:00Z", "pushed_at": "2024-03-01T10:00:00Z",
|
|
"owner": {"login": "mcuadros", "avatar_url": "https://avatars.githubusercontent.com/u/1573114?v=4"},
|
|
"license": {"spdx_id": "MIT"}}]}`
|
|
|
|
func TestSearchMapsAndCaches(t *testing.T) {
|
|
gh, f := newFake(http.StatusOK, searchBody)
|
|
c := Criteria{Text: "syslog", Language: "Go", Sort: "stars", Order: "desc", Page: 1, PerPage: 30}
|
|
|
|
res, err := gh.Search(context.Background(), c)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res.Total != 14 || len(res.Items) != 1 {
|
|
t.Fatalf("got total %d, %d items", res.Total, len(res.Items))
|
|
}
|
|
r := res.Items[0]
|
|
if r.FullName != "mcuadros/go-syslog" || r.Owner != "mcuadros" || r.License != "MIT" || r.Stars != 540 || r.Topics == nil {
|
|
t.Errorf("bad mapping: %+v", r)
|
|
}
|
|
if res.Rate.Limit != 10 || res.Rate.Remaining != 9 {
|
|
t.Errorf("bad rate: %+v", res.Rate)
|
|
}
|
|
q := f.last.URL.Query()
|
|
if q.Get("sort") != "stars" || q.Get("q") != "syslog language:Go archived:false" {
|
|
t.Errorf("bad request: %s", f.last.URL)
|
|
}
|
|
if f.last.Header.Get("Authorization") != "Bearer secret" {
|
|
t.Errorf("token not sent")
|
|
}
|
|
|
|
res, err = gh.Search(context.Background(), c)
|
|
if err != nil || !res.Cached || f.calls != 1 {
|
|
t.Errorf("second identical search should come from the cache (calls=%d, cached=%v, err=%v)", f.calls, res.Cached, err)
|
|
}
|
|
}
|
|
|
|
func TestSearchErrors(t *testing.T) {
|
|
for status, code := range map[int]string{
|
|
http.StatusForbidden: "rate_limited",
|
|
http.StatusUnauthorized: "github_token",
|
|
http.StatusUnprocessableEntity: "bad_query",
|
|
} {
|
|
gh, _ := newFake(status, `{"message": "nope", "errors": [{"message": "detail"}]}`)
|
|
_, err := gh.Search(context.Background(), Criteria{Page: 1, PerPage: 30})
|
|
ce, ok := err.(*codedError)
|
|
if !ok || ce.code != code || ce.detail != "detail" {
|
|
t.Errorf("status %d: got %v, want code %s", status, err, code)
|
|
}
|
|
}
|
|
}
|