Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

266 lignes
11 KiB

  1. package service
  2. import (
  3. "fmt"
  4. "os"
  5. "strings"
  6. "testing"
  7. "github.com/QuantumNous/new-api/common"
  8. "github.com/QuantumNous/new-api/constant"
  9. "github.com/QuantumNous/new-api/model"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/stretchr/testify/require"
  12. "gorm.io/gorm"
  13. "gorm.io/gorm/logger"
  14. )
  15. func setupDoubaoAssetChannelDB(t *testing.T) *gorm.DB {
  16. t.Helper()
  17. oldDB := model.DB
  18. oldSQLitePath := common.SQLitePath
  19. oldMemoryCacheEnabled := common.MemoryCacheEnabled
  20. oldIsMasterNode := common.IsMasterNode
  21. oldUsingSQLite := common.UsingSQLite
  22. oldUsingMySQL := common.UsingMySQL
  23. oldUsingPostgreSQL := common.UsingPostgreSQL
  24. oldSQLDSN, hadSQLDSN := os.LookupEnv("SQL_DSN")
  25. common.SQLitePath = "file:" + strings.NewReplacer("/", "_", " ", "_").Replace(t.Name()) + "?mode=memory&cache=shared"
  26. common.MemoryCacheEnabled = false
  27. common.IsMasterNode = false
  28. common.UsingSQLite = false
  29. common.UsingMySQL = false
  30. common.UsingPostgreSQL = false
  31. require.NoError(t, os.Setenv("SQL_DSN", "local"))
  32. require.NoError(t, model.InitDB())
  33. model.DB = model.DB.Session(&gorm.Session{Logger: logger.Default.LogMode(logger.Silent)})
  34. db := model.DB
  35. sqlDB, err := db.DB()
  36. require.NoError(t, err)
  37. sqlDB.SetMaxOpenConns(1)
  38. require.NoError(t, db.AutoMigrate(&model.Channel{}, &model.Ability{}, &model.UserAssetChannel{}))
  39. t.Cleanup(func() {
  40. _ = sqlDB.Close()
  41. model.DB = oldDB
  42. common.SQLitePath = oldSQLitePath
  43. common.MemoryCacheEnabled = oldMemoryCacheEnabled
  44. common.IsMasterNode = oldIsMasterNode
  45. common.UsingSQLite = oldUsingSQLite
  46. common.UsingMySQL = oldUsingMySQL
  47. common.UsingPostgreSQL = oldUsingPostgreSQL
  48. if hadSQLDSN {
  49. _ = os.Setenv("SQL_DSN", oldSQLDSN)
  50. } else {
  51. _ = os.Unsetenv("SQL_DSN")
  52. }
  53. })
  54. return db
  55. }
  56. func createDoubaoAssetChannelForTest(t *testing.T, db *gorm.DB, id int, channelType int, group string, key string, status int) {
  57. t.Helper()
  58. priority := int64(id)
  59. weight := uint(10)
  60. autoBan := 1
  61. channel := &model.Channel{
  62. Id: id,
  63. Type: channelType,
  64. Key: key,
  65. Status: status,
  66. Name: fmt.Sprintf("channel-%d", id),
  67. Group: group,
  68. Models: "seedance-2",
  69. Priority: &priority,
  70. Weight: &weight,
  71. AutoBan: &autoBan,
  72. CreatedTime: int64(id),
  73. }
  74. require.NoError(t, db.Create(channel).Error)
  75. }
  76. func createDoubaoAssetAbilityForTest(t *testing.T, db *gorm.DB, group string, modelName string, channelId int, enabled bool) {
  77. t.Helper()
  78. priority := int64(channelId)
  79. require.NoError(t, db.Create(&model.Ability{
  80. Group: group,
  81. Model: modelName,
  82. ChannelId: channelId,
  83. Enabled: enabled,
  84. Priority: &priority,
  85. Weight: 10,
  86. }).Error)
  87. }
  88. func TestResolveDoubaoAssetChannel_PrefersBoundChannelWithKey(t *testing.T) {
  89. db := setupDoubaoAssetChannelDB(t)
  90. createDoubaoAssetChannelForTest(t, db, 1, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", "bound-key", common.ChannelStatusEnabled)
  91. createDoubaoAssetChannelForTest(t, db, 2, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", "auto-key", common.ChannelStatusEnabled)
  92. require.NoError(t, model.BindUserAssetChannel(10, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", 1))
  93. channel, err := ResolveDoubaoAssetChannel(10, "default")
  94. require.NoError(t, err)
  95. require.NotNil(t, channel)
  96. assert.Equal(t, 1, channel.Id)
  97. assert.Equal(t, "bound-key", channel.Key)
  98. }
  99. func TestResolveDoubaoAssetChannel_AutoMatchesAndPersistsWhenUnbound(t *testing.T) {
  100. db := setupDoubaoAssetChannelDB(t)
  101. createDoubaoAssetChannelForTest(t, db, 1, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", "auto-key", common.ChannelStatusEnabled)
  102. channel, err := ResolveDoubaoAssetChannel(10, "default")
  103. require.NoError(t, err)
  104. require.NotNil(t, channel)
  105. assert.Equal(t, 1, channel.Id)
  106. binding, err := model.GetUserAssetChannel(10, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default")
  107. require.NoError(t, err)
  108. require.NotNil(t, binding)
  109. assert.Equal(t, 1, binding.ChannelId)
  110. }
  111. func TestResolveDoubaoAssetChannel_AutoMatchesTianyiYunAndPersistsByType(t *testing.T) {
  112. db := setupDoubaoAssetChannelDB(t)
  113. createDoubaoAssetChannelForTest(t, db, 1, constant.ChannelTypeDoubaoVideoCompatibleTianyiYun, "default", "tianyiyun-key", common.ChannelStatusEnabled)
  114. createDoubaoAssetAbilityForTest(t, db, "default", "Doubao-Seedance-2.0", 1, true)
  115. channel, err := ResolveDoubaoAssetChannelForModel(10, "default", "Doubao-Seedance-2.0")
  116. require.NoError(t, err)
  117. require.NotNil(t, channel)
  118. assert.Equal(t, 1, channel.Id)
  119. assert.Equal(t, constant.ChannelTypeDoubaoVideoCompatibleTianyiYun, channel.Type)
  120. binding, err := model.GetUserAssetChannel(10, constant.ChannelTypeDoubaoVideoCompatibleTianyiYun, "default")
  121. require.NoError(t, err)
  122. require.NotNil(t, binding)
  123. assert.Equal(t, 1, binding.ChannelId)
  124. }
  125. func TestGetBoundDoubaoAssetChannelForModelFindsTianyiYunBindingForPublicModel(t *testing.T) {
  126. db := setupDoubaoAssetChannelDB(t)
  127. createDoubaoAssetChannelForTest(t, db, 16, constant.ChannelTypeDoubaoVideoCompatibleTianyiYun, "default", "tianyiyun-key", common.ChannelStatusEnabled)
  128. createDoubaoAssetAbilityForTest(t, db, "default", "Doubao-Seedance-2.0", 16, true)
  129. require.NoError(t, model.BindUserAssetChannel(10, constant.ChannelTypeDoubaoVideoCompatibleTianyiYun, "default", 16))
  130. channel, err := GetBoundDoubaoAssetChannelForModel(10, "default", "Doubao-Seedance-2.0")
  131. require.NoError(t, err)
  132. require.NotNil(t, channel)
  133. assert.Equal(t, 16, channel.Id)
  134. }
  135. func TestResolveDoubaoAssetChannel_RebindsOpenAIMisbindToAiping(t *testing.T) {
  136. db := setupDoubaoAssetChannelDB(t)
  137. createDoubaoAssetChannelForTest(t, db, 1, constant.ChannelTypeOpenAI, "default", "openai-key", common.ChannelStatusEnabled)
  138. createDoubaoAssetChannelForTest(t, db, 2, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", "aiping-key", common.ChannelStatusEnabled)
  139. require.NoError(t, model.BindUserAssetChannel(10, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", 1))
  140. channel, err := ResolveDoubaoAssetChannel(10, "default")
  141. require.NoError(t, err)
  142. require.NotNil(t, channel)
  143. assert.Equal(t, 2, channel.Id)
  144. binding, err := model.GetUserAssetChannel(10, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default")
  145. require.NoError(t, err)
  146. require.NotNil(t, binding)
  147. assert.Equal(t, 2, binding.ChannelId)
  148. }
  149. func TestResolveDoubaoAssetChannel_RebindsOnlyCurrentGroupWhenBoundGroupMismatches(t *testing.T) {
  150. db := setupDoubaoAssetChannelDB(t)
  151. createDoubaoAssetChannelForTest(t, db, 1, constant.ChannelTypeDoubaoVideoCompatibleAiping, "vip", "vip-key", common.ChannelStatusEnabled)
  152. createDoubaoAssetChannelForTest(t, db, 2, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", "default-key", common.ChannelStatusEnabled)
  153. require.NoError(t, model.BindUserAssetChannel(10, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", 1))
  154. require.NoError(t, model.BindUserAssetChannel(10, constant.ChannelTypeDoubaoVideoCompatibleAiping, "vip", 1))
  155. channel, err := ResolveDoubaoAssetChannel(10, "default")
  156. require.NoError(t, err)
  157. require.NotNil(t, channel)
  158. assert.Equal(t, 2, channel.Id)
  159. defaultBinding, err := model.GetUserAssetChannel(10, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default")
  160. require.NoError(t, err)
  161. require.NotNil(t, defaultBinding)
  162. assert.Equal(t, 2, defaultBinding.ChannelId)
  163. vipBinding, err := model.GetUserAssetChannel(10, constant.ChannelTypeDoubaoVideoCompatibleAiping, "vip")
  164. require.NoError(t, err)
  165. require.NotNil(t, vipBinding)
  166. assert.Equal(t, 1, vipBinding.ChannelId)
  167. }
  168. func TestResolveDoubaoAssetChannel_AutoMatchScansPastFirstPage(t *testing.T) {
  169. db := setupDoubaoAssetChannelDB(t)
  170. createDoubaoAssetChannelForTest(t, db, 1, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", "default-key", common.ChannelStatusEnabled)
  171. for i := 2; i <= DoubaoAssetChannelPageSize+1; i++ {
  172. createDoubaoAssetChannelForTest(t, db, i, constant.ChannelTypeDoubaoVideoCompatibleAiping, "vip", fmt.Sprintf("vip-key-%d", i), common.ChannelStatusEnabled)
  173. }
  174. channel, err := ResolveDoubaoAssetChannel(10, "default")
  175. require.NoError(t, err)
  176. require.NotNil(t, channel)
  177. assert.Equal(t, 1, channel.Id)
  178. }
  179. func TestResolveDoubaoAssetChannel_ReturnsErrorWhenBindingWriteFails(t *testing.T) {
  180. db := setupDoubaoAssetChannelDB(t)
  181. createDoubaoAssetChannelForTest(t, db, 1, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", "auto-key", common.ChannelStatusEnabled)
  182. const callbackName = "doubao_asset_test:fail_user_asset_channel_create"
  183. require.NoError(t, db.Callback().Create().Before("gorm:create").Register(callbackName, func(tx *gorm.DB) {
  184. if _, ok := tx.Statement.Dest.(*model.UserAssetChannel); ok {
  185. tx.AddError(assert.AnError)
  186. }
  187. }))
  188. channel, err := ResolveDoubaoAssetChannel(10, "default")
  189. require.Error(t, err)
  190. assert.Nil(t, channel)
  191. }
  192. func TestHasDoubaoVideoChannelForGroupModel(t *testing.T) {
  193. db := setupDoubaoAssetChannelDB(t)
  194. createDoubaoAssetChannelForTest(t, db, 1, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", "default-key", common.ChannelStatusEnabled)
  195. createDoubaoAssetChannelForTest(t, db, 2, constant.ChannelTypeDoubaoVideoCompatibleAiping, "vip", "vip-key", common.ChannelStatusEnabled)
  196. createDoubaoAssetChannelForTest(t, db, 3, constant.ChannelTypeOpenAI, "default", "openai-key", common.ChannelStatusEnabled)
  197. createDoubaoAssetChannelForTest(t, db, 4, constant.ChannelTypeDoubaoVideoCompatibleTianyiYun, "default", "tianyiyun-key", common.ChannelStatusEnabled)
  198. createDoubaoAssetAbilityForTest(t, db, "default", "seedance-2", 1, true)
  199. createDoubaoAssetAbilityForTest(t, db, "vip", "seedance-2", 2, true)
  200. createDoubaoAssetAbilityForTest(t, db, "default", "disabled-model", 1, false)
  201. createDoubaoAssetAbilityForTest(t, db, "default", "seedance-2", 3, true)
  202. createDoubaoAssetAbilityForTest(t, db, "default", "Doubao-Seedance-2.0", 4, true)
  203. assert.True(t, HasDoubaoVideoChannelForGroupModel("default", "seedance-2"))
  204. assert.True(t, HasDoubaoVideoChannelForGroupModel("default", "Doubao-Seedance-2.0"))
  205. assert.False(t, HasDoubaoVideoChannelForGroupModel("default", "disabled-model"))
  206. assert.False(t, HasDoubaoVideoChannelForGroupModel("missing", "seedance-2"))
  207. }
  208. func TestAutoMatchDoubaoAssetChannel_IgnoresLegacyDoubaoVideoChannels(t *testing.T) {
  209. db := setupDoubaoAssetChannelDB(t)
  210. createDoubaoAssetChannelForTest(t, db, 1, constant.ChannelTypeDoubaoVideo, "default", "legacy-key", common.ChannelStatusEnabled)
  211. createDoubaoAssetChannelForTest(t, db, 2, constant.ChannelTypeDoubaoVideoCompatibleAiping, "default", "aiping-key", common.ChannelStatusEnabled)
  212. channel, err := AutoMatchDoubaoAssetChannel("default")
  213. require.NoError(t, err)
  214. require.NotNil(t, channel)
  215. assert.Equal(t, 2, channel.Id)
  216. assert.Equal(t, constant.ChannelTypeDoubaoVideoCompatibleAiping, channel.Type)
  217. }