Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions cmd/plugins/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package plugins

import (
"fmt"
"os"
"text/tabwriter"

"github.com/spf13/cobra"
)

// NewPluginsCommand creates the plugins management command
func NewPluginsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "plugins",
Short: "Manage HyperShift CLI plugins",
Long: `Enable, disable, and list available HyperShift CLI plugins.`,
}

cmd.AddCommand(newListCommand())
cmd.AddCommand(newEnableCommand())
cmd.AddCommand(newDisableCommand())

return cmd
}

func newListCommand() *cobra.Command {
return &cobra.Command{
Use: "list",
Short: "List all available plugins",
RunE: func(cmd *cobra.Command, args []string) error {
return listPlugins()
},
}
}

func newEnableCommand() *cobra.Command {
return &cobra.Command{
Use: "enable <plugin-name>",
Short: "Enable a plugin",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return enablePlugin(args[0])
},
}
}

func newDisableCommand() *cobra.Command {
return &cobra.Command{
Use: "disable <plugin-name>",
Short: "Disable a plugin",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return disablePlugin(args[0])
},
}
}

func listPlugins() error {
status := GetPluginStatus()

if len(status) == 0 {
fmt.Println("No plugins available")
return nil
}

w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NAME\tSTATUS\tDESCRIPTION")

for _, plugin := range status {
status := "disabled"
if plugin.Enabled {
status = "enabled"
}
fmt.Fprintf(w, "%s\t%s\t%s\n", plugin.Name, status, plugin.Description)
}

return w.Flush()
}

// verifyPluginExists checks if a plugin with the given name is registered
func verifyPluginExists(pluginName string) error {
for _, plugin := range GetAllPlugins() {
if plugin.Name() == pluginName {
return nil
}
}
return fmt.Errorf("plugin '%s' not found", pluginName)
}

func enablePlugin(pluginName string) error {
if err := verifyPluginExists(pluginName); err != nil {
return err
}

if err := SetPluginEnabled(pluginName, true); err != nil {
return fmt.Errorf("failed to enable plugin '%s': %w", pluginName, err)
}

fmt.Printf("Plugin '%s' enabled successfully\n", pluginName)
return nil
}

func disablePlugin(pluginName string) error {
if err := verifyPluginExists(pluginName); err != nil {
return err
}

if err := SetPluginEnabled(pluginName, false); err != nil {
return fmt.Errorf("failed to disable plugin '%s': %w", pluginName, err)
}

fmt.Printf("Plugin '%s' disabled successfully\n", pluginName)
return nil
}

// ListPlugins lists all available plugins with their status
func ListPlugins() error {
return listPlugins()
}

// EnablePlugin enables a plugin by name and saves the configuration
func EnablePlugin(pluginName string) error {
return enablePlugin(pluginName)
}

// DisablePlugin disables a plugin by name and saves the configuration
func DisablePlugin(pluginName string) error {
return disablePlugin(pluginName)
}
224 changes: 224 additions & 0 deletions cmd/plugins/command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package plugins

import (
"path/filepath"
"testing"

. "github.com/onsi/gomega"
)

func TestNewPluginsCommand(t *testing.T) {
g := NewWithT(t)

cmd := NewPluginsCommand()

g.Expect(cmd.Use).To(Equal("plugins"))
g.Expect(cmd.Short).To(Equal("Manage HyperShift CLI plugins"))
g.Expect(cmd.Long).To(ContainSubstring("Enable, disable, and list available HyperShift CLI plugins"))

// Verify subcommands
subcommands := cmd.Commands()
g.Expect(subcommands).To(HaveLen(3))

subcommandNames := make([]string, len(subcommands))
for i, subcmd := range subcommands {
subcommandNames[i] = subcmd.Use
}

g.Expect(subcommandNames).To(ContainElement("list"))
g.Expect(subcommandNames).To(ContainElement("enable <plugin-name>"))
g.Expect(subcommandNames).To(ContainElement("disable <plugin-name>"))
}

func TestListPlugins_NoPlugins(t *testing.T) {
g := NewWithT(t)

// Backup original state
originalPlugins := make([]Plugin, len(registeredPlugins))
copy(originalPlugins, registeredPlugins)
defer func() {
registeredPlugins = originalPlugins
}()

// Setup: empty registry
registeredPlugins = []Plugin{}

err := listPlugins()
g.Expect(err).NotTo(HaveOccurred())
}

func TestEnablePlugin_Success(t *testing.T) {
g := NewWithT(t)

// Backup original state
originalPlugins := make([]Plugin, len(registeredPlugins))
copy(originalPlugins, registeredPlugins)
defer func() {
registeredPlugins = originalPlugins
}()

// Setup: temporary directory for configuration
tempDir := t.TempDir()
configPath := filepath.Join(tempDir, "config.yaml")

originalGetConfigPath := getConfigPath
defer func() {
getConfigPath = func() string { return originalGetConfigPath() }
}()

getConfigPath = func() string { return configPath }

// Reset global config
config = nil

// Setup: add test plugin
registeredPlugins = []Plugin{
&mockPlugin{name: "test-plugin", description: "Test Plugin"},
}

err := enablePlugin("test-plugin")
g.Expect(err).NotTo(HaveOccurred())

// Verify that the plugin is enabled in configuration
g.Expect(IsPluginEnabled("test-plugin")).To(BeTrue())
}

func TestEnablePlugin_PluginNotFound(t *testing.T) {
g := NewWithT(t)

// Backup original state
originalPlugins := make([]Plugin, len(registeredPlugins))
copy(originalPlugins, registeredPlugins)
defer func() {
registeredPlugins = originalPlugins
}()

// Setup: empty registry
registeredPlugins = []Plugin{}

err := enablePlugin("nonexistent-plugin")
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(ContainSubstring("plugin 'nonexistent-plugin' not found"))
}

func TestDisablePlugin_Success(t *testing.T) {
g := NewWithT(t)

// Backup original state
originalPlugins := make([]Plugin, len(registeredPlugins))
copy(originalPlugins, registeredPlugins)
defer func() {
registeredPlugins = originalPlugins
}()

// Setup: temporary directory for configuration
tempDir := t.TempDir()
configPath := filepath.Join(tempDir, "config.yaml")

originalGetConfigPath := getConfigPath
defer func() {
getConfigPath = func() string { return originalGetConfigPath() }
}()

getConfigPath = func() string { return configPath }

// Reset global config
config = nil

// Setup: add test plugin
registeredPlugins = []Plugin{
&mockPlugin{name: "test-plugin", description: "Test Plugin"},
}

// First enable the plugin
err := SetPluginEnabled("test-plugin", true)
g.Expect(err).NotTo(HaveOccurred())

err = disablePlugin("test-plugin")
g.Expect(err).NotTo(HaveOccurred())

// Verify that the plugin is disabled in configuration
g.Expect(IsPluginEnabled("test-plugin")).To(BeFalse())
}

func TestDisablePlugin_PluginNotFound(t *testing.T) {
g := NewWithT(t)

// Backup original state
originalPlugins := make([]Plugin, len(registeredPlugins))
copy(originalPlugins, registeredPlugins)
defer func() {
registeredPlugins = originalPlugins
}()

// Setup: empty registry
registeredPlugins = []Plugin{}

err := disablePlugin("nonexistent-plugin")
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(ContainSubstring("plugin 'nonexistent-plugin' not found"))
}

func TestVerifyPluginExists_PluginExists(t *testing.T) {
g := NewWithT(t)

// Backup original state
originalPlugins := make([]Plugin, len(registeredPlugins))
copy(originalPlugins, registeredPlugins)
defer func() {
registeredPlugins = originalPlugins
}()

// Setup: add test plugin
registeredPlugins = []Plugin{
&mockPlugin{name: "test-plugin", description: "Test Plugin"},
}

err := verifyPluginExists("test-plugin")
g.Expect(err).NotTo(HaveOccurred())
}

func TestVerifyPluginExists_PluginNotFound(t *testing.T) {
g := NewWithT(t)

// Backup original state
originalPlugins := make([]Plugin, len(registeredPlugins))
copy(originalPlugins, registeredPlugins)
defer func() {
registeredPlugins = originalPlugins
}()

// Setup: empty registry
registeredPlugins = []Plugin{}

err := verifyPluginExists("nonexistent-plugin")
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(Equal("plugin 'nonexistent-plugin' not found"))
}

func TestVerifyPluginExists_MultiplePlugins(t *testing.T) {
g := NewWithT(t)

// Backup original state
originalPlugins := make([]Plugin, len(registeredPlugins))
copy(originalPlugins, registeredPlugins)
defer func() {
registeredPlugins = originalPlugins
}()

// Setup: add multiple test plugins
registeredPlugins = []Plugin{
&mockPlugin{name: "plugin-one", description: "First Plugin"},
&mockPlugin{name: "plugin-two", description: "Second Plugin"},
&mockPlugin{name: "plugin-three", description: "Third Plugin"},
}

// Test existing plugin
err := verifyPluginExists("plugin-two")
g.Expect(err).NotTo(HaveOccurred())

// Test non-existing plugin
err = verifyPluginExists("plugin-four")
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(Equal("plugin 'plugin-four' not found"))
}
Loading