mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-12 22:55:45 +08:00
### What problem does this PR solve? - Tools management - Pregel engine wrapper for better usage - UT race - Coding style ### Type of change - [x] Refactoring
218 lines
5.3 KiB
Go
218 lines
5.3 KiB
Go
package core
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"ragflow/internal/harness/core/schema"
|
|
)
|
|
|
|
func TestToolRegistry_RegisterAndLookup(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
tool := newTestTool("get_weather", "Get weather")
|
|
r.Register(tool)
|
|
|
|
found, ok := r.Lookup("get_weather")
|
|
if !ok {
|
|
t.Fatal("expected to find 'get_weather'")
|
|
}
|
|
if found.Name() != "get_weather" {
|
|
t.Errorf("expected 'get_weather', got %s", found.Name())
|
|
}
|
|
}
|
|
|
|
func TestToolRegistry_LookupNotFound(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
_, ok := r.Lookup("nonexistent")
|
|
if ok {
|
|
t.Error("expected false for nonexistent tool")
|
|
}
|
|
}
|
|
|
|
func TestToolRegistry_WithAlias(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
tool := newTestTool("web_search_v2", "Search web")
|
|
r.Register(tool, WithAlias("search"))
|
|
|
|
// Lookup by alias
|
|
found, ok := r.Lookup("search")
|
|
if !ok {
|
|
t.Fatal("expected to find via alias 'search'")
|
|
}
|
|
if found.Name() != "web_search_v2" {
|
|
t.Errorf("expected 'web_search_v2', got %s", found.Name())
|
|
}
|
|
|
|
// Lookup by canonical name still works
|
|
found, ok = r.Lookup("web_search_v2")
|
|
if !ok {
|
|
t.Fatal("expected to find via canonical name")
|
|
}
|
|
}
|
|
|
|
func TestToolRegistry_WithCategory(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
r.Register(newTestTool("cat_tool1", ""), WithCategory("file", "read"))
|
|
r.Register(newTestTool("cat_tool2", ""), WithCategory("file"))
|
|
r.Register(newTestTool("other_tool", ""), WithCategory("network"))
|
|
|
|
fileTools := r.LookupByCategory("file")
|
|
if len(fileTools) != 2 {
|
|
t.Errorf("expected 2 file tools, got %d", len(fileTools))
|
|
}
|
|
|
|
readTools := r.LookupByCategory("read")
|
|
if len(readTools) != 1 {
|
|
t.Errorf("expected 1 read tool, got %d", len(readTools))
|
|
}
|
|
|
|
networkTools := r.LookupByCategory("network")
|
|
if len(networkTools) != 1 {
|
|
t.Errorf("expected 1 network tool, got %d", len(networkTools))
|
|
}
|
|
|
|
emptyTools := r.LookupByCategory("nonexistent")
|
|
if len(emptyTools) != 0 {
|
|
t.Errorf("expected 0 tools for nonexistent category, got %d", len(emptyTools))
|
|
}
|
|
}
|
|
|
|
func TestToolRegistry_AllTools(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
r.Register(newTestTool("a", ""))
|
|
r.Register(newTestTool("b", ""))
|
|
|
|
all := r.AllTools()
|
|
if len(all) != 2 {
|
|
t.Errorf("expected 2 tools, got %d", len(all))
|
|
}
|
|
}
|
|
|
|
func TestToolRegistry_ToSlice(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
r.Register(newTestTool("x", ""))
|
|
r.Register(newTestTool("y", ""))
|
|
|
|
slice := r.ToSlice()
|
|
if len(slice) != 2 {
|
|
t.Errorf("expected 2 tools, got %d", len(slice))
|
|
}
|
|
}
|
|
|
|
func TestToolRegistry_Filter(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
r.Register(newTestTool("search_web", ""), WithCategory("web"))
|
|
r.Register(newTestTool("search_file", ""), WithCategory("file"))
|
|
r.Register(newTestTool("read_file", ""), WithCategory("file"))
|
|
|
|
filtered := r.Filter(func(tool Tool) bool {
|
|
return tool.Name() == "search_file"
|
|
})
|
|
if len(filtered.AllTools()) != 1 {
|
|
t.Errorf("expected 1 filtered tool, got %d", len(filtered.AllTools()))
|
|
}
|
|
}
|
|
|
|
func TestToolRegistry_Merge(t *testing.T) {
|
|
r1 := NewToolRegistry()
|
|
r1.Register(newTestTool("a", ""), WithAlias("alias_a"))
|
|
|
|
r2 := NewToolRegistry()
|
|
r2.Register(newTestTool("b", ""), WithAlias("alias_b"))
|
|
|
|
r1.Merge(r2)
|
|
|
|
if _, ok := r1.Lookup("a"); !ok {
|
|
t.Error("expected 'a' after merge")
|
|
}
|
|
if _, ok := r1.Lookup("b"); !ok {
|
|
t.Error("expected 'b' after merge")
|
|
}
|
|
if _, ok := r1.Lookup("alias_b"); !ok {
|
|
t.Error("expected 'alias_b' after merge")
|
|
}
|
|
}
|
|
|
|
func TestToolRegistry_Unregister(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
r.Register(newTestTool("temp", ""), WithAlias("t"), WithCategory("test"))
|
|
|
|
r.Unregister("temp")
|
|
|
|
if _, ok := r.Lookup("temp"); ok {
|
|
t.Error("expected 'temp' to be removed")
|
|
}
|
|
if _, ok := r.Lookup("t"); ok {
|
|
t.Error("expected alias 't' to be removed")
|
|
}
|
|
if len(r.LookupByCategory("test")) != 0 {
|
|
t.Error("expected category 'test' to be empty")
|
|
}
|
|
}
|
|
|
|
func TestToolRegistry_MustLookup(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
r.Register(newTestTool("safe", ""))
|
|
|
|
defer func() {
|
|
if r := recover(); r == nil {
|
|
t.Error("expected panic for missing tool")
|
|
}
|
|
}()
|
|
r.MustLookup("nonexistent")
|
|
}
|
|
|
|
func TestToolRegistry_ConcurrentAccess(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
r.Register(newTestTool("concurrent_tool", ""))
|
|
|
|
done := make(chan struct{}, 2)
|
|
go func() {
|
|
for i := 0; i < 100; i++ {
|
|
r.Lookup("concurrent_tool")
|
|
}
|
|
done <- struct{}{}
|
|
}()
|
|
go func() {
|
|
for i := 0; i < 100; i++ {
|
|
r.Register(newTestTool("other", ""))
|
|
}
|
|
done <- struct{}{}
|
|
}()
|
|
<-done
|
|
<-done
|
|
}
|
|
|
|
func TestToolToSliceForReActConfig(t *testing.T) {
|
|
r := NewToolRegistry()
|
|
r.Register(newTestTool("t1", ""))
|
|
r.Register(newTestTool("t2", ""))
|
|
|
|
cfg := &ReActConfig[Message]{
|
|
Tools: r.ToSlice(),
|
|
}
|
|
if len(cfg.Tools) != 2 {
|
|
t.Errorf("expected 2 tools in config, got %d", len(cfg.Tools))
|
|
}
|
|
}
|
|
|
|
func TestToolRegistry_LookupPanicsForTool(t *testing.T) {
|
|
// Non-existent tool via MustLookup
|
|
r := NewToolRegistry()
|
|
r.Register(&testPanicTool{name: "good"})
|
|
|
|
// Should not panic
|
|
_ = r.MustLookup("good")
|
|
}
|
|
|
|
type testPanicTool struct{ name string }
|
|
|
|
func (t *testPanicTool) Name() string { return t.name }
|
|
func (t *testPanicTool) Description() string { return "" }
|
|
func (t *testPanicTool) Invoke(ctx context.Context, s string, opts ...ToolOption) (string, error) {
|
|
return "", nil
|
|
}
|
|
func (t *testPanicTool) Stream(ctx context.Context, s string, opts ...ToolOption) (*schema.StreamReader[string], error) {
|
|
return schema.StreamReaderFromArray([]string{""}), nil
|
|
}
|