- Add PluginCtlConfig struct with IgnoreAssets field for glob patterns - Add ParsePluginCtlConfig function to parse manifest props["pluginctl"] - Update updateassets command to respect ignore patterns with glob matching - Add comprehensive logging when files are skipped due to ignore patterns - Support patterns like *.test.js, build/, node_modules for flexible exclusion - Add extensive tests for config parsing and path matching functionality - Maintain backward compatibility with existing manifests - Fix Makefile check-changes target and add logger init to tests 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
325 lines
7.4 KiB
Go
325 lines
7.4 KiB
Go
package pluginctl
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
)
|
|
|
|
func TestPrintPluginInfo(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
manifest *model.Manifest
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "Complete plugin with all features",
|
|
manifest: &model.Manifest{
|
|
Id: "com.example.testplugin",
|
|
Name: "Test Plugin",
|
|
Version: "1.0.0",
|
|
MinServerVersion: "7.0.0",
|
|
Description: "A test plugin for unit testing",
|
|
Server: &model.ManifestServer{
|
|
Executables: map[string]string{
|
|
"linux-amd64": "server/dist/plugin-linux-amd64",
|
|
"darwin-amd64": "server/dist/plugin-darwin-amd64",
|
|
"windows-amd64": "server/dist/plugin-windows-amd64.exe",
|
|
},
|
|
},
|
|
Webapp: &model.ManifestWebapp{
|
|
BundlePath: "webapp/dist/main.js",
|
|
},
|
|
SettingsSchema: &model.PluginSettingsSchema{
|
|
Header: "Test Settings",
|
|
},
|
|
},
|
|
expected: []string{
|
|
"Plugin Information:",
|
|
"ID: com.example.testplugin",
|
|
"Name: Test Plugin",
|
|
"Version: 1.0.0",
|
|
"Min MM Version: 7.0.0",
|
|
"Description: A test plugin for unit testing",
|
|
"Code Components:",
|
|
"Server Code: Yes",
|
|
"linux-amd64",
|
|
"darwin-amd64",
|
|
"windows-amd64",
|
|
"Webapp Code: Yes",
|
|
"Bundle Path: webapp/dist/main.js",
|
|
"Settings Schema: Yes",
|
|
},
|
|
},
|
|
{
|
|
name: "Minimal plugin with no optional fields",
|
|
manifest: &model.Manifest{
|
|
Id: "com.example.minimal",
|
|
Name: "Minimal Plugin",
|
|
Version: "0.1.0",
|
|
},
|
|
expected: []string{
|
|
"Plugin Information:",
|
|
"ID: com.example.minimal",
|
|
"Name: Minimal Plugin",
|
|
"Version: 0.1.0",
|
|
"Min MM Version: Not specified",
|
|
"Code Components:",
|
|
"Server Code: No",
|
|
"Webapp Code: No",
|
|
"Settings Schema: No",
|
|
},
|
|
},
|
|
{
|
|
name: "Plugin with server code only",
|
|
manifest: &model.Manifest{
|
|
Id: "com.example.serveronly",
|
|
Name: "Server Only Plugin",
|
|
Version: "2.0.0",
|
|
MinServerVersion: "8.0.0",
|
|
Server: &model.ManifestServer{
|
|
Executables: map[string]string{
|
|
"linux-amd64": "server/plugin",
|
|
},
|
|
},
|
|
},
|
|
expected: []string{
|
|
"Plugin Information:",
|
|
"ID: com.example.serveronly",
|
|
"Name: Server Only Plugin",
|
|
"Version: 2.0.0",
|
|
"Min MM Version: 8.0.0",
|
|
"Server Code: Yes",
|
|
"linux-amd64",
|
|
"Webapp Code: No",
|
|
"Settings Schema: No",
|
|
},
|
|
},
|
|
{
|
|
name: "Plugin with webapp code only",
|
|
manifest: &model.Manifest{
|
|
Id: "com.example.webapponly",
|
|
Name: "Webapp Only Plugin",
|
|
Version: "1.5.0",
|
|
Webapp: &model.ManifestWebapp{
|
|
BundlePath: "dist/bundle.js",
|
|
},
|
|
},
|
|
expected: []string{
|
|
"Plugin Information:",
|
|
"ID: com.example.webapponly",
|
|
"Name: Webapp Only Plugin",
|
|
"Version: 1.5.0",
|
|
"Min MM Version: Not specified",
|
|
"Server Code: No",
|
|
"Webapp Code: Yes",
|
|
"Bundle Path: dist/bundle.js",
|
|
"Settings Schema: No",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Initialize logger for testing
|
|
InitLogger()
|
|
|
|
// Capture stdout
|
|
oldStdout := os.Stdout
|
|
r, w, _ := os.Pipe()
|
|
os.Stdout = w
|
|
|
|
// Run the function
|
|
err := PrintPluginInfo(tt.manifest)
|
|
if err != nil {
|
|
t.Fatalf("PrintPluginInfo returned error: %v", err)
|
|
}
|
|
|
|
// Restore stdout and get output
|
|
w.Close()
|
|
os.Stdout = oldStdout
|
|
|
|
output, _ := io.ReadAll(r)
|
|
outputStr := string(output)
|
|
|
|
// Check that all expected strings are present
|
|
for _, expected := range tt.expected {
|
|
if !strings.Contains(outputStr, expected) {
|
|
t.Errorf("Expected output to contain %q, but it didn't.\nOutput:\n%s", expected, outputStr)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHasServerCode(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
manifest *model.Manifest
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "Plugin with server executables",
|
|
manifest: &model.Manifest{
|
|
Server: &model.ManifestServer{
|
|
Executables: map[string]string{
|
|
"linux-amd64": "server/plugin",
|
|
},
|
|
},
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Plugin with empty server executables",
|
|
manifest: &model.Manifest{
|
|
Server: &model.ManifestServer{
|
|
Executables: map[string]string{},
|
|
},
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Plugin with nil server",
|
|
manifest: &model.Manifest{
|
|
Server: nil,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Plugin with no server field",
|
|
manifest: &model.Manifest{},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := HasServerCode(tt.manifest)
|
|
if result != tt.expected {
|
|
t.Errorf("hasServerCode() = %v, expected %v", result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHasWebappCode(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
manifest *model.Manifest
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "Plugin with webapp bundle",
|
|
manifest: &model.Manifest{
|
|
Webapp: &model.ManifestWebapp{
|
|
BundlePath: "webapp/dist/main.js",
|
|
},
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Plugin with empty webapp bundle path",
|
|
manifest: &model.Manifest{
|
|
Webapp: &model.ManifestWebapp{
|
|
BundlePath: "",
|
|
},
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Plugin with nil webapp",
|
|
manifest: &model.Manifest{
|
|
Webapp: nil,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Plugin with no webapp field",
|
|
manifest: &model.Manifest{},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := HasWebappCode(tt.manifest)
|
|
if result != tt.expected {
|
|
t.Errorf("hasWebappCode() = %v, expected %v", result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInfoCommandWithPath_InvalidPath(t *testing.T) {
|
|
// Test with non-existent directory
|
|
err := InfoCommandWithPath("/non/existent/path")
|
|
if err == nil {
|
|
t.Error("Expected error for non-existent path, but got nil")
|
|
}
|
|
|
|
expectedErrMsg := "plugin.json not found"
|
|
if !strings.Contains(err.Error(), expectedErrMsg) {
|
|
t.Errorf("Expected error to contain %q, but got: %v", expectedErrMsg, err)
|
|
}
|
|
}
|
|
|
|
// captureOutput captures stdout during function execution
|
|
func captureOutput(fn func()) string {
|
|
oldStdout := os.Stdout
|
|
r, w, _ := os.Pipe()
|
|
os.Stdout = w
|
|
|
|
fn()
|
|
|
|
w.Close()
|
|
os.Stdout = oldStdout
|
|
|
|
var buf bytes.Buffer
|
|
io.Copy(&buf, r)
|
|
return buf.String()
|
|
}
|
|
|
|
func TestPrintPluginInfo_OutputFormat(t *testing.T) {
|
|
manifest := &model.Manifest{
|
|
Id: "test.plugin",
|
|
Name: "Test Plugin",
|
|
Version: "1.0.0",
|
|
}
|
|
|
|
output := captureOutput(func() {
|
|
PrintPluginInfo(manifest)
|
|
})
|
|
|
|
// Check for proper formatting
|
|
|
|
// Should have header separators
|
|
if !strings.Contains(output, "==================") {
|
|
t.Error("Output should contain header separators")
|
|
}
|
|
|
|
// Should have proper sections
|
|
if !strings.Contains(output, "Plugin Information:") {
|
|
t.Error("Output should contain 'Plugin Information:' header")
|
|
}
|
|
|
|
if !strings.Contains(output, "Code Components:") {
|
|
t.Error("Output should contain 'Code Components:' header")
|
|
}
|
|
|
|
// Should have proper field formatting
|
|
expectedFields := []string{
|
|
"ID: test.plugin",
|
|
"Name: Test Plugin",
|
|
"Version: 1.0.0",
|
|
}
|
|
|
|
for _, field := range expectedFields {
|
|
if !strings.Contains(output, field) {
|
|
t.Errorf("Output should contain field: %q", field)
|
|
}
|
|
}
|
|
}
|