Files
Yeuoly d02885674a feat: add repository URL support for plugin manifest (#262)
* feat: add repository URL support for plugin manifest

- Introduced a new optional flag for specifying the plugin repository URL during initialization.
- Updated the InitPluginWithFlags function to handle the new repository parameter.
- Enhanced profile management to include repository input.
- Modified related tests to validate the new repository functionality.

* fix: improve input validation in profile checkRule method

- Updated the checkRule method to ensure cursor is within valid range before checking for empty input values. This change prevents potential out-of-bounds errors and enhances the robustness of the input validation process.
2025-05-07 18:35:38 +08:00

356 lines
9.2 KiB
Go

package plugin
import (
"os"
"testing"
"github.com/langgenius/dify-plugin-daemon/pkg/plugin_packager/decoder"
"github.com/stretchr/testify/assert"
)
func TestInitPluginWithFlags(t *testing.T) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "plugin-test-*")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
// Change to the temporary directory
oldDir, err := os.Getwd()
assert.NoError(t, err)
defer os.Chdir(oldDir)
err = os.Chdir(tempDir)
assert.NoError(t, err)
tests := []struct {
testName string
author string
pluginName string
repo string
description string
quick bool
permissions struct {
endpoint bool
tool bool
model bool
llm bool
textEmbedding bool
rerank bool
tts bool
speech2text bool
moderation bool
node bool
app bool
storage bool
}
expectedFiles []string
}{
{
testName: "Quick mode with minimal permissions",
author: "test-author",
pluginName: "test-plugin",
repo: "https://github.com/langgenius/dify-official-plugins",
description: "Test plugin description",
quick: true,
permissions: struct {
endpoint bool
tool bool
model bool
llm bool
textEmbedding bool
rerank bool
tts bool
speech2text bool
moderation bool
node bool
app bool
storage bool
}{
endpoint: false,
tool: false,
model: false,
},
expectedFiles: []string{
"test-plugin/manifest.yaml",
"test-plugin/_assets/icon.svg",
"test-plugin/README.md",
"test-plugin/.env.example",
"test-plugin/PRIVACY.md",
"test-plugin/.github/workflows/plugin-publish.yml",
},
},
{
testName: "Quick mode with all permissions",
author: "test-author",
pluginName: "test-plugin-full",
repo: "https://github.com/langgenius/dify-official-plugins",
description: "Test plugin with all permissions",
quick: true,
permissions: struct {
endpoint bool
tool bool
model bool
llm bool
textEmbedding bool
rerank bool
tts bool
speech2text bool
moderation bool
node bool
app bool
storage bool
}{
endpoint: true,
tool: true,
model: true,
llm: true,
textEmbedding: true,
rerank: true,
tts: true,
speech2text: true,
moderation: true,
node: true,
app: true,
storage: true,
},
expectedFiles: []string{
"test-plugin-full/manifest.yaml",
"test-plugin-full/_assets/icon.svg",
"test-plugin-full/README.md",
"test-plugin-full/.env.example",
"test-plugin-full/PRIVACY.md",
"test-plugin-full/.github/workflows/plugin-publish.yml",
},
},
{
testName: "Non-quick mode with tool permissions",
author: "test-author",
pluginName: "test-plugin-tool",
repo: "https://github.com/langgenius/dify-official-plugins",
description: "Test plugin with tool permissions",
quick: false,
permissions: struct {
endpoint bool
tool bool
model bool
llm bool
textEmbedding bool
rerank bool
tts bool
speech2text bool
moderation bool
node bool
app bool
storage bool
}{
tool: true,
},
expectedFiles: []string{
"test-plugin-tool/manifest.yaml",
"test-plugin-tool/_assets/icon.svg",
"test-plugin-tool/README.md",
"test-plugin-tool/.env.example",
"test-plugin-tool/PRIVACY.md",
"test-plugin-tool/.github/workflows/plugin-publish.yml",
},
},
}
for _, tt := range tests {
t.Run(tt.testName, func(t *testing.T) {
// Call InitPluginWithFlags with test parameters
InitPluginWithFlags(
tt.author,
tt.pluginName,
tt.repo,
tt.description,
tt.permissions.endpoint,
tt.permissions.tool,
tt.permissions.model,
tt.permissions.llm,
tt.permissions.textEmbedding,
tt.permissions.rerank,
tt.permissions.tts,
tt.permissions.speech2text,
tt.permissions.moderation,
tt.permissions.node,
tt.permissions.app,
tt.permissions.storage,
1024*1024, // 1MB storage
"tool", // category
"python", // language
"0.0.1", // min dify version
true, // always use quick mode
)
// Verify that all expected files exist
for _, file := range tt.expectedFiles {
_, err := os.Stat(file)
assert.NoError(t, err, "Expected file %s to exist", file)
}
// If in quick mode, verify the manifest content
if tt.quick {
// Create a decoder for the plugin directory
decoder, err := decoder.NewFSPluginDecoder(tt.pluginName)
assert.NoError(t, err)
defer decoder.Close()
// Get the manifest
manifest, err := decoder.Manifest()
assert.NoError(t, err)
// Verify basic information
assert.Equal(t, tt.author, manifest.Author)
assert.Equal(t, tt.pluginName, manifest.Name)
assert.Equal(t, tt.description, manifest.Description.EnUS)
// Verify permissions
if tt.permissions.endpoint {
assert.True(t, manifest.Resource.Permission.Endpoint.Enabled)
}
if tt.permissions.tool {
assert.True(t, manifest.Resource.Permission.Tool.Enabled)
}
if tt.permissions.model {
assert.True(t, manifest.Resource.Permission.Model.Enabled)
if tt.permissions.llm {
assert.True(t, manifest.Resource.Permission.Model.LLM)
}
if tt.permissions.textEmbedding {
assert.True(t, manifest.Resource.Permission.Model.TextEmbedding)
}
if tt.permissions.rerank {
assert.True(t, manifest.Resource.Permission.Model.Rerank)
}
if tt.permissions.tts {
assert.True(t, manifest.Resource.Permission.Model.TTS)
}
if tt.permissions.speech2text {
assert.True(t, manifest.Resource.Permission.Model.Speech2text)
}
if tt.permissions.moderation {
assert.True(t, manifest.Resource.Permission.Model.Moderation)
}
}
if tt.permissions.node {
assert.True(t, manifest.Resource.Permission.Node.Enabled)
}
if tt.permissions.app {
assert.True(t, manifest.Resource.Permission.App.Enabled)
}
if tt.permissions.storage {
assert.True(t, manifest.Resource.Permission.Storage.Enabled)
assert.Equal(t, uint64(1024*1024), manifest.Resource.Permission.Storage.Size)
}
// Check assets validity
err = decoder.CheckAssetsValid()
assert.NoError(t, err)
// Get unique identity
identity, err := decoder.UniqueIdentity()
assert.NoError(t, err)
assert.NotEmpty(t, identity)
// Get checksum
checksum, err := decoder.Checksum()
assert.NoError(t, err)
assert.NotEmpty(t, checksum)
}
})
}
}
func TestInitPluginWithFlagsValidation(t *testing.T) {
tests := []struct {
name string
author string
pluginName string
repo string
description string
expectError bool
}{
{
name: "Valid inputs",
author: "test-author",
pluginName: "test-plugin",
repo: "https://github.com/langgenius/dify-official-plugins",
description: "Test description",
expectError: false,
},
{
name: "Invalid author (uppercase)",
author: "Test-Author",
pluginName: "test-plugin",
repo: "https://github.com/langgenius/dify-official-plugins",
description: "Test description",
expectError: true,
},
{
name: "Invalid plugin name (uppercase)",
author: "test-author",
pluginName: "Test-Plugin",
repo: "https://github.com/langgenius/dify-official-plugins",
description: "Test description",
expectError: true,
},
{
name: "Empty description",
author: "test-author",
pluginName: "test-plugin",
repo: "https://github.com/langgenius/dify-official-plugins",
description: "",
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "plugin-test-*")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
// Change to the temporary directory
oldDir, err := os.Getwd()
assert.NoError(t, err)
defer os.Chdir(oldDir)
err = os.Chdir(tempDir)
assert.NoError(t, err)
// Call InitPluginWithFlags
InitPluginWithFlags(
tt.author,
tt.pluginName,
tt.repo,
tt.description,
false, // allowRegisterEndpoint
false, // allowInvokeTool
false, // allowInvokeModel
false, // allowInvokeLLM
false, // allowInvokeTextEmbedding
false, // allowInvokeRerank
false, // allowInvokeTTS
false, // allowInvokeSpeech2Text
false, // allowInvokeModeration
false, // allowInvokeNode
false, // allowInvokeApp
false, // allowUseStorage
0, // storageSize
"", // category
"", // language
"", // minDifyVersion
true, // quick
)
// Check if the plugin directory was created
_, err = os.Stat(tt.pluginName)
if tt.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}