| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- // 用户显示名批量只读仓储单测(D4:academic 端点 53 的 `realName` 数据源)。
- //
- // 为什么自带 sqlite 库而不走 `setup(t)`:本包的 `setup` 依赖 `UNIFIED_TEST_DSN`(MySQL 集成测试),
- // 未设置时整包 Skip;本用例要证明的是**三条纯逻辑口径**(NULL → 不返回、空白 → 不返回、批量去重),
- // 与方言无关,用 sqlite 才能在默认 CI 环境真跑起来。
- package repository
- import (
- "context"
- "path/filepath"
- "testing"
- "time"
- gormsqlite "github.com/glebarez/sqlite"
- "gorm.io/gorm"
- "yuansiwei/unified-backend/internal/identity/model"
- )
- // newUserNameTestDB 建只含 `t_user` 的 sqlite 库。
- func newUserNameTestDB(t *testing.T) *gorm.DB {
- t.Helper()
- dsn := filepath.Join(t.TempDir(), "user_name_repo_test.db")
- db, err := gorm.Open(gormsqlite.Open(dsn), &gorm.Config{})
- if err != nil {
- t.Fatalf("gorm.Open(显示名测试库) 失败: %v", err)
- }
- sqlDB, err := db.DB()
- if err != nil {
- t.Fatalf("取 sql.DB 失败: %v", err)
- }
- sqlDB.SetMaxOpenConns(1)
- t.Cleanup(func() { _ = sqlDB.Close() })
- if err := db.AutoMigrate(&model.User{}); err != nil {
- t.Fatalf("AutoMigrate(t_user) 失败: %v", err)
- }
- return db
- }
- // insertUser 落一个用户(display_name 可 NULL)。
- func insertUser(t *testing.T, db *gorm.DB, uid string, displayName *string) {
- t.Helper()
- row := &model.User{CanonicalUserID: uid, DisplayName: displayName}
- if err := db.Create(row).Error; err != nil {
- t.Fatalf("插入用户 %s 失败: %v", uid, err)
- }
- _ = time.Now()
- }
- // TestDisplayNamesContracts 三条口径 + 批量去重(**任一条写错都会让端点 53 的姓名列出现脏值**)。
- //
- // A 有名字 → 返回 "张三"
- // B display_name 为 NULL → **不进 map**(调用方下发 null)
- // C display_name 为空白串 → **不进 map**(不能把 " " 当姓名)
- // D 有名字但重复传两次 → 只查一次、结果一次
- // E 库里不存在 → 不进 map
- func TestDisplayNamesContracts(t *testing.T) {
- db := newUserNameTestDB(t)
- const (
- userA = "01HUSERA0000000000000000A"
- userB = "01HUSERB0000000000000000B"
- userC = "01HUSERC0000000000000000C"
- userD = "01HUSERD0000000000000000D"
- userE = "01HUSERE0000000000000000E"
- )
- insertUser(t, db, userA, strPtr("张三"))
- insertUser(t, db, userB, nil)
- insertUser(t, db, userC, strPtr(" "))
- insertUser(t, db, userD, strPtr("李四"))
- repo := NewUserNameRepo(db)
- names, err := repo.DisplayNames(context.Background(),
- []string{userA, userB, userC, userD, userD, userE, " "})
- if err != nil {
- t.Fatalf("DisplayNames 失败: %v", err)
- }
- if got, ok := names[userA]; !ok || got != "张三" {
- t.Fatalf("A = %q(在 map 内=%v),期望 张三", got, ok)
- }
- if _, ok := names[userB]; ok {
- t.Fatalf("B 在 map 内:display_name 为 NULL 时**不得**返回(调用方要据此下发 null)")
- }
- if _, ok := names[userC]; ok {
- t.Fatalf("C 在 map 内:display_name 为空白串时**不得**返回(空白不是姓名)")
- }
- if got, ok := names[userD]; !ok || got != "李四" {
- t.Fatalf("D = %q(在 map 内=%v),期望 李四", got, ok)
- }
- if _, ok := names[userE]; ok {
- t.Fatalf("E 在 map 内:库中不存在该用户,不得返回")
- }
- if len(names) != 2 {
- t.Fatalf("返回条数 = %d,期望 2(只有 A/D 有真名字):%v", len(names), names)
- }
- }
- // TestDisplayNamesEmptyInput 空入参不发 SQL、返回空 map(不是 nil,避免调用方 nil 解引用)。
- func TestDisplayNamesEmptyInput(t *testing.T) {
- db := newUserNameTestDB(t)
- repo := NewUserNameRepo(db)
- for _, ids := range [][]string{nil, {}, {"", " "}} {
- names, err := repo.DisplayNames(context.Background(), ids)
- if err != nil {
- t.Fatalf("空入参不该报错: %v", err)
- }
- if names == nil {
- t.Fatalf("空入参应返回空 map 而非 nil(入参 %v)", ids)
- }
- if len(names) != 0 {
- t.Fatalf("空入参应返回 0 条,实际 %d", len(names))
- }
- }
- }
- // TestDisplayNamesNilDB 未装配 DB 时返回错误(不得 panic)。
- func TestDisplayNamesNilDB(t *testing.T) {
- repo := NewUserNameRepo(nil)
- if _, err := repo.DisplayNames(context.Background(), []string{"01HUSERA0000000000000000A"}); err == nil {
- t.Fatal("DB 为 nil 时应返回错误,而不是静默返回空 map(那会让\"没名字\"与\"库连不上\"混为一谈)")
- }
- }
|