Test extension command update behaviors

This commit expands upon the previous work by creating tests around extension command execution and how various extension update scenarios are handled.

Along the way, the logic handling formatting update messaging has been switched to use `ColorScheme` in order to honor color behavior flags.
This commit is contained in:
Andy Feller 2024-11-06 22:49:02 -05:00
parent 47d77bd51b
commit ddf7287ab8
2 changed files with 165 additions and 15 deletions

View file

@ -5,10 +5,10 @@ import (
"fmt"
"os/exec"
"strings"
"time"
"github.com/cli/cli/v2/pkg/extensions"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/mgutz/ansi"
"github.com/spf13/cobra"
)
@ -24,8 +24,8 @@ type extensionReleaseInfo struct {
}
func NewCmdExtension(io *iostreams.IOStreams, em extensions.ExtensionManager, ext extensions.Extension) *cobra.Command {
// Setup channel containing information about potential latest release info
updateMessageChan := make(chan *extensionReleaseInfo)
cs := io.ColorScheme()
return &cobra.Command{
Use: ext.Name(),
@ -56,20 +56,24 @@ func NewCmdExtension(io *iostreams.IOStreams, em extensions.ExtensionManager, ex
},
// PostRun handles communicating extension release information if found
PostRun: func(c *cobra.Command, args []string) {
releaseInfo := <-updateMessageChan
if releaseInfo != nil {
stderr := io.ErrOut
fmt.Fprintf(stderr, "\n\n%s %s → %s\n",
ansi.Color(fmt.Sprintf("A new release of %s is available:", ext.Name()), "yellow"),
ansi.Color(strings.TrimPrefix(releaseInfo.CurrentVersion, "v"), "cyan"),
ansi.Color(strings.TrimPrefix(releaseInfo.LatestVersion, "v"), "cyan"))
if releaseInfo.Pinned {
fmt.Fprintf(stderr, "To upgrade, run: gh extension upgrade %s --force\n", ext.Name())
} else {
fmt.Fprintf(stderr, "To upgrade, run: gh extension upgrade %s\n", ext.Name())
select {
case releaseInfo := <-updateMessageChan:
if releaseInfo != nil {
stderr := io.ErrOut
fmt.Fprintf(stderr, "\n\n%s %s → %s\n",
cs.Yellowf("A new release of %s is available:", ext.Name()),
cs.Cyan(strings.TrimPrefix(releaseInfo.CurrentVersion, "v")),
cs.Cyan(strings.TrimPrefix(releaseInfo.LatestVersion, "v")))
if releaseInfo.Pinned {
fmt.Fprintf(stderr, "To upgrade, run: gh extension upgrade %s --force\n", ext.Name())
} else {
fmt.Fprintf(stderr, "To upgrade, run: gh extension upgrade %s\n", ext.Name())
}
fmt.Fprintf(stderr, "%s\n\n",
cs.Yellow(releaseInfo.URL))
}
fmt.Fprintf(stderr, "%s\n\n",
ansi.Color(releaseInfo.URL, "yellow"))
case <-time.After(3 * time.Second):
// Bail on checking for new extension update as its taking too long
}
},
GroupID: "extension",

View file

@ -0,0 +1,146 @@
package root_test
import (
"io"
"testing"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/pkg/cmd/root"
"github.com/cli/cli/v2/pkg/extensions"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewCmdExtension_Updates(t *testing.T) {
tests := []struct {
name string
extCurrentVersion string
extIsPinned bool
extLatestVersion string
extName string
extUpdateAvailable bool
extURL string
wantStderr string
}{
{
name: "no update available",
extName: "no-update",
extUpdateAvailable: false,
extCurrentVersion: "1.0.0",
extLatestVersion: "1.0.0",
},
{
name: "major update",
extName: "major-update",
extUpdateAvailable: true,
extCurrentVersion: "1.0.0",
extLatestVersion: "2.0.0",
wantStderr: heredoc.Doc(`
A new release of major-update is available: 1.0.0 2.0.0
To upgrade, run: gh extension upgrade major-update
`),
},
{
name: "major update, pinned",
extName: "major-update",
extUpdateAvailable: true,
extCurrentVersion: "1.0.0",
extLatestVersion: "2.0.0",
extIsPinned: true,
wantStderr: heredoc.Doc(`
A new release of major-update is available: 1.0.0 2.0.0
To upgrade, run: gh extension upgrade major-update --force
`),
},
{
name: "minor update",
extName: "minor-update",
extUpdateAvailable: true,
extCurrentVersion: "1.0.0",
extLatestVersion: "1.1.0",
wantStderr: heredoc.Doc(`
A new release of minor-update is available: 1.0.0 1.1.0
To upgrade, run: gh extension upgrade minor-update
`),
},
{
name: "minor update, pinned",
extName: "minor-update",
extUpdateAvailable: true,
extCurrentVersion: "1.0.0",
extLatestVersion: "1.1.0",
extIsPinned: true,
wantStderr: heredoc.Doc(`
A new release of minor-update is available: 1.0.0 1.1.0
To upgrade, run: gh extension upgrade minor-update --force
`),
},
{
name: "patch update",
extName: "patch-update",
extUpdateAvailable: true,
extCurrentVersion: "1.0.0",
extLatestVersion: "1.0.1",
wantStderr: heredoc.Doc(`
A new release of patch-update is available: 1.0.0 1.0.1
To upgrade, run: gh extension upgrade patch-update
`),
},
{
name: "patch update, pinned",
extName: "patch-update",
extUpdateAvailable: true,
extCurrentVersion: "1.0.0",
extLatestVersion: "1.0.1",
extIsPinned: true,
wantStderr: heredoc.Doc(`
A new release of patch-update is available: 1.0.0 1.0.1
To upgrade, run: gh extension upgrade patch-update --force
`),
},
}
for _, tt := range tests {
ios, _, _, stderr := iostreams.Test()
em := &extensions.ExtensionManagerMock{
DispatchFunc: func(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error) {
// Assume extension executed / dispatched without problems as test is focused on upgrade checking.
return true, nil
},
}
ext := &extensions.ExtensionMock{
CurrentVersionFunc: func() string {
return tt.extCurrentVersion
},
IsPinnedFunc: func() bool {
return tt.extIsPinned
},
LatestVersionFunc: func() string {
return tt.extLatestVersion
},
NameFunc: func() string {
return tt.extName
},
UpdateAvailableFunc: func() bool {
return tt.extUpdateAvailable
},
URLFunc: func() string {
return tt.extURL
},
}
cmd := root.NewCmdExtension(ios, em, ext)
_, err := cmd.ExecuteC()
require.NoError(t, err)
if tt.wantStderr == "" {
assert.Emptyf(t, stderr.String(), "executing extension command should output nothing to stderr")
} else {
assert.Containsf(t, stderr.String(), tt.wantStderr, "executing extension command should output message about upgrade to stderr")
}
}
}