migrate_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. // Package migrate provides tests for migration functions, including
  2. // performance tests for large-scale user migrations.
  3. //
  4. // To run the large-scale user performance tests:
  5. //
  6. // go test -v ./migrate -run TestSyncUsersLargeScale
  7. // go test -v ./migrate -run TestMigrateToUUIDsLargeScale
  8. //
  9. // To run all tests (excluding large-scale):
  10. //
  11. // go test -v ./migrate -short
  12. //
  13. // To run benchmarks:
  14. //
  15. // go test -bench=. ./migrate
  16. package migrate
  17. import (
  18. "testing"
  19. "time"
  20. "github.com/google/uuid"
  21. "github.com/gravitl/netmaker/database"
  22. "github.com/gravitl/netmaker/db"
  23. "github.com/gravitl/netmaker/logic"
  24. "github.com/gravitl/netmaker/models"
  25. "github.com/gravitl/netmaker/schema"
  26. "github.com/stretchr/testify/assert"
  27. "github.com/stretchr/testify/require"
  28. )
  29. // TestSyncUsersLargeScale tests syncUsers() with a large number of users
  30. // to verify performance improvements
  31. func TestSyncUsersLargeScale(t *testing.T) {
  32. // Skip if short test flag is set (allows quick test runs)
  33. if testing.Short() {
  34. t.Skip("Skipping large scale test in short mode")
  35. }
  36. // Initialize test database
  37. err := db.InitializeDB(schema.ListModels()...)
  38. require.NoError(t, err)
  39. defer db.CloseDB()
  40. err = database.InitializeDatabase()
  41. require.NoError(t, err)
  42. defer database.CloseDB()
  43. // Create test users with various roles
  44. numUsers := 1000
  45. t.Logf("Creating %d test users...", numUsers)
  46. startCreate := time.Now()
  47. for i := 0; i < numUsers; i++ {
  48. user := models.User{
  49. UserName: "testuser" + uuid.New().String()[:8],
  50. Password: "testpassword123",
  51. DisplayName: "Test User " + uuid.New().String()[:8],
  52. IsAdmin: i%10 == 0, // 10% are admins
  53. IsSuperAdmin: i%100 == 0, // 1% are super admins
  54. PlatformRoleID: models.ServiceUser, // Most are service users
  55. }
  56. // Assign different platform roles
  57. if i%100 == 0 {
  58. user.PlatformRoleID = models.SuperAdminRole
  59. } else if i%10 == 0 {
  60. user.PlatformRoleID = models.AdminRole
  61. } else if i%5 == 0 {
  62. user.PlatformRoleID = models.PlatformUser
  63. }
  64. // Some users have network roles
  65. if i%3 == 0 {
  66. user.NetworkRoles = make(map[models.NetworkID]map[models.UserRoleID]struct{})
  67. user.NetworkRoles[models.NetworkID("test-network")] = make(map[models.UserRoleID]struct{})
  68. }
  69. // Some users have user groups
  70. if i%4 == 0 {
  71. user.UserGroups = make(map[models.UserGroupID]struct{})
  72. }
  73. err := logic.UpsertUser(user)
  74. require.NoError(t, err, "Failed to create user %d", i)
  75. }
  76. createDuration := time.Since(startCreate)
  77. t.Logf("Created %d users in %v (avg: %v per user)", numUsers, createDuration, createDuration/time.Duration(numUsers))
  78. // Verify users were created
  79. users, err := logic.GetUsersDB()
  80. require.NoError(t, err)
  81. assert.GreaterOrEqual(t, len(users), numUsers, "Expected at least %d users", numUsers)
  82. // Test syncUsers() performance
  83. t.Log("Running syncUsers() migration...")
  84. startSync := time.Now()
  85. syncUsers()
  86. syncDuration := time.Since(startSync)
  87. t.Logf("syncUsers() completed in %v for %d users (avg: %v per user)",
  88. syncDuration, len(users), syncDuration/time.Duration(len(users)))
  89. // Verify users were migrated correctly
  90. usersAfter, err := logic.GetUsersDB()
  91. require.NoError(t, err)
  92. assert.Equal(t, len(users), len(usersAfter), "User count should remain the same")
  93. // Verify migration correctness - check a sample of users
  94. sampleSize := 100
  95. if len(usersAfter) < sampleSize {
  96. sampleSize = len(usersAfter)
  97. }
  98. for i := 0; i < sampleSize; i++ {
  99. user := usersAfter[i]
  100. // Verify platform role is set
  101. assert.NotEmpty(t, user.PlatformRoleID.String(), "User %s should have PlatformRoleID", user.UserName)
  102. // Verify admin flags match platform role
  103. if user.PlatformRoleID == models.SuperAdminRole {
  104. assert.True(t, user.IsSuperAdmin, "SuperAdmin user should have IsSuperAdmin=true")
  105. assert.True(t, user.IsAdmin, "SuperAdmin user should have IsAdmin=true")
  106. } else if user.PlatformRoleID == models.AdminRole {
  107. assert.True(t, user.IsAdmin, "Admin user should have IsAdmin=true")
  108. assert.False(t, user.IsSuperAdmin, "Admin user should not have IsSuperAdmin=true")
  109. } else {
  110. assert.False(t, user.IsSuperAdmin, "Non-admin user should not have IsSuperAdmin=true")
  111. assert.False(t, user.IsAdmin, "Non-admin user should not have IsAdmin=true")
  112. }
  113. // Verify user groups are initialized
  114. assert.NotNil(t, user.UserGroups, "User should have UserGroups map")
  115. }
  116. // Performance assertion - syncUsers should complete in reasonable time
  117. // With optimizations, 1000 users should complete in under 10 seconds
  118. maxDuration := 30 * time.Second
  119. assert.Less(t, syncDuration, maxDuration,
  120. "syncUsers() took too long: %v (expected < %v)", syncDuration, maxDuration)
  121. t.Logf("✓ syncUsers() performance test passed: %v for %d users", syncDuration, len(users))
  122. }
  123. // TestMigrateToUUIDsLargeScale tests MigrateToUUIDs() with a large number of users
  124. func TestMigrateToUUIDsLargeScale(t *testing.T) {
  125. // Skip if short test flag is set
  126. if testing.Short() {
  127. t.Skip("Skipping large scale test in short mode")
  128. }
  129. // Initialize test database
  130. err := db.InitializeDB(schema.ListModels()...)
  131. require.NoError(t, err)
  132. defer db.CloseDB()
  133. err = database.InitializeDatabase()
  134. require.NoError(t, err)
  135. defer database.CloseDB()
  136. // Create test users with user groups (needed for UUID migration)
  137. numUsers := 1000
  138. t.Logf("Creating %d test users with user groups...", numUsers)
  139. startCreate := time.Now()
  140. for i := 0; i < numUsers; i++ {
  141. user := models.User{
  142. UserName: "testuser" + uuid.New().String()[:8],
  143. Password: "testpassword123",
  144. DisplayName: "Test User " + uuid.New().String()[:8],
  145. PlatformRoleID: models.ServiceUser,
  146. UserGroups: make(map[models.UserGroupID]struct{}),
  147. }
  148. // Add some user groups with non-UUID IDs (to trigger migration)
  149. if i%2 == 0 {
  150. user.UserGroups[models.UserGroupID("old-group-1")] = struct{}{}
  151. }
  152. if i%3 == 0 {
  153. user.UserGroups[models.UserGroupID("old-group-2")] = struct{}{}
  154. }
  155. err := logic.UpsertUser(user)
  156. require.NoError(t, err, "Failed to create user %d", i)
  157. }
  158. createDuration := time.Since(startCreate)
  159. t.Logf("Created %d users in %v", numUsers, createDuration)
  160. // Verify users were created
  161. users, err := logic.GetUsersDB()
  162. require.NoError(t, err)
  163. assert.GreaterOrEqual(t, len(users), numUsers, "Expected at least %d users", numUsers)
  164. // Test MigrateToUUIDs() performance
  165. t.Log("Running MigrateToUUIDs() migration...")
  166. startMigrate := time.Now()
  167. migrateToUUIDs()
  168. migrateDuration := time.Since(startMigrate)
  169. t.Logf("MigrateToUUIDs() completed in %v for %d users (avg: %v per user)",
  170. migrateDuration, len(users), migrateDuration/time.Duration(len(users)))
  171. // Verify users still exist after migration
  172. usersAfter, err := logic.GetUsersDB()
  173. require.NoError(t, err)
  174. assert.Equal(t, len(users), len(usersAfter), "User count should remain the same")
  175. // Performance assertion - MigrateToUUIDs should complete in reasonable time
  176. maxDuration := 30 * time.Second
  177. assert.Less(t, migrateDuration, maxDuration,
  178. "MigrateToUUIDs() took too long: %v (expected < %v)", migrateDuration, maxDuration)
  179. t.Logf("✓ MigrateToUUIDs() performance test passed: %v for %d users", migrateDuration, len(users))
  180. }
  181. // TestSyncUsersCorrectness tests that syncUsers() correctly migrates user data
  182. func TestSyncUsersCorrectness(t *testing.T) {
  183. // Initialize test database
  184. err := db.InitializeDB(schema.ListModels()...)
  185. require.NoError(t, err)
  186. defer db.CloseDB()
  187. err = database.InitializeDatabase()
  188. require.NoError(t, err)
  189. defer database.CloseDB()
  190. // Create test users with various states
  191. testCases := []struct {
  192. name string
  193. user models.User
  194. expectedRole models.UserRoleID
  195. expectedAdmin bool
  196. expectedSuper bool
  197. }{
  198. {
  199. name: "user with AdminRole but IsAdmin=false",
  200. user: models.User{
  201. UserName: "admin1",
  202. Password: "password",
  203. PlatformRoleID: models.AdminRole,
  204. IsAdmin: false,
  205. IsSuperAdmin: false,
  206. },
  207. expectedRole: models.AdminRole,
  208. expectedAdmin: true,
  209. expectedSuper: false,
  210. },
  211. {
  212. name: "user with SuperAdminRole but IsSuperAdmin=false",
  213. user: models.User{
  214. UserName: "superadmin1",
  215. Password: "password",
  216. PlatformRoleID: models.SuperAdminRole,
  217. IsAdmin: false,
  218. IsSuperAdmin: false,
  219. },
  220. expectedRole: models.SuperAdminRole,
  221. expectedAdmin: true,
  222. expectedSuper: true,
  223. },
  224. {
  225. name: "user with IsSuperAdmin=true but no PlatformRoleID",
  226. user: models.User{
  227. UserName: "superadmin2",
  228. Password: "password",
  229. PlatformRoleID: "",
  230. IsAdmin: true,
  231. IsSuperAdmin: true,
  232. },
  233. expectedRole: models.SuperAdminRole,
  234. expectedAdmin: true,
  235. expectedSuper: true,
  236. },
  237. {
  238. name: "user with IsAdmin=true but no PlatformRoleID",
  239. user: models.User{
  240. UserName: "admin2",
  241. Password: "password",
  242. PlatformRoleID: "",
  243. IsAdmin: true,
  244. IsSuperAdmin: false,
  245. },
  246. expectedRole: models.AdminRole,
  247. expectedAdmin: true,
  248. expectedSuper: false,
  249. },
  250. {
  251. name: "regular user with no role",
  252. user: models.User{
  253. UserName: "user1",
  254. Password: "password",
  255. PlatformRoleID: "",
  256. IsAdmin: false,
  257. IsSuperAdmin: false,
  258. },
  259. expectedRole: models.ServiceUser,
  260. expectedAdmin: false,
  261. expectedSuper: false,
  262. },
  263. }
  264. for _, tc := range testCases {
  265. t.Run(tc.name, func(t *testing.T) {
  266. // Create user
  267. err := logic.UpsertUser(tc.user)
  268. require.NoError(t, err)
  269. // Run migration
  270. syncUsers()
  271. // Verify migration
  272. user, err := logic.GetUser(tc.user.UserName)
  273. require.NoError(t, err)
  274. assert.Equal(t, tc.expectedRole, user.PlatformRoleID, "PlatformRoleID should match")
  275. assert.Equal(t, tc.expectedAdmin, user.IsAdmin, "IsAdmin should match")
  276. assert.Equal(t, tc.expectedSuper, user.IsSuperAdmin, "IsSuperAdmin should match")
  277. assert.NotNil(t, user.UserGroups, "UserGroups should be initialized")
  278. assert.NotNil(t, user.NetworkRoles, "NetworkRoles should be initialized")
  279. // Cleanup
  280. _ = database.DeleteRecord(database.USERS_TABLE_NAME, tc.user.UserName)
  281. })
  282. }
  283. }
  284. // BenchmarkSyncUsers benchmarks syncUsers() performance
  285. func BenchmarkSyncUsers(b *testing.B) {
  286. // Initialize test database
  287. err := db.InitializeDB(schema.ListModels()...)
  288. if err != nil {
  289. b.Fatal(err)
  290. }
  291. defer db.CloseDB()
  292. err = database.InitializeDatabase()
  293. if err != nil {
  294. b.Fatal(err)
  295. }
  296. defer database.CloseDB()
  297. // Create test users
  298. numUsers := 1000
  299. for i := 0; i < numUsers; i++ {
  300. user := models.User{
  301. UserName: "benchuser" + uuid.New().String()[:8],
  302. Password: "password",
  303. PlatformRoleID: models.ServiceUser,
  304. }
  305. if i%10 == 0 {
  306. user.PlatformRoleID = models.AdminRole
  307. }
  308. _ = logic.UpsertUser(user)
  309. }
  310. b.ResetTimer()
  311. for i := 0; i < b.N; i++ {
  312. syncUsers()
  313. }
  314. }