Autolink delete tests

This commit is contained in:
Michael Hoffman 2025-02-01 15:46:09 -05:00
parent 908513f97f
commit e226a79dc5
5 changed files with 273 additions and 8 deletions

1
go.mod
View file

@ -54,6 +54,7 @@ require (
google.golang.org/protobuf v1.36.4
gopkg.in/h2non/gock.v1 v1.1.2
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.0.3
)
require (

6
go.sum
View file

@ -206,6 +206,7 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/certificate-transparency-go v1.3.1 h1:akbcTfQg0iZlANZLn0L9xOeWtyCIdeoYhKrqi5iH3Go=
github.com/google/certificate-transparency-go v1.3.1/go.mod h1:gg+UQlx6caKEDQ9EElFOujyxEQEfOiQzAt6782Bvi8k=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
@ -360,6 +361,7 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -433,6 +435,7 @@ github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
@ -508,6 +511,7 @@ golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCR
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@ -544,11 +548,13 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.216.0 h1:xnEHy+xWFrtYInWPy8OdGFsyIfWJjtVnO39g7pz2BFY=
google.golang.org/api v0.216.0/go.mod h1:K9wzQMvWi47Z9IU7OgdOofvZuw75Ge3PPITImZR/UyI=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=

View file

@ -0,0 +1,257 @@
package delete
import (
"bytes"
"errors"
"net/http"
"testing"
"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/repo/autolink/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/google/shlex"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
input string
output deleteOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "accepts 1 arg(s), received 0",
},
{
name: "id provided",
input: "123",
output: deleteOptions{ID: "123"},
},
{
name: "yes flag",
input: "123 --yes",
output: deleteOptions{ID: "123", Confirmed: true},
},
}
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 *deleteOptions
cmd := NewCmdDelete(f, func(opts *deleteOptions) 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.output.ID, gotOpts.ID)
assert.Equal(t, tt.output.Confirmed, gotOpts.Confirmed)
}
})
}
}
type stubAutolinkDeleter struct {
err error
}
func (d *stubAutolinkDeleter) Delete(repo ghrepo.Interface, id string) error {
return d.err
}
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
}
var errTestPrompt = errors.New("prompt error")
var errTestAutolinkClientView = errors.New("autolink client view error")
var errTestAutolinkClientDelete = errors.New("autolink client delete error")
func TestDeleteRun(t *testing.T) {
tests := []struct {
name string
opts *deleteOptions
stubDeleter stubAutolinkDeleter
stubViewer stubAutolinkViewer
prompterStubs func(*prompter.PrompterMock)
wantStdout string
wantStderr string
expectedErr error
expectedErrMsg string
}{
{
name: "delete",
opts: &deleteOptions{
ID: "123",
},
stubViewer: stubAutolinkViewer{
autolink: &shared.Autolink{
ID: 123,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
},
stubDeleter: stubAutolinkDeleter{
err: nil,
},
prompterStubs: func(pm *prompter.PrompterMock) {
pm.ConfirmDeletionFunc = func(_ string) error {
return nil
}
},
wantStdout: "Autolink 123 has key prefix TICKET-.✓ Autolink 123 deleted from OWNER/REPO\n",
},
{
name: "delete with confirm flag",
opts: &deleteOptions{
ID: "123",
Confirmed: true,
},
stubViewer: stubAutolinkViewer{
autolink: &shared.Autolink{
ID: 123,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
},
stubDeleter: stubAutolinkDeleter{
err: nil,
},
wantStdout: "✓ Autolink 123 deleted from OWNER/REPO\n",
},
{
name: "confirmation fails",
opts: &deleteOptions{
ID: "123",
},
stubViewer: stubAutolinkViewer{
autolink: &shared.Autolink{
ID: 123,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
},
stubDeleter: stubAutolinkDeleter{
err: nil,
},
prompterStubs: func(pm *prompter.PrompterMock) {
pm.ConfirmDeletionFunc = func(_ string) error {
return errTestPrompt
}
},
expectedErr: errTestPrompt,
expectedErrMsg: errTestPrompt.Error(),
wantStdout: "Autolink 123 has key prefix TICKET-.",
},
{
name: "view error",
opts: &deleteOptions{
ID: "123",
},
stubViewer: stubAutolinkViewer{
err: errTestAutolinkClientView,
},
stubDeleter: stubAutolinkDeleter{
err: nil,
},
expectedErr: errTestAutolinkClientView,
expectedErrMsg: "error deleting autolink: autolink client view error",
},
{
name: "delete error",
opts: &deleteOptions{
ID: "123",
},
stubViewer: stubAutolinkViewer{
autolink: &shared.Autolink{
ID: 123,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
},
stubDeleter: stubAutolinkDeleter{
err: errTestAutolinkClientDelete,
},
prompterStubs: func(pm *prompter.PrompterMock) {
pm.ConfirmDeletionFunc = func(_ string) error {
return nil
}
},
expectedErr: errTestAutolinkClientDelete,
expectedErrMsg: errTestAutolinkClientDelete.Error(),
wantStdout: "Autolink 123 has key prefix TICKET-.",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, stdout, stderr := 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.AutolinkDeleteClient = &tt.stubDeleter
opts.AutolinkViewClient = &tt.stubViewer
pm := &prompter.PrompterMock{}
if tt.prompterStubs != nil {
tt.prompterStubs(pm)
}
tt.opts.Prompter = pm
err := deleteRun(opts)
if tt.expectedErr != nil {
require.Error(t, err, "expected error but got none")
assert.ErrorIs(t, err, tt.expectedErr, "unexpected error")
assert.Equal(t, tt.expectedErrMsg, err.Error(), "unexpected error message")
} else {
require.NoError(t, err)
}
assert.Equal(t, tt.wantStdout, stdout.String(), "unexpected stdout")
assert.Equal(t, tt.wantStderr, stderr.String(), "unexpected stderr")
})
}
}

View file

@ -0,0 +1 @@
package delete

View file

@ -49,13 +49,12 @@ func TestNewCmdView(t *testing.T) {
{
name: "json flag",
input: "123 --json id",
output: viewOptions{},
output: viewOptions{ID: "123"},
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",
},
@ -91,17 +90,18 @@ func TestNewCmdView(t *testing.T) {
} else {
require.NoError(t, err)
assert.Equal(t, tt.wantExporter, gotOpts.Exporter != nil)
assert.Equal(t, tt.output.ID, gotOpts.ID)
}
})
}
}
type stubAutoLinkViewer struct {
type stubAutolinkViewer struct {
autolink *shared.Autolink
err error
}
func (g stubAutoLinkViewer) View(repo ghrepo.Interface, id string) (*shared.Autolink, error) {
func (g stubAutolinkViewer) View(repo ghrepo.Interface, id string) (*shared.Autolink, error) {
return g.autolink, g.err
}
@ -115,7 +115,7 @@ func TestViewRun(t *testing.T) {
tests := []struct {
name string
opts *viewOptions
stubViewer stubAutoLinkViewer
stubViewer stubAutolinkViewer
expectedErr error
wantStdout string
}{
@ -124,7 +124,7 @@ func TestViewRun(t *testing.T) {
opts: &viewOptions{
ID: "1",
},
stubViewer: stubAutoLinkViewer{
stubViewer: stubAutolinkViewer{
autolink: &shared.Autolink{
ID: 1,
KeyPrefix: "TICKET-",
@ -150,7 +150,7 @@ func TestViewRun(t *testing.T) {
return exporter
}(),
},
stubViewer: stubAutoLinkViewer{
stubViewer: stubAutolinkViewer{
autolink: &shared.Autolink{
ID: 1,
KeyPrefix: "TICKET-",
@ -163,7 +163,7 @@ func TestViewRun(t *testing.T) {
{
name: "client error",
opts: &viewOptions{},
stubViewer: stubAutoLinkViewer{
stubViewer: stubAutolinkViewer{
autolink: nil,
err: testAutolinkClientViewError{},
},