Repository: cli/cli (go)

Issue:
Add support for `gh repo autolink view`
This issue is to implement the command necessary to view a repository autolinks as discussed within #9420 underneath `gh repo autolink`.

This should follow similar form as existing GitHub CLI commands as well as necessary tests.

### Acceptance Criteria

> [!NOTE]  
> Successful commands should return `0` exit code whereas errors should return `1` unless otherwise stated.

**When** I run `gh repo autolink view --help`
**Then** I see an informative help usage containing descriptive command information:

1. Long description explaining command purpose.

2. Usage: `gh repo autolink view <id> [flags]`

3. Flags:
```
     -q, --jq expression     Filter JSON output using a jq expression
         --json fields       Output JSON with the specified fields
     -t, --template string   Format JSON output using a Go template; see "gh help formatting"
```

4. Inherited flags:
```
INHERITED FLAGS
         --help                     Show help for command
     -R, --repo [HOST/]OWNER/REPO   Select another repository using the [HOST/]OWNER/REPO format
```
 
5. JSON fields:  
```
 JSON FIELDS
     id, isAlphanumeric, keyPrefix, urlTemplate
```

_The web UI does not provide a route representing a single autolink, so we don't provide a `--web` flag here._

---

**Given** I don't have the `admin` role on the repository
**And Given** I have a local repository cloned from GitHub
**When** I run `gh repo autolink view <id>`
**Then** I see an informative error

```
error getting autolink: HTTP 404: Either no autolink with this ID exists for this repository or you are missing admin rights to the repository. (https://api.github.com/repos/{owner}/{repo}/autolinks)
```

_The REST API provides identical 404 responses for (1) repo doesn't exist, (2) repo exists but autolink doesn't, and (3) both exists but user lacks admin rights._

---

**When** I run `gh repo autolink view` without an argument
**Then** I see an informative error

```
accepts 1 arg(s), received 0
```

---

**Given** I have the `admin` role on the repository
**And Given** I have a local repository cloned from GitHub
**When** I run `gh repo autolink view <id>` where `<id>` is the ID of an autolink in the repository.
**Then** I see a message of the following form:

```
Autolink in <owner>/<repo>

ID: <id>
Key Prefix: <keyPrefix>
URL Template: <urlTemplate>
Alphanumeric: <isAlphanumeric>
```

---

**Given** I have a repository with an autolink
**When** I run `gh repo autolink view --json`
**Then** I see a list of repository autolink fields that can be outputted in JSON format:

```
Specify one or more comma-separated fields for `--json`:
  id
  isAlphanumeric
  keyPrefix
  urlTemplate
```

---

**Given** I have a repository with an autolink
**When** I run `gh repo autolink view <id> --json <field>[,<field>...]` with one or more relevant fields
**Then** I see a JSON representation of the autolink only containing the specified fields:

```
{
  "id": 1,
  "isAlphanumeric": false,
  "keyPrefix": "DISCORD-",
  "urlTemplate": "https://discord.com/channels/<num>"
}
```

---

**Given** I have a repository with an autolink
**When** I run `gh repo autolink view <id> --json <field>[,<field>...] --jq '...'` with a valid [`jq` filter](https://jqlang.github.io/jq/manual/)
**Then** I see the JSON result from applying the `jq` filter to the standard `gh repo autolink --json <field>[,<field>...]` result:

```
gh repo autolink list --json keyPrefix --jq '.keyPrefix'
```

resulting in:

```
TICKET-
```

---

**Given** I have a repository with an autolink
**When** I run `gh repo autolink view <id> --json <field>[,<field>...] --template '...'` with a Go template
**Then** I see the formatted JSON result from applying the `template` Go template filter to the standard `gh repo autolink view <id> --json <field>[,<field>...]` result


Pull request description:
feat: Add support for viewing autolink references
Fixes #10200 

## Overview

Introduce the `gh repo autolink view <id>` subcommand as described in the linked issue.

## Example Output

### Help

```
$ bin/gh repo autolink view --help
View an autolink reference for a repository.

For more information about output formatting flags, see `gh help formatting`.

USAGE
  gh repo autolink view <id> [flags]

FLAGS
  -q, --jq expression     Filter JSON output using a jq expression
      --json fields       Output JSON with the specified fields
  -t, --template string   Format JSON output using a Go template; see "gh help formatting"

INHERITED FLAGS
      --help                     Show help for command
  -R, --repo [HOST/]OWNER/REPO   Select another repository using the [HOST/]OWNER/REPO format

JSON FIELDS
  id, isAlphanumeric, keyPrefix, urlTemplate

LEARN MORE
  Use `gh <command> <subcommand> --help` for more information about a command.
  Read the manual at https://cli.github.com/manual
  Learn about exit codes using `gh help exit-codes`
```

### Successful view

```
$ ./bin/gh repo autolink view 7166925 --repo nytimes/games-dsr
Autolink in nytimes/games-dsr

ID: 7166925
Key Prefix: TEST-
URL Template: https://www.example.com/<num>
Alphanumeric: true
```


### Successful view, JSON

```
$  ./bin/gh repo autolink view 7168016 --repo nytimes/games-dsr --json id,urlTemplate
{
  "id": 7168016,
  "urlTemplate": "https://www.example.com/<num>"
}
```

### Successful view, JSON w/ `jq`

```
$ ./bin/gh repo autolink view 7168016 --repo nytimes/games-dsr --json keyPrefix --jq '.keyPrefix'
TEST-
```

### Successful view, JSON w/ Go template

```
$ ./bin/gh repo autolink view 7168016 --repo nytimes/games-dsr --json keyPrefix --template 'The key prefix is {{.keyPrefix}}'
The key prefix is TEST-
```


### JSON flag with no args

```
$   ./bin/gh repo autolink view 7168016 --repo nytimes/games-dsr --json
Specify one or more comma-separated fields for `--json`:
  id
  isAlphanumeric
  keyPrefix
  urlTemplate
```


### 404 (No admin access OR autolink doesn't exist OR repo doesn't exist)

```
$  ./bin/gh repo autolink view 7166925 --repo cli/cli
error viewing autolink: HTTP 404: Either no autolink with this ID exists for this repository or you are missing admin rights to the repository. (https://api.github.com/repos/cli/cli/autolinks/7168016)
```

### No Args

```
$  ./bin/gh repo autolink view
accepts 1 arg(s), received 0
```

Patch:
diff --git a/pkg/cmd/repo/autolink/autolink.go b/pkg/cmd/repo/autolink/autolink.go
index 1fd218354..09b766b85 100644
--- a/pkg/cmd/repo/autolink/autolink.go
+++ b/pkg/cmd/repo/autolink/autolink.go
@@ -4,6 +4,7 @@ import (
 	"github.com/MakeNowJust/heredoc"
 	cmdCreate "github.com/cli/cli/v2/pkg/cmd/repo/autolink/create"
 	cmdList "github.com/cli/cli/v2/pkg/cmd/repo/autolink/list"
+	cmdView "github.com/cli/cli/v2/pkg/cmd/repo/autolink/view"
 	"github.com/cli/cli/v2/pkg/cmdutil"
 	"github.com/spf13/cobra"
 )
@@ -24,6 +25,7 @@ func NewCmdAutolink(f *cmdutil.Factory) *cobra.Command {
 
 	cmd.AddCommand(cmdList.NewCmdList(f, nil))
 	cmd.AddCommand(cmdCreate.NewCmdCreate(f, nil))
+	cmd.AddCommand(cmdView.NewCmdView(f, nil))
 
 	return cmd
 }
diff --git a/pkg/cmd/repo/autolink/create/create.go b/pkg/cmd/repo/autolink/create/create.go
index ac3035c79..aad71a941 100644
--- a/pkg/cmd/repo/autolink/create/create.go
+++ b/pkg/cmd/repo/autolink/create/create.go
@@ -109,9 +109,9 @@ func createRun(opts *createOptions) error {
 
 	cs := opts.IO.ColorScheme()
 	fmt.Fprintf(opts.IO.Out,
-		"%s Created repository autolink %d on %s\n",
+		"%s Created repository autolink %s on %s\n",
 		cs.SuccessIconWithColor(cs.Green),
-		autolink.ID,
+		cs.Cyanf("%d", autolink.ID),
 		ghrepo.FullName(repo))
 
 	return nil
diff --git a/pkg/cmd/repo/autolink/list/list.go b/pkg/cmd/repo/autolink/list/list.go
index a68c03f3c..402479c44 100644
--- a/pkg/cmd/repo/autolink/list/list.go
+++ b/pkg/cmd/repo/autolink/list/list.go
@@ -15,13 +15,6 @@ import (
 	"github.com/spf13/cobra"
 )
 
-var autolinkFields = []string{
-	"id",
-	"isAlphanumeric",
-	"keyPrefix",
-	"urlTemplate",
-}
-
 type listOptions struct {
 	BaseRepo       func() (ghrepo.Interface, error)
 	Browser        browser.Browser
@@ -70,7 +63,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*listOptions) error) *cobra.Comman
 	}
 
 	cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "List autolink references in the web browser")
-	cmdutil.AddJSONFlags(cmd, &opts.Exporter, autolinkFields)
+	cmdutil.AddJSONFlags(cmd, &opts.Exporter, shared.AutolinkFields)
 
 	return cmd
 }
@@ -111,8 +104,10 @@ func listRun(opts *listOptions) error {
 
 	tp := tableprinter.New(opts.IO, tableprinter.WithHeader("ID", "KEY PREFIX", "URL TEMPLATE", "ALPHANUMERIC"))
 
+	cs := opts.IO.ColorScheme()
+
 	for _, autolink := range autolinks {
-		tp.AddField(fmt.Sprintf("%d", autolink.ID))
+		tp.AddField(cs.Cyanf("%d", autolink.ID))
 		tp.AddField(autolink.KeyPrefix)
 		tp.AddField(autolink.URLTemplate)
 		tp.AddField(strconv.FormatBool(autolink.IsAlphanumeric))
diff --git a/pkg/cmd/repo/autolink/shared/autolink.go b/pkg/cmd/repo/autolink/shared/autolink.go
index d79537c31..28e975e7c 100644
--- a/pkg/cmd/repo/autolink/shared/autolink.go
+++ b/pkg/cmd/repo/autolink/shared/autolink.go
@@ -9,6 +9,13 @@ type Autolink struct {
 	URLTemplate    string `json:"url_template"`
 }
 
+var AutolinkFields = []string{
+	"id",
+	"isAlphanumeric",
+	"keyPrefix",
+	"urlTemplate",
+}
+
 func (a *Autolink) ExportData(fields []string) map[string]interface{} {
 	return cmdutil.StructExportData(a, fields)
 }
diff --git a/pkg/cmd/repo/autolink/view/http.go b/pkg/cmd/repo/autolink/view/http.go
new file mode 100644
index 000000000..8dd6dc12d
--- /dev/null
+++ b/pkg/cmd/repo/autolink/view/http.go
@@ -0,0 +1,46 @@
+package view
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/cli/cli/v2/api"
+	"github.com/cli/cli/v2/internal/ghinstance"
+	"github.com/cli/cli/v2/internal/ghrepo"
+	"github.com/cli/cli/v2/pkg/cmd/repo/autolink/shared"
+)
+
+type AutolinkViewer struct {
+	HTTPClient *http.Client
+}
+
+func (a *AutolinkViewer) View(repo ghrepo.Interface, id string) (*shared.Autolink, error) {
+	path := fmt.Sprintf("repos/%s/%s/autolinks/%s", repo.RepoOwner(), repo.RepoName(), id)
+	url := ghinstance.RESTPrefix(repo.RepoHost()) + path
+	req, err := http.NewRequest(http.MethodGet, url, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := a.HTTPClient.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode == http.StatusNotFound {
+		return nil, fmt.Errorf("HTTP 404: Either no autolink with this ID exists for this repository or you are missing admin rights to the repository. (https://api.github.com/%s)", path)
+	} else if resp.StatusCode > 299 {
+		return nil, api.HandleHTTPError(resp)
+	}
+
+	var autolink shared.Autolink
+	err = json.NewDecoder(resp.Body).Decode(&autolink)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &autolink, nil
+}
diff --git a/pkg/cmd/repo/autolink/view/view.go b/pkg/cmd/repo/autolink/view/view.go
new file mode 100644
index 000000000..0b212aece
--- /dev/null
+++ b/pkg/cmd/repo/autolink/view/view.go
@@ -0,0 +1,96 @@
+package view
+
+import (
+	"fmt"
+
+	"github.com/cli/cli/v2/internal/browser"
+	"github.com/cli/cli/v2/internal/ghrepo"
+	"github.com/cli/cli/v2/pkg/cmd/repo/autolink/shared"
+	"github.com/cli/cli/v2/pkg/cmdutil"
+	"github.com/cli/cli/v2/pkg/iostreams"
+	"github.com/spf13/cobra"
+)
+
+type viewOptions struct {
+	BaseRepo       func() (ghrepo.Interface, error)
+	Browser        browser.Browser
+	AutolinkClient AutolinkViewClient
+	IO             *iostreams.IOStreams
+	Exporter       cmdutil.Exporter
+
+	ID string
+}
+
+type AutolinkViewClient interface {
+	View(repo ghrepo.Interface, id string) (*shared.Autolink, error)
+}
+
+func NewCmdView(f *cmdutil.Factory, runF func(*viewOptions) error) *cobra.Command {
+	opts := &viewOptions{
+		Browser: f.Browser,
+		IO:      f.IOStreams,
+	}
+
+	cmd := &cobra.Command{
+		Use:   "view <id>",
+		Short: "View an autolink reference",
+		Long:  "View an autolink reference for a repository.",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			httpClient, err := f.HttpClient()
+			if err != nil {
+				return err
+			}
+
+			opts.BaseRepo = f.BaseRepo
+			opts.ID = args[0]
+			opts.AutolinkClient = &AutolinkViewer{HTTPClient: httpClient}
+
+			if runF != nil {
+				return runF(opts)
+			}
+
+			return viewRun(opts)
+		},
+	}
+
+	cmdutil.AddJSONFlags(cmd, &opts.Exporter, shared.AutolinkFields)
+
+	return cmd
+}
+
+func viewRun(opts *viewOptions) error {
+	repo, err := opts.BaseRepo()
+	if err != nil {
+		return err
+	}
+
+	out := opts.IO.Out
+	cs := opts.IO.ColorScheme()
+
+	autolink, err := opts.AutolinkClient.View(repo, opts.ID)
+
+	if err != nil {
+		return fmt.Errorf("%s %w", cs.Red("error viewing autolink:"), err)
+	}
+
+	if opts.Exporter != nil {
+		return opts.Exporter.Write(opts.IO, autolink)
+	}
+
+	fmt.Fprintf(out, "Autolink in %s\n\n", ghrepo.FullName(repo))
+
+	fmt.Fprint(out, cs.Bold("ID: "))
+	fmt.Fprintln(out, cs.Cyanf("%d", autolink.ID))
+
+	fmt.Fprint(out, cs.Bold("Key Prefix: "))
+	fmt.Fprintln(out, autolink.KeyPrefix)
+
+	fmt.Fprint(out, cs.Bold("URL Template: "))
+	fmt.Fprintln(out, autolink.URLTemplate)
+
+	fmt.Fprint(out, cs.Bold("Alphanumeric: "))
+	fmt.Fprintln(out, autolink.IsAlphanumeric)
+
+	return nil
+}


Test patch:
diff --git a/pkg/cmd/repo/autolink/list/http_test.go b/pkg/cmd/repo/autolink/list/http_test.go
index 0fa918f61..65289c419 100644
--- a/pkg/cmd/repo/autolink/list/http_test.go
+++ b/pkg/cmd/repo/autolink/list/http_test.go
@@ -12,7 +12,7 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-func TestAutoLinkLister_List(t *testing.T) {
+func TestAutolinkLister_List(t *testing.T) {
 	tests := []struct {
 		name   string
 		repo   ghrepo.Interface
diff --git a/pkg/cmd/repo/autolink/view/http_test.go b/pkg/cmd/repo/autolink/view/http_test.go
new file mode 100644
index 000000000..5bfe9369f
--- /dev/null
+++ b/pkg/cmd/repo/autolink/view/http_test.go
@@ -0,0 +1,102 @@
+package view
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/cli/cli/v2/internal/ghrepo"
+	"github.com/cli/cli/v2/pkg/cmd/repo/autolink/shared"
+	"github.com/cli/cli/v2/pkg/httpmock"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestAutolinkViewer_View(t *testing.T) {
+	repo := ghrepo.New("OWNER", "REPO")
+
+	tests := []struct {
+		name         string
+		id           string
+		stubStatus   int
+		stubRespJSON string
+
+		expectedAutolink *shared.Autolink
+		expectErr        bool
+		expectedErrMsg   string
+	}{
+		{
+			name:       "200 successful alphanumeric view",
+			id:         "123",
+			stubStatus: 200,
+			stubRespJSON: `{
+				"id": 123,
+				"key_prefix": "TICKET-",
+				"url_template": "https://example.com/TICKET?query=<num>",
+				"is_alphanumeric": true
+			}`,
+			expectedAutolink: &shared.Autolink{
+				ID:             123,
+				KeyPrefix:      "TICKET-",
+				URLTemplate:    "https://example.com/TICKET?query=<num>",
+				IsAlphanumeric: true,
+			},
+		},
+		{
+			name:       "200 successful numeric view",
+			id:         "123",
+			stubStatus: 200,
+			stubRespJSON: `{
+				"id": 123,
+				"key_prefix": "TICKET-",
+				"url_template": "https://example.com/TICKET?query=<num>",
+				"is_alphanumeric": false
+			}`,
+			expectedAutolink: &shared.Autolink{
+				ID:             123,
+				KeyPrefix:      "TICKET-",
+				URLTemplate:    "https://example.com/TICKET?query=<num>",
+				IsAlphanumeric: false,
+			},
+		},
+		{
+			name:       "404 repo or autolink not found",
+			id:         "123",
+			stubStatus: 404,
+			stubRespJSON: `{
+				"message": "Not Found",
+				"documentation_url": "https://docs.github.com/rest/repos/autolinks#get-an-autolink-reference-of-a-repository",
+				"status": "404"
+			}`,
+			expectErr:      true,
+			expectedErrMsg: "HTTP 404: Either no autolink with this ID exists for this repository or you are missing admin rights to the repository. (https://api.github.com/repos/OWNER/REPO/autolinks/123)",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			reg := &httpmock.Registry{}
+			reg.Register(
+				httpmock.REST(
+					http.MethodGet,
+					fmt.Sprintf("repos/%s/%s/autolinks/%s", repo.RepoOwner(), repo.RepoName(), tt.id),
+				),
+				httpmock.StatusStringResponse(tt.stubStatus, tt.stubRespJSON),
+			)
+			defer reg.Verify(t)
+
+			autolinkCreator := &AutolinkViewer{
+				HTTPClient: &http.Client{Transport: reg},
+			}
+
+			autolink, err := autolinkCreator.View(repo, tt.id)
+
+			if tt.expectErr {
+				require.EqualError(t, err, tt.expectedErrMsg)
+			} else {
+				require.NoError(t, err)
+				assert.Equal(t, tt.expectedAutolink, autolink)
+			}
+		})
+	}
+}
diff --git a/pkg/cmd/repo/autolink/view/view_test.go b/pkg/cmd/repo/autolink/view/view_test.go
new file mode 100644
index 000000000..bc0a1bf7f
--- /dev/null
+++ b/pkg/cmd/repo/autolink/view/view_test.go
@@ -0,0 +1,197 @@
+package view
+
+import (
+	"bytes"
+	"net/http"
+	"testing"
+
+	"github.com/MakeNowJust/heredoc"
+	"github.com/cli/cli/v2/internal/browser"
+	"github.com/cli/cli/v2/internal/ghrepo"
+	"github.com/cli/cli/v2/pkg/cmd/repo/autolink/shared"
+	"github.com/cli/cli/v2/pkg/cmdutil"
+	"github.com/cli/cli/v2/pkg/iostreams"
+	"github.com/cli/cli/v2/pkg/jsonfieldstest"
+	"github.com/google/shlex"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestJSONFields(t *testing.T) {
+	jsonfieldstest.ExpectCommandToSupportJSONFields(t, NewCmdView, []string{
+		"id",
+		"isAlphanumeric",
+		"keyPrefix",
+		"urlTemplate",
+	})
+}
+
+func TestNewCmdView(t *testing.T) {
+	tests := []struct {
+		name         string
+		input        string
+		output       viewOptions
+		wantErr      bool
+		wantExporter bool
+		errMsg       string
+	}{
+		{
+			name:    "no argument",
+			input:   "",
+			wantErr: true,
+			errMsg:  "accepts 1 arg(s), received 0",
+		},
+		{
+			name:   "id provided",
+			input:  "123",
+			output: viewOptions{ID: "123"},
+		},
+		{
+			name:         "json flag",
+			input:        "123 --json id",
+			output:       viewOptions{},
+			wantExporter: true,
+		},
+		{
+			name:    "invalid json flag",
+			input:   "123 --json invalid",
+			output:  viewOptions{},
+			wantErr: true,
+			errMsg:  "Unknown JSON field: \"invalid\"\nAvailable fields:\n  id\n  isAlphanumeric\n  keyPrefix\n  urlTemplate",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ios, _, _, _ := iostreams.Test()
+			f := &cmdutil.Factory{
+				IOStreams: ios,
+			}
+			f.HttpClient = func() (*http.Client, error) {
+				return &http.Client{}, nil
+			}
+
+			argv, err := shlex.Split(tt.input)
+			require.NoError(t, err)
+
+			var gotOpts *viewOptions
+			cmd := NewCmdView(f, func(opts *viewOptions) error {
+				gotOpts = opts
+				return nil
+			})
+
+			cmd.SetArgs(argv)
+			cmd.SetIn(&bytes.Buffer{})
+			cmd.SetOut(&bytes.Buffer{})
+			cmd.SetErr(&bytes.Buffer{})
+
+			_, err = cmd.ExecuteC()
+			if tt.wantErr {
+				require.EqualError(t, err, tt.errMsg)
+			} else {
+				require.NoError(t, err)
+				assert.Equal(t, tt.wantExporter, gotOpts.Exporter != nil)
+			}
+		})
+	}
+}
+
+type stubAutoLinkViewer struct {
+	autolink *shared.Autolink
+	err      error
+}
+
+func (g stubAutoLinkViewer) View(repo ghrepo.Interface, id string) (*shared.Autolink, error) {
+	return g.autolink, g.err
+}
+
+type testAutolinkClientViewError struct{}
+
+func (e testAutolinkClientViewError) Error() string {
+	return "autolink client view error"
+}
+
+func TestViewRun(t *testing.T) {
+	tests := []struct {
+		name        string
+		opts        *viewOptions
+		stubViewer  stubAutoLinkViewer
+		expectedErr error
+		wantStdout  string
+	}{
+		{
+			name: "view",
+			opts: &viewOptions{
+				ID: "1",
+			},
+			stubViewer: stubAutoLinkViewer{
+				autolink: &shared.Autolink{
+					ID:             1,
+					KeyPrefix:      "TICKET-",
+					URLTemplate:    "https://example.com/TICKET?query=<num>",
+					IsAlphanumeric: true,
+				},
+			},
+			wantStdout: heredoc.Doc(`
+				Autolink in OWNER/REPO
+
+				ID: 1
+				Key Prefix: TICKET-
+				URL Template: https://example.com/TICKET?query=<num>
+				Alphanumeric: true
+			`),
+		},
+		{
+			name: "view json",
+			opts: &viewOptions{
+				Exporter: func() cmdutil.Exporter {
+					exporter := cmdutil.NewJSONExporter()
+					exporter.SetFields([]string{"id"})
+					return exporter
+				}(),
+			},
+			stubViewer: stubAutoLinkViewer{
+				autolink: &shared.Autolink{
+					ID:             1,
+					KeyPrefix:      "TICKET-",
+					URLTemplate:    "https://example.com/TICKET?query=<num>",
+					IsAlphanumeric: true,
+				},
+			},
+			wantStdout: "{\"id\":1}\n",
+		},
+		{
+			name: "client error",
+			opts: &viewOptions{},
+			stubViewer: stubAutoLinkViewer{
+				autolink: nil,
+				err:      testAutolinkClientViewError{},
+			},
+			expectedErr: testAutolinkClientViewError{},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ios, _, stdout, _ := iostreams.Test()
+
+			opts := tt.opts
+			opts.IO = ios
+			opts.Browser = &browser.Stub{}
+
+			opts.IO = ios
+			opts.BaseRepo = func() (ghrepo.Interface, error) { return ghrepo.New("OWNER", "REPO"), nil }
+
+			opts.AutolinkClient = &tt.stubViewer
+			err := viewRun(opts)
+
+			if tt.expectedErr != nil {
+				require.Error(t, err)
+				assert.ErrorIs(t, err, tt.expectedErr)
+			} else {
+				require.NoError(t, err)
+				assert.Equal(t, tt.wantStdout, stdout.String())
+			}
+		})
+	}
+}