82 lines
2.5 KiB
Go
82 lines
2.5 KiB
Go
package repository
|
||
|
||
import (
|
||
"math"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
// TestFormatEmbeddingVector 向量串格式化是写入正确性的关键:
|
||
// 维度必须精确、float32 精度不能丢(截断会让相似度漂移)。
|
||
func TestFormatEmbeddingVector(t *testing.T) {
|
||
v := make([]float32, 768)
|
||
v[0] = 0.5
|
||
v[767] = -0.25
|
||
|
||
s := FormatEmbeddingVector(v)
|
||
if !strings.HasPrefix(s, "[") || !strings.HasSuffix(s, "]") {
|
||
t.Fatalf("应输出 [..] 形式的向量串,得到 %q", truncForTest(s))
|
||
}
|
||
if got := strings.Count(s, ",") + 1; got != 768 {
|
||
t.Fatalf("维度应为 768,得到 %d", got)
|
||
}
|
||
if !strings.Contains(s, "0.5") || !strings.Contains(s, "-0.25") {
|
||
t.Fatalf("首尾元素未按 float32 精度输出: %s", truncForTest(s))
|
||
}
|
||
}
|
||
|
||
// TestFormatEmbeddingVectorEmpty 空向量必须能安全格式化,不能 panic。
|
||
func TestFormatEmbeddingVectorEmpty(t *testing.T) {
|
||
if got := FormatEmbeddingVector(nil); got != "[]" {
|
||
t.Fatalf("nil 应格式化为 [],得到 %q", got)
|
||
}
|
||
}
|
||
|
||
// TestParseEmbeddingVectorRoundTrip 格式化与解析必须能精确往返,
|
||
// 否则「写进去的向量」与「读出来算相似度的向量」不是同一个,阈值校准会失真。
|
||
func TestParseEmbeddingVectorRoundTrip(t *testing.T) {
|
||
v := make([]float32, 768)
|
||
for i := range v {
|
||
v[i] = float32(i%17-8) * 0.0173
|
||
}
|
||
got := ParseEmbeddingVector(FormatEmbeddingVector(v))
|
||
if len(got) != len(v) {
|
||
t.Fatalf("往返后长度应一致:%d != %d", len(got), len(v))
|
||
}
|
||
for i := range v {
|
||
if got[i] != v[i] {
|
||
t.Fatalf("第 %d 维往返后不一致:%v != %v", i, got[i], v[i])
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestParseEmbeddingVectorInvalid 非法输入返回 nil(调用方据此跳过该行,不阻断渲染)。
|
||
func TestParseEmbeddingVectorInvalid(t *testing.T) {
|
||
for _, bad := range []string{"", "[]", "0.1,0.2", "[0.1,abc]", "[", "null"} {
|
||
if got := ParseEmbeddingVector(bad); got != nil {
|
||
t.Fatalf("非法输入 %q 应返回 nil,得到 %v", bad, got)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestFormatEmbeddingVectorNormalized 归一化向量点积等于余弦相似度:
|
||
// 用同一向量的自点积验证模长≈1(服务端已归一化的前提)。
|
||
func TestFormatEmbeddingVectorNormalized(t *testing.T) {
|
||
v := []float32{0.6, 0.8} // 模长为 1
|
||
got := ParseEmbeddingVector(FormatEmbeddingVector(v))
|
||
var dot float64
|
||
for _, f := range got {
|
||
dot += float64(f) * float64(f)
|
||
}
|
||
if math.Abs(dot-1) > 1e-6 {
|
||
t.Fatalf("模长应≈1,得到 %f", dot)
|
||
}
|
||
}
|
||
|
||
func truncForTest(s string) string {
|
||
if len(s) > 80 {
|
||
return s[:80] + "..."
|
||
}
|
||
return s
|
||
}
|