Files

192 lines
4.9 KiB
Go

package vector
import (
"context"
"errors"
"testing"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/embedding"
)
func TestSQLite_New_NilDB_异常(t *testing.T) {
t.Parallel()
if _, err := NewSQLite(context.Background(), nil, 4, testLogger()); !errors.Is(err, ErrNilDB) {
t.Fatalf("NewSQLite(nil db) err = %v, want ErrNilDB", err)
}
}
func TestSQLite_New_InvalidDim_异常(t *testing.T) {
t.Parallel()
ctx := context.Background()
if _, err := NewSQLite(ctx, openTestDB(t), 0, testLogger()); !errors.Is(err, ErrInvalidDim) {
t.Fatalf("NewSQLite(dim 0) err = %v, want ErrInvalidDim", err)
}
}
func TestSQLite_AddSearch_正常(t *testing.T) {
t.Parallel()
ctx := context.Background()
s := newInMemorySQLite(t, 3)
for _, e := range []struct {
id string
v Vector
}{
{id: "cat", v: Vector{1, 0.5, 0}},
{id: "dog", v: Vector{0.5, 1, 0}},
{id: "bird", v: Vector{0, 0, 1}},
} {
if err := s.Add(ctx, e.id, e.v); err != nil {
t.Fatalf("Add(%s): %v", e.id, err)
}
}
hits, err := s.Search(ctx, Vector{1, 0, 0}, 2)
if err != nil {
t.Fatalf("Search: %v", err)
}
if len(hits) != 2 {
t.Fatalf("got %d hits, want 2", len(hits))
}
if hits[0].ID != "cat" || hits[1].ID != "dog" {
t.Fatalf("hits = [%s %s], want [cat dog]", hits[0].ID, hits[1].ID)
}
if hits[0].Score <= hits[1].Score {
t.Fatalf("top score not descending: %v <= %v", hits[0].Score, hits[1].Score)
}
if hits[0].Score <= 0.89 || hits[0].Score >= 0.9 {
t.Errorf("cat score = %v, want ~0.894", hits[0].Score)
}
}
func TestSQLite_Add_Upsert_正常(t *testing.T) {
t.Parallel()
ctx := context.Background()
s := newInMemorySQLite(t, 3)
if err := s.Add(ctx, "a", Vector{1, 0, 0}); err != nil {
t.Fatalf("Add first: %v", err)
}
if err := s.Add(ctx, "a", Vector{0, 1, 0}); err != nil {
t.Fatalf("Add update: %v", err)
}
hits, err := s.Search(ctx, Vector{0, 1, 0}, 5)
if err != nil {
t.Fatalf("Search: %v", err)
}
if len(hits) != 1 || hits[0].ID != "a" {
t.Fatalf("hits = %+v, want single a", hits)
}
if hits[0].Score < 0.99 {
t.Fatalf("a score = %v, want ~1.0 after upsert", hits[0].Score)
}
}
func TestSQLite_Add_MissingID_异常(t *testing.T) {
t.Parallel()
ctx := context.Background()
s := newInMemorySQLite(t, 4)
if err := s.Add(ctx, " ", Vector{1, 0, 0, 0}); !errors.Is(err, ErrMissingID) {
t.Fatalf("Add(blank id) err = %v, want ErrMissingID", err)
}
}
func TestSQLite_Add_DimMismatch_异常(t *testing.T) {
t.Parallel()
ctx := context.Background()
s := newInMemorySQLite(t, 4)
if err := s.Add(ctx, "x", Vector{1, 2}); !errors.Is(err, ErrDimMismatch) {
t.Fatalf("Add(wrong dim) err = %v, want ErrDimMismatch", err)
}
}
func TestSQLite_Search_Empty_正常(t *testing.T) {
t.Parallel()
ctx := context.Background()
s := newInMemorySQLite(t, 4)
hits, err := s.Search(ctx, Vector{1, 0, 0, 0}, 3)
if err != nil {
t.Fatalf("Search empty: %v", err)
}
if hits == nil || len(hits) != 0 {
t.Fatalf("Search(empty) = %v, want non-nil empty slice", hits)
}
}
func TestSQLite_Search_TopKNonPositive_正常(t *testing.T) {
t.Parallel()
ctx := context.Background()
s := newInMemorySQLite(t, 4)
if err := s.Add(ctx, "a", Vector{1, 0, 0, 0}); err != nil {
t.Fatalf("Add: %v", err)
}
for _, k := range []int{0, -1} {
hits, err := s.Search(ctx, Vector{1, 0, 0, 0}, k)
if err != nil {
t.Fatalf("Search(topK %d): %v", k, err)
}
if len(hits) != 0 {
t.Fatalf("Search(topK %d) = %v, want empty", k, hits)
}
}
}
func TestSQLite_Search_DimMismatch_异常(t *testing.T) {
t.Parallel()
ctx := context.Background()
s := newInMemorySQLite(t, 4)
if _, err := s.Search(ctx, Vector{1, 0}, 3); !errors.Is(err, ErrDimMismatch) {
t.Fatalf("Search(wrong dim) err = %v, want ErrDimMismatch", err)
}
}
func TestSQLite_Embed_正常(t *testing.T) {
t.Parallel()
ctx := context.Background()
s := newInMemorySQLite(t, 4)
s.withEmbedder(&mockEmbedder{dim: 4})
vs, err := s.Embed(ctx, []string{"hello", "world"})
if err != nil {
t.Fatalf("Embed: %v", err)
}
if len(vs) != 2 || len(vs[0]) != 4 || len(vs[1]) != 4 {
t.Fatalf("Embed => %d vectors, want 2 x 4-dim", len(vs))
}
}
func TestSQLite_Embed_NoEmbedder_异常(t *testing.T) {
t.Parallel()
ctx := context.Background()
s := newInMemorySQLite(t, 4)
if _, err := s.Embed(ctx, []string{"hi"}); !errors.Is(err, ErrNoEmbedder) {
t.Fatalf("Embed(no embedder) err = %v, want ErrNoEmbedder", err)
}
}
func TestSQLite_Embed_EmbedderError_异常(t *testing.T) {
t.Parallel()
ctx := context.Background()
s := newInMemorySQLite(t, 4)
boom := errors.New("embed boom")
s.withEmbedder(&mockEmbedder{
dim: 4,
fn: func(context.Context, []string) ([]embedding.Vector, error) { return nil, boom },
})
if _, err := s.Embed(ctx, []string{"hi"}); !errors.Is(err, boom) {
t.Fatalf("Embed(driver error) err = %v, want wrap of %v", err, boom)
}
}
func TestSQLite_Close_正常(t *testing.T) {
t.Parallel()
s := newInMemorySQLite(t, 4)
if err := s.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
}