mirror of
https://github.com/langgenius/dify-plugin-daemon.git
synced 2026-07-21 17:25:23 -04:00
d02885674a
* 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.
356 lines
9.2 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|