Files
backend_v2/internal/repository/dedup_integration_test.go

109 lines
3.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build integration
// 集成测试:去重核心查询需要真实 PostgreSQL + pgvector 环境。
//
// 运行方式(先起库,见 scripts/pgvector):
//
// docker compose -f scripts/pgvector/docker-compose.yml up -d
// go test -tags integration ./internal/repository/ -run TestDedup -v
//
// 默认 `go test ./...` 不会编译本文件(build tag 隔离),CI 无库环境不受影响。
package repository
import (
"context"
"database/sql"
"testing"
"fashionapi/internal/config"
"fashionapi/internal/database"
"fashionapi/internal/model"
"fashionapi/internal/pkg/phash"
"gorm.io/gorm"
)
// testDB 连真实库并校验去重所需的结构已就绪。
//
// 结构不再由 AutoMigrate 自动创建(已移除),须先自行准备好库,例如:
//
// ./bin/dbtool dump -out db_dump.sql
// psql -U fashion -d fashion -v ON_ERROR_STOP=1 -f db_dump.sql
//
// 这里只做存在性校验并给出明确提示,避免后续查询报出难懂的错。
func testDB(t *testing.T) *gorm.DB {
t.Helper()
cfg, err := config.Load("")
if err != nil {
t.Fatalf("加载配置失败: %v", err)
}
db, err := database.New(cfg.Database)
if err != nil {
t.Fatalf("连接数据库失败: %v", err)
}
t.Cleanup(func() { _ = database.Close(db) })
if err := db.Exec("SELECT 1 FROM brand_runway_images LIMIT 1").Error; err != nil {
t.Fatalf("brand_runway_images 不可用(库结构未就绪?先 dbtool dump 再 psql -f 灌库): %v", err)
}
return db
}
// insertRunwayImage 直接插入一条走秀图片行(无外键约束,runway_id / brand_id 可填占位值)。
func insertRunwayImage(t *testing.T, db *gorm.DB, image, phashBits string) {
t.Helper()
var ph sql.NullString
if phashBits != "" {
ph = sql.NullString{String: phashBits, Valid: true}
}
row := model.BrandRunwayImage{
RunwayID: 1,
BrandID: 1,
Image: image,
Name: "look",
SortOrder: 1,
Phash: ph,
}
if err := db.Create(&row).Error; err != nil {
t.Fatalf("插入图片失败: %v", err)
}
t.Cleanup(func() {
db.Where("image = ?", image).Delete(&model.BrandRunwayImage{})
})
}
// TestDedupNearDuplicate 近重复:dHash 汉明距离 ≤ 阈值应被 FindNearDuplicateImage 命中。
func TestDedupNearDuplicate(t *testing.T) {
db := testDB(t)
repo := NewIngestRepository(db)
ctx := context.Background()
// 插入一张已有图片,phash 为 64 维全 1 向量。
insertRunwayImage(t, db, "runway/near-dup.jpg", phash.ToVectorBits(^uint64(0)))
tables := []string{"brand_runway_images"}
// 探测向量:仅第 0 位为 0、其余为 1 → 与全 1 向量汉明距离 = 1,应命中。
probe := ^uint64(0) &^ 1
_, found, err := repo.FindNearDuplicateImage(ctx, tables, phash.ToVectorBits(probe), phash.DefaultThreshold)
if err != nil {
t.Fatalf("FindNearDuplicateImage 出错: %v", err)
}
if !found {
t.Fatalf("汉明距离 1 的近重复应被命中")
}
// 全 1 向量(与库中那条距离 0)的取反不是合法探针(会有空串问题),
// 这里改用与库中那条完全相同的全 1 探针(非空、合法)并先断言查询本身不报错。
probeSame, foundSame, errSame := repo.FindNearDuplicateImage(ctx, tables, phash.ToVectorBits(^uint64(0)), phash.DefaultThreshold)
if errSame != nil {
t.Fatalf("去重查询不应报错: %v", errSame)
}
if !foundSame {
t.Fatalf("与库中那条完全相同的 phash 应命中")
}
if probeSame == 0 {
t.Fatalf("命中的行 id 不应为 0")
}
}