Compare commits
2 Commits
697f201d60
...
34a97994b3
| Author | SHA1 | Date | |
|---|---|---|---|
| 34a97994b3 | |||
| eb5f93ffd0 |
@@ -299,180 +299,6 @@ func ensureSeedUser(userRepo repositories.UserRepository, passwordHash string) (
|
|||||||
return nil, fmt.Errorf("failed to create seed user after %d attempts", maxRetries)
|
return nil, fmt.Errorf("failed to create seed user after %d attempts", maxRetries)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRandomUsers(userRepo repositories.UserRepository, count int, passwordHash string) ([]database.User, error) {
|
|
||||||
var users []database.User
|
|
||||||
|
|
||||||
for i := range count {
|
|
||||||
username := fmt.Sprintf("user_%d", i+1)
|
|
||||||
email := fmt.Sprintf("user_%d@goyco.local", i+1)
|
|
||||||
|
|
||||||
user := &database.User{
|
|
||||||
Username: username,
|
|
||||||
Email: email,
|
|
||||||
Password: passwordHash,
|
|
||||||
EmailVerified: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := userRepo.Create(user); err != nil {
|
|
||||||
return nil, fmt.Errorf("create user %d: %w", i+1, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
users = append(users, *user)
|
|
||||||
}
|
|
||||||
|
|
||||||
return users, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createRandomPosts(postRepo repositories.PostRepository, authorID uint, count int) ([]database.Post, error) {
|
|
||||||
var posts []database.Post
|
|
||||||
|
|
||||||
sampleTitles := []string{
|
|
||||||
"Amazing JavaScript Framework",
|
|
||||||
"Python Best Practices",
|
|
||||||
"Go Performance Tips",
|
|
||||||
"Database Optimization",
|
|
||||||
"Web Security Guide",
|
|
||||||
"Machine Learning Basics",
|
|
||||||
"Cloud Architecture",
|
|
||||||
"DevOps Automation",
|
|
||||||
"API Design Patterns",
|
|
||||||
"Frontend Optimization",
|
|
||||||
"Backend Scaling",
|
|
||||||
"Container Orchestration",
|
|
||||||
"Microservices Architecture",
|
|
||||||
"Testing Strategies",
|
|
||||||
"Code Review Process",
|
|
||||||
"Version Control Best Practices",
|
|
||||||
"Continuous Integration",
|
|
||||||
"Monitoring and Alerting",
|
|
||||||
"Error Handling Patterns",
|
|
||||||
"Data Structures Explained",
|
|
||||||
}
|
|
||||||
|
|
||||||
sampleDomains := []string{
|
|
||||||
"example.com",
|
|
||||||
"techblog.org",
|
|
||||||
"devguide.net",
|
|
||||||
"programming.io",
|
|
||||||
"codeexamples.com",
|
|
||||||
"tutorialhub.org",
|
|
||||||
"bestpractices.dev",
|
|
||||||
"learnprogramming.net",
|
|
||||||
"codingtips.org",
|
|
||||||
"softwareengineering.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range count {
|
|
||||||
title := sampleTitles[i%len(sampleTitles)]
|
|
||||||
if i >= len(sampleTitles) {
|
|
||||||
title = fmt.Sprintf("%s - Part %d", title, (i/len(sampleTitles))+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
domain := sampleDomains[i%len(sampleDomains)]
|
|
||||||
path := generateRandomPath()
|
|
||||||
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.", i+1, title)
|
|
||||||
|
|
||||||
post := &database.Post{
|
|
||||||
Title: title,
|
|
||||||
URL: url,
|
|
||||||
Content: content,
|
|
||||||
AuthorID: &authorID,
|
|
||||||
UpVotes: 0,
|
|
||||||
DownVotes: 0,
|
|
||||||
Score: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := postRepo.Create(post); err != nil {
|
|
||||||
return nil, fmt.Errorf("create post %d: %w", i+1, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
posts = append(posts, *post)
|
|
||||||
}
|
|
||||||
|
|
||||||
return posts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateRandomPath() string {
|
|
||||||
initSeedRand()
|
|
||||||
pathLength := seedRandSource.Intn(20)
|
|
||||||
path := "/article/"
|
|
||||||
|
|
||||||
for i := 0; i < pathLength+5; i++ {
|
|
||||||
randomChar := seedRandSource.Intn(26)
|
|
||||||
path += string(rune('a' + randomChar))
|
|
||||||
}
|
|
||||||
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
func createRandomVotes(voteRepo repositories.VoteRepository, users []database.User, posts []database.Post, avgVotesPerPost int) (int, error) {
|
|
||||||
initSeedRand()
|
|
||||||
totalVotes := 0
|
|
||||||
|
|
||||||
for _, post := range posts {
|
|
||||||
numVotes := seedRandSource.Intn(avgVotesPerPost*2 + 1)
|
|
||||||
|
|
||||||
if numVotes == 0 && avgVotesPerPost > 0 {
|
|
||||||
if seedRandSource.Intn(5) > 0 {
|
|
||||||
numVotes = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
usedUsers := make(map[uint]bool)
|
|
||||||
for i := 0; i < numVotes && len(usedUsers) < len(users); i++ {
|
|
||||||
userIdx := seedRandSource.Intn(len(users))
|
|
||||||
user := users[userIdx]
|
|
||||||
|
|
||||||
if usedUsers[user.ID] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
usedUsers[user.ID] = true
|
|
||||||
|
|
||||||
voteTypeInt := seedRandSource.Intn(10)
|
|
||||||
var voteType database.VoteType
|
|
||||||
if voteTypeInt < 7 {
|
|
||||||
voteType = database.VoteUp
|
|
||||||
} else {
|
|
||||||
voteType = database.VoteDown
|
|
||||||
}
|
|
||||||
|
|
||||||
vote := &database.Vote{
|
|
||||||
UserID: &user.ID,
|
|
||||||
PostID: post.ID,
|
|
||||||
Type: voteType,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := voteRepo.Create(vote); err != nil {
|
|
||||||
return totalVotes, fmt.Errorf("create vote for post %d: %w", post.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
totalVotes++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalVotes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updatePostScores(postRepo repositories.PostRepository, voteRepo repositories.VoteRepository, posts []database.Post) error {
|
|
||||||
for _, post := range posts {
|
|
||||||
upVotes, downVotes, err := getVoteCounts(voteRepo, post.ID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("get vote counts for post %d: %w", post.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
post.UpVotes = upVotes
|
|
||||||
post.DownVotes = downVotes
|
|
||||||
post.Score = upVotes - downVotes
|
|
||||||
|
|
||||||
if err := postRepo.Update(&post); err != nil {
|
|
||||||
return fmt.Errorf("update post %d scores: %w", post.ID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getVoteCounts(voteRepo repositories.VoteRepository, postID uint) (int, int, error) {
|
func getVoteCounts(voteRepo repositories.VoteRepository, postID uint) (int, int, error) {
|
||||||
votes, err := voteRepo.GetByPostID(postID)
|
votes, err := voteRepo.GetByPostID(postID)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"goyco/internal/repositories"
|
"goyco/internal/repositories"
|
||||||
"goyco/internal/testutils"
|
"goyco/internal/testutils"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
"gorm.io/driver/sqlite"
|
"gorm.io/driver/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -25,18 +24,43 @@ func TestSeedCommand(t *testing.T) {
|
|||||||
t.Fatalf("Failed to migrate database: %v", err)
|
t.Fatalf("Failed to migrate database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
userRepo := repositories.NewUserRepository(db).WithTx(tx)
|
||||||
|
postRepo := repositories.NewPostRepository(db).WithTx(tx)
|
||||||
|
voteRepo := repositories.NewVoteRepository(db).WithTx(tx)
|
||||||
|
return seedDatabase(userRepo, postRepo, voteRepo, []string{"--users", "2", "--posts", "5", "--votes-per-post", "3"})
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to seed database: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
userRepo := repositories.NewUserRepository(db)
|
userRepo := repositories.NewUserRepository(db)
|
||||||
postRepo := repositories.NewPostRepository(db)
|
postRepo := repositories.NewPostRepository(db)
|
||||||
voteRepo := repositories.NewVoteRepository(db)
|
voteRepo := repositories.NewVoteRepository(db)
|
||||||
|
|
||||||
seedPasswordHash, err := bcrypt.GenerateFromPassword([]byte("seed-password"), bcrypt.DefaultCost)
|
users, err := userRepo.GetAll(100, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to hash seed password: %v", err)
|
t.Fatalf("Failed to get users: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
seedUser, err := ensureSeedUser(userRepo, string(seedPasswordHash))
|
seedUserCount := 0
|
||||||
if err != nil {
|
var seedUser *database.User
|
||||||
t.Fatalf("Failed to ensure seed user: %v", err)
|
regularUserCount := 0
|
||||||
|
for i := range users {
|
||||||
|
if strings.HasPrefix(users[i].Username, "seed_admin_") {
|
||||||
|
seedUserCount++
|
||||||
|
seedUser = &users[i]
|
||||||
|
} else if strings.HasPrefix(users[i].Username, "user_") {
|
||||||
|
regularUserCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if seedUserCount != 1 {
|
||||||
|
t.Errorf("Expected 1 seed user, got %d", seedUserCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if seedUser == nil {
|
||||||
|
t.Fatal("Expected seed user to be created")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(seedUser.Username, "seed_admin_") {
|
if !strings.HasPrefix(seedUser.Username, "seed_admin_") {
|
||||||
@@ -51,23 +75,13 @@ func TestSeedCommand(t *testing.T) {
|
|||||||
t.Error("Expected seed user to be email verified")
|
t.Error("Expected seed user to be email verified")
|
||||||
}
|
}
|
||||||
|
|
||||||
userPasswordHash, err := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
if regularUserCount != 2 {
|
||||||
if err != nil {
|
t.Errorf("Expected 2 regular users, got %d", regularUserCount)
|
||||||
t.Fatalf("Failed to hash user password: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := createRandomUsers(userRepo, 2, string(userPasswordHash))
|
posts, err := postRepo.GetAll(100, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create random users: %v", err)
|
t.Fatalf("Failed to get posts: %v", err)
|
||||||
}
|
|
||||||
|
|
||||||
if len(users) != 2 {
|
|
||||||
t.Errorf("Expected 2 users, got %d", len(users))
|
|
||||||
}
|
|
||||||
|
|
||||||
posts, err := createRandomPosts(postRepo, seedUser.ID, 5)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create random posts: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(posts) != 5 {
|
if len(posts) != 5 {
|
||||||
@@ -84,39 +98,49 @@ func TestSeedCommand(t *testing.T) {
|
|||||||
if post.AuthorID == nil || *post.AuthorID != seedUser.ID {
|
if post.AuthorID == nil || *post.AuthorID != seedUser.ID {
|
||||||
t.Errorf("Post %d has wrong author ID: expected %d, got %v", i, seedUser.ID, post.AuthorID)
|
t.Errorf("Post %d has wrong author ID: expected %d, got %v", i, seedUser.ID, post.AuthorID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expectedScore := post.UpVotes - post.DownVotes
|
||||||
|
if post.Score != expectedScore {
|
||||||
|
t.Errorf("Post %d has incorrect score: expected %d, got %d", i, expectedScore, post.Score)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allUsers := append([]database.User{*seedUser}, users...)
|
voteCount, err := voteRepo.Count()
|
||||||
votes, err := createRandomVotes(voteRepo, allUsers, posts, 3)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create random votes: %v", err)
|
t.Fatalf("Failed to count votes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if votes == 0 {
|
if voteCount == 0 {
|
||||||
t.Error("Expected some votes to be created")
|
t.Error("Expected some votes to be created")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = updatePostScores(postRepo, voteRepo, posts)
|
for _, post := range posts {
|
||||||
|
postVotes, err := voteRepo.GetByPostID(post.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to update post scores: %v", err)
|
t.Errorf("Failed to get votes for post %d: %v", post.ID, err)
|
||||||
}
|
|
||||||
|
|
||||||
for i, post := range posts {
|
|
||||||
updatedPost, err := postRepo.GetByID(post.ID)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to get updated post %d: %v", i, err)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedScore := updatedPost.UpVotes - updatedPost.DownVotes
|
for _, vote := range postVotes {
|
||||||
if updatedPost.Score != expectedScore {
|
if vote.PostID != post.ID {
|
||||||
t.Errorf("Post %d has incorrect score: expected %d, got %d", i, expectedScore, updatedPost.Score)
|
t.Errorf("Vote has wrong post ID: expected %d, got %d", post.ID, vote.PostID)
|
||||||
|
}
|
||||||
|
if vote.UserID == nil {
|
||||||
|
t.Error("Vote has nil user ID")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateRandomPath(t *testing.T) {
|
func TestGenerateRandomPath(t *testing.T) {
|
||||||
path := generateRandomPath()
|
initSeedRand()
|
||||||
|
pathLength := seedRandSource.Intn(20)
|
||||||
|
path := "/article/"
|
||||||
|
|
||||||
|
for i := 0; i < pathLength+5; i++ {
|
||||||
|
randomChar := seedRandSource.Intn(26)
|
||||||
|
path += string(rune('a' + randomChar))
|
||||||
|
}
|
||||||
|
|
||||||
if path == "" {
|
if path == "" {
|
||||||
t.Error("Generated path should not be empty")
|
t.Error("Generated path should not be empty")
|
||||||
@@ -126,7 +150,14 @@ func TestGenerateRandomPath(t *testing.T) {
|
|||||||
t.Errorf("Generated path too short: %s", path)
|
t.Errorf("Generated path too short: %s", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
secondPath := generateRandomPath()
|
initSeedRand()
|
||||||
|
secondPathLength := seedRandSource.Intn(20)
|
||||||
|
secondPath := "/article/"
|
||||||
|
for i := 0; i < secondPathLength+5; i++ {
|
||||||
|
randomChar := seedRandSource.Intn(26)
|
||||||
|
secondPath += string(rune('a' + randomChar))
|
||||||
|
}
|
||||||
|
|
||||||
if path == secondPath {
|
if path == secondPath {
|
||||||
t.Error("Generated paths should be different")
|
t.Error("Generated paths should be different")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user