Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements. Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate. Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable. Refs: Versioned dependencies U1/U3; feature/dependency-workspace
86 lines
2.6 KiB
Go
86 lines
2.6 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
agenttools "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/tools"
|
|
core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git"
|
|
)
|
|
|
|
type fakeProvider struct {
|
|
resp agenttools.SearchResponse
|
|
err error
|
|
}
|
|
|
|
func (f *fakeProvider) Search(ctx context.Context, req agenttools.SearchRequest) (agenttools.SearchResponse, error) {
|
|
return f.resp, f.err
|
|
}
|
|
|
|
func newSearchRegistry(t *testing.T, p agenttools.SearchProvider) *Registry {
|
|
t.Helper()
|
|
reg := NewRegistry()
|
|
if err := reg.RegisterBuiltin(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := RegisterSearchTools(reg, p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return reg
|
|
}
|
|
|
|
func TestSearchToolsRegistered(t *testing.T) {
|
|
reg := newSearchRegistry(t, &fakeProvider{})
|
|
for _, name := range []string{"tavily_search", "web_search"} {
|
|
if _, _, ok := reg.Get(name); !ok {
|
|
t.Errorf("tool %s not registered", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func execSearchTool(t *testing.T, reg *Registry, name, args string) core.ToolCallResult {
|
|
t.Helper()
|
|
return reg.Execute(nil, core.ToolCallRequest{CallID: "c1", Name: name, Arguments: json.RawMessage(args)})
|
|
}
|
|
|
|
func TestSearchExecuteFormatsResults(t *testing.T) {
|
|
reg := newSearchRegistry(t, &fakeProvider{resp: agenttools.SearchResponse{
|
|
Answer: "概要答案",
|
|
Results: []agenttools.SearchResult{
|
|
{Title: "结果一", Content: "内容甲", Score: 0.9},
|
|
{Title: "结果二", Content: "内容乙", Score: 0.8},
|
|
},
|
|
}})
|
|
res := execSearchTool(t, reg, "tavily_search", `{"query":"超声 法规","search_depth":"basic","max_results":5}`)
|
|
if res.Status != "ok" {
|
|
t.Fatalf("status=%s output=%q", res.Status, res.Output)
|
|
}
|
|
for _, want := range []string{"Tavily 实时网络搜索结果", "概要答案", "结果一", "内容乙"} {
|
|
if !strings.Contains(res.Output, want) {
|
|
t.Errorf("output missing %q:\n%s", want, res.Output)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSearchEmptyQuery(t *testing.T) {
|
|
reg := newSearchRegistry(t, &fakeProvider{})
|
|
res := execSearchTool(t, reg, "web_search", `{"query":" "}`)
|
|
if res.Status == "ok" || !strings.Contains(res.Output, "query cannot be empty") {
|
|
t.Errorf("empty query: status=%s out=%q", res.Status, res.Output)
|
|
}
|
|
}
|
|
|
|
func TestSearchFailureFallback(t *testing.T) {
|
|
reg := newSearchRegistry(t, &fakeProvider{err: errors.New("all keys failed")})
|
|
res := execSearchTool(t, reg, "tavily_search", `{"query":"q"}`)
|
|
if res.Status == "ok" {
|
|
t.Error("provider failure should surface as tool error status")
|
|
}
|
|
if !strings.Contains(res.Output, "[Tavily Search Failed]") || !strings.Contains(res.Output, "fallback") {
|
|
t.Errorf("fallback copy missing: %q", res.Output)
|
|
}
|
|
}
|