fix(forge): track rate limits for raw requests
All checks were successful
Security Scan / security (push) Successful in 16s
Test / test (push) Successful in 1m49s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 06:51:49 +00:00
parent 7444ad47f9
commit 597f4e0bb8
3 changed files with 72 additions and 0 deletions

View file

@ -223,6 +223,8 @@ func (c *Client) postRawJSON(ctx context.Context, path string, body any) ([]byte
}
defer resp.Body.Close()
c.updateRateLimit(resp)
if resp.StatusCode >= 400 {
return nil, c.parseError(resp, path)
}
@ -256,6 +258,8 @@ func (c *Client) postRawText(ctx context.Context, path, body string) ([]byte, er
}
defer resp.Body.Close()
c.updateRateLimit(resp)
if resp.StatusCode >= 400 {
return nil, c.parseError(resp, path)
}
@ -354,6 +358,8 @@ func (c *Client) GetRaw(ctx context.Context, path string) ([]byte, error) {
}
defer resp.Body.Close()
c.updateRateLimit(resp)
if resp.StatusCode >= 400 {
return nil, c.parseError(resp, path)
}

View file

@ -63,6 +63,36 @@ func TestClient_Post_Good(t *testing.T) {
}
}
func TestClient_PostRaw_Good(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Errorf("expected POST, got %s", r.Method)
}
if got := r.URL.Path; got != "/api/v1/markdown" {
t.Errorf("wrong path: %s", got)
}
w.Header().Set("X-RateLimit-Limit", "100")
w.Header().Set("X-RateLimit-Remaining", "98")
w.Header().Set("X-RateLimit-Reset", "1700000001")
w.Write([]byte("<p>Hello</p>"))
}))
defer srv.Close()
c := NewClient(srv.URL, "test-token")
body := map[string]string{"text": "Hello"}
got, err := c.PostRaw(context.Background(), "/api/v1/markdown", body)
if err != nil {
t.Fatal(err)
}
if string(got) != "<p>Hello</p>" {
t.Errorf("got body=%q", string(got))
}
rl := c.RateLimit()
if rl.Limit != 100 || rl.Remaining != 98 || rl.Reset != 1700000001 {
t.Fatalf("unexpected rate limit: %+v", rl)
}
}
func TestClient_Delete_Good(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodDelete {
@ -79,6 +109,35 @@ func TestClient_Delete_Good(t *testing.T) {
}
}
func TestClient_GetRaw_Good(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
t.Errorf("expected GET, got %s", r.Method)
}
if got := r.URL.Path; got != "/api/v1/signing-key.gpg" {
t.Errorf("wrong path: %s", got)
}
w.Header().Set("X-RateLimit-Limit", "60")
w.Header().Set("X-RateLimit-Remaining", "59")
w.Header().Set("X-RateLimit-Reset", "1700000002")
w.Write([]byte("key-data"))
}))
defer srv.Close()
c := NewClient(srv.URL, "test-token")
got, err := c.GetRaw(context.Background(), "/api/v1/signing-key.gpg")
if err != nil {
t.Fatal(err)
}
if string(got) != "key-data" {
t.Errorf("got body=%q", string(got))
}
rl := c.RateLimit()
if rl.Limit != 60 || rl.Remaining != 59 || rl.Reset != 1700000002 {
t.Fatalf("unexpected rate limit: %+v", rl)
}
}
func TestClient_ServerError_Bad(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)

View file

@ -101,6 +101,9 @@ func TestMiscService_RenderMarkdownRaw_Good(t *testing.T) {
if string(data) != "# Hello" {
t.Errorf("got body=%q, want %q", string(data), "# Hello")
}
w.Header().Set("X-RateLimit-Limit", "80")
w.Header().Set("X-RateLimit-Remaining", "79")
w.Header().Set("X-RateLimit-Reset", "1700000003")
w.Header().Set("Content-Type", "text/html")
w.Write([]byte("<h1>Hello</h1>\n"))
}))
@ -115,6 +118,10 @@ func TestMiscService_RenderMarkdownRaw_Good(t *testing.T) {
if html != want {
t.Errorf("got %q, want %q", html, want)
}
rl := f.Client().RateLimit()
if rl.Limit != 80 || rl.Remaining != 79 || rl.Reset != 1700000003 {
t.Fatalf("unexpected rate limit: %+v", rl)
}
}
func TestMiscService_GetVersion_Good(t *testing.T) {