diff --git a/cmd/goyco/commands/parallel_processor.go b/cmd/goyco/commands/parallel_processor.go index fe4ea47..444e56e 100644 --- a/cmd/goyco/commands/parallel_processor.go +++ b/cmd/goyco/commands/parallel_processor.go @@ -280,28 +280,53 @@ type voteResult struct { index int } +func generateRandomIdentifier() string { + const length = 12 + const chars = "abcdefghijklmnopqrstuvwxyz0123456789" + identifier := make([]byte, length) + for i := range identifier { + num, _ := rand.Int(rand.Reader, big.NewInt(int64(len(chars)))) + identifier[i] = chars[num.Int64()] + } + return string(identifier) +} + func (p *ParallelProcessor) createSingleUser(userRepo repositories.UserRepository, index int) (database.User, error) { - username := fmt.Sprintf("user_%d", index) - email := fmt.Sprintf("user_%d@goyco.local", index) password := "password123" + randomID := generateRandomIdentifier() + username := fmt.Sprintf("user_%s", randomID) + email := fmt.Sprintf("user_%s@goyco.local", randomID) hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return database.User{}, fmt.Errorf("hash password: %w", err) } - user := &database.User{ - Username: username, - Email: email, - Password: string(hashedPassword), - EmailVerified: true, + const maxRetries = 10 + for attempt := 0; attempt < maxRetries; attempt++ { + user, err := userRepo.GetByEmail(email) + if err == nil { + return *user, nil + } + + user = &database.User{ + Username: username, + Email: email, + Password: string(hashedPassword), + EmailVerified: true, + } + + if err := userRepo.Create(user); err != nil { + randomID = generateRandomIdentifier() + username = fmt.Sprintf("user_%s", randomID) + email = fmt.Sprintf("user_%s@goyco.local", randomID) + continue + } + + return *user, nil } - if err := userRepo.Create(user); err != nil { - return database.User{}, fmt.Errorf("create user: %w", err) - } - - return *user, nil + return database.User{}, fmt.Errorf("failed to create user after %d attempts", maxRetries) } func (p *ParallelProcessor) createSinglePost(postRepo repositories.PostRepository, authorID uint, index int) (database.Post, error) { @@ -347,26 +372,35 @@ func (p *ParallelProcessor) createSinglePost(postRepo repositories.PostRepositor } domain := sampleDomains[index%len(sampleDomains)] - path := generateRandomPath() + randomID := generateRandomIdentifier() + path := fmt.Sprintf("/article/%s", randomID) url := fmt.Sprintf("https://%s%s", domain, path) content := fmt.Sprintf("Autogenerated seed post #%d\n\nThis is sample content for testing purposes. The post discusses %s and provides valuable insights.", index, title) - post := &database.Post{ - Title: title, - URL: url, - Content: content, - AuthorID: &authorID, - UpVotes: 0, - DownVotes: 0, - Score: 0, + const maxRetries = 10 + for attempt := 0; attempt < maxRetries; attempt++ { + post := &database.Post{ + Title: title, + URL: url, + Content: content, + AuthorID: &authorID, + UpVotes: 0, + DownVotes: 0, + Score: 0, + } + + if err := postRepo.Create(post); err != nil { + randomID = generateRandomIdentifier() + path = fmt.Sprintf("/article/%s", randomID) + url = fmt.Sprintf("https://%s%s", domain, path) + continue + } + + return *post, nil } - if err := postRepo.Create(post); err != nil { - return database.Post{}, fmt.Errorf("create post: %w", err) - } - - return *post, nil + return database.Post{}, fmt.Errorf("failed to create post after %d attempts", maxRetries) } func (p *ParallelProcessor) createVotesForPost(voteRepo repositories.VoteRepository, users []database.User, post database.Post, avgVotesPerPost int) (int, error) { @@ -406,8 +440,8 @@ func (p *ParallelProcessor) createVotesForPost(voteRepo repositories.VoteReposit Type: voteType, } - if err := voteRepo.Create(vote); err != nil { - return totalVotes, fmt.Errorf("create vote: %w", err) + if err := voteRepo.CreateOrUpdate(vote); err != nil { + return totalVotes, fmt.Errorf("create or update vote: %w", err) } totalVotes++ diff --git a/cmd/goyco/commands/seed.go b/cmd/goyco/commands/seed.go index 4973312..780fa3c 100644 --- a/cmd/goyco/commands/seed.go +++ b/cmd/goyco/commands/seed.go @@ -8,11 +8,12 @@ import ( "math/big" "os" - "golang.org/x/crypto/bcrypt" - "gorm.io/gorm" "goyco/internal/config" "goyco/internal/database" "goyco/internal/repositories" + + "golang.org/x/crypto/bcrypt" + "gorm.io/gorm" ) func HandleSeedCommand(cfg *config.Config, name string, args []string) error { @@ -169,12 +170,12 @@ func seedDatabase(userRepo repositories.UserRepository, postRepo repositories.Po } if IsJSONOutput() { - outputJSON(map[string]interface{}{ + outputJSON(map[string]any{ "action": "seed_completed", "users": len(allUsers), "posts": len(posts), "votes": votes, - "seed_user": map[string]interface{}{ + "seed_user": map[string]any{ "id": seedUser.ID, "username": seedUser.Username, }, @@ -188,32 +189,41 @@ func seedDatabase(userRepo repositories.UserRepository, postRepo repositories.Po } func ensureSeedUser(userRepo repositories.UserRepository) (*database.User, error) { - seedUsername := "seed_admin" - seedEmail := "seed_admin@goyco.local" seedPassword := "seed-password" - - user, err := userRepo.GetByEmail(seedEmail) - if err == nil { - return user, nil - } + randomID := generateRandomIdentifier() + seedUsername := fmt.Sprintf("seed_admin_%s", randomID) + seedEmail := fmt.Sprintf("seed_admin_%s@goyco.local", randomID) hashedPassword, err := bcrypt.GenerateFromPassword([]byte(seedPassword), bcrypt.DefaultCost) if err != nil { return nil, fmt.Errorf("hash password: %w", err) } - user = &database.User{ - Username: seedUsername, - Email: seedEmail, - Password: string(hashedPassword), - EmailVerified: true, + const maxRetries = 10 + for range maxRetries { + user, err := userRepo.GetByEmail(seedEmail) + if err == nil { + return user, nil + } + + user = &database.User{ + Username: seedUsername, + Email: seedEmail, + Password: string(hashedPassword), + EmailVerified: true, + } + + if err := userRepo.Create(user); err != nil { + randomID = generateRandomIdentifier() + seedUsername = fmt.Sprintf("seed_admin_%s", randomID) + seedEmail = fmt.Sprintf("seed_admin_%s@goyco.local", randomID) + continue + } + + return user, nil } - if err := userRepo.Create(user); err != nil { - return nil, fmt.Errorf("create seed user: %w", err) - } - - return user, nil + return nil, fmt.Errorf("failed to create seed user after %d attempts", maxRetries) } func createRandomUsers(userRepo repositories.UserRepository, count int) ([]database.User, error) {