mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-10-13 08:50:22 +08:00
158 lines
4.7 KiB
Go
158 lines
4.7 KiB
Go
package host
|
|
|
|
import (
|
|
"context"
|
|
"io/fs"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Tencent/WeKnora/internal/plugin/manifest"
|
|
"github.com/Tencent/WeKnora/internal/plugin/reconcile"
|
|
"github.com/Tencent/WeKnora/pluginsdk/pluginapi"
|
|
)
|
|
|
|
// pythonEcho is the echo plugin written with the Python SDK.
|
|
const pythonEcho = `import os
|
|
from weknora_plugin import Plugin, SearchResult
|
|
|
|
plugin = Plugin(os.environ["WEKNORA_PLUGIN_ID"], os.environ["WEKNORA_PLUGIN_VERSION"])
|
|
|
|
|
|
@plugin.web_search("echo")
|
|
def search(call, q):
|
|
if q.query == "env":
|
|
return [SearchResult(title=",".join(sorted(os.environ)), url="env")]
|
|
if q.query == "crash":
|
|
os._exit(3)
|
|
if q.query == "pid":
|
|
return [SearchResult(title=str(os.getpid()), url="pid")]
|
|
return [SearchResult(title=q.query, url="echo")]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
plugin.serve()
|
|
`
|
|
|
|
// installPython lays out an extracted python package: main.py and the SDK
|
|
// vendored under vendor/, as a plugin author ships it.
|
|
func installPython(t *testing.T) *reconcile.Loaded {
|
|
t.Helper()
|
|
if _, err := exec.LookPath(PythonCommand()); err != nil {
|
|
t.Skipf("%s is not installed", PythonCommand())
|
|
}
|
|
dir := t.TempDir()
|
|
sdk := filepath.Join("..", "..", "..", "pluginsdk", "python", "src", "weknora_plugin")
|
|
err := filepath.WalkDir(sdk, func(p string, d fs.DirEntry, err error) error {
|
|
if err != nil || d.IsDir() || filepath.Ext(p) != ".py" {
|
|
return err
|
|
}
|
|
data, err := os.ReadFile(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
target := filepath.Join(dir, "vendor", "weknora_plugin", filepath.Base(p))
|
|
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(target, data, 0o644)
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "main.py"), []byte(pythonEcho), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m := &manifest.Manifest{
|
|
SchemaVersion: manifest.SchemaVersion, ID: "acme.echo", Version: "1.0.0", APIVersion: pluginapi.APIVersion,
|
|
Name: manifest.Text("Echo", nil), Publisher: manifest.Publisher{ID: "acme"},
|
|
Runtime: manifest.Runtime{Type: manifest.RuntimeHost, Kind: KindPython, Entry: "main.py"},
|
|
Contributes: manifest.Contributions{
|
|
manifest.PointWebSearch: {{ID: "echo", Name: manifest.Text("Echo", nil)}},
|
|
},
|
|
}
|
|
return &reconcile.Loaded{Manifest: m, Dir: dir}
|
|
}
|
|
|
|
func TestHostRunsAPythonPlugin(t *testing.T) {
|
|
fastTimings(t)
|
|
t.Setenv("DB_PASSWORD", "must-not-leak")
|
|
ctx := context.Background()
|
|
m := NewManager()
|
|
rep := &reports{}
|
|
m.SetReporter(rep)
|
|
defer m.Close()
|
|
|
|
l := installPython(t)
|
|
if err := m.Activate(ctx, l); err != nil {
|
|
t.Fatalf("Activate: %v", err)
|
|
}
|
|
if got, err := search(t, m, "hello"); err != nil || got != "hello" {
|
|
t.Fatalf("search = %q, %v", got, err)
|
|
}
|
|
env, err := search(t, m, "env")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(env, "DB_PASSWORD") || !strings.Contains(env, "PYTHONPATH") ||
|
|
!strings.Contains(env, pluginapi.EnvToken) {
|
|
t.Fatalf("plugin environment = %s", env)
|
|
}
|
|
|
|
pid1, _ := search(t, m, "pid")
|
|
_, _ = search(t, m, "crash")
|
|
waitFor(t, "restart", func() bool {
|
|
pid, err := search(t, m, "pid")
|
|
return err == nil && pid != pid1
|
|
})
|
|
if err := m.Deactivate(ctx, "acme.echo"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if entries, _ := os.ReadDir(l.Dir); len(entries) != 2 {
|
|
t.Fatalf("the plugin wrote into its package: %v", entries)
|
|
}
|
|
}
|
|
|
|
func TestEntryName(t *testing.T) {
|
|
py := manifest.Runtime{Kind: KindPython, Entry: "main.py"}
|
|
if got := EntryName(py); got != "main.py" {
|
|
t.Fatalf("python entry = %s", got)
|
|
}
|
|
bin := manifest.Runtime{Kind: KindBinary, Entry: "bin/{os}-{arch}/x"}
|
|
if got := EntryName(bin); strings.Contains(got, "{") {
|
|
t.Fatalf("binary entry = %s", got)
|
|
}
|
|
if !Supported(KindPython) || Supported("node") {
|
|
t.Fatal("this host runs binaries and python")
|
|
}
|
|
}
|
|
|
|
func TestKindsDecideWhereAPluginRuns(t *testing.T) {
|
|
ctx := context.Background()
|
|
l := install(t, "1.0.0", "")
|
|
|
|
embedded := NewManager()
|
|
embedded.SetKinds([]string{KindPython})
|
|
defer embedded.Close()
|
|
if err := embedded.Activate(ctx, l); err != nil || embedded.Local("acme.echo") {
|
|
t.Fatalf("an app node hands other kinds on: %v", err)
|
|
}
|
|
|
|
standalone := NewStandaloneManager([]string{KindPython})
|
|
defer standalone.Close()
|
|
if err := standalone.Activate(ctx, l); err == nil || !strings.Contains(err.Error(), "does not run binary") {
|
|
t.Fatalf("a standalone host refuses other kinds, got %v", err)
|
|
}
|
|
|
|
t.Setenv("WEKNORA_PLUGIN_EMBEDDED_KINDS", "none")
|
|
if got := KindsFromEnv("WEKNORA_PLUGIN_EMBEDDED_KINDS"); len(got) != 0 {
|
|
t.Fatalf("none = %v", got)
|
|
}
|
|
t.Setenv("WEKNORA_PLUGIN_EMBEDDED_KINDS", "binary, node")
|
|
if got := KindsFromEnv("WEKNORA_PLUGIN_EMBEDDED_KINDS"); len(got) != 1 || got[0] != KindBinary {
|
|
t.Fatalf("binary, node = %v", got)
|
|
}
|
|
}
|