package api import ( "testing" "github.com/openteam/server/internal/store" "gorm.io/gorm" ) // 邀请码必须精确匹配(子串匹配会让任意后缀放行)且用后消费(一码一用)。 func TestInviteCodesLoadAndConsume(t *testing.T) { db := openInviteTestDB(t) // 历史形态:JSON 字符串 + 逗号分隔 if err := db.Create(&store.SystemConfig{Key: "invite_codes", Value: `"ALPHA-2026, BETA-7;"`}).Error; err != nil { t.Fatal(err) } codes, err := loadInviteCodes(db) if err != nil { t.Fatal(err) } if len(codes) != 2 || codes[0] != "ALPHA-2026" || codes[1] != "BETA-7" { t.Fatalf("应解析出 2 个邀请码,实际 %v", codes) } // 消费:精确码被移除,其余保留;写回仍是字符串形态(管理后台按逗号分隔编辑) if err := consumeInviteCode(db, "ALPHA-2026"); err != nil { t.Fatal(err) } codes, _ = loadInviteCodes(db) if len(codes) != 1 || codes[0] != "BETA-7" { t.Fatalf("消费后应剩 1 个邀请码,实际 %v", codes) } var raw string db.Model(&store.SystemConfig{}).Where("key = ?", "invite_codes").Pluck("value", &raw) if raw != `"BETA-7"` { t.Fatalf("写回应为 JSON 字符串形态,实际 %s", raw) } // 消费不存在的码:无变化、不报错 if err := consumeInviteCode(db, "NOT-THERE"); err != nil { t.Fatalf("消费不存在的码不应报错: %v", err) } // 新形态:JSON 数组 db.Where("key = ?", "invite_codes").Delete(&store.SystemConfig{}) db.Create(&store.SystemConfig{Key: "invite_codes", Value: `["CODE-A","CODE-B"]`}) codes, err = loadInviteCodes(db) if err != nil { t.Fatal(err) } if len(codes) != 2 || codes[0] != "CODE-A" { t.Fatalf("数组形态应可解析,实际 %v", codes) } // 非法 JSON:报错而不是静默全放行 db.Where("key = ?", "invite_codes").Delete(&store.SystemConfig{}) db.Create(&store.SystemConfig{Key: "invite_codes", Value: `{invalid`}) if _, err := loadInviteCodes(db); err == nil { t.Fatal("非法配置应报错") } } // 消费时同码重复录入只删一个。 func TestConsumeInviteCodeDuplicateEntry(t *testing.T) { db := openInviteTestDB(t) db.Create(&store.SystemConfig{Key: "invite_codes", Value: `"CODE-A,CODE-A,CODE-B"`}) if err := consumeInviteCode(db, "CODE-A"); err != nil { t.Fatal(err) } codes, _ := loadInviteCodes(db) if len(codes) != 2 || codes[0] != "CODE-A" || codes[1] != "CODE-B" { t.Fatalf("同码重复只应删一个,实际 %v", codes) } } func openInviteTestDB(t *testing.T) *gorm.DB { t.Helper() db, err := store.Open("sqlite", ":memory:") if err != nil { t.Fatalf("open db: %v", err) } t.Cleanup(func() { sqlDB, _ := db.DB() sqlDB.Close() }) return db }