| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- // Package migrate provides tests for migration functions, including
- // performance tests for large-scale user migrations.
- //
- // To run the large-scale user performance tests:
- //
- // go test -v ./migrate -run TestSyncUsersLargeScale
- // go test -v ./migrate -run TestMigrateToUUIDsLargeScale
- //
- // To run all tests (excluding large-scale):
- //
- // go test -v ./migrate -short
- //
- // To run benchmarks:
- //
- // go test -bench=. ./migrate
- package migrate
- import (
- "testing"
- "time"
- "github.com/google/uuid"
- "github.com/gravitl/netmaker/database"
- "github.com/gravitl/netmaker/db"
- "github.com/gravitl/netmaker/logic"
- "github.com/gravitl/netmaker/models"
- "github.com/gravitl/netmaker/schema"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- // TestSyncUsersLargeScale tests syncUsers() with a large number of users
- // to verify performance improvements
- func TestSyncUsersLargeScale(t *testing.T) {
- // Skip if short test flag is set (allows quick test runs)
- if testing.Short() {
- t.Skip("Skipping large scale test in short mode")
- }
- // Initialize test database
- err := db.InitializeDB(schema.ListModels()...)
- require.NoError(t, err)
- defer db.CloseDB()
- err = database.InitializeDatabase()
- require.NoError(t, err)
- defer database.CloseDB()
- // Create test users with various roles
- numUsers := 1000
- t.Logf("Creating %d test users...", numUsers)
- startCreate := time.Now()
- for i := 0; i < numUsers; i++ {
- user := models.User{
- UserName: "testuser" + uuid.New().String()[:8],
- Password: "testpassword123",
- DisplayName: "Test User " + uuid.New().String()[:8],
- IsAdmin: i%10 == 0, // 10% are admins
- IsSuperAdmin: i%100 == 0, // 1% are super admins
- PlatformRoleID: models.ServiceUser, // Most are service users
- }
- // Assign different platform roles
- if i%100 == 0 {
- user.PlatformRoleID = models.SuperAdminRole
- } else if i%10 == 0 {
- user.PlatformRoleID = models.AdminRole
- } else if i%5 == 0 {
- user.PlatformRoleID = models.PlatformUser
- }
- // Some users have network roles
- if i%3 == 0 {
- user.NetworkRoles = make(map[models.NetworkID]map[models.UserRoleID]struct{})
- user.NetworkRoles[models.NetworkID("test-network")] = make(map[models.UserRoleID]struct{})
- }
- // Some users have user groups
- if i%4 == 0 {
- user.UserGroups = make(map[models.UserGroupID]struct{})
- }
- err := logic.UpsertUser(user)
- require.NoError(t, err, "Failed to create user %d", i)
- }
- createDuration := time.Since(startCreate)
- t.Logf("Created %d users in %v (avg: %v per user)", numUsers, createDuration, createDuration/time.Duration(numUsers))
- // Verify users were created
- users, err := logic.GetUsersDB()
- require.NoError(t, err)
- assert.GreaterOrEqual(t, len(users), numUsers, "Expected at least %d users", numUsers)
- // Test syncUsers() performance
- t.Log("Running syncUsers() migration...")
- startSync := time.Now()
- syncUsers()
- syncDuration := time.Since(startSync)
- t.Logf("syncUsers() completed in %v for %d users (avg: %v per user)",
- syncDuration, len(users), syncDuration/time.Duration(len(users)))
- // Verify users were migrated correctly
- usersAfter, err := logic.GetUsersDB()
- require.NoError(t, err)
- assert.Equal(t, len(users), len(usersAfter), "User count should remain the same")
- // Verify migration correctness - check a sample of users
- sampleSize := 100
- if len(usersAfter) < sampleSize {
- sampleSize = len(usersAfter)
- }
- for i := 0; i < sampleSize; i++ {
- user := usersAfter[i]
- // Verify platform role is set
- assert.NotEmpty(t, user.PlatformRoleID.String(), "User %s should have PlatformRoleID", user.UserName)
- // Verify admin flags match platform role
- if user.PlatformRoleID == models.SuperAdminRole {
- assert.True(t, user.IsSuperAdmin, "SuperAdmin user should have IsSuperAdmin=true")
- assert.True(t, user.IsAdmin, "SuperAdmin user should have IsAdmin=true")
- } else if user.PlatformRoleID == models.AdminRole {
- assert.True(t, user.IsAdmin, "Admin user should have IsAdmin=true")
- assert.False(t, user.IsSuperAdmin, "Admin user should not have IsSuperAdmin=true")
- } else {
- assert.False(t, user.IsSuperAdmin, "Non-admin user should not have IsSuperAdmin=true")
- assert.False(t, user.IsAdmin, "Non-admin user should not have IsAdmin=true")
- }
- // Verify user groups are initialized
- assert.NotNil(t, user.UserGroups, "User should have UserGroups map")
- }
- // Performance assertion - syncUsers should complete in reasonable time
- // With optimizations, 1000 users should complete in under 10 seconds
- maxDuration := 30 * time.Second
- assert.Less(t, syncDuration, maxDuration,
- "syncUsers() took too long: %v (expected < %v)", syncDuration, maxDuration)
- t.Logf("✓ syncUsers() performance test passed: %v for %d users", syncDuration, len(users))
- }
- // TestMigrateToUUIDsLargeScale tests MigrateToUUIDs() with a large number of users
- func TestMigrateToUUIDsLargeScale(t *testing.T) {
- // Skip if short test flag is set
- if testing.Short() {
- t.Skip("Skipping large scale test in short mode")
- }
- // Initialize test database
- err := db.InitializeDB(schema.ListModels()...)
- require.NoError(t, err)
- defer db.CloseDB()
- err = database.InitializeDatabase()
- require.NoError(t, err)
- defer database.CloseDB()
- // Create test users with user groups (needed for UUID migration)
- numUsers := 1000
- t.Logf("Creating %d test users with user groups...", numUsers)
- startCreate := time.Now()
- for i := 0; i < numUsers; i++ {
- user := models.User{
- UserName: "testuser" + uuid.New().String()[:8],
- Password: "testpassword123",
- DisplayName: "Test User " + uuid.New().String()[:8],
- PlatformRoleID: models.ServiceUser,
- UserGroups: make(map[models.UserGroupID]struct{}),
- }
- // Add some user groups with non-UUID IDs (to trigger migration)
- if i%2 == 0 {
- user.UserGroups[models.UserGroupID("old-group-1")] = struct{}{}
- }
- if i%3 == 0 {
- user.UserGroups[models.UserGroupID("old-group-2")] = struct{}{}
- }
- err := logic.UpsertUser(user)
- require.NoError(t, err, "Failed to create user %d", i)
- }
- createDuration := time.Since(startCreate)
- t.Logf("Created %d users in %v", numUsers, createDuration)
- // Verify users were created
- users, err := logic.GetUsersDB()
- require.NoError(t, err)
- assert.GreaterOrEqual(t, len(users), numUsers, "Expected at least %d users", numUsers)
- // Test MigrateToUUIDs() performance
- t.Log("Running MigrateToUUIDs() migration...")
- startMigrate := time.Now()
- migrateToUUIDs()
- migrateDuration := time.Since(startMigrate)
- t.Logf("MigrateToUUIDs() completed in %v for %d users (avg: %v per user)",
- migrateDuration, len(users), migrateDuration/time.Duration(len(users)))
- // Verify users still exist after migration
- usersAfter, err := logic.GetUsersDB()
- require.NoError(t, err)
- assert.Equal(t, len(users), len(usersAfter), "User count should remain the same")
- // Performance assertion - MigrateToUUIDs should complete in reasonable time
- maxDuration := 30 * time.Second
- assert.Less(t, migrateDuration, maxDuration,
- "MigrateToUUIDs() took too long: %v (expected < %v)", migrateDuration, maxDuration)
- t.Logf("✓ MigrateToUUIDs() performance test passed: %v for %d users", migrateDuration, len(users))
- }
- // TestSyncUsersCorrectness tests that syncUsers() correctly migrates user data
- func TestSyncUsersCorrectness(t *testing.T) {
- // Initialize test database
- err := db.InitializeDB(schema.ListModels()...)
- require.NoError(t, err)
- defer db.CloseDB()
- err = database.InitializeDatabase()
- require.NoError(t, err)
- defer database.CloseDB()
- // Create test users with various states
- testCases := []struct {
- name string
- user models.User
- expectedRole models.UserRoleID
- expectedAdmin bool
- expectedSuper bool
- }{
- {
- name: "user with AdminRole but IsAdmin=false",
- user: models.User{
- UserName: "admin1",
- Password: "password",
- PlatformRoleID: models.AdminRole,
- IsAdmin: false,
- IsSuperAdmin: false,
- },
- expectedRole: models.AdminRole,
- expectedAdmin: true,
- expectedSuper: false,
- },
- {
- name: "user with SuperAdminRole but IsSuperAdmin=false",
- user: models.User{
- UserName: "superadmin1",
- Password: "password",
- PlatformRoleID: models.SuperAdminRole,
- IsAdmin: false,
- IsSuperAdmin: false,
- },
- expectedRole: models.SuperAdminRole,
- expectedAdmin: true,
- expectedSuper: true,
- },
- {
- name: "user with IsSuperAdmin=true but no PlatformRoleID",
- user: models.User{
- UserName: "superadmin2",
- Password: "password",
- PlatformRoleID: "",
- IsAdmin: true,
- IsSuperAdmin: true,
- },
- expectedRole: models.SuperAdminRole,
- expectedAdmin: true,
- expectedSuper: true,
- },
- {
- name: "user with IsAdmin=true but no PlatformRoleID",
- user: models.User{
- UserName: "admin2",
- Password: "password",
- PlatformRoleID: "",
- IsAdmin: true,
- IsSuperAdmin: false,
- },
- expectedRole: models.AdminRole,
- expectedAdmin: true,
- expectedSuper: false,
- },
- {
- name: "regular user with no role",
- user: models.User{
- UserName: "user1",
- Password: "password",
- PlatformRoleID: "",
- IsAdmin: false,
- IsSuperAdmin: false,
- },
- expectedRole: models.ServiceUser,
- expectedAdmin: false,
- expectedSuper: false,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- // Create user
- err := logic.UpsertUser(tc.user)
- require.NoError(t, err)
- // Run migration
- syncUsers()
- // Verify migration
- user, err := logic.GetUser(tc.user.UserName)
- require.NoError(t, err)
- assert.Equal(t, tc.expectedRole, user.PlatformRoleID, "PlatformRoleID should match")
- assert.Equal(t, tc.expectedAdmin, user.IsAdmin, "IsAdmin should match")
- assert.Equal(t, tc.expectedSuper, user.IsSuperAdmin, "IsSuperAdmin should match")
- assert.NotNil(t, user.UserGroups, "UserGroups should be initialized")
- assert.NotNil(t, user.NetworkRoles, "NetworkRoles should be initialized")
- // Cleanup
- _ = database.DeleteRecord(database.USERS_TABLE_NAME, tc.user.UserName)
- })
- }
- }
- // BenchmarkSyncUsers benchmarks syncUsers() performance
- func BenchmarkSyncUsers(b *testing.B) {
- // Initialize test database
- err := db.InitializeDB(schema.ListModels()...)
- if err != nil {
- b.Fatal(err)
- }
- defer db.CloseDB()
- err = database.InitializeDatabase()
- if err != nil {
- b.Fatal(err)
- }
- defer database.CloseDB()
- // Create test users
- numUsers := 1000
- for i := 0; i < numUsers; i++ {
- user := models.User{
- UserName: "benchuser" + uuid.New().String()[:8],
- Password: "password",
- PlatformRoleID: models.ServiceUser,
- }
- if i%10 == 0 {
- user.PlatformRoleID = models.AdminRole
- }
- _ = logic.UpsertUser(user)
- }
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- syncUsers()
- }
- }
|