diff --git a/internal/linear/client.go b/internal/linear/client.go index 075b84a3b2..e5e3c9d32a 100644 --- a/internal/linear/client.go +++ b/internal/linear/client.go @@ -748,7 +748,11 @@ func (c *Client) createIssueSingleAttempt(ctx context.Context, title, descriptio } httpReq.Header.Set("Content-Type", "application/json") - httpReq.Header.Set("Authorization", c.APIKey) + authValue, err := c.authHeader() + if err != nil { + return nil, err + } + httpReq.Header.Set("Authorization", authValue) resp, err := c.HTTPClient.Do(httpReq) if err != nil { diff --git a/internal/linear/idempotency_test.go b/internal/linear/idempotency_test.go index 6d41d4ca1c..350b440676 100644 --- a/internal/linear/idempotency_test.go +++ b/internal/linear/idempotency_test.go @@ -7,6 +7,7 @@ import ( "net/http/httptest" "strings" "testing" + "time" ) func TestGenerateIdempotencyMarker(t *testing.T) { @@ -187,6 +188,83 @@ func TestCreateIssueEmbedsMarker(t *testing.T) { } } +func TestCreateIssueIdempotentOAuthUsesBearerAuth(t *testing.T) { + const expectedAuth = "Bearer oauth-test-token" + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if got := r.Header.Get("Authorization"); got != expectedAuth { + t.Fatalf("Authorization header = %q, want %q", got, expectedAuth) + } + + var req GraphQLRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + t.Fatalf("failed to decode request: %v", err) + } + w.Header().Set("Content-Type", "application/json") + + if strings.Contains(req.Query, "FindByDescription") { + _ = json.NewEncoder(w).Encode(map[string]interface{}{ + "data": map[string]interface{}{ + "issues": map[string]interface{}{ + "nodes": []Issue{}, + "pageInfo": map[string]interface{}{"hasNextPage": false, "endCursor": ""}, + }, + }, + }) + return + } + + if strings.Contains(req.Query, "issueCreate") { + _ = json.NewEncoder(w).Encode(map[string]interface{}{ + "data": map[string]interface{}{ + "issueCreate": map[string]interface{}{ + "success": true, + "issue": map[string]interface{}{ + "id": "oauth-new-uuid", + "identifier": "TEAM-101", + "title": "OAuth Issue", + "description": "desc", + "url": "https://linear.app/team/issue/TEAM-101", + "priority": 2, + "createdAt": "2026-05-01T10:00:00Z", + "updatedAt": "2026-05-01T10:00:00Z", + }, + }, + }, + }) + return + } + + t.Fatalf("unexpected query: %s", req.Query) + })) + defer server.Close() + + client := NewOAuthClient(OAuthConfig{}, "team-1").WithEndpoint(server.URL) + client.TokenManager = &OAuthTokenManager{ + token: "oauth-test-token", + expiresAt: time.Now().Add(10 * time.Minute), + nowFunc: time.Now, + } + + marker := GenerateIdempotencyMarker("bead-oauth", "oauth@test.com", 123) + issue, deduped, err := client.CreateIssueIdempotent( + context.Background(), + "OAuth Issue", + "desc", + 2, "", nil, + marker, + ) + if err != nil { + t.Fatalf("CreateIssueIdempotent failed: %v", err) + } + if deduped { + t.Error("expected deduped=false for fresh OAuth create") + } + if issue == nil || issue.Identifier != "TEAM-101" { + t.Fatalf("unexpected issue returned: %+v", issue) + } +} + func TestCreateIssueDedups(t *testing.T) { existing := Issue{ ID: "existing-uuid",