diff --git a/gitea/client.go b/gitea/client.go index bdc9a4a..a5391f8 100644 --- a/gitea/client.go +++ b/gitea/client.go @@ -71,6 +71,9 @@ type Client struct { // MaxDiffSize is the maximum number of bytes allowed when fetching a PR diff. // If zero, defaults to DefaultMaxDiffSize (10 MB). Set to any negative value to disable the limit. + // + // This field must be configured before the first request is made. + // Modifying it while requests are in flight is not safe. MaxDiffSize int64 } diff --git a/gitea/diff_size_test.go b/gitea/diff_size_test.go index 6601143..3657d2d 100644 --- a/gitea/diff_size_test.go +++ b/gitea/diff_size_test.go @@ -10,134 +10,81 @@ import ( "time" ) -func TestGetPullRequestDiff_ExceedsMaxSize(t *testing.T) { - // Create a diff that exceeds a small limit - largeDiff := strings.Repeat("+ added line\n", 1000) // ~13 KB - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(largeDiff)) - })) - defer server.Close() - - client := NewClient(server.URL, "test-token") - client.MaxDiffSize = 100 // 100 bytes limit - client.RetryBackoff = []time.Duration{} // no delay in tests - - _, err := client.GetPullRequestDiff(context.Background(), "owner", "repo", 1) - if err == nil { - t.Fatal("expected error for oversized diff, got nil") +func TestGetPullRequestDiff_SizeLimits(t *testing.T) { + tests := []struct { + name string + diff string + maxDiffSize int64 + wantErr error + wantDiff string + }{ + { + name: "exceeds max size", + diff: strings.Repeat("+ added line\n", 1000), // ~13 KB + maxDiffSize: 100, + wantErr: ErrDiffTooLarge, + }, + { + name: "within max size", + diff: "diff --git a/f.go b/f.go\n--- a/f.go\n+++ b/f.go\n@@ -1 +1 @@\n-old\n+new\n", + maxDiffSize: 1024, + wantDiff: "diff --git a/f.go b/f.go\n--- a/f.go\n+++ b/f.go\n@@ -1 +1 @@\n-old\n+new\n", + }, + { + name: "exactly at limit", + diff: strings.Repeat("x", 50), + maxDiffSize: 50, + wantDiff: strings.Repeat("x", 50), + }, + { + name: "one byte over limit", + diff: strings.Repeat("x", 51), + maxDiffSize: 50, + wantErr: ErrDiffTooLarge, + }, + { + name: "disabled limit", + diff: strings.Repeat("x", 10000), + maxDiffSize: -1, + wantDiff: strings.Repeat("x", 10000), + }, + { + name: "default limit", + diff: "diff content", + maxDiffSize: 0, // zero means use DefaultMaxDiffSize + wantDiff: "diff content", + }, } - if !errors.Is(err, ErrDiffTooLarge) { - t.Errorf("expected ErrDiffTooLarge, got: %v", err) - } -} - -func TestGetPullRequestDiff_WithinMaxSize(t *testing.T) { - smallDiff := "diff --git a/f.go b/f.go\n--- a/f.go\n+++ b/f.go\n@@ -1 +1 @@\n-old\n+new\n" - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(smallDiff)) - })) - defer server.Close() - - client := NewClient(server.URL, "test-token") - client.MaxDiffSize = 1024 // 1 KB limit — more than enough - client.RetryBackoff = []time.Duration{} - - got, err := client.GetPullRequestDiff(context.Background(), "owner", "repo", 1) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if got != smallDiff { - t.Errorf("expected diff %q, got %q", smallDiff, got) - } -} - -func TestGetPullRequestDiff_ExactlyAtLimit(t *testing.T) { - // A diff that is exactly at the limit should succeed - exactDiff := strings.Repeat("x", 50) - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(exactDiff)) - })) - defer server.Close() - - client := NewClient(server.URL, "test-token") - client.MaxDiffSize = 50 // exactly the size of the diff - client.RetryBackoff = []time.Duration{} - - got, err := client.GetPullRequestDiff(context.Background(), "owner", "repo", 1) - if err != nil { - t.Fatalf("unexpected error for diff at exact limit: %v", err) - } - if got != exactDiff { - t.Errorf("expected diff to match, got length %d", len(got)) - } -} - -func TestGetPullRequestDiff_OneByteOverLimit(t *testing.T) { - // A diff that is one byte over the limit should fail - overDiff := strings.Repeat("x", 51) - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(overDiff)) - })) - defer server.Close() - - client := NewClient(server.URL, "test-token") - client.MaxDiffSize = 50 - client.RetryBackoff = []time.Duration{} - - _, err := client.GetPullRequestDiff(context.Background(), "owner", "repo", 1) - if err == nil { - t.Fatal("expected error for diff one byte over limit") - } - if !errors.Is(err, ErrDiffTooLarge) { - t.Errorf("expected ErrDiffTooLarge, got: %v", err) - } -} - -func TestGetPullRequestDiff_DisabledLimit(t *testing.T) { - // When MaxDiffSize is -1, no limit is enforced - largeDiff := strings.Repeat("x", 10000) - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(largeDiff)) - })) - defer server.Close() - - client := NewClient(server.URL, "test-token") - client.MaxDiffSize = -1 // disabled - client.RetryBackoff = []time.Duration{} - - got, err := client.GetPullRequestDiff(context.Background(), "owner", "repo", 1) - if err != nil { - t.Fatalf("unexpected error with disabled limit: %v", err) - } - if got != largeDiff { - t.Errorf("expected full diff with disabled limit, got length %d", len(got)) - } -} - -func TestGetPullRequestDiff_DefaultLimit(t *testing.T) { - // With zero MaxDiffSize (default), should use DefaultMaxDiffSize. - // A small diff should succeed without setting MaxDiffSize. - smallDiff := "diff content" - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(smallDiff)) - })) - defer server.Close() - - client := NewClient(server.URL, "test-token") - // MaxDiffSize is zero (default) — should use DefaultMaxDiffSize (10 MB) - client.RetryBackoff = []time.Duration{} - - got, err := client.GetPullRequestDiff(context.Background(), "owner", "repo", 1) - if err != nil { - t.Fatalf("unexpected error with default limit: %v", err) - } - if got != smallDiff { - t.Errorf("expected diff %q, got %q", smallDiff, got) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(tt.diff)) //nolint:errcheck // test handler + })) + defer server.Close() + + client := NewClient(server.URL, "test-token") + client.MaxDiffSize = tt.maxDiffSize + client.RetryBackoff = []time.Duration{} + + got, err := client.GetPullRequestDiff(context.Background(), "owner", "repo", 1) + + if tt.wantErr != nil { + if err == nil { + t.Fatal("expected error, got nil") + } + if !errors.Is(err, tt.wantErr) { + t.Errorf("expected %v, got: %v", tt.wantErr, err) + } + return + } + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got != tt.wantDiff { + t.Errorf("diff mismatch: got length %d, want length %d", len(got), len(tt.wantDiff)) + } + }) } }