yao/agent/search/jsapi_test.go
Max 632c0f5674 Refactor Context Management to Use Memory Instead of Space
- Replaced all instances of `ctx.Space` with `ctx.Memory.Context` in the context management code, ensuring a more structured approach to handling temporary request-scoped data.
- Updated related test cases to reflect the changes in context memory usage, enhancing the reliability and clarity of tests.
- Removed the deprecated `Space` references and adjusted comments and documentation to align with the new memory management strategy.
2025-12-22 11:19:00 +08:00

351 lines
8.1 KiB
Go

package search_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/search"
"github.com/yaoapp/yao/agent/search/types"
"github.com/yaoapp/yao/agent/testutils"
)
func TestNewJSAPI(t *testing.T) {
api := search.NewJSAPI(nil, nil, nil)
require.NotNil(t, api)
}
func TestJSAPI_Web(t *testing.T) {
api := search.NewJSAPI(nil, &types.Config{
Web: &types.WebConfig{Provider: "tavily"},
}, &search.Uses{Web: "builtin"})
result := api.Web("test query", nil)
require.NotNil(t, result)
r, ok := result.(*types.Result)
require.True(t, ok)
assert.Equal(t, types.SearchTypeWeb, r.Type)
assert.Equal(t, "test query", r.Query)
assert.Equal(t, types.SourceHook, r.Source)
}
func TestJSAPI_Web_WithOptions(t *testing.T) {
api := search.NewJSAPI(nil, &types.Config{
Web: &types.WebConfig{Provider: "tavily"},
}, &search.Uses{Web: "builtin"})
opts := map[string]interface{}{
"limit": float64(5),
"sites": []interface{}{"github.com", "stackoverflow.com"},
"time_range": "week",
}
result := api.Web("golang concurrency", opts)
require.NotNil(t, result)
r, ok := result.(*types.Result)
require.True(t, ok)
assert.Equal(t, types.SearchTypeWeb, r.Type)
assert.Equal(t, "golang concurrency", r.Query)
}
func TestJSAPI_KB(t *testing.T) {
testutils.Prepare(t)
defer testutils.Clean(t)
api := search.NewJSAPI(nil, &types.Config{
KB: &types.KBConfig{Collections: []string{"docs"}},
}, nil)
result := api.KB("test query", nil)
require.NotNil(t, result)
r, ok := result.(*types.Result)
require.True(t, ok)
assert.Equal(t, types.SearchTypeKB, r.Type)
assert.Equal(t, "test query", r.Query)
assert.Equal(t, types.SourceHook, r.Source)
}
func TestJSAPI_KB_WithOptions(t *testing.T) {
testutils.Prepare(t)
defer testutils.Clean(t)
api := search.NewJSAPI(nil, &types.Config{
KB: &types.KBConfig{Collections: []string{"docs"}},
}, nil)
opts := map[string]interface{}{
"collections": []interface{}{"docs", "faq"},
"threshold": 0.8,
"limit": float64(10),
"graph": true,
}
result := api.KB("knowledge base query", opts)
require.NotNil(t, result)
r, ok := result.(*types.Result)
require.True(t, ok)
assert.Equal(t, types.SearchTypeKB, r.Type)
assert.Equal(t, "knowledge base query", r.Query)
}
func TestJSAPI_DB(t *testing.T) {
testutils.Prepare(t)
defer testutils.Clean(t)
api := search.NewJSAPI(nil, &types.Config{
DB: &types.DBConfig{Models: []string{"product"}},
}, &search.Uses{QueryDSL: "builtin"})
result := api.DB("test query", nil)
require.NotNil(t, result)
r, ok := result.(*types.Result)
require.True(t, ok)
assert.Equal(t, types.SearchTypeDB, r.Type)
assert.Equal(t, "test query", r.Query)
assert.Equal(t, types.SourceHook, r.Source)
}
func TestJSAPI_DB_WithOptions(t *testing.T) {
testutils.Prepare(t)
defer testutils.Clean(t)
api := search.NewJSAPI(nil, &types.Config{
DB: &types.DBConfig{Models: []string{"product"}},
}, &search.Uses{QueryDSL: "builtin"})
opts := map[string]interface{}{
"models": []interface{}{"product", "order"},
"select": []interface{}{"id", "name", "price"},
"limit": float64(20),
}
result := api.DB("database query", opts)
require.NotNil(t, result)
r, ok := result.(*types.Result)
require.True(t, ok)
assert.Equal(t, types.SearchTypeDB, r.Type)
assert.Equal(t, "database query", r.Query)
}
func TestJSAPI_All(t *testing.T) {
testutils.Prepare(t)
defer testutils.Clean(t)
api := search.NewJSAPI(nil, &types.Config{
KB: &types.KBConfig{Collections: []string{"docs"}},
DB: &types.DBConfig{Models: []string{"product"}},
}, nil)
requests := []interface{}{
map[string]interface{}{
"type": "kb",
"query": "KB query",
},
map[string]interface{}{
"type": "db",
"query": "DB query",
},
}
results := api.All(requests)
require.Len(t, results, 2)
// First result (KB)
r0, ok := results[0].(*types.Result)
require.True(t, ok)
assert.Equal(t, types.SearchTypeKB, r0.Type)
assert.Equal(t, "KB query", r0.Query)
// Second result (DB)
r1, ok := results[1].(*types.Result)
require.True(t, ok)
assert.Equal(t, types.SearchTypeDB, r1.Type)
assert.Equal(t, "DB query", r1.Query)
}
func TestJSAPI_Any(t *testing.T) {
testutils.Prepare(t)
defer testutils.Clean(t)
api := search.NewJSAPI(nil, &types.Config{
KB: &types.KBConfig{Collections: []string{"docs"}},
DB: &types.DBConfig{Models: []string{"product"}},
}, nil)
requests := []interface{}{
map[string]interface{}{
"type": "kb",
"query": "KB query",
},
map[string]interface{}{
"type": "db",
"query": "DB query",
},
}
results := api.Any(requests)
require.Len(t, results, 2)
// At least one result should be present
hasResult := false
for _, r := range results {
if r != nil {
hasResult = true
break
}
}
assert.True(t, hasResult)
}
func TestJSAPI_Race(t *testing.T) {
testutils.Prepare(t)
defer testutils.Clean(t)
api := search.NewJSAPI(nil, &types.Config{
KB: &types.KBConfig{Collections: []string{"docs"}},
DB: &types.DBConfig{Models: []string{"product"}},
}, nil)
requests := []interface{}{
map[string]interface{}{
"type": "kb",
"query": "KB query",
},
map[string]interface{}{
"type": "db",
"query": "DB query",
},
}
results := api.Race(requests)
require.Len(t, results, 2)
// At least one result should be present
hasResult := false
for _, r := range results {
if r != nil {
hasResult = true
break
}
}
assert.True(t, hasResult)
}
func TestJSAPI_All_Empty(t *testing.T) {
api := search.NewJSAPI(nil, nil, nil)
results := api.All([]interface{}{})
assert.Len(t, results, 0)
}
func TestJSAPI_Any_Empty(t *testing.T) {
api := search.NewJSAPI(nil, nil, nil)
results := api.Any([]interface{}{})
assert.Len(t, results, 0)
}
func TestJSAPI_Race_Empty(t *testing.T) {
api := search.NewJSAPI(nil, nil, nil)
results := api.Race([]interface{}{})
assert.Len(t, results, 0)
}
func TestJSAPI_Web_WithRerank(t *testing.T) {
api := search.NewJSAPI(nil, &types.Config{
Web: &types.WebConfig{Provider: "tavily"},
}, &search.Uses{Web: "builtin"})
opts := map[string]interface{}{
"limit": float64(10),
"rerank": map[string]interface{}{
"top_n": float64(5),
},
}
result := api.Web("test query", opts)
require.NotNil(t, result)
r, ok := result.(*types.Result)
require.True(t, ok)
assert.Equal(t, types.SearchTypeWeb, r.Type)
}
func TestJSAPI_All_InvalidRequests(t *testing.T) {
api := search.NewJSAPI(nil, &types.Config{
Web: &types.WebConfig{Provider: "tavily"},
}, &search.Uses{Web: "builtin"})
// Mix of invalid and valid requests
requests := []interface{}{
"invalid", // Not a map
map[string]interface{}{
"query": "no type", // Missing type
},
map[string]interface{}{
"type": "web", // Missing query
},
map[string]interface{}{
"type": "web",
"query": "valid query",
},
}
results := api.All(requests)
// Only the valid request should produce a result
assert.Len(t, results, 1)
}
func TestSetJSAPIFactory(t *testing.T) {
// Reset factory
context.SearchAPIFactory = nil
// Set factory with nil getter (uses defaults)
search.SetJSAPIFactory(nil)
// Verify factory is set
require.NotNil(t, context.SearchAPIFactory)
// Create a mock context
ctx := context.New(nil, nil, "test-chat")
// Get search API
searchAPI := context.SearchAPIFactory(ctx)
require.NotNil(t, searchAPI)
}
func TestSetJSAPIFactory_WithGetter(t *testing.T) {
// Reset factory
context.SearchAPIFactory = nil
// Set factory with custom getter
search.SetJSAPIFactory(func(assistantID string) (*types.Config, *search.Uses) {
if assistantID == "test-assistant" {
return &types.Config{
Web: &types.WebConfig{Provider: "tavily"},
}, &search.Uses{Web: "builtin"}
}
return nil, nil
})
// Verify factory is set
require.NotNil(t, context.SearchAPIFactory)
// Create a context with assistant ID
ctx := context.New(nil, nil, "test-chat")
ctx.AssistantID = "test-assistant"
// Get search API
searchAPI := context.SearchAPIFactory(ctx)
require.NotNil(t, searchAPI)
}
func TestJSAPI_ImplementsSearchAPI(t *testing.T) {
// Verify JSAPI implements context.SearchAPI interface
var _ context.SearchAPI = search.NewJSAPI(nil, nil, nil)
}