chore: import upstream snapshot with attribution
CF: Deploy Dev Docs / deploy (push) Has been cancelled
Sync Labels / build (push) Has been cancelled
tests / unit tests (macos-latest) (push) Has been cancelled
tests / unit tests (windows-latest) (push) Has been cancelled
tests / unit tests (ubuntu-latest) (push) Has been cancelled
CF: Deploy Dev Docs / deploy (push) Has been cancelled
Sync Labels / build (push) Has been cancelled
tests / unit tests (macos-latest) (push) Has been cancelled
tests / unit tests (windows-latest) (push) Has been cancelled
tests / unit tests (ubuntu-latest) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,583 @@
|
||||
// Copyright 2026 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cloudsqlpg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/googleapis/mcp-toolbox/internal/testutils"
|
||||
"github.com/googleapis/mcp-toolbox/tests"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
func createPostgresExtension(t *testing.T, ctx context.Context, pool *pgxpool.Pool, extensionName string) func() {
|
||||
createExtensionCmd := fmt.Sprintf("CREATE EXTENSION IF NOT EXISTS %s", extensionName)
|
||||
_, err := pool.Exec(ctx, createExtensionCmd)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create extension: %v", err)
|
||||
}
|
||||
return func() {
|
||||
dropExtensionCmd := fmt.Sprintf("DROP EXTENSION IF EXISTS %s", extensionName)
|
||||
_, err := pool.Exec(ctx, dropExtensionCmd)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to drop extension: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setupVectorAssistTable prepares the database extensions and test data needed
|
||||
// to test the definespec, modifyspec, applyspec, and generatequery tools.
|
||||
func setupVectorAssistTable(t *testing.T, ctx context.Context, pool *pgxpool.Pool) (string, func(t *testing.T), func()) {
|
||||
// Install necessary extensions for VectorAssist
|
||||
dropExtensionFunc := createPostgresExtension(t, ctx, pool, "vector_assist")
|
||||
|
||||
uniqueID := strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
uniqueID = uniqueID[:10]
|
||||
tableName := "vector_assist_test_" + uniqueID
|
||||
|
||||
// Create a table with vector data for defining/modifying/applying specs
|
||||
createStmt := fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
name TEXT,
|
||||
category TEXT,
|
||||
content TEXT,
|
||||
embedding vector(3)
|
||||
);
|
||||
`, tableName)
|
||||
|
||||
_, err := pool.Exec(ctx, createStmt)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create vector assist test table: %v", err)
|
||||
}
|
||||
|
||||
// Insert sample data to generate queries against
|
||||
insertDataStmt := fmt.Sprintf(`
|
||||
INSERT INTO %s (name, category, content, embedding)
|
||||
VALUES
|
||||
('Item 1', 'Document', 'Sample text document about AI', array_fill(0.1, ARRAY[3])::vector),
|
||||
('Item 2', 'Document', 'Sample text document about databases', array_fill(0.2, ARRAY[3])::vector);
|
||||
`, tableName)
|
||||
|
||||
_, err = pool.Exec(ctx, insertDataStmt)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to insert data into vector assist table: %v", err)
|
||||
}
|
||||
|
||||
// Return teardown function
|
||||
teardown := func(t *testing.T) {
|
||||
_, err := pool.Exec(context.Background(), fmt.Sprintf("DROP TABLE IF EXISTS %s;", tableName))
|
||||
if err != nil {
|
||||
t.Errorf("failed to drop vector assist table %s: %v", tableName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return tableName, teardown, dropExtensionFunc
|
||||
}
|
||||
|
||||
// TODO: Remove the test from this file and follow the existing test pattern
|
||||
// by calling the tests from cloudsqlpg_integration_test.go
|
||||
func TestVectorAssistIntegration(t *testing.T) {
|
||||
// temporarily skip vector assist test since "Vector assist is temporarily
|
||||
// disabled for all Cloud SQL for PostgreSQL instances"
|
||||
// ref: https://docs.cloud.google.com/sql/docs/postgres/vector-assist-overview
|
||||
t.Skip("Temporarily skipping vector assist test. Google had temporarily disabled vector assist for all CloudSQL for PostgreSQL instances.")
|
||||
|
||||
sourceConfig := getCloudSQLPgVars(t)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
args := []string{"--enable-api"}
|
||||
|
||||
pool, err := initCloudSQLPgConnectionPool(CloudSQLPostgresProject, CloudSQLPostgresRegion, CloudSQLPostgresInstance, "public", CloudSQLPostgresUser, CloudSQLPostgresPass, CloudSQLPostgresDatabase)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create Cloud SQL connection pool: %s", err)
|
||||
}
|
||||
|
||||
// Generate a unique ID
|
||||
uniqueID := strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// This will execute after all tool tests complete (success, fail, or t.Fatal)
|
||||
t.Cleanup(func() {
|
||||
tests.CleanupPostgresTables(t, context.Background(), pool, uniqueID)
|
||||
})
|
||||
|
||||
//Create table names using the UUID
|
||||
tableNameParam := "param_table_" + uniqueID
|
||||
tableNameAuth := "auth_table_" + uniqueID
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := tests.GetPostgresSQLParamToolInfo(tableNameParam)
|
||||
teardownTable1 := tests.SetupPostgresSQLTable(t, ctx, pool, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
|
||||
defer teardownTable1(t)
|
||||
|
||||
// set up data for auth tool
|
||||
createAuthTableStmt, insertAuthTableStmt, authToolStmt, authTestParams := tests.GetPostgresSQLAuthToolInfo(tableNameAuth)
|
||||
teardownTable2 := tests.SetupPostgresSQLTable(t, ctx, pool, createAuthTableStmt, insertAuthTableStmt, tableNameAuth, authTestParams)
|
||||
defer teardownTable2(t)
|
||||
|
||||
// // Set up data for vector assist tools
|
||||
vectorAssistTableName, teardownVectorAssistTable, dropExtension := setupVectorAssistTable(t, ctx, pool)
|
||||
defer teardownVectorAssistTable(t)
|
||||
defer dropExtension()
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, CloudSQLPostgresToolType, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
toolsFile = tests.AddExecuteSqlConfig(t, toolsFile, "postgres-execute-sql")
|
||||
tmplSelectCombined, tmplSelectFilterCombined := tests.GetPostgresSQLTmplToolStatement()
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, CloudSQLPostgresToolType, tmplSelectCombined, tmplSelectFilterCombined, "")
|
||||
|
||||
// Add vector assist tools to the configuration
|
||||
toolsFile = AddVectorAssistConfig(t, toolsFile, "my-instance")
|
||||
|
||||
toolsFile = tests.AddPostgresPrebuiltConfig(t, toolsFile)
|
||||
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("command initialization returned an error: %s", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
|
||||
if err != nil {
|
||||
t.Logf("toolbox command logs: \n%s", out)
|
||||
t.Fatalf("toolbox didn't start successfully: %s", err)
|
||||
}
|
||||
|
||||
// Run vectorassist tool tests
|
||||
specID := "va_spec_001"
|
||||
RunVectorAssistDefineSpecToolInvokeTest(t, ctx, pool, vectorAssistTableName, specID)
|
||||
RunVectorAssistModifySpecToolInvokeTest(t, ctx, pool, specID)
|
||||
RunVectorAssistApplySpecToolInvokeTest(t, ctx, pool, specID)
|
||||
RunVectorAssistGenerateQueryToolInvokeTest(t, ctx, pool, specID)
|
||||
RunVectorAssistImproveQueryRecallToolInvokeTest(t, ctx, pool, vectorAssistTableName)
|
||||
RunVectorAssistListSpecsToolInvokeTest(t, ctx, pool, vectorAssistTableName)
|
||||
RunVectorAssistGetSpecToolInvokeTest(t, ctx, pool, specID)
|
||||
RunVectorAssistDeleteSpecToolInvokeTest(t, ctx, pool, specID)
|
||||
}
|
||||
|
||||
// AddVectorAssistConfig appends the vector assist tool configurations to the given tools file.
|
||||
func AddVectorAssistConfig(t *testing.T, config map[string]any, sourceName string) map[string]any {
|
||||
tools, ok := config["tools"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("unable to get tools from config")
|
||||
}
|
||||
|
||||
tools["define_spec"] = map[string]any{
|
||||
"type": "vector-assist-define-spec",
|
||||
"source": sourceName,
|
||||
}
|
||||
tools["modify_spec"] = map[string]any{
|
||||
"type": "vector-assist-modify-spec",
|
||||
"source": sourceName,
|
||||
}
|
||||
tools["apply_spec"] = map[string]any{
|
||||
"type": "vector-assist-apply-spec",
|
||||
"source": sourceName,
|
||||
}
|
||||
tools["generate_query"] = map[string]any{
|
||||
"type": "vector-assist-generate-query",
|
||||
"source": sourceName,
|
||||
}
|
||||
tools["improve_query_recall"] = map[string]any{
|
||||
"type": "vector-assist-improve-query-recall",
|
||||
"source": sourceName,
|
||||
}
|
||||
tools["list_specs"] = map[string]any{
|
||||
"type": "vector-assist-list-specs",
|
||||
"source": sourceName,
|
||||
}
|
||||
tools["get_spec"] = map[string]any{
|
||||
"type": "vector-assist-get-spec",
|
||||
"source": sourceName,
|
||||
}
|
||||
tools["delete_spec"] = map[string]any{
|
||||
"type": "vector-assist-delete-spec",
|
||||
"source": sourceName,
|
||||
}
|
||||
config["tools"] = tools
|
||||
return config
|
||||
}
|
||||
|
||||
func RunVectorAssistImproveQueryRecallToolInvokeTest(t *testing.T, ctx context.Context, pool *pgxpool.Pool, tableName string) {
|
||||
validPayload := fmt.Sprintf(`{
|
||||
"schema_name": "public",
|
||||
"table_name": "%s",
|
||||
"vector_column_name": "embedding",
|
||||
"index_name": "%s_embedding_idx",
|
||||
"top_k": 10,
|
||||
"target_recall": 0.95,
|
||||
"distance_func": "cosine"
|
||||
}`,
|
||||
tableName, tableName)
|
||||
|
||||
tcs := []struct {
|
||||
name string
|
||||
requestBody io.Reader
|
||||
api string
|
||||
wantStatusCode int
|
||||
wantContains []string
|
||||
}{
|
||||
{
|
||||
name: "improve query recall valid parameters",
|
||||
requestBody: bytes.NewBuffer([]byte(validPayload)),
|
||||
api: "http://127.0.0.1:5000/api/tool/improve_query_recall/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`ef_search`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "improve query recall missing required table_name",
|
||||
requestBody: bytes.NewBuffer([]byte(`{"schema_name": "public"}`)),
|
||||
api: "http://127.0.0.1:5000/api/tool/improve_query_recall/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`"error"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
runVectorAssistToolInvokeTest(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func RunVectorAssistListSpecsToolInvokeTest(t *testing.T, ctx context.Context, pool *pgxpool.Pool, tableName string) {
|
||||
validPayload := fmt.Sprintf(`{
|
||||
"table_name": "%s",
|
||||
"column_name": "content"
|
||||
}`,
|
||||
tableName)
|
||||
|
||||
tcs := []struct {
|
||||
name string
|
||||
requestBody io.Reader
|
||||
api string
|
||||
wantStatusCode int
|
||||
wantContains []string
|
||||
}{
|
||||
{
|
||||
name: "list specs valid parameters",
|
||||
requestBody: bytes.NewBuffer([]byte(validPayload)),
|
||||
api: "http://127.0.0.1:5000/api/tool/list_specs/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`va_spec_001`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list specs missing required table_name",
|
||||
requestBody: bytes.NewBuffer([]byte(`{"column_name": "content"}`)),
|
||||
api: "http://127.0.0.1:5000/api/tool/list_specs/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`"error"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
runVectorAssistToolInvokeTest(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func RunVectorAssistGetSpecToolInvokeTest(t *testing.T, ctx context.Context, pool *pgxpool.Pool, specID string) {
|
||||
validPayload := fmt.Sprintf(`{
|
||||
"spec_id": "%s"
|
||||
}`,
|
||||
specID)
|
||||
|
||||
tcs := []struct {
|
||||
name string
|
||||
requestBody io.Reader
|
||||
api string
|
||||
wantStatusCode int
|
||||
wantContains []string
|
||||
}{
|
||||
{
|
||||
name: "get spec valid parameters",
|
||||
requestBody: bytes.NewBuffer([]byte(validPayload)),
|
||||
api: "http://127.0.0.1:5000/api/tool/get_spec/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
specID,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "get spec missing spec_id",
|
||||
requestBody: bytes.NewBuffer([]byte(`{}`)),
|
||||
api: "http://127.0.0.1:5000/api/tool/get_spec/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`"error"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
runVectorAssistToolInvokeTest(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func RunVectorAssistDeleteSpecToolInvokeTest(t *testing.T, ctx context.Context, pool *pgxpool.Pool, specID string) {
|
||||
validPayload := fmt.Sprintf(`{
|
||||
"spec_id": "%s"
|
||||
}`,
|
||||
specID)
|
||||
|
||||
tcs := []struct {
|
||||
name string
|
||||
requestBody io.Reader
|
||||
api string
|
||||
wantStatusCode int
|
||||
wantContains []string
|
||||
}{
|
||||
{
|
||||
name: "delete spec valid parameters",
|
||||
requestBody: bytes.NewBuffer([]byte(validPayload)),
|
||||
api: "http://127.0.0.1:5000/api/tool/delete_spec/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`delete_spec`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete spec missing spec_id",
|
||||
requestBody: bytes.NewBuffer([]byte(`{}`)),
|
||||
api: "http://127.0.0.1:5000/api/tool/delete_spec/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`"error"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
runVectorAssistToolInvokeTest(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func RunVectorAssistDefineSpecToolInvokeTest(t *testing.T, ctx context.Context, pool *pgxpool.Pool, tableName string, specID string) {
|
||||
validPayload := fmt.Sprintf(`{
|
||||
"table_name": "%s",
|
||||
"schema_name": "public",
|
||||
"spec_id": "%s",
|
||||
"vector_column_name": "embedding",
|
||||
"text_column_name": "content",
|
||||
"vector_index_type": "hnsw",
|
||||
"embeddings_available": true,
|
||||
"num_vectors": 2,
|
||||
"dimensionality": 3,
|
||||
"embedding_model": "textembedding-gecko",
|
||||
"prefilter_column_names": ["category"],
|
||||
"distance_func": "cosine",
|
||||
"quantization": "halfvec",
|
||||
"memory_budget_kb": 1024,
|
||||
"target_recall": 0.95,
|
||||
"target_top_k": 10,
|
||||
"tune_vector_index": true
|
||||
}`, tableName, specID)
|
||||
|
||||
tcs := []struct {
|
||||
name string
|
||||
requestBody io.Reader
|
||||
api string
|
||||
wantStatusCode int
|
||||
wantContains []string
|
||||
}{
|
||||
{
|
||||
name: "invoke define_spec with all valid parameters",
|
||||
requestBody: bytes.NewBuffer([]byte(validPayload)),
|
||||
api: "http://127.0.0.1:5000/api/tool/define_spec/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
// Check for key identifiers instead of the entire JSON string
|
||||
wantContains: []string{
|
||||
`"vector_spec_id":"va_spec_001"`,
|
||||
fmt.Sprintf(`"table_name":"%s"`, tableName),
|
||||
`"recommendation_id"`, // Ensure a recommendation was generated
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invoke define_spec with missing required table_name",
|
||||
requestBody: bytes.NewBuffer([]byte(`{"schema_name": "public", "spec_id": "va_spec_002"}`)),
|
||||
api: "http://127.0.0.1:5000/api/tool/define_spec/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`"error"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
runVectorAssistToolInvokeTest(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func RunVectorAssistModifySpecToolInvokeTest(t *testing.T, ctx context.Context, pool *pgxpool.Pool, specID string) {
|
||||
validPayload := fmt.Sprintf(`{
|
||||
"spec_id": "%s",
|
||||
"memory_budget_kb": 2048,
|
||||
"target_recall": 0.99
|
||||
}`, specID)
|
||||
|
||||
tcs := []struct {
|
||||
name string
|
||||
requestBody io.Reader
|
||||
api string
|
||||
wantStatusCode int
|
||||
wantContains []string
|
||||
}{
|
||||
{
|
||||
name: "modify existing spec with new constraints",
|
||||
requestBody: bytes.NewBuffer([]byte(validPayload)),
|
||||
api: "http://127.0.0.1:5000/api/tool/modify_spec/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`"recommendation_id"`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "modify existing spec without required spec id",
|
||||
requestBody: bytes.NewBuffer([]byte(`{"target_recall": 0.99}`)),
|
||||
api: "http://127.0.0.1:5000/api/tool/modify_spec/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`"error"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
runVectorAssistToolInvokeTest(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func RunVectorAssistApplySpecToolInvokeTest(t *testing.T, ctx context.Context, pool *pgxpool.Pool, recommendationID string) {
|
||||
validPayload := fmt.Sprintf(`{
|
||||
"spec_id": "%s"
|
||||
}`, recommendationID)
|
||||
|
||||
tcs := []struct {
|
||||
name string
|
||||
requestBody io.Reader
|
||||
api string
|
||||
wantStatusCode int
|
||||
wantContains []string
|
||||
}{
|
||||
{
|
||||
name: "apply recommendation to database",
|
||||
requestBody: bytes.NewBuffer([]byte(validPayload)),
|
||||
api: "http://127.0.0.1:5000/api/tool/apply_spec/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`{"apply_spec":true}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "apply recommendation to database without spec id",
|
||||
requestBody: bytes.NewBuffer([]byte(`{"schema_name": "public"}`)),
|
||||
api: "http://127.0.0.1:5000/api/tool/apply_spec/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`"error"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
runVectorAssistToolInvokeTest(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func RunVectorAssistGenerateQueryToolInvokeTest(t *testing.T, ctx context.Context, pool *pgxpool.Pool, specID string) {
|
||||
validPayload := fmt.Sprintf(`{
|
||||
"spec_id": "%s",
|
||||
"search_text": "What is the capital of France?",
|
||||
"top_k": 5
|
||||
}`, specID)
|
||||
|
||||
tcs := []struct {
|
||||
name string
|
||||
requestBody io.Reader
|
||||
api string
|
||||
wantStatusCode int
|
||||
wantContains []string
|
||||
}{
|
||||
{
|
||||
name: "generate SQL for vector search",
|
||||
requestBody: bytes.NewBuffer([]byte(validPayload)),
|
||||
api: "http://127.0.0.1:5000/api/tool/generate_query/invoke",
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantContains: []string{
|
||||
`"generate_query"`,
|
||||
`LIMIT 5`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
runVectorAssistToolInvokeTest(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func runVectorAssistToolInvokeTest(t *testing.T, tc struct {
|
||||
name string
|
||||
requestBody io.Reader
|
||||
api string
|
||||
wantStatusCode int
|
||||
wantContains []string
|
||||
}) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
resp, body := tests.RunRequest(t, http.MethodPost, tc.api, tc.requestBody, nil)
|
||||
|
||||
if resp.StatusCode != tc.wantStatusCode {
|
||||
t.Fatalf("tool %s: wrong status code: got %d, want %d, body: %s", tc.api, resp.StatusCode, tc.wantStatusCode, string(body))
|
||||
}
|
||||
|
||||
if tc.wantStatusCode != http.StatusOK {
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshal the standard response wrapper
|
||||
var bodyWrapper struct {
|
||||
Result json.RawMessage `json:"result"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &bodyWrapper); err != nil {
|
||||
t.Fatalf("error decoding response wrapper: %v", err)
|
||||
}
|
||||
|
||||
// Handle the double-unmarshal logic for stringified results
|
||||
var resultString string
|
||||
if err := json.Unmarshal(bodyWrapper.Result, &resultString); err != nil {
|
||||
resultString = string(bodyWrapper.Result)
|
||||
}
|
||||
|
||||
// Verification loop
|
||||
for _, expectedSubstr := range tc.wantContains {
|
||||
if !strings.Contains(resultString, expectedSubstr) {
|
||||
t.Errorf("Expected result to contain %q, but it did not.\nFull result: %s", expectedSubstr, resultString)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user