Merge pull request #11704 from cli/babakks/add-web-flag-to-view-cmd

`gh agent-task view`: add `--web` flag
This commit is contained in:
Kynan Ware 2025-09-10 11:54:59 -06:00 committed by GitHub
commit 20c89043bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 401 additions and 67 deletions

View file

@ -21,7 +21,7 @@ type CapiClient interface {
GetJob(ctx context.Context, owner, repo, jobID string) (*Job, error)
GetSession(ctx context.Context, id string) (*Session, error)
ListSessionsByResourceID(ctx context.Context, resourceType string, resourceID int64, limit int) ([]*Session, error)
GetPullRequestDatabaseID(ctx context.Context, hostname string, owner string, repo string, number int) (int64, error)
GetPullRequestDatabaseID(ctx context.Context, hostname string, owner string, repo string, number int) (int64, string, error)
}
// CAPIClient is a client for interacting with the Copilot API

View file

@ -24,7 +24,7 @@ var _ CapiClient = &CapiClientMock{}
// GetJobFunc: func(ctx context.Context, owner string, repo string, jobID string) (*Job, error) {
// panic("mock out the GetJob method")
// },
// GetPullRequestDatabaseIDFunc: func(ctx context.Context, hostname string, owner string, repo string, number int) (int64, error) {
// GetPullRequestDatabaseIDFunc: func(ctx context.Context, hostname string, owner string, repo string, number int) (int64, string, error) {
// panic("mock out the GetPullRequestDatabaseID method")
// },
// GetSessionFunc: func(ctx context.Context, id string) (*Session, error) {
@ -53,7 +53,7 @@ type CapiClientMock struct {
GetJobFunc func(ctx context.Context, owner string, repo string, jobID string) (*Job, error)
// GetPullRequestDatabaseIDFunc mocks the GetPullRequestDatabaseID method.
GetPullRequestDatabaseIDFunc func(ctx context.Context, hostname string, owner string, repo string, number int) (int64, error)
GetPullRequestDatabaseIDFunc func(ctx context.Context, hostname string, owner string, repo string, number int) (int64, string, error)
// GetSessionFunc mocks the GetSession method.
GetSessionFunc func(ctx context.Context, id string) (*Session, error)
@ -245,7 +245,7 @@ func (mock *CapiClientMock) GetJobCalls() []struct {
}
// GetPullRequestDatabaseID calls GetPullRequestDatabaseIDFunc.
func (mock *CapiClientMock) GetPullRequestDatabaseID(ctx context.Context, hostname string, owner string, repo string, number int) (int64, error) {
func (mock *CapiClientMock) GetPullRequestDatabaseID(ctx context.Context, hostname string, owner string, repo string, number int) (int64, string, error) {
if mock.GetPullRequestDatabaseIDFunc == nil {
panic("CapiClientMock.GetPullRequestDatabaseIDFunc: method is nil but CapiClient.GetPullRequestDatabaseID was just called")
}

View file

@ -18,6 +18,8 @@ import (
"github.com/vmihailenco/msgpack/v5"
)
const AgentsHomeURL = "https://github.com/copilot/agents"
var defaultSessionsPerPage = 50
var ErrSessionNotFound = errors.New("not found")
@ -378,12 +380,13 @@ func (c *CAPIClient) hydrateSessionPullRequestsAndUsers(sessions []session) ([]*
return newSessions, nil
}
// GetPullRequestDatabaseID retrieves the database ID of a pull request given its number in a repository.
func (c *CAPIClient) GetPullRequestDatabaseID(ctx context.Context, hostname string, owner string, repo string, number int) (int64, error) {
// GetPullRequestDatabaseID retrieves the database ID and URL of a pull request given its number in a repository.
func (c *CAPIClient) GetPullRequestDatabaseID(ctx context.Context, hostname string, owner string, repo string, number int) (int64, string, error) {
var resp struct {
Repository struct {
PullRequest struct {
FullDatabaseID string `graphql:"fullDatabaseId"`
URL string `graphql:"url"`
} `graphql:"pullRequest(number: $number)"`
} `graphql:"repository(owner: $owner, name: $repo)"`
}
@ -396,14 +399,14 @@ func (c *CAPIClient) GetPullRequestDatabaseID(ctx context.Context, hostname stri
apiClient := api.NewClientFromHTTP(c.httpClient)
if err := apiClient.Query(hostname, "GetPullRequestFullDatabaseID", &resp, variables); err != nil {
return 0, err
return 0, "", err
}
databaseID, err := strconv.ParseInt(resp.Repository.PullRequest.FullDatabaseID, 10, 64)
if err != nil {
return 0, err
return 0, "", err
}
return databaseID, nil
return databaseID, resp.Repository.PullRequest.URL, nil
}
// generatePullRequestNodeID converts an int64 databaseID and repoID to a GraphQL Node ID format

View file

@ -1743,10 +1743,11 @@ func TestGetSession(t *testing.T) {
}
func TestGetPullRequestDatabaseID(t *testing.T) {
tests := []struct {
name string
httpStubs func(*testing.T, *httpmock.Registry)
wantErr string
wantOut int64
name string
httpStubs func(*testing.T, *httpmock.Registry)
wantErr string
wantDatabaseID int64
wantURL string
}{
{
name: "graphql error",
@ -1764,24 +1765,26 @@ func TestGetPullRequestDatabaseID(t *testing.T) {
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
reg.Register(
httpmock.WithHost(httpmock.GraphQL(`query GetPullRequestFullDatabaseID\b`), "api.github.com"),
httpmock.StringResponse(`{"data": {"repository": {"pullRequest": {"fullDatabaseId": "non-int"}}}}`),
httpmock.StringResponse(`{"data": {"repository": {"pullRequest": {"fullDatabaseId": "non-int", "url": "some-url"}}}}`),
)
},
wantErr: `strconv.ParseInt: parsing "non-int": invalid syntax`,
wantURL: "some-url",
},
{
name: "success",
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
reg.Register(
httpmock.WithHost(httpmock.GraphQL(`query GetPullRequestFullDatabaseID\b`), "api.github.com"),
httpmock.GraphQLQuery(`{"data": {"repository": {"pullRequest": {"fullDatabaseId": "999"}}}}`, func(s string, m map[string]interface{}) {
httpmock.GraphQLQuery(`{"data": {"repository": {"pullRequest": {"fullDatabaseId": "999", "url": "some-url"}}}}`, func(s string, m map[string]interface{}) {
assert.Equal(t, "OWNER", m["owner"])
assert.Equal(t, "REPO", m["repo"])
assert.Equal(t, float64(42), m["number"])
}),
)
},
wantOut: 999,
wantDatabaseID: 999,
wantURL: "some-url",
},
}
@ -1798,7 +1801,7 @@ func TestGetPullRequestDatabaseID(t *testing.T) {
cfg := config.NewBlankConfig()
capiClient := NewCAPIClient(httpClient, cfg.Authentication())
databaseID, err := capiClient.GetPullRequestDatabaseID(context.Background(), "github.com", "OWNER", "REPO", 42)
databaseID, url, err := capiClient.GetPullRequestDatabaseID(context.Background(), "github.com", "OWNER", "REPO", 42)
if tt.wantErr != "" {
require.ErrorContains(t, err, tt.wantErr)
@ -1807,7 +1810,8 @@ func TestGetPullRequestDatabaseID(t *testing.T) {
}
require.NoError(t, err)
require.Equal(t, tt.wantOut, databaseID)
require.Equal(t, tt.wantDatabaseID, databaseID)
require.Equal(t, tt.wantURL, url)
})
}
}

View file

@ -70,7 +70,7 @@ func listRun(opts *ListOptions) error {
// based on repo, so we just open the agents dashboard with no args.
// If that page is ever added in the future, we should route to that
// page instead of the global one when --repo is set.
const webURL = "https://github.com/copilot/agents"
webURL := capi.AgentsHomeURL
if opts.IO.IsStdoutTTY() {
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", text.DisplayURL(webURL))
}

View file

@ -10,6 +10,7 @@ import (
"time"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/browser"
"github.com/cli/cli/v2/internal/ghinstance"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/prompter"
@ -31,10 +32,12 @@ type ViewOptions struct {
HttpClient func() (*http.Client, error)
Finder prShared.PRFinder
Prompter prompter.Prompter
Browser browser.Browser
SelectorArg string
PRNumber int
SessionID string
Web bool
}
func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Command {
@ -43,14 +46,31 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman
HttpClient: f.HttpClient,
CapiClient: shared.CapiClientFunc(f),
Prompter: f.Prompter,
Browser: f.Browser,
}
cmd := &cobra.Command{
Use: "view [<session-id> | <pr-number> | <pr-url> | <pr-branch>]",
Short: "View an agent task session",
Short: "View an agent task session (preview)",
Long: heredoc.Doc(`
View an agent task session.
`),
Example: heredoc.Doc(`
# View an agent task by session ID
$ gh agent-task view e2fa49d2-f164-4a56-ab99-498090b8fcdf
# View an agent task by pull request number in current repo
$ gh agent-task view 12345
# View an agent task by pull request number
$ gh agent-task view --repo OWNER/REPO 12345
# View an agent task by pull request reference
$ gh agent-task view OWNER/REPO#12345
# View a pull request agents tasks in the browser
$ gh agent-task view 12345 --web
`),
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
// Support -R/--repo override
@ -80,6 +100,8 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman
cmdutil.EnableRepoOverride(cmd, f)
cmd.Flags().BoolVarP(&opts.Web, "web", "w", false, "Open agent task in the browser")
return cmd
}
@ -98,17 +120,37 @@ func viewRun(opts *ViewOptions) error {
var session *capi.Session
if opts.SessionID != "" {
if sess, err := capiClient.GetSession(ctx, opts.SessionID); err != nil {
sess, err := capiClient.GetSession(ctx, opts.SessionID)
if err != nil {
if errors.Is(err, capi.ErrSessionNotFound) {
fmt.Fprintln(opts.IO.ErrOut, "session not found")
return cmdutil.SilentError
}
return err
} else {
session = sess
}
opts.IO.StopProgressIndicator()
if opts.Web {
var webURL string
if sess.PullRequest != nil {
webURL = fmt.Sprintf("%s/agent-sessions/%s", sess.PullRequest.URL, url.PathEscape(sess.ID))
} else {
// Currently the web Copilot Agents home GUI does not support focusing
// on a given session, so we should just navigate to the home page.
webURL = capi.AgentsHomeURL
}
if opts.IO.IsStdoutTTY() {
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", text.DisplayURL(webURL))
}
return opts.Browser.Browse(webURL)
}
session = sess
} else {
var resourceID int64
var prURL string
if opts.SelectorArg != "" {
// Finder does not support the PR/issue reference format (e.g. owner/repo#123)
@ -127,7 +169,7 @@ func viewRun(opts *ViewOptions) error {
return fmt.Errorf("agent tasks are not supported on this host: %s", hostname)
}
resourceID, err = capiClient.GetPullRequestDatabaseID(ctx, hostname, repo.RepoOwner(), repo.RepoName(), num)
resourceID, prURL, err = capiClient.GetPullRequestDatabaseID(ctx, hostname, repo.RepoOwner(), repo.RepoName(), num)
if err != nil {
return fmt.Errorf("failed to fetch pull request: %w", err)
}
@ -155,6 +197,7 @@ func viewRun(opts *ViewOptions) error {
}
resourceID = databaseID
prURL = pr.URL
}
// TODO(babakks): currently we just fetch a pre-defined number of
@ -171,6 +214,23 @@ func viewRun(opts *ViewOptions) error {
return cmdutil.SilentError
}
opts.IO.StopProgressIndicator()
if opts.Web {
// Note that, we needed to make sure the PR exists and it has at least one session
// associated with it, other wise the `/agent-sessions` page would display the 404
// error.
// We don't need to navigate to a specific session; if there's only one session
// then the GUI will automatically show it, otherwise the user can select from the
// list. This is to avoid unnecessary prompting.
webURL := prURL + "/agent-sessions"
if opts.IO.IsStdoutTTY() {
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", text.DisplayURL(webURL))
}
return opts.Browser.Browse(webURL)
}
session = sessions[0]
if len(sessions) > 1 {
now := time.Now()
@ -184,7 +244,6 @@ func viewRun(opts *ViewOptions) error {
))
}
opts.IO.StopProgressIndicator()
selected, err := opts.Prompter.Select("Select a session", "", options)
if err != nil {
return err
@ -194,8 +253,6 @@ func viewRun(opts *ViewOptions) error {
}
}
opts.IO.StopProgressIndicator()
out := opts.IO.Out
if session.PullRequest != nil {

View file

@ -10,6 +10,7 @@ import (
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/browser"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/prompter"
"github.com/cli/cli/v2/pkg/cmd/agent-task/capi"
@ -68,6 +69,15 @@ func TestNewCmdList(t *testing.T) {
SelectorArg: "some-arg",
},
},
{
name: "web mode",
tty: true,
args: "some-arg -w",
wantOpts: ViewOptions{
SelectorArg: "some-arg",
Web: true,
},
},
}
for _, tt := range tests {
@ -116,14 +126,15 @@ func Test_viewRun(t *testing.T) {
sampleDate := time.Now().Add(-6 * time.Hour) // 6h ago
tests := []struct {
name string
tty bool
opts ViewOptions
promptStubs func(*testing.T, *prompter.MockPrompter)
capiStubs func(*testing.T, *capi.CapiClientMock)
wantOut string
wantErr error
wantStderr string
name string
tty bool
opts ViewOptions
promptStubs func(*testing.T, *prompter.MockPrompter)
capiStubs func(*testing.T, *capi.CapiClientMock)
wantOut string
wantErr error
wantStderr string
wantBrowserURL string
}{
{
name: "with session id, not found (tty)",
@ -273,14 +284,85 @@ func Test_viewRun(t *testing.T) {
Started about 6 hours ago
`),
},
{
name: "with session id, not found, web mode (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "some-session-id",
SessionID: "some-session-id",
Web: true,
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.GetSessionFunc = func(_ context.Context, _ string) (*capi.Session, error) {
return nil, capi.ErrSessionNotFound
}
},
wantStderr: "session not found\n",
wantErr: cmdutil.SilentError,
},
{
name: "with session id, without pr data, web mode (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "some-session-id",
SessionID: "some-session-id",
Web: true,
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.GetSessionFunc = func(_ context.Context, id string) (*capi.Session, error) {
assert.Equal(t, "some-session-id", id)
return &capi.Session{
ID: "some-session-id",
State: "completed",
CreatedAt: sampleDate,
// User data is irrelevant in this case
}, nil
}
},
wantBrowserURL: "https://github.com/copilot/agents",
wantStderr: "Opening https://github.com/copilot/agents in your browser.\n",
},
{
name: "with session id, with pr data, web mode (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "some-session-id",
SessionID: "some-session-id",
Web: true,
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.GetSessionFunc = func(_ context.Context, id string) (*capi.Session, error) {
assert.Equal(t, "some-session-id", id)
return &capi.Session{
ID: "some-session-id",
State: "completed",
CreatedAt: sampleDate,
PullRequest: &api.PullRequest{
Title: "fix something",
Number: 101,
URL: "https://github.com/OWNER/REPO/pull/101",
Repository: &api.PRRepository{
NameWithOwner: "OWNER/REPO",
},
},
// User data is irrelevant in this case
}, nil
}
},
wantBrowserURL: "https://github.com/OWNER/REPO/pull/101/agent-sessions/some-session-id",
wantStderr: "Opening https://github.com/OWNER/REPO/pull/101/agent-sessions/some-session-id in your browser.\n",
},
{
name: "with pr number, api error (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "pr-number",
SelectorArg: "101",
Finder: prShared.NewMockFinder(
"pr-number",
&api.PullRequest{FullDatabaseID: "999999"},
"101",
&api.PullRequest{
FullDatabaseID: "999999",
URL: "https://github.com/OWNER/REPO/pull/101",
},
ghrepo.New("OWNER", "REPO"),
),
},
@ -295,7 +377,7 @@ func Test_viewRun(t *testing.T) {
name: "with pr reference, unsupported hostname (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "OWNER/REPO#999",
SelectorArg: "OWNER/REPO#101",
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.NewWithHost("OWNER", "REPO", "foo.com"), nil
},
@ -306,14 +388,14 @@ func Test_viewRun(t *testing.T) {
name: "with pr reference, api error when fetching pr database ID (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "OWNER/REPO#999",
SelectorArg: "OWNER/REPO#101",
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.GetPullRequestDatabaseIDFunc = func(_ context.Context, _ string, _ string, _ string, _ int) (int64, error) {
return 0, errors.New("some error")
m.GetPullRequestDatabaseIDFunc = func(_ context.Context, _ string, _ string, _ string, _ int) (int64, string, error) {
return 0, "", errors.New("some error")
}
},
wantErr: errors.New("failed to fetch pull request: some error"),
@ -322,14 +404,14 @@ func Test_viewRun(t *testing.T) {
name: "with pr reference, api error when fetching session (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "OWNER/REPO#999",
SelectorArg: "OWNER/REPO#101",
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.GetPullRequestDatabaseIDFunc = func(_ context.Context, _ string, _ string, _ string, _ int) (int64, error) {
return 999999, nil
m.GetPullRequestDatabaseIDFunc = func(_ context.Context, _ string, _ string, _ string, _ int) (int64, string, error) {
return 999999, "some-url", nil
}
m.ListSessionsByResourceIDFunc = func(_ context.Context, _ string, _ int64, _ int) ([]*capi.Session, error) {
return nil, errors.New("some error")
@ -338,13 +420,16 @@ func Test_viewRun(t *testing.T) {
wantErr: errors.New("failed to list sessions for pull request: some error"),
},
{
name: "with pr number, success, single session with pr and user data (tty)",
name: "with pr number, success, single session (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "pr-number",
SelectorArg: "101",
Finder: prShared.NewMockFinder(
"pr-number",
&api.PullRequest{FullDatabaseID: "999999"},
"101",
&api.PullRequest{
FullDatabaseID: "999999",
URL: "https://github.com/OWNER/REPO/pull/101",
},
ghrepo.New("OWNER", "REPO"),
),
},
@ -382,13 +467,16 @@ func Test_viewRun(t *testing.T) {
`),
},
{
name: "with pr number, success, multiple sessions with pr and user data (tty)",
name: "with pr number, success, multiple sessions (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "pr-number",
SelectorArg: "101",
Finder: prShared.NewMockFinder(
"pr-number",
&api.PullRequest{FullDatabaseID: "999999"},
"101",
&api.PullRequest{
FullDatabaseID: "999999",
URL: "https://github.com/OWNER/REPO/pull/101",
},
ghrepo.New("OWNER", "REPO"),
),
},
@ -421,9 +509,9 @@ func Test_viewRun(t *testing.T) {
State: "completed",
CreatedAt: sampleDate,
PullRequest: &api.PullRequest{
Title: "fix something else",
Number: 102,
URL: "https://github.com/OWNER/REPO/pull/102",
Title: "fix something",
Number: 101,
URL: "https://github.com/OWNER/REPO/pull/101",
Repository: &api.PRRepository{
NameWithOwner: "OWNER/REPO",
},
@ -456,21 +544,21 @@ func Test_viewRun(t *testing.T) {
`),
},
{
name: "with pr reference, success, multiple sessions with pr and user data (tty)",
name: "with pr reference, success, multiple sessions (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "OWNER/REPO#999",
SelectorArg: "OWNER/REPO#101",
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.GetPullRequestDatabaseIDFunc = func(_ context.Context, hostname string, owner string, repo string, number int) (int64, error) {
m.GetPullRequestDatabaseIDFunc = func(_ context.Context, hostname string, owner string, repo string, number int) (int64, string, error) {
assert.Equal(t, "github.com", hostname)
assert.Equal(t, "OWNER", owner)
assert.Equal(t, "REPO", repo)
assert.Equal(t, 999, number)
return 999999, nil
assert.Equal(t, 101, number)
return 999999, "https://github.com/OWNER/REPO/pull/101", nil
}
m.ListSessionsByResourceIDFunc = func(_ context.Context, resourceType string, resourceID int64, limit int) ([]*capi.Session, error) {
assert.Equal(t, "pull", resourceType)
@ -500,9 +588,9 @@ func Test_viewRun(t *testing.T) {
State: "completed",
CreatedAt: sampleDate,
PullRequest: &api.PullRequest{
Title: "fix something else",
Number: 102,
URL: "https://github.com/OWNER/REPO/pull/102",
Title: "fix something",
Number: 101,
URL: "https://github.com/OWNER/REPO/pull/101",
Repository: &api.PRRepository{
NameWithOwner: "OWNER/REPO",
},
@ -534,6 +622,185 @@ func Test_viewRun(t *testing.T) {
https://github.com/OWNER/REPO/pull/101/agent-sessions/some-session-id
`),
},
{
name: "with pr number, api error, web mode (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "101",
Finder: prShared.NewMockFinder(
"101",
&api.PullRequest{
FullDatabaseID: "999999",
URL: "https://github.com/OWNER/REPO/pull/101",
},
ghrepo.New("OWNER", "REPO"),
),
Web: true,
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.ListSessionsByResourceIDFunc = func(_ context.Context, _ string, _ int64, _ int) ([]*capi.Session, error) {
return nil, errors.New("some error")
}
},
wantErr: errors.New("failed to list sessions for pull request: some error"),
},
{
name: "with pr number, single session, web mode (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "101",
Finder: prShared.NewMockFinder(
"101",
&api.PullRequest{
FullDatabaseID: "999999",
URL: "https://github.com/OWNER/REPO/pull/101",
},
ghrepo.New("OWNER", "REPO"),
),
Web: true,
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.ListSessionsByResourceIDFunc = func(_ context.Context, resourceType string, resourceID int64, limit int) ([]*capi.Session, error) {
assert.Equal(t, "pull", resourceType)
assert.Equal(t, int64(999999), resourceID)
assert.Equal(t, defaultLimit, limit)
return []*capi.Session{
{
ID: "some-session-id",
State: "completed",
CreatedAt: sampleDate,
PullRequest: &api.PullRequest{
Title: "fix something",
Number: 101,
URL: "https://github.com/OWNER/REPO/pull/101",
Repository: &api.PRRepository{
NameWithOwner: "OWNER/REPO",
},
},
// User data is irrelevant in this case
},
}, nil
}
},
wantBrowserURL: "https://github.com/OWNER/REPO/pull/101/agent-sessions",
wantStderr: "Opening https://github.com/OWNER/REPO/pull/101/agent-sessions in your browser.\n",
},
{
name: "with pr number, multiple sessions, web mode (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "101",
Finder: prShared.NewMockFinder(
"101",
&api.PullRequest{
FullDatabaseID: "999999",
URL: "https://github.com/OWNER/REPO/pull/101",
},
ghrepo.New("OWNER", "REPO"),
),
Web: true,
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.ListSessionsByResourceIDFunc = func(_ context.Context, resourceType string, resourceID int64, limit int) ([]*capi.Session, error) {
assert.Equal(t, "pull", resourceType)
assert.Equal(t, int64(999999), resourceID)
assert.Equal(t, defaultLimit, limit)
return []*capi.Session{
{
ID: "some-session-id",
Name: "session one",
State: "completed",
CreatedAt: sampleDate,
PullRequest: &api.PullRequest{
Title: "fix something",
Number: 101,
URL: "https://github.com/OWNER/REPO/pull/101",
Repository: &api.PRRepository{
NameWithOwner: "OWNER/REPO",
},
},
// User data is irrelevant in this case
},
{
ID: "some-other-session-id",
Name: "session two",
State: "completed",
CreatedAt: sampleDate,
PullRequest: &api.PullRequest{
Title: "fix something",
Number: 101,
URL: "https://github.com/OWNER/REPO/pull/101",
Repository: &api.PRRepository{
NameWithOwner: "OWNER/REPO",
},
},
// User data is irrelevant in this case
},
}, nil
}
},
wantBrowserURL: "https://github.com/OWNER/REPO/pull/101/agent-sessions",
wantStderr: "Opening https://github.com/OWNER/REPO/pull/101/agent-sessions in your browser.\n",
},
{
name: "with pr reference, multiple sessions, web mode (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "OWNER/REPO#101",
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
Web: true,
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.GetPullRequestDatabaseIDFunc = func(_ context.Context, hostname string, owner string, repo string, number int) (int64, string, error) {
assert.Equal(t, "github.com", hostname)
assert.Equal(t, "OWNER", owner)
assert.Equal(t, "REPO", repo)
assert.Equal(t, 101, number)
return 999999, "https://github.com/OWNER/REPO/pull/101", nil
}
m.ListSessionsByResourceIDFunc = func(_ context.Context, resourceType string, resourceID int64, limit int) ([]*capi.Session, error) {
assert.Equal(t, "pull", resourceType)
assert.Equal(t, int64(999999), resourceID)
assert.Equal(t, defaultLimit, limit)
return []*capi.Session{
{
ID: "some-session-id",
Name: "session one",
State: "completed",
CreatedAt: sampleDate,
PullRequest: &api.PullRequest{
Title: "fix something",
Number: 101,
URL: "https://github.com/OWNER/REPO/pull/101",
Repository: &api.PRRepository{
NameWithOwner: "OWNER/REPO",
},
},
// User data is irrelevant in this case
},
{
ID: "some-other-session-id",
Name: "session two",
State: "completed",
CreatedAt: sampleDate,
PullRequest: &api.PullRequest{
Title: "fix something",
Number: 101,
URL: "https://github.com/OWNER/REPO/pull/101",
Repository: &api.PRRepository{
NameWithOwner: "OWNER/REPO",
},
},
// User data is irrelevant in this case
},
}, nil
}
},
wantBrowserURL: "https://github.com/OWNER/REPO/pull/101/agent-sessions",
wantStderr: "Opening https://github.com/OWNER/REPO/pull/101/agent-sessions in your browser.\n",
},
}
for _, tt := range tests {
@ -551,9 +818,12 @@ func Test_viewRun(t *testing.T) {
ios, _, stdout, stderr := iostreams.Test()
ios.SetStdoutTTY(tt.tty)
browser := &browser.Stub{}
opts := tt.opts
opts.IO = ios
opts.Prompter = prompter
opts.Browser = browser
opts.CapiClient = func() (capi.CapiClient, error) {
return capiClientMock, nil
}
@ -566,9 +836,9 @@ func Test_viewRun(t *testing.T) {
require.NoError(t, err)
}
got := stdout.String()
require.Equal(t, tt.wantOut, got)
require.Equal(t, tt.wantStderr, stderr.String())
assert.Equal(t, tt.wantOut, stdout.String())
assert.Equal(t, tt.wantStderr, stderr.String())
assert.Equal(t, tt.wantBrowserURL, browser.BrowsedURL())
})
}
}