Some checks failed
Pipeline: Test, Lint, Build / Get version info (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test JS code (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint i18n files (push) Has been cancelled
Pipeline: Test, Lint, Build / Check Docker configuration (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v5) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v6) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v7) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to GHCR (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to Docker Hub (push) Has been cancelled
Pipeline: Test, Lint, Build / Cleanup digest artifacts (push) Has been cancelled
Pipeline: Test, Lint, Build / Build Windows installers (push) Has been cancelled
Pipeline: Test, Lint, Build / Package/Release (push) Has been cancelled
Pipeline: Test, Lint, Build / Upload Linux PKG (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
POEditor import / update-translations (push) Has been cancelled
194 lines
5.7 KiB
Go
194 lines
5.7 KiB
Go
package cmd
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/navidrome/navidrome/conf"
|
|
"github.com/navidrome/navidrome/conf/configtest"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var _ = Describe("Plugin CLI Commands", func() {
|
|
var tempDir string
|
|
var cmd *cobra.Command
|
|
var stdOut *os.File
|
|
var origStdout *os.File
|
|
var outReader *os.File
|
|
|
|
// Helper to create a test plugin with the given name and details
|
|
createTestPlugin := func(name, author, version string, capabilities []string) string {
|
|
pluginDir := filepath.Join(tempDir, name)
|
|
Expect(os.MkdirAll(pluginDir, 0755)).To(Succeed())
|
|
|
|
// Create a properly formatted capabilities JSON array
|
|
capabilitiesJSON := `"` + strings.Join(capabilities, `", "`) + `"`
|
|
|
|
manifest := `{
|
|
"name": "` + name + `",
|
|
"author": "` + author + `",
|
|
"version": "` + version + `",
|
|
"description": "Plugin for testing",
|
|
"website": "https://test.navidrome.org/` + name + `",
|
|
"capabilities": [` + capabilitiesJSON + `],
|
|
"permissions": {}
|
|
}`
|
|
|
|
Expect(os.WriteFile(filepath.Join(pluginDir, "manifest.json"), []byte(manifest), 0600)).To(Succeed())
|
|
|
|
// Create a dummy WASM file
|
|
wasmContent := []byte("dummy wasm content for testing")
|
|
Expect(os.WriteFile(filepath.Join(pluginDir, "plugin.wasm"), wasmContent, 0600)).To(Succeed())
|
|
|
|
return pluginDir
|
|
}
|
|
|
|
// Helper to execute a command and return captured output
|
|
captureOutput := func(reader io.Reader) string {
|
|
stdOut.Close()
|
|
outputBytes, err := io.ReadAll(reader)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
return string(outputBytes)
|
|
}
|
|
|
|
BeforeEach(func() {
|
|
DeferCleanup(configtest.SetupConfig())
|
|
tempDir = GinkgoT().TempDir()
|
|
|
|
// Setup config
|
|
conf.Server.Plugins.Enabled = true
|
|
conf.Server.Plugins.Folder = tempDir
|
|
|
|
// Create a command for testing
|
|
cmd = &cobra.Command{Use: "test"}
|
|
|
|
// Setup stdout capture
|
|
origStdout = os.Stdout
|
|
var err error
|
|
outReader, stdOut, err = os.Pipe()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
os.Stdout = stdOut
|
|
|
|
DeferCleanup(func() {
|
|
os.Stdout = origStdout
|
|
})
|
|
})
|
|
|
|
AfterEach(func() {
|
|
os.Stdout = origStdout
|
|
if stdOut != nil {
|
|
stdOut.Close()
|
|
}
|
|
if outReader != nil {
|
|
outReader.Close()
|
|
}
|
|
})
|
|
|
|
Describe("Plugin list command", func() {
|
|
It("should list installed plugins", func() {
|
|
// Create test plugins
|
|
createTestPlugin("plugin1", "Test Author", "1.0.0", []string{"MetadataAgent"})
|
|
createTestPlugin("plugin2", "Another Author", "2.1.0", []string{"Scrobbler"})
|
|
|
|
// Execute command
|
|
pluginList(cmd, []string{})
|
|
|
|
// Verify output
|
|
output := captureOutput(outReader)
|
|
|
|
Expect(output).To(ContainSubstring("plugin1"))
|
|
Expect(output).To(ContainSubstring("Test Author"))
|
|
Expect(output).To(ContainSubstring("1.0.0"))
|
|
Expect(output).To(ContainSubstring("MetadataAgent"))
|
|
|
|
Expect(output).To(ContainSubstring("plugin2"))
|
|
Expect(output).To(ContainSubstring("Another Author"))
|
|
Expect(output).To(ContainSubstring("2.1.0"))
|
|
Expect(output).To(ContainSubstring("Scrobbler"))
|
|
})
|
|
})
|
|
|
|
Describe("Plugin info command", func() {
|
|
It("should display information about an installed plugin", func() {
|
|
// Create test plugin with multiple capabilities
|
|
createTestPlugin("test-plugin", "Test Author", "1.0.0",
|
|
[]string{"MetadataAgent", "Scrobbler"})
|
|
|
|
// Execute command
|
|
pluginInfo(cmd, []string{"test-plugin"})
|
|
|
|
// Verify output
|
|
output := captureOutput(outReader)
|
|
|
|
Expect(output).To(ContainSubstring("Name: test-plugin"))
|
|
Expect(output).To(ContainSubstring("Author: Test Author"))
|
|
Expect(output).To(ContainSubstring("Version: 1.0.0"))
|
|
Expect(output).To(ContainSubstring("Description: Plugin for testing"))
|
|
Expect(output).To(ContainSubstring("Capabilities: MetadataAgent, Scrobbler"))
|
|
})
|
|
})
|
|
|
|
Describe("Plugin remove command", func() {
|
|
It("should remove a regular plugin directory", func() {
|
|
// Create test plugin
|
|
pluginDir := createTestPlugin("regular-plugin", "Test Author", "1.0.0",
|
|
[]string{"MetadataAgent"})
|
|
|
|
// Execute command
|
|
pluginRemove(cmd, []string{"regular-plugin"})
|
|
|
|
// Verify output
|
|
output := captureOutput(outReader)
|
|
Expect(output).To(ContainSubstring("Plugin 'regular-plugin' removed successfully"))
|
|
|
|
// Verify directory is actually removed
|
|
_, err := os.Stat(pluginDir)
|
|
Expect(os.IsNotExist(err)).To(BeTrue())
|
|
})
|
|
|
|
It("should remove only the symlink for a development plugin", func() {
|
|
// Create a real source directory
|
|
sourceDir := filepath.Join(GinkgoT().TempDir(), "dev-plugin-source")
|
|
Expect(os.MkdirAll(sourceDir, 0755)).To(Succeed())
|
|
|
|
manifest := `{
|
|
"name": "dev-plugin",
|
|
"author": "Dev Author",
|
|
"version": "0.1.0",
|
|
"description": "Development plugin for testing",
|
|
"website": "https://test.navidrome.org/dev-plugin",
|
|
"capabilities": ["Scrobbler"],
|
|
"permissions": {}
|
|
}`
|
|
Expect(os.WriteFile(filepath.Join(sourceDir, "manifest.json"), []byte(manifest), 0600)).To(Succeed())
|
|
|
|
// Create a dummy WASM file
|
|
wasmContent := []byte("dummy wasm content for testing")
|
|
Expect(os.WriteFile(filepath.Join(sourceDir, "plugin.wasm"), wasmContent, 0600)).To(Succeed())
|
|
|
|
// Create a symlink in the plugins directory
|
|
symlinkPath := filepath.Join(tempDir, "dev-plugin")
|
|
Expect(os.Symlink(sourceDir, symlinkPath)).To(Succeed())
|
|
|
|
// Execute command
|
|
pluginRemove(cmd, []string{"dev-plugin"})
|
|
|
|
// Verify output
|
|
output := captureOutput(outReader)
|
|
Expect(output).To(ContainSubstring("Development plugin symlink 'dev-plugin' removed successfully"))
|
|
Expect(output).To(ContainSubstring("target directory preserved"))
|
|
|
|
// Verify the symlink is removed but source directory exists
|
|
_, err := os.Lstat(symlinkPath)
|
|
Expect(os.IsNotExist(err)).To(BeTrue())
|
|
|
|
_, err = os.Stat(sourceDir)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
})
|
|
})
|