mirror of
https://github.com/vxcontrol/langchaingo.git
synced 2026-07-21 08:55:25 -04:00
77504b877f
* scripts: cleanup test scripts * lint: add test pattern and architectural linting * internal/httprr: expand httprr testing * test: add comprehensive unit test coverage * docs: add comprehensive testing guide to CONTRIBUTING.md * examples: standardize module versions and cleanup dependencies * all: re-record several httprr recordings
252 lines
6.0 KiB
Go
252 lines
6.0 KiB
Go
// nolint
|
|
package cloudsql_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/tmc/langchaingo/internal/httprr"
|
|
"github.com/tmc/langchaingo/schema"
|
|
"github.com/tmc/langchaingo/util/cloudsqlutil"
|
|
"github.com/tmc/langchaingo/vectorstores/cloudsql"
|
|
)
|
|
|
|
type EnvVariables struct {
|
|
Username string
|
|
Password string
|
|
Database string
|
|
ProjectID string
|
|
Region string
|
|
Instance string
|
|
Cluster string
|
|
Table string
|
|
}
|
|
|
|
func getEnvVariables(t *testing.T) EnvVariables {
|
|
t.Helper()
|
|
|
|
// Check for OpenAI API key since we use OpenAI embeddings
|
|
httprr.SkipIfNoCredentialsAndRecordingMissing(t, "OPENAI_API_KEY")
|
|
|
|
username := os.Getenv("CLOUDSQL_USERNAME")
|
|
if username == "" {
|
|
t.Skip("env variable CLOUDSQL_USERNAME is empty")
|
|
}
|
|
// Requires environment variable CLOUDSQL_PASSWORD to be set.
|
|
password := os.Getenv("CLOUDSQL_PASSWORD")
|
|
if password == "" {
|
|
t.Skip("env variable CLOUDSQL_PASSWORD is empty")
|
|
}
|
|
// Requires environment variable CLOUDSQL_DATABASE to be set.
|
|
database := os.Getenv("CLOUDSQL_DATABASE")
|
|
if database == "" {
|
|
t.Skip("env variable CLOUDSQL_DATABASE is empty")
|
|
}
|
|
// Requires environment variable CLOUDSQL_ID to be set.
|
|
projectID := os.Getenv("PROJECT_ID")
|
|
if projectID == "" {
|
|
t.Skip("env variable PROJECT_ID is empty")
|
|
}
|
|
// Requires environment variable ALLOYDB_REGION to be set.
|
|
region := os.Getenv("CLOUDSQL_REGION")
|
|
if region == "" {
|
|
t.Skip("env variable CLOUDSQL_REGION is empty")
|
|
}
|
|
// Requires environment variable ALLOYDB_INSTANCE to be set.
|
|
instance := os.Getenv("CLOUDSQL_INSTANCE")
|
|
if instance == "" {
|
|
t.Skip("env variable CLOUDSQL_INSTANCE is empty")
|
|
}
|
|
// Requires environment variable CLOUDSQL_CLUSTER to be set.
|
|
cluster := os.Getenv("CLOUDSQL_CLUSTER")
|
|
if cluster == "" {
|
|
t.Skip("env variable CLOUDSQL_CLUSTER is empty")
|
|
}
|
|
// Requires environment variable CLOUDSQL_TABLE to be set.
|
|
table := os.Getenv("CLOUDSQL_TABLE")
|
|
if table == "" {
|
|
t.Skip("env variable CLOUDSQL_TABLE is empty")
|
|
}
|
|
|
|
envVariables := EnvVariables{
|
|
Username: username,
|
|
Password: password,
|
|
Database: database,
|
|
ProjectID: projectID,
|
|
Region: region,
|
|
Instance: instance,
|
|
Cluster: cluster,
|
|
Table: table,
|
|
}
|
|
|
|
return envVariables
|
|
}
|
|
|
|
func setEngine(t *testing.T, envVariables EnvVariables) cloudsqlutil.PostgresEngine {
|
|
t.Helper()
|
|
ctx := context.Background()
|
|
pgEngine, err := cloudsqlutil.NewPostgresEngine(ctx,
|
|
cloudsqlutil.WithUser(envVariables.Username),
|
|
cloudsqlutil.WithPassword(envVariables.Password),
|
|
cloudsqlutil.WithDatabase(envVariables.Database),
|
|
cloudsqlutil.WithCloudSQLInstance(envVariables.ProjectID, envVariables.Region, envVariables.Instance),
|
|
)
|
|
if err != nil {
|
|
t.Fatal("Could not set Engine: ", err)
|
|
}
|
|
|
|
return pgEngine
|
|
}
|
|
|
|
func vectorStore(t *testing.T, envVariables EnvVariables) (cloudsql.VectorStore, func() error) {
|
|
t.Helper()
|
|
if testing.Short() {
|
|
t.Skip("skipping cloudsql tests in short mode")
|
|
}
|
|
pgEngine := setEngine(t, envVariables)
|
|
ctx := context.Background()
|
|
vectorstoreTableoptions := cloudsqlutil.VectorstoreTableOptions{
|
|
TableName: envVariables.Table,
|
|
OverwriteExisting: true,
|
|
VectorSize: 1536,
|
|
StoreMetadata: true,
|
|
}
|
|
err := pgEngine.InitVectorstoreTable(ctx, vectorstoreTableoptions)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Initialize OpenAI embedder with httprr
|
|
e := createOpenAIEmbedder(t)
|
|
vs, err := cloudsql.NewVectorStore(pgEngine, e, envVariables.Table)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cleanUpTableFn := func() error {
|
|
_, err := pgEngine.Pool.Exec(ctx, fmt.Sprintf("DROP TABLE IF EXISTS %s", envVariables.Table))
|
|
return err
|
|
}
|
|
return vs, cleanUpTableFn
|
|
}
|
|
|
|
func TestApplyVectorIndexAndDropIndex(t *testing.T) {
|
|
t.Parallel()
|
|
envVariables := getEnvVariables(t)
|
|
vs, cleanUpTableFn := vectorStore(t, envVariables)
|
|
ctx := context.Background()
|
|
idx := vs.NewBaseIndex("testindex", "hnsw", cloudsql.CosineDistance{}, []string{}, cloudsql.HNSWOptions{M: 4, EfConstruction: 16})
|
|
err := vs.ApplyVectorIndex(ctx, idx, "testindex", false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = vs.DropVectorIndex(ctx, "testindex")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = cleanUpTableFn()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestIsValidIndex(t *testing.T) {
|
|
t.Parallel()
|
|
envVariables := getEnvVariables(t)
|
|
vs, cleanUpTableFn := vectorStore(t, envVariables)
|
|
ctx := context.Background()
|
|
idx := vs.NewBaseIndex("testindex", "hnsw", cloudsql.CosineDistance{}, []string{}, cloudsql.HNSWOptions{M: 4, EfConstruction: 16})
|
|
err := vs.ApplyVectorIndex(ctx, idx, "testindex", false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = vs.IsValidIndex(ctx, "testindex")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = vs.DropVectorIndex(ctx, "testindex")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = cleanUpTableFn()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestAddDocuments(t *testing.T) {
|
|
httprr.SkipIfNoCredentialsAndRecordingMissing(t, "OPENAI_API_KEY")
|
|
rr := httprr.OpenForTest(t, http.DefaultTransport)
|
|
defer rr.Close()
|
|
if !rr.Recording() {
|
|
t.Parallel()
|
|
}
|
|
|
|
ctx := context.Background()
|
|
envVariables := getEnvVariables(t)
|
|
vs, cleanUpTableFn := vectorStore(t, envVariables)
|
|
|
|
_, err := vs.AddDocuments(ctx, []schema.Document{
|
|
{
|
|
PageContent: "Tokyo",
|
|
Metadata: map[string]any{
|
|
"population": 38,
|
|
"area": 2190,
|
|
},
|
|
},
|
|
{
|
|
PageContent: "Paris",
|
|
Metadata: map[string]any{
|
|
"population": 11,
|
|
"area": 105,
|
|
},
|
|
},
|
|
{
|
|
PageContent: "London",
|
|
Metadata: map[string]any{
|
|
"population": 9.5,
|
|
"area": 1572,
|
|
},
|
|
},
|
|
{
|
|
PageContent: "Santiago",
|
|
Metadata: map[string]any{
|
|
"population": 6.9,
|
|
"area": 641,
|
|
},
|
|
},
|
|
{
|
|
PageContent: "Buenos Aires",
|
|
Metadata: map[string]any{
|
|
"population": 15.5,
|
|
"area": 203,
|
|
},
|
|
},
|
|
{
|
|
PageContent: "Rio de Janeiro",
|
|
Metadata: map[string]any{
|
|
"population": 13.7,
|
|
"area": 1200,
|
|
},
|
|
},
|
|
{
|
|
PageContent: "Sao Paulo",
|
|
Metadata: map[string]any{
|
|
"population": 22.6,
|
|
"area": 1523,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = cleanUpTableFn()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|