feat(plugins): implement web-search, web-fetch, http and file tools
- web-search: real SearXNG /search?format=json query, top-5 formatted results (was a 'not configured' stub) - web-fetch: fetch page, strip scripts/styles/tags, truncate to 4000 runes for the LLM context - http: generic request tool (GET/POST/PUT/DELETE/PATCH) with scheme and method validation, status + truncated body - file: sandboxed read/write/list inside the data directory with path escape rejection; split into file_read/file_write/file_list - all tool schemas converted to proper JSON Schema; httptest/tempdir tests for each plugin
This commit is contained in:
1 parent
d80710d85a
commit
9d040fde0b
8 files changed
+599
-44
No files matched your search
@@ -0,0 +1,57 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/plugin"
|
||||
)
|
||||
|
||||
func setup(t *testing.T) *Plugin {
|
||||
t.Helper()
|
||||
p := New()
|
||||
if err := p.Init(context.Background(), plugin.PluginConfig{DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Init: %v", err)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func TestWriteReadList(t *testing.T) {
|
||||
p := setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
out, err := p.Execute(ctx, "file_write", map[string]any{"path": "notes/todo.txt", "content": "买牛奶"})
|
||||
if err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, "已写入") {
|
||||
t.Errorf("unexpected write output: %q", out)
|
||||
}
|
||||
|
||||
out, err = p.Execute(ctx, "file_read", map[string]any{"path": "notes/todo.txt"})
|
||||
if err != nil || out != "买牛奶" {
|
||||
t.Errorf("read = %q, err = %v", out, err)
|
||||
}
|
||||
|
||||
out, err = p.Execute(ctx, "file_list", map[string]any{"path": "notes"})
|
||||
if err != nil || !strings.Contains(out, "todo.txt") {
|
||||
t.Errorf("list = %q, err = %v", out, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathEscapeRejected(t *testing.T) {
|
||||
p := setup(t)
|
||||
for _, path := range []string{"../secret", "../../etc/passwd", "/etc/passwd"} {
|
||||
if _, err := p.Execute(context.Background(), "file_read", map[string]any{"path": path}); err == nil {
|
||||
t.Errorf("path %q should be rejected", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadMissingFile(t *testing.T) {
|
||||
p := setup(t)
|
||||
if _, err := p.Execute(context.Background(), "file_read", map[string]any{"path": "nope.txt"}); err == nil {
|
||||
t.Error("expected error for missing file")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user