update backend
This commit is contained in:
96
internal/repository/dedup_integration_test.go
Normal file
96
internal/repository/dedup_integration_test.go
Normal file
@ -0,0 +1,96 @@
|
||||
//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 + 去重 schema 初始化。
|
||||
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)
|
||||
}
|
||||
if err := database.AutoMigrate(db); err != nil {
|
||||
t.Fatalf("AutoMigrate 失败: %v", err)
|
||||
}
|
||||
if err := database.EnsureDedupSchema(db); err != nil {
|
||||
t.Fatalf("去重 schema 失败: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = database.Close(db) })
|
||||
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 的近重复应被命中")
|
||||
}
|
||||
|
||||
// 全 0 向量:与全 1 向量汉明距离 = 64,远超阈值,不应命中。
|
||||
_, found2, _ := repo.FindNearDuplicateImage(ctx, tables, phash.ToVectorBits(0), phash.DefaultThreshold)
|
||||
if found2 {
|
||||
t.Fatalf("汉明距离 64 的图不应被误判为近重复")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user