Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
test(plugin): add comprehensive plugin package tests
Add tests for Load, FindAll and plugin Exec functionality. Includes test
coverage for plugin loading, discovery and command execution.

Signed-off-by: Ville Vesilehto <[email protected]>
  • Loading branch information
thevilledev committed Feb 1, 2025
commit 9b1e876f2cefce0e6de63ac3a9828cd302c86b8f
195 changes: 194 additions & 1 deletion plugin/install_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
package plugin

import "testing"
import (
"archive/tar"
"bytes"
"compress/gzip"
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
)

func TestIsDirectory(t *testing.T) {
testCases := []struct {
Expand Down Expand Up @@ -29,3 +41,184 @@ func TestIsDirectory(t *testing.T) {
}
}
}

func TestInstall(t *testing.T) {
tests := []struct {
name string
setup func(t *testing.T) (source string, wantPluginName string)
wantErr bool
wantErrSubstring string
}{
{
name: "install from directory",
setup: func(t *testing.T) (string, string) {
pluginDir := createInstallTestPlugin(t, "test-plugin")
return pluginDir, "test-plugin"
},
wantErr: false,
},
{
name: "install from valid URL",
setup: func(t *testing.T) (string, string) {
server := createTestArchiveServer(t, "test-plugin")
return fmt.Sprintf("http::%s/plugin.tar.gz?archive=tar.gz", server.URL), "test-plugin"
},
wantErr: false,
},
{
name: "install from invalid directory",
setup: func(_ *testing.T) (string, string) {
return "/nonexistent/directory", ""
},
wantErr: true,
},
{
name: "install from URL with invalid plugin contents",
setup: func(t *testing.T) (string, string) {
server := createInvalidArchiveServer(t)
return fmt.Sprintf("http::%s/invalid.tar.gz?archive=tar.gz", server.URL), ""
},
wantErr: true,
wantErrSubstring: "loading plugin from dir",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("XDG_DATA_HOME", tmpDir)

source, wantPluginName := tt.setup(t)
err := Install(context.Background(), source)

assertError(t, tt.wantErr, tt.wantErrSubstring, err)
assertInstallationResult(t, tmpDir, wantPluginName, tt.wantErr)
})
}
}

// Helper functions
func createInstallTestPlugin(t *testing.T, name string) string {
t.Helper()

pluginDir := filepath.Join(t.TempDir(), name)
if err := os.MkdirAll(pluginDir, 0755); err != nil {
t.Fatal(err)
}

configContent := []byte(fmt.Sprintf(`name: %s
version: 1.0.0
command: test-command`, name))
if err := os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), configContent, 0o600); err != nil {
t.Fatal(err)
}

return pluginDir
}

func createTestArchiveServer(t *testing.T, pluginName string) *httptest.Server {
t.Helper()

var buf bytes.Buffer
gw := gzip.NewWriter(&buf)
tw := tar.NewWriter(gw)

configContent := []byte(fmt.Sprintf(`name: %s
version: 1.0.0
command: test-command`, pluginName))

header := &tar.Header{
Name: "plugin.yaml",
Mode: 0o600,
Size: int64(len(configContent)),
}
if err := tw.WriteHeader(header); err != nil {
t.Fatal(err)
}
if _, err := tw.Write(configContent); err != nil {
t.Fatal(err)
}

if err := tw.Close(); err != nil {
t.Fatal(err)
}
if err := gw.Close(); err != nil {
t.Fatal(err)
}

return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/x-gzip")
_, _ = w.Write(buf.Bytes())
}))
}

func createInvalidArchiveServer(t *testing.T) *httptest.Server {
t.Helper()

var buf bytes.Buffer
gw := gzip.NewWriter(&buf)
tw := tar.NewWriter(gw)

header := &tar.Header{
Name: "dummy.txt",
Mode: 0o600,
Size: 0,
}
if err := tw.WriteHeader(header); err != nil {
t.Fatal(err)
}
if err := tw.Close(); err != nil {
t.Fatal(err)
}
if err := gw.Close(); err != nil {
t.Fatal(err)
}

return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/x-gzip")
_, _ = w.Write(buf.Bytes())
}))
}

func assertError(t *testing.T, wantErr bool, wantMsg string, actual error) {
t.Helper()

if wantErr {
if actual == nil {
t.Fatal("expected error but got none")
}
if wantMsg != "" && !strings.Contains(actual.Error(), wantMsg) {
t.Errorf("error %q should contain %q", actual.Error(), wantMsg)
}
} else if actual != nil {
t.Fatalf("unexpected error: %v", actual)
}
}

func assertInstallationResult(t *testing.T, tmpDir, wantPluginName string, wantErr bool) {
t.Helper()

if wantErr {
assertTempFilesCleaned(t, tmpDir)
return
}

pluginDir := filepath.Join(tmpDir, ".conftest", "plugins", wantPluginName)
if _, err := os.Stat(pluginDir); os.IsNotExist(err) {
t.Errorf("plugin directory %q not found", pluginDir)
}

configPath := filepath.Join(pluginDir, "plugin.yaml")
if _, err := os.Stat(configPath); os.IsNotExist(err) {
t.Error("plugin.yaml not found in plugin directory")
}
}

func assertTempFilesCleaned(t *testing.T, tmpDir string) {
t.Helper()

matches, _ := filepath.Glob(filepath.Join(tmpDir, ".conftest", "plugins", "conftest-plugin-*"))
if len(matches) > 0 {
t.Errorf("temporary directories not cleaned up: %v", matches)
}
}
Loading