Files
go-github/github/agent_tasks_test.go

375 lines
11 KiB
Go

// Copyright 2026 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"fmt"
"net/http"
"net/url"
"testing"
"time"
"github.com/google/go-cmp/cmp"
)
func agentTaskJSON() string {
return `{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"url": "https://api.github.com/agents/repos/octocat/hello-world/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"html_url": "https://github.com/octocat/hello-world/copilot/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Fix the login button on the homepage",
"creator": { "id": 1 },
"creator_type": "user",
"user_collaborators": [{ "id": 3 }],
"owner": { "id": 2 },
"repository": { "id": 1296269 },
"state": "completed",
"session_count": 1,
"artifacts": [
{
"provider": "github",
"type": "pull",
"data": { "id": 42 }
}
],
"archived_at": null,
"created_at": "2025-03-14T00:00:00Z",
"updated_at": "2025-03-14T01:00:00Z"
}`
}
func agentTaskWithSessionsJSON() string {
return `{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"url": "https://api.github.com/agents/repos/octocat/hello-world/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"html_url": "https://github.com/octocat/hello-world/copilot/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Fix the login button on the homepage",
"creator": { "id": 1 },
"creator_type": "user",
"user_collaborators": [{ "id": 3 }],
"owner": { "id": 2 },
"repository": { "id": 1296269 },
"state": "failed",
"session_count": 1,
"artifacts": [
{
"provider": "github",
"type": "pull",
"data": { "id": 42 }
}
],
"archived_at": null,
"created_at": "2025-03-14T00:00:00Z",
"updated_at": "2025-03-14T01:00:00Z",
"sessions": [
{
"id": "s1a2b3c4-d5e6-7890-abcd-ef1234567890",
"name": "Fix the login button on the homepage",
"user": { "id": 1 },
"owner": { "id": 2 },
"repository": { "id": 1296269 },
"task_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"state": "failed",
"created_at": "2025-03-14T00:00:00Z",
"updated_at": "2025-03-14T01:00:00Z",
"completed_at": "2025-03-14T01:00:00Z",
"prompt": "Fix the login button on the homepage",
"head_ref": "copilot/fix-1",
"base_ref": "main",
"model": "claude-sonnet-4.6",
"error": { "message": "agent task failed" }
}
]
}`
}
func agentTask() *AgentTask {
return &AgentTask{
ID: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
URL: new("https://api.github.com/agents/repos/octocat/hello-world/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
HTMLURL: new("https://github.com/octocat/hello-world/copilot/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
Name: new("Fix the login button on the homepage"),
Creator: &AgentTaskCreator{ID: new(int64(1))},
CreatorType: new("user"),
UserCollaborators: []*User{
{ID: new(int64(3))},
},
Owner: &AgentTaskOwner{ID: new(int64(2))},
Repository: &AgentTaskRepository{ID: new(int64(1296269))},
State: "completed",
SessionCount: new(1),
Artifacts: []*AgentTaskArtifact{
{
Provider: "github",
Type: "pull",
Data: &AgentTaskArtifactData{ID: new(int64(42))},
},
},
CreatedAt: Timestamp{time.Date(2025, time.March, 14, 0, 0, 0, 0, time.UTC)},
UpdatedAt: &Timestamp{time.Date(2025, time.March, 14, 1, 0, 0, 0, time.UTC)},
}
}
func agentTaskWithSessions() *AgentTask {
task := agentTask()
task.State = "failed"
task.Sessions = []*AgentTaskSession{
{
ID: "s1a2b3c4-d5e6-7890-abcd-ef1234567890",
Name: new("Fix the login button on the homepage"),
User: &User{ID: new(int64(1))},
Owner: &AgentTaskOwner{ID: new(int64(2))},
Repository: &AgentTaskRepository{ID: new(int64(1296269))},
TaskID: new("a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
State: "failed",
Prompt: new("Fix the login button on the homepage"),
HeadRef: new("copilot/fix-1"),
BaseRef: new("main"),
Model: new("claude-sonnet-4.6"),
Error: &AgentTaskSessionError{Message: new("agent task failed")},
CreatedAt: Timestamp{time.Date(2025, time.March, 14, 0, 0, 0, 0, time.UTC)},
UpdatedAt: &Timestamp{time.Date(2025, time.March, 14, 1, 0, 0, 0, time.UTC)},
CompletedAt: &Timestamp{time.Date(2025, time.March, 14, 1, 0, 0, 0, time.UTC)},
},
}
return task
}
func TestAgentTasksService_ListByRepo(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)
mux.HandleFunc("/agents/repos/o/r/tasks", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValuesList(t, r, url.Values{
"creator_id": {"1", "2"},
"direction": {"asc"},
"is_archived": {"true"},
"page": {"2"},
"per_page": {"1"},
"since": {"2025-03-14T00:00:00Z"},
"sort": {"created_at"},
"state": {"queued,completed"},
})
w.Header().Set("Link", `<https://api.github.com/agents/repos/o/r/tasks?page=3>; rel="next"`)
fmt.Fprintf(w, `{"tasks":[%v],"total_active_count":5,"total_archived_count":2}`, agentTaskJSON())
})
opts := &AgentTaskListByRepoOptions{
AgentTaskListOptions: AgentTaskListOptions{
Sort: "created_at",
Direction: "asc",
State: "queued,completed",
IsArchived: true,
Since: new(time.Date(2025, time.March, 14, 0, 0, 0, 0, time.UTC)),
ListOptions: ListOptions{Page: 2, PerPage: 1},
},
CreatorID: []int64{1, 2},
}
ctx := t.Context()
tasks, resp, err := client.AgentTasks.ListByRepo(ctx, "o", "r", opts)
if err != nil {
t.Fatalf("AgentTasks.ListByRepo returned error: %v", err)
}
want := &AgentTaskList{
Tasks: []*AgentTask{agentTask()},
TotalActiveCount: new(5),
TotalArchivedCount: new(2),
}
if diff := cmp.Diff(want, tasks); diff != "" {
t.Errorf("AgentTasks.ListByRepo mismatch (-want +got):\n%v", diff)
}
if got, want := resp.NextPage, 3; got != want {
t.Errorf("AgentTasks.ListByRepo NextPage = %v, want %v", got, want)
}
const methodName = "ListByRepo"
testBadOptions(t, methodName, func() error {
_, _, err := client.AgentTasks.ListByRepo(ctx, "\n", "\n", opts)
return err
})
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.AgentTasks.ListByRepo(ctx, "o", "r", opts)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}
func TestAgentTasksService_Create(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)
input := CreateAgentTaskRequest{
Prompt: "Fix the login button on the homepage",
Model: new("gpt-5.3-codex"),
CreatePullRequest: new(true),
HeadRef: new("copilot/fix-1"),
BaseRef: new("main"),
}
mux.HandleFunc("/agents/repos/o/r/tasks", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testHeader(t, r, "Content-Type", "application/json")
testJSONBody(t, r, input)
w.WriteHeader(http.StatusCreated)
fmt.Fprint(w, agentTaskJSON())
})
ctx := t.Context()
task, _, err := client.AgentTasks.Create(ctx, "o", "r", input)
if err != nil {
t.Fatalf("AgentTasks.Create returned error: %v", err)
}
if diff := cmp.Diff(agentTask(), task); diff != "" {
t.Errorf("AgentTasks.Create mismatch (-want +got):\n%v", diff)
}
const methodName = "Create"
testBadOptions(t, methodName, func() error {
_, _, err := client.AgentTasks.Create(ctx, "\n", "\n", input)
return err
})
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.AgentTasks.Create(ctx, "o", "r", input)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}
func TestAgentTasksService_GetByRepoAndID(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)
const taskID = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
mux.HandleFunc("/agents/repos/o/r/tasks/"+taskID, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, agentTaskWithSessionsJSON())
})
ctx := t.Context()
task, _, err := client.AgentTasks.GetByRepoAndID(ctx, "o", "r", taskID)
if err != nil {
t.Fatalf("AgentTasks.GetByRepoAndID returned error: %v", err)
}
if diff := cmp.Diff(agentTaskWithSessions(), task); diff != "" {
t.Errorf("AgentTasks.GetByRepoAndID mismatch (-want +got):\n%v", diff)
}
const methodName = "GetByRepoAndID"
testBadOptions(t, methodName, func() error {
_, _, err := client.AgentTasks.GetByRepoAndID(ctx, "\n", "\n", "\n")
return err
})
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.AgentTasks.GetByRepoAndID(ctx, "o", "r", taskID)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}
func TestAgentTasksService_List(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)
mux.HandleFunc("/agents/tasks", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{
"direction": "desc",
"is_archived": "true",
"page": "2",
"per_page": "1",
"since": "2025-03-14T00:00:00Z",
"sort": "updated_at",
"state": "completed",
})
w.Header().Set("Link", `<https://api.github.com/agents/tasks?page=3>; rel="next"`)
fmt.Fprintf(w, `{"tasks":[%v],"total_active_count":5,"total_archived_count":2}`, agentTaskJSON())
})
opts := &AgentTaskListOptions{
Sort: "updated_at",
Direction: "desc",
State: "completed",
IsArchived: true,
Since: new(time.Date(2025, time.March, 14, 0, 0, 0, 0, time.UTC)),
ListOptions: ListOptions{Page: 2, PerPage: 1},
}
ctx := t.Context()
tasks, resp, err := client.AgentTasks.List(ctx, opts)
if err != nil {
t.Fatalf("AgentTasks.List returned error: %v", err)
}
want := &AgentTaskList{
Tasks: []*AgentTask{agentTask()},
TotalActiveCount: new(5),
TotalArchivedCount: new(2),
}
if diff := cmp.Diff(want, tasks); diff != "" {
t.Errorf("AgentTasks.List mismatch (-want +got):\n%v", diff)
}
if got, want := resp.NextPage, 3; got != want {
t.Errorf("AgentTasks.List NextPage = %v, want %v", got, want)
}
const methodName = "List"
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.AgentTasks.List(ctx, opts)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}
func TestAgentTasksService_Get(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)
const taskID = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
mux.HandleFunc("/agents/tasks/"+taskID, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, agentTaskWithSessionsJSON())
})
ctx := t.Context()
task, _, err := client.AgentTasks.Get(ctx, taskID)
if err != nil {
t.Fatalf("AgentTasks.Get returned error: %v", err)
}
if diff := cmp.Diff(agentTaskWithSessions(), task); diff != "" {
t.Errorf("AgentTasks.Get mismatch (-want +got):\n%v", diff)
}
const methodName = "Get"
testBadOptions(t, methodName, func() error {
_, _, err := client.AgentTasks.Get(ctx, "\n")
return err
})
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.AgentTasks.Get(ctx, taskID)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}