You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

247 lines
5.8 KiB

  1. package model
  2. import (
  3. "errors"
  4. "testing"
  5. "github.com/QuantumNous/new-api/common"
  6. "github.com/glebarez/sqlite"
  7. "github.com/stretchr/testify/require"
  8. "gorm.io/gorm"
  9. )
  10. func setupRegionMigrationUserDB(t *testing.T) *gorm.DB {
  11. t.Helper()
  12. db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
  13. require.NoError(t, err)
  14. sqlDB, err := db.DB()
  15. require.NoError(t, err)
  16. sqlDB.SetMaxOpenConns(1)
  17. require.NoError(t, db.AutoMigrate(&User{}, &CustomOAuthProvider{}, &UserOAuthBinding{}))
  18. return db
  19. }
  20. func TestInsertImportedUserWithHashedPassword_PreservesHash(t *testing.T) {
  21. db := setupRegionMigrationUserDB(t)
  22. orig := DB
  23. DB = db
  24. defer func() {
  25. DB = orig
  26. }()
  27. user := &User{
  28. Username: "ov-alice",
  29. Password: "already-hashed-value",
  30. Email: "alice@example.com",
  31. DisplayName: "Alice",
  32. Status: common.UserStatusEnabled,
  33. Role: common.RoleCommonUser,
  34. Group: "default",
  35. Quota: 123,
  36. AffCode: "A001",
  37. CreatedAt: 1710000000,
  38. }
  39. require.NoError(t, InsertImportedUserWithHashedPassword(user))
  40. var saved User
  41. require.NoError(t, db.Where("username = ?", "ov-alice").First(&saved).Error)
  42. require.Equal(t, "already-hashed-value", saved.Password)
  43. require.Greater(t, saved.Id, 0)
  44. }
  45. func TestInsertImportedUserWithHashedPassword_RejectsExplicitId(t *testing.T) {
  46. db := setupRegionMigrationUserDB(t)
  47. orig := DB
  48. DB = db
  49. defer func() {
  50. DB = orig
  51. }()
  52. user := &User{
  53. Id: 10000001,
  54. Username: "bad-import",
  55. Password: "hash",
  56. AffCode: "X001",
  57. }
  58. err := InsertImportedUserWithHashedPassword(user)
  59. require.Error(t, err)
  60. require.Contains(t, err.Error(), "Id must not be set")
  61. }
  62. func TestCopyOAuthBindingsIfMissing_Idempotent(t *testing.T) {
  63. db := setupRegionMigrationUserDB(t)
  64. orig := DB
  65. DB = db
  66. defer func() {
  67. DB = orig
  68. }()
  69. require.NoError(t, db.Create(&CustomOAuthProvider{
  70. Id: 10,
  71. Name: "GitHub Enterprise",
  72. Slug: "gh-enterprise",
  73. Enabled: true,
  74. ClientId: "x",
  75. AuthorizationEndpoint: "a",
  76. TokenEndpoint: "t",
  77. UserInfoEndpoint: "u",
  78. }).Error)
  79. require.NoError(t, db.Create(&User{Id: 1, Username: "cn-user", Password: "hash", AffCode: "AFF1"}).Error)
  80. bindings := []*UserOAuthBinding{{
  81. ProviderId: 10,
  82. ProviderUserId: "u-100",
  83. }}
  84. require.NoError(t, CopyOAuthBindingsIfMissing(1, bindings))
  85. require.NoError(t, CopyOAuthBindingsIfMissing(1, bindings))
  86. var count int64
  87. require.NoError(t, db.Model(&UserOAuthBinding{}).Where("user_id = ?", 1).Count(&count).Error)
  88. require.EqualValues(t, 1, count)
  89. }
  90. func TestConvertUserToSynced_Idempotent(t *testing.T) {
  91. db := setupRegionMigrationUserDB(t)
  92. orig := DB
  93. DB = db
  94. defer func() {
  95. DB = orig
  96. }()
  97. require.NoError(t, db.Create(&User{
  98. Id: 10000001,
  99. Username: "ov-user",
  100. Password: "hash",
  101. AffCode: "AFF2",
  102. Quota: 88,
  103. Source: common.UserSourceLocal,
  104. }).Error)
  105. require.NoError(t, ConvertUserToSynced(10000001, 2001, 88))
  106. require.NoError(t, ConvertUserToSynced(10000001, 2001, 88))
  107. var saved User
  108. require.NoError(t, db.First(&saved, 10000001).Error)
  109. require.Equal(t, common.UserSourceSynced, saved.Source)
  110. require.Equal(t, 2001, saved.RemoteUserId)
  111. }
  112. func TestConvertUserToSynced_DifferentRemoteUserIdFails(t *testing.T) {
  113. db := setupRegionMigrationUserDB(t)
  114. orig := DB
  115. DB = db
  116. defer func() {
  117. DB = orig
  118. }()
  119. require.NoError(t, db.Create(&User{
  120. Id: 10000002,
  121. Username: "ov-user2",
  122. Password: "hash",
  123. AffCode: "AFF3",
  124. Source: common.UserSourceLocal,
  125. }).Error)
  126. require.NoError(t, ConvertUserToSynced(10000002, 3001, 100))
  127. err := ConvertUserToSynced(10000002, 9999, 100)
  128. require.Error(t, err)
  129. require.ErrorIs(t, err, ErrSyncedUserRemoteUserIDImmutable)
  130. }
  131. func TestListLocalUsersForMigration_FiltersLocalOnly(t *testing.T) {
  132. db := setupRegionMigrationUserDB(t)
  133. orig := DB
  134. DB = db
  135. defer func() {
  136. DB = orig
  137. }()
  138. require.NoError(t, db.Create(&User{
  139. Id: 10000001,
  140. Username: "local-1",
  141. Password: "h",
  142. AffCode: "L1",
  143. Source: common.UserSourceLocal,
  144. }).Error)
  145. require.NoError(t, db.Create(&User{
  146. Id: 10000002,
  147. Username: "synced-1",
  148. Password: "h",
  149. AffCode: "S1",
  150. Source: common.UserSourceSynced,
  151. RemoteUserId: 1,
  152. }).Error)
  153. require.NoError(t, db.Create(&User{
  154. Id: 10000003,
  155. Username: "root",
  156. Password: "h",
  157. AffCode: "R1",
  158. Source: common.UserSourceLocal,
  159. Role: common.RoleRootUser,
  160. }).Error)
  161. users, total, err := ListLocalUsersForMigration(0, 20)
  162. require.NoError(t, err)
  163. require.EqualValues(t, 1, total)
  164. require.Len(t, users, 1)
  165. require.Equal(t, "local-1", users[0].Username)
  166. }
  167. func TestConvertUserToSynced_RejectsRootUser(t *testing.T) {
  168. db := setupRegionMigrationUserDB(t)
  169. orig := DB
  170. DB = db
  171. defer func() {
  172. DB = orig
  173. }()
  174. require.NoError(t, db.Create(&User{
  175. Id: 10000010,
  176. Username: "root",
  177. Password: "hash",
  178. AffCode: "ROOT1",
  179. Source: common.UserSourceLocal,
  180. Role: common.RoleRootUser,
  181. }).Error)
  182. err := ConvertUserToSynced(10000010, 2001, 88)
  183. require.Error(t, err)
  184. require.ErrorIs(t, err, ErrConvertRootUserToSynced)
  185. var saved User
  186. require.NoError(t, db.First(&saved, 10000010).Error)
  187. require.Equal(t, common.UserSourceLocal, saved.Source)
  188. require.Equal(t, 0, saved.RemoteUserId)
  189. }
  190. func TestConvertUserToSynced_SentinelErrorsRemainDistinct(t *testing.T) {
  191. db := setupRegionMigrationUserDB(t)
  192. orig := DB
  193. DB = db
  194. defer func() {
  195. DB = orig
  196. }()
  197. require.NoError(t, db.Create(&User{
  198. Id: 10000020,
  199. Username: "already-synced",
  200. Password: "hash",
  201. AffCode: "SYNC20",
  202. Source: common.UserSourceSynced,
  203. RemoteUserId: 3001,
  204. }).Error)
  205. err := ConvertUserToSynced(10000020, 9999, 100)
  206. require.Error(t, err)
  207. require.True(t, errors.Is(err, ErrSyncedUserRemoteUserIDImmutable))
  208. require.False(t, errors.Is(err, ErrConvertRootUserToSynced))
  209. }