Skip to content

Commit

Permalink
Load extension binary as subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
XiaoConstantine committed Jul 16, 2024
1 parent a7090fd commit 7daa328
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 0 deletions.
33 changes: 33 additions & 0 deletions pkg/commands/extensions/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func NewCmdExtension(iostream *iostreams.IOStreams) *cobra.Command {
cmd.AddCommand(newExtensionListCmd(iostream))
cmd.AddCommand(newExtensionRemoveCmd(iostream))
cmd.AddCommand(newExtensionUpdateCmd(iostream))
cmd.AddCommand(newExtensionRunCmd()) // Add this new subcommand

return cmd
}
Expand Down Expand Up @@ -163,3 +164,35 @@ func newExtensionUpdateCmd(iostream *iostreams.IOStreams) *cobra.Command {
},
}
}

func newExtensionRunCmd() *cobra.Command {
return &cobra.Command{
Use: "run <extension-name> [args...]",
Short: "Run a mycli extension",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("extension name is required")
}
extName := args[0]
extArgs := args[1:]
return runExtension(extName, extArgs)
},
}
}

func runExtension(extName string, args []string) error {
extDir := getExtensionDir()
extPath := filepath.Join(extDir, "mycli-"+extName, "mycli-"+extName)

if _, err := os.Stat(extPath); os.IsNotExist(err) {
return fmt.Errorf("extension '%s' not found", extName)
}

cmd := exec.Command(extPath, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

return cmd.Run()
}
168 changes: 168 additions & 0 deletions pkg/commands/extensions/extension_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package extensions

import (
"bytes"
"net/http"
"net/http/httptest"
"os"
Expand All @@ -9,6 +10,7 @@ import (
"testing"

"github.com/XiaoConstantine/mycli/pkg/iostreams"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -238,6 +240,172 @@ func TestExtensionInstallCommand(t *testing.T) {
}
}

func TestRunExtension(t *testing.T) {
// Create a temporary directory to simulate the extensions directory
tempDir, err := os.MkdirTemp("", "mycli-test-extensions")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)

// Mock the getExtensionDir function
oldGetExtensionDir := getExtensionDir
getExtensionDir = func() string { return tempDir }
defer func() { getExtensionDir = oldGetExtensionDir }()

// Create a mock extension
mockExtName := "mock-extension"
mockExtDir := filepath.Join(tempDir, "mycli-"+mockExtName)
mockExtPath := filepath.Join(mockExtDir, "mycli-"+mockExtName)
err = os.MkdirAll(mockExtDir, 0755)
assert.NoError(t, err)

// Create a mock executable
mockExtContent := []byte("#!/bin/sh\necho \"Mock extension executed with args: $@\"")
err = os.WriteFile(mockExtPath, mockExtContent, 0755)
assert.NoError(t, err)

// Test cases
testCases := []struct {
name string
extName string
args []string
expectedError bool
expectedOutput string
}{
{
name: "Existing extension",
extName: mockExtName,
args: []string{"arg1", "arg2"},
expectedError: false,
expectedOutput: "Mock extension executed with args: arg1 arg2\n",
},
{
name: "Non-existent extension",
extName: "non-existent",
args: []string{},
expectedError: true,
expectedOutput: "",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

err := runExtension(tc.extName, tc.args)

// Restore stdout
w.Close()
os.Stdout = oldStdout

// Read captured output
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
output := buf.String()

if tc.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tc.expectedOutput, output)
}
})
}
}

func TestNewExtensionRunCmd(t *testing.T) {
// Create a temporary directory to simulate the extensions directory
tempDir, err := os.MkdirTemp("", "mycli-test-extensions")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)

// Mock the getExtensionDir function
oldGetExtensionDir := getExtensionDir
getExtensionDir = func() string { return tempDir }
defer func() { getExtensionDir = oldGetExtensionDir }()

// Create a mock extension
mockExtName := "mock-extension"
mockExtDir := filepath.Join(tempDir, "mycli-"+mockExtName)
mockExtPath := filepath.Join(mockExtDir, "mycli-"+mockExtName)
err = os.MkdirAll(mockExtDir, 0755)
assert.NoError(t, err)

// Create a mock executable
mockExtContent := []byte("#!/bin/sh\necho \"Mock extension executed with args: $@\"")
err = os.WriteFile(mockExtPath, mockExtContent, 0755)
assert.NoError(t, err)

// Create the run command
runCmd := newExtensionRunCmd()

testCases := []struct {
name string
args []string
expectedError bool
expectedOutput string
expectedErrMsg string
}{
{
name: "Run existing extension",
args: []string{mockExtName, "arg1", "arg2"},
expectedError: false,
expectedOutput: "Mock extension executed with args: arg1 arg2\n",
},
{
name: "Run non-existent extension",
args: []string{"non-existent"},
expectedError: true,
expectedErrMsg: "extension 'non-existent' not found",
},
{
name: "No arguments",
args: []string{},
expectedError: true,
expectedErrMsg: "extension name is required",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

// Execute the command
cmd := &cobra.Command{}
cmd.SetArgs(tc.args)
err := runCmd.RunE(cmd, tc.args)

// Restore stdout
w.Close()
os.Stdout = oldStdout

// Read captured output
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
output := buf.String()

if tc.expectedError {
assert.Error(t, err)
if tc.expectedErrMsg != "" {
assert.Contains(t, err.Error(), tc.expectedErrMsg)
}
} else {
assert.NoError(t, err)
assert.Equal(t, tc.expectedOutput, output)
}
})
}
}

// TestHelperProcess isn't a real test. It's used to mock exec.Command in the main test.
func TestHelperProcess(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
Expand Down

0 comments on commit 7daa328

Please sign in to comment.