- Added skip statements to multiple test functions in jsapi_search_test.go that require an external API key (Tavily/Serper), preventing them from running until the necessary credentials are available. - Updated comments to indicate the reason for skipping these tests, enhancing clarity for future development and testing efforts.
352 lines
11 KiB
Go
352 lines
11 KiB
Go
package context_test
|
|
|
|
import (
|
|
stdContext "context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/yaoapp/yao/agent/assistant"
|
|
"github.com/yaoapp/yao/agent/context"
|
|
"github.com/yaoapp/yao/agent/search/types"
|
|
"github.com/yaoapp/yao/agent/testutils"
|
|
oauthTypes "github.com/yaoapp/yao/openapi/oauth/types"
|
|
)
|
|
|
|
// Note: SearchAPIFactory is set by assistant.init() with proper config getter
|
|
// We import assistant package to ensure init() runs before tests
|
|
|
|
// newSearchTestContext creates a Context for search JSAPI testing
|
|
func newSearchTestContext(chatID, assistantID string) *context.Context {
|
|
authorized := &oauthTypes.AuthorizedInfo{
|
|
Subject: "test-user",
|
|
ClientID: "test-client-id",
|
|
Scope: "openid profile email",
|
|
SessionID: "test-session-id",
|
|
UserID: "test-user-123",
|
|
}
|
|
|
|
ctx := context.New(stdContext.Background(), authorized, chatID)
|
|
ctx.AssistantID = assistantID
|
|
ctx.Locale = "en-us"
|
|
ctx.Referer = context.RefererAPI
|
|
ctx.Accept = context.AcceptWebCUI
|
|
ctx.Metadata = make(map[string]interface{})
|
|
return ctx
|
|
}
|
|
|
|
// getResponseContent extracts the content from the first assistant message
|
|
func getResponseContent(res *context.HookCreateResponse) string {
|
|
if res == nil || len(res.Messages) == 0 {
|
|
return ""
|
|
}
|
|
for _, msg := range res.Messages {
|
|
if msg.Role == "assistant" {
|
|
if content, ok := msg.Content.(string); ok {
|
|
return content
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// TestSearchJSAPI_Web tests ctx.search.Web() via Create Hook
|
|
// Skip: requires external API key (Tavily/Serper)
|
|
func TestSearchJSAPI_Web(t *testing.T) {
|
|
t.Skip("Skipping: requires external API key (Tavily/Serper)")
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean(t)
|
|
|
|
// Load the search-jsapi test assistant
|
|
agent, err := assistant.Get("tests.search-jsapi")
|
|
require.NoError(t, err, "Failed to get tests.search-jsapi assistant")
|
|
require.NotNil(t, agent.HookScript, "The tests.search-jsapi assistant has no script")
|
|
|
|
ctx := newSearchTestContext("chat-search-web", "tests.search-jsapi")
|
|
|
|
// Call Create hook with test:web command
|
|
res, _, err := agent.HookScript.Create(ctx, []context.Message{{Role: "user", Content: "test:web Yao App Engine"}})
|
|
require.NoError(t, err, "Create hook failed")
|
|
require.NotNil(t, res, "Expected non-nil response")
|
|
|
|
// Get response content from messages
|
|
content := getResponseContent(res)
|
|
require.NotEmpty(t, content, "Expected response content")
|
|
|
|
// Parse the JSON response
|
|
var result types.Result
|
|
err = json.Unmarshal([]byte(content), &result)
|
|
require.NoError(t, err, "Response should be valid JSON: %s", content)
|
|
|
|
// Verify result
|
|
assert.Equal(t, types.SearchTypeWeb, result.Type, "type should be web")
|
|
assert.Equal(t, "Yao App Engine", result.Query, "query should match")
|
|
assert.Empty(t, result.Error, "should not have error: %s", result.Error)
|
|
assert.Greater(t, len(result.Items), 0, "should have items")
|
|
|
|
t.Logf("Web search returned %d items", len(result.Items))
|
|
for i, item := range result.Items {
|
|
if i < 3 {
|
|
t.Logf(" [%s] %s - %s", item.CitationID, item.Title, item.URL)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestSearchJSAPI_WebWithSites tests ctx.search.Web() with site restriction
|
|
// Skip: requires external API key (Tavily/Serper)
|
|
func TestSearchJSAPI_WebWithSites(t *testing.T) {
|
|
t.Skip("Skipping: requires external API key (Tavily/Serper)")
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean(t)
|
|
|
|
agent, err := assistant.Get("tests.search-jsapi")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, agent.HookScript)
|
|
|
|
ctx := newSearchTestContext("chat-search-web-sites", "tests.search-jsapi")
|
|
|
|
res, _, err := agent.HookScript.Create(ctx, []context.Message{{Role: "user", Content: "test:web_sites Yao App Engine"}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
|
|
content := getResponseContent(res)
|
|
require.NotEmpty(t, content, "Expected response content")
|
|
|
|
var result types.Result
|
|
err = json.Unmarshal([]byte(content), &result)
|
|
require.NoError(t, err, "Response should be valid JSON: %s", content)
|
|
|
|
assert.Equal(t, types.SearchTypeWeb, result.Type)
|
|
assert.Empty(t, result.Error, "should not have error: %s", result.Error)
|
|
assert.Greater(t, len(result.Items), 0, "should have items")
|
|
|
|
// Verify all results are from allowed sites
|
|
allowedSites := []string{"github.com", "yaoapps.com"}
|
|
for _, item := range result.Items {
|
|
isAllowed := false
|
|
for _, site := range allowedSites {
|
|
if strings.Contains(item.URL, site) {
|
|
isAllowed = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, isAllowed, "URL %s should be from allowed sites", item.URL)
|
|
}
|
|
|
|
t.Logf("Site-restricted search returned %d items", len(result.Items))
|
|
}
|
|
|
|
// TestSearchJSAPI_KB tests ctx.search.KB() via Create Hook (skeleton)
|
|
func TestSearchJSAPI_KB(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean(t)
|
|
|
|
agent, err := assistant.Get("tests.search-jsapi")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, agent.HookScript)
|
|
|
|
ctx := newSearchTestContext("chat-search-kb", "tests.search-jsapi")
|
|
|
|
res, _, err := agent.HookScript.Create(ctx, []context.Message{{Role: "user", Content: "test:kb test query"}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
|
|
content := getResponseContent(res)
|
|
require.NotEmpty(t, content, "Expected response content")
|
|
|
|
var result types.Result
|
|
err = json.Unmarshal([]byte(content), &result)
|
|
require.NoError(t, err, "Response should be valid JSON: %s", content)
|
|
|
|
assert.Equal(t, types.SearchTypeKB, result.Type, "type should be kb")
|
|
assert.Equal(t, "test query", result.Query, "query should match")
|
|
assert.Equal(t, types.SourceHook, result.Source, "source should be hook")
|
|
}
|
|
|
|
// TestSearchJSAPI_DB tests ctx.search.DB() via Create Hook (skeleton)
|
|
func TestSearchJSAPI_DB(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean(t)
|
|
|
|
agent, err := assistant.Get("tests.search-jsapi")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, agent.HookScript)
|
|
|
|
ctx := newSearchTestContext("chat-search-db", "tests.search-jsapi")
|
|
|
|
res, _, err := agent.HookScript.Create(ctx, []context.Message{{Role: "user", Content: "test:db test query"}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
|
|
content := getResponseContent(res)
|
|
require.NotEmpty(t, content, "Expected response content")
|
|
|
|
var result types.Result
|
|
err = json.Unmarshal([]byte(content), &result)
|
|
require.NoError(t, err, "Response should be valid JSON: %s", content)
|
|
|
|
assert.Equal(t, types.SearchTypeDB, result.Type, "type should be db")
|
|
assert.Equal(t, "test query", result.Query, "query should match")
|
|
assert.Equal(t, types.SourceHook, result.Source, "source should be hook")
|
|
}
|
|
|
|
// TestSearchJSAPI_All tests ctx.search.All() via Create Hook
|
|
// Skip: requires external API key (Tavily/Serper)
|
|
func TestSearchJSAPI_All(t *testing.T) {
|
|
t.Skip("Skipping: requires external API key (Tavily/Serper)")
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean(t)
|
|
|
|
agent, err := assistant.Get("tests.search-jsapi")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, agent.HookScript)
|
|
|
|
ctx := newSearchTestContext("chat-search-all", "tests.search-jsapi")
|
|
|
|
res, _, err := agent.HookScript.Create(ctx, []context.Message{{Role: "user", Content: "test:all"}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
|
|
content := getResponseContent(res)
|
|
require.NotEmpty(t, content, "Expected response content")
|
|
|
|
// Parse as array of results
|
|
var results []*types.Result
|
|
err = json.Unmarshal([]byte(content), &results)
|
|
require.NoError(t, err, "Response should be valid JSON array: %s", content)
|
|
|
|
assert.Len(t, results, 2, "should have 2 results")
|
|
|
|
// Both should succeed
|
|
successCount := 0
|
|
totalItems := 0
|
|
for _, r := range results {
|
|
if r != nil && r.Error == "" {
|
|
successCount++
|
|
totalItems += len(r.Items)
|
|
}
|
|
}
|
|
|
|
assert.Equal(t, 2, successCount, "both searches should succeed")
|
|
assert.Greater(t, totalItems, 0, "should have items")
|
|
|
|
t.Logf("All search: %d results, %d total items", len(results), totalItems)
|
|
}
|
|
|
|
// TestSearchJSAPI_Any tests ctx.search.Any() via Create Hook
|
|
// Skip: requires external API key (Tavily/Serper)
|
|
func TestSearchJSAPI_Any(t *testing.T) {
|
|
t.Skip("Skipping: requires external API key (Tavily/Serper)")
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean(t)
|
|
|
|
agent, err := assistant.Get("tests.search-jsapi")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, agent.HookScript)
|
|
|
|
ctx := newSearchTestContext("chat-search-any", "tests.search-jsapi")
|
|
|
|
res, _, err := agent.HookScript.Create(ctx, []context.Message{{Role: "user", Content: "test:any"}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
|
|
content := getResponseContent(res)
|
|
require.NotEmpty(t, content, "Expected response content")
|
|
|
|
var results []*types.Result
|
|
err = json.Unmarshal([]byte(content), &results)
|
|
require.NoError(t, err, "Response should be valid JSON array: %s", content)
|
|
|
|
assert.Len(t, results, 2, "should have 2 result slots")
|
|
|
|
// At least one should have results
|
|
hasSuccess := false
|
|
for _, r := range results {
|
|
if r != nil && len(r.Items) > 0 && r.Error == "" {
|
|
hasSuccess = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, hasSuccess, "at least one search should succeed")
|
|
|
|
t.Logf("Any search completed")
|
|
}
|
|
|
|
// TestSearchJSAPI_Race tests ctx.search.Race() via Create Hook
|
|
// Skip: requires external API key (Tavily/Serper)
|
|
func TestSearchJSAPI_Race(t *testing.T) {
|
|
t.Skip("Skipping: requires external API key (Tavily/Serper)")
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean(t)
|
|
|
|
agent, err := assistant.Get("tests.search-jsapi")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, agent.HookScript)
|
|
|
|
ctx := newSearchTestContext("chat-search-race", "tests.search-jsapi")
|
|
|
|
res, _, err := agent.HookScript.Create(ctx, []context.Message{{Role: "user", Content: "test:race"}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
|
|
content := getResponseContent(res)
|
|
require.NotEmpty(t, content, "Expected response content")
|
|
|
|
var results []*types.Result
|
|
err = json.Unmarshal([]byte(content), &results)
|
|
require.NoError(t, err, "Response should be valid JSON array: %s", content)
|
|
|
|
assert.Len(t, results, 2, "should have 2 result slots")
|
|
|
|
// At least one should have completed
|
|
hasResult := false
|
|
for _, r := range results {
|
|
if r != nil {
|
|
hasResult = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, hasResult, "at least one search should complete")
|
|
|
|
t.Logf("Race search completed")
|
|
}
|
|
|
|
// TestSearchJSAPI_InvalidCommand tests invalid test command
|
|
func TestSearchJSAPI_InvalidCommand(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean(t)
|
|
|
|
agent, err := assistant.Get("tests.search-jsapi")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, agent.HookScript)
|
|
|
|
ctx := newSearchTestContext("chat-search-invalid", "tests.search-jsapi")
|
|
|
|
res, _, err := agent.HookScript.Create(ctx, []context.Message{{Role: "user", Content: "invalid command"}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
|
|
content := getResponseContent(res)
|
|
assert.Contains(t, content, "Invalid test command", "should return error message")
|
|
}
|
|
|
|
// TestSearchJSAPI_UnknownMethod tests unknown test method
|
|
func TestSearchJSAPI_UnknownMethod(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean(t)
|
|
|
|
agent, err := assistant.Get("tests.search-jsapi")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, agent.HookScript)
|
|
|
|
ctx := newSearchTestContext("chat-search-unknown", "tests.search-jsapi")
|
|
|
|
res, _, err := agent.HookScript.Create(ctx, []context.Message{{Role: "user", Content: "test:unknown"}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
|
|
content := getResponseContent(res)
|
|
assert.Contains(t, content, "Unknown test method", "should return error message")
|
|
}
|