package models_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "rocketgit.ru/rsmon/worker/app/models" ) func TestCreateAccountForUserStartsTeamTrial(t *testing.T) { models.Drop() models.Migrate() email := "team-trial@example.test" user := models.User{Name: "Trial User", Email: &email, Enabled: true, Confirmed: true} require.NoError(t, models.DB().Create(&user).Error) account, err := models.CreateAccountForUser("Trial Account", &user) require.NoError(t, err) require.NotNil(t, account.PlanID) require.NotNil(t, account.TrialEndsAt) var plan models.Plan require.NoError(t, models.DB().First(&plan, *account.PlanID).Error) assert.Equal(t, "team", plan.Code) var subscription models.Subscription require.NoError(t, models.DB().Where("account_id = ?", account.ID).First(&subscription).Error) assert.Equal(t, models.SubscriptionStatusTrialing, subscription.Status) assert.Equal(t, plan.ID, subscription.PlanID) require.NotNil(t, subscription.TrialEndsAt) assert.WithinDuration(t, *account.TrialEndsAt, *subscription.TrialEndsAt, time.Millisecond) } // TestAccountModel tests basic Account model functionality func TestAccountModel(t *testing.T) { // Test Account structure account := models.Account{ Name: "Test Account", Timezone: "UTC", Language: "en", Deleted: false, } assert.Equal(t, "Test Account", account.Name) assert.Equal(t, "UTC", account.Timezone) assert.Equal(t, "en", account.Language) assert.False(t, account.Deleted) } // TestAccountDisplayName tests User.DisplayName method func TestUserDisplayName(t *testing.T) { tests := []struct { name string user models.User expected string }{ { name: "User with email", user: models.User{ Name: "John Doe", Email: stringPtr("john@example.com"), }, expected: "John Doe john@example.com", }, { name: "User without email", user: models.User{ Name: "Jane Doe", Email: nil, }, expected: "Jane Doe", }, { name: "User with empty name and email", user: models.User{ Name: "", Email: stringPtr("test@example.com"), }, expected: " test@example.com", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.user.DisplayName() assert.Equal(t, tt.expected, result) }) } } // TestUserGravatar tests User.Gravatar method func TestUserGravatar(t *testing.T) { tests := []struct { name string user models.User size int expected string }{ { name: "User with email", user: models.User{ Email: stringPtr("test@example.com"), }, size: 32, expected: "https://www.gravatar.com/avatar/55502f40dc8b7c769880b10874abc9d0?s=32&d=blank", }, { name: "User without email", user: models.User{Email: nil}, size: 32, expected: "", }, { name: "Different size", user: models.User{ Email: stringPtr("test@example.com"), }, size: 64, expected: "https://www.gravatar.com/avatar/55502f40dc8b7c769880b10874abc9d0?s=64&d=blank", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.user.Gravatar(tt.size) assert.Equal(t, tt.expected, result) }) } } // TestUserAsJSON tests User.AsJSON method func TestUserAsJSON(t *testing.T) { email := "test@example.com" user := models.User{ ID: 123, Email: &email, Name: "Test User", } result := user.AsJSON() assert.NotNil(t, result) assert.Equal(t, int64(123), result["id"]) assert.Equal(t, &email, result["email"]) assert.Contains(t, result["avatar"], "gravatar.com") } // TestAccessModel tests Access model structure func TestAccessModel(t *testing.T) { access := models.Access{ AccountID: 1, Kind: "account", Role: "owner", } assert.Equal(t, int64(1), access.AccountID) assert.Equal(t, "account", access.Kind) assert.Equal(t, "owner", access.Role) } // TestGroupModel tests Group model structure func TestGroupModel(t *testing.T) { group := models.Group{ AccountID: 1, Name: "Test Group", MonitorsCount: 5, } assert.Equal(t, int64(1), group.AccountID) assert.Equal(t, "Test Group", group.Name) assert.Equal(t, 5, group.MonitorsCount) } // TestGroupIdsForAccountId tests GroupIdsForAccountId function func TestGroupIdsForAccountId(t *testing.T) { // This test would require a database connection // For now, we test that it doesn't panic with invalid input t.Run("handles zero account id", func(t *testing.T) { // Note: This will panic without DB connection, which is expected behavior // In a real test, we'd set up a test database }) } // TestAccountTableDrivenTests demonstrates table-driven testing pattern func TestAccountValidationTableDriven(t *testing.T) { tests := []struct { name string account models.Account wantErr bool }{ { name: "Valid account", account: models.Account{ Name: "Valid Account", Timezone: "UTC", Language: "en", }, wantErr: false, }, { name: "Account with empty name", account: models.Account{ Name: "", Timezone: "UTC", Language: "en", }, wantErr: true, // Name should be required }, { name: "Account with invalid timezone", account: models.Account{ Name: "Test Account", Timezone: "Invalid/Timezone", Language: "en", }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Validation logic would go here // For now, we just verify the test structure assert.NotNil(t, tt.account) }) } } // Helper function to create string pointer func stringPtr(s string) *string { return &s } // BenchmarkUserDisplayName benchmarks the DisplayName method func BenchmarkUserDisplayName(b *testing.B) { user := models.User{ Name: "Test User", Email: stringPtr("test@example.com"), } b.ResetTimer() for i := 0; i < b.N; i++ { _ = user.DisplayName() } } // TestAccountConcurrentOperations tests concurrent access to account data func TestAccountConcurrentOperations(t *testing.T) { account := models.Account{ Name: "Concurrent Test", Timezone: "UTC", } done := make(chan bool) // Simulate concurrent reads for i := 0; i < 10; i++ { go func() { _ = account.Name done <- true }() } // Wait for all goroutines for i := 0; i < 10; i++ { <-done } } // ExampleAccountUsage provides an example of how to use Account model func ExampleAccount() { account := models.Account{ Name: "Example Account", Timezone: "America/New_York", Language: "en", } _ = account.Name // Output: }