To gitea and beyond, let's go(-yco)

This commit is contained in:
2025-11-10 19:12:09 +01:00
parent 8f6133392d
commit 71a031342b
245 changed files with 83994 additions and 0 deletions

View File

@@ -0,0 +1,603 @@
package testutils
import (
"fmt"
"testing"
"time"
"golang.org/x/crypto/bcrypt"
"goyco/internal/database"
"goyco/internal/repositories"
)
type TestDataFactory struct{}
type AuthResult struct {
User *database.User `json:"user"`
AccessToken string `json:"access_token"`
}
type RegistrationResult struct {
User *database.User `json:"user"`
}
type VoteRequest struct {
Type database.VoteType `json:"type"`
UserID uint `json:"user_id"`
PostID uint `json:"post_id"`
IPAddress string `json:"ip_address"`
UserAgent string `json:"user_agent"`
}
func NewTestDataFactory() *TestDataFactory {
return &TestDataFactory{}
}
type UserBuilder struct {
user *database.User
}
func (f *TestDataFactory) NewUserBuilder() *UserBuilder {
return &UserBuilder{
user: &database.User{
ID: 1,
Username: "testuser",
Email: "test@example.com",
EmailVerified: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
},
}
}
func (b *UserBuilder) WithID(id uint) *UserBuilder {
b.user.ID = id
return b
}
func (b *UserBuilder) WithUsername(username string) *UserBuilder {
b.user.Username = username
return b
}
func (b *UserBuilder) WithEmail(email string) *UserBuilder {
b.user.Email = email
return b
}
func (b *UserBuilder) WithPassword(password string) *UserBuilder {
hashed, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
b.user.Password = string(hashed)
return b
}
func (b *UserBuilder) WithEmailVerified(verified bool) *UserBuilder {
b.user.EmailVerified = verified
return b
}
func (b *UserBuilder) WithCreatedAt(t time.Time) *UserBuilder {
b.user.CreatedAt = t
return b
}
func (b *UserBuilder) Build() *database.User {
return b.user
}
type PostBuilder struct {
post *database.Post
}
func (f *TestDataFactory) NewPostBuilder() *PostBuilder {
return &PostBuilder{
post: &database.Post{
ID: 1,
Title: "Test Post",
URL: "https://example.com",
Content: "Test content",
AuthorID: uintPtr(1),
UpVotes: 0,
DownVotes: 0,
Score: 0,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
},
}
}
func (b *PostBuilder) WithID(id uint) *PostBuilder {
b.post.ID = id
return b
}
func (b *PostBuilder) WithTitle(title string) *PostBuilder {
b.post.Title = title
return b
}
func (b *PostBuilder) WithURL(url string) *PostBuilder {
b.post.URL = url
return b
}
func (b *PostBuilder) WithContent(content string) *PostBuilder {
b.post.Content = content
return b
}
func (b *PostBuilder) WithAuthorID(authorID uint) *PostBuilder {
b.post.AuthorID = &authorID
return b
}
func (b *PostBuilder) WithVotes(upVotes, downVotes int) *PostBuilder {
b.post.UpVotes = upVotes
b.post.DownVotes = downVotes
b.post.Score = upVotes - downVotes
return b
}
func (b *PostBuilder) WithCreatedAt(t time.Time) *PostBuilder {
b.post.CreatedAt = t
return b
}
func (b *PostBuilder) Build() *database.Post {
return b.post
}
type VoteBuilder struct {
vote *database.Vote
}
func (f *TestDataFactory) NewVoteBuilder() *VoteBuilder {
return &VoteBuilder{
vote: &database.Vote{
ID: 1,
Type: database.VoteUp,
UserID: uintPtr(1),
PostID: 1,
},
}
}
func (b *VoteBuilder) WithID(id uint) *VoteBuilder {
b.vote.ID = id
return b
}
func (b *VoteBuilder) WithType(voteType database.VoteType) *VoteBuilder {
b.vote.Type = voteType
return b
}
func (b *VoteBuilder) WithUserID(userID uint) *VoteBuilder {
b.vote.UserID = &userID
return b
}
func (b *VoteBuilder) WithPostID(postID uint) *VoteBuilder {
b.vote.PostID = postID
return b
}
func (b *VoteBuilder) Build() *database.Vote {
return b.vote
}
type AuthResultBuilder struct {
result *AuthResult
}
func (f *TestDataFactory) NewAuthResultBuilder() *AuthResultBuilder {
return &AuthResultBuilder{
result: &AuthResult{
User: &database.User{ID: 1, Username: "testuser"},
AccessToken: "access_token",
},
}
}
func (b *AuthResultBuilder) WithUser(user *database.User) *AuthResultBuilder {
b.result.User = user
return b
}
func (b *AuthResultBuilder) WithAccessToken(token string) *AuthResultBuilder {
b.result.AccessToken = token
return b
}
func (b *AuthResultBuilder) Build() *AuthResult {
return b.result
}
type RegistrationResultBuilder struct {
result *RegistrationResult
}
func (f *TestDataFactory) NewRegistrationResultBuilder() *RegistrationResultBuilder {
return &RegistrationResultBuilder{
result: &RegistrationResult{
User: &database.User{ID: 1, Username: "testuser"},
},
}
}
func (b *RegistrationResultBuilder) WithUser(user *database.User) *RegistrationResultBuilder {
b.result.User = user
return b
}
func (b *RegistrationResultBuilder) WithMessage(message string) *RegistrationResultBuilder {
return b
}
func (b *RegistrationResultBuilder) Build() *RegistrationResult {
return b.result
}
type VoteRequestBuilder struct {
request VoteRequest
}
func (f *TestDataFactory) NewVoteRequestBuilder() *VoteRequestBuilder {
return &VoteRequestBuilder{
request: VoteRequest{
Type: database.VoteUp,
UserID: 1,
PostID: 1,
IPAddress: "127.0.0.1",
UserAgent: "test-agent",
},
}
}
func (b *VoteRequestBuilder) WithType(voteType database.VoteType) *VoteRequestBuilder {
b.request.Type = voteType
return b
}
func (b *VoteRequestBuilder) WithUserID(userID uint) *VoteRequestBuilder {
b.request.UserID = userID
return b
}
func (b *VoteRequestBuilder) WithPostID(postID uint) *VoteRequestBuilder {
b.request.PostID = postID
return b
}
func (b *VoteRequestBuilder) WithIPAddress(ip string) *VoteRequestBuilder {
b.request.IPAddress = ip
return b
}
func (b *VoteRequestBuilder) WithUserAgent(agent string) *VoteRequestBuilder {
b.request.UserAgent = agent
return b
}
func (b *VoteRequestBuilder) Build() VoteRequest {
return b.request
}
func (f *TestDataFactory) CreateTestUsers(count int) []*database.User {
users := make([]*database.User, count)
for i := 0; i < count; i++ {
users[i] = f.NewUserBuilder().
WithID(uint(i + 1)).
WithUsername(fmt.Sprintf("user%d", i+1)).
WithEmail(fmt.Sprintf("user%d@example.com", i+1)).
Build()
}
return users
}
func (f *TestDataFactory) CreateTestPosts(count int) []*database.Post {
posts := make([]*database.Post, count)
for i := 0; i < count; i++ {
posts[i] = f.NewPostBuilder().
WithID(uint(i+1)).
WithTitle(fmt.Sprintf("Post %d", i+1)).
WithURL(fmt.Sprintf("https://example.com/post%d", i+1)).
WithContent(fmt.Sprintf("Content for post %d", i+1)).
WithAuthorID(uint((i%10)+1)).
WithVotes(i%10, i%5).
Build()
}
return posts
}
func (f *TestDataFactory) CreateTestVotes(count int) []*database.Vote {
votes := make([]*database.Vote, count)
for i := range count {
voteType := database.VoteUp
if i%3 == 0 {
voteType = database.VoteDown
} else if i%5 == 0 {
voteType = database.VoteNone
}
votes[i] = f.NewVoteBuilder().
WithID(uint(i + 1)).
WithType(voteType).
WithUserID(uint((i % 20) + 1)).
WithPostID(uint((i % 100) + 1)).
Build()
}
return votes
}
func (f *TestDataFactory) CreateTestAuthResults(count int) []*AuthResult {
results := make([]*AuthResult, count)
for i := 0; i < count; i++ {
results[i] = f.NewAuthResultBuilder().
WithUser(f.NewUserBuilder().
WithID(uint(i + 1)).
WithUsername(fmt.Sprintf("user%d", i+1)).
Build()).
WithAccessToken(fmt.Sprintf("token_%d", i+1)).
Build()
}
return results
}
func (f *TestDataFactory) CreateTestVoteRequests(count int) []VoteRequest {
requests := make([]VoteRequest, count)
for i := 0; i < count; i++ {
voteType := database.VoteUp
if i%3 == 0 {
voteType = database.VoteDown
} else if i%5 == 0 {
voteType = database.VoteNone
}
requests[i] = f.NewVoteRequestBuilder().
WithType(voteType).
WithUserID(uint((i % 20) + 1)).
WithPostID(uint((i % 100) + 1)).
WithIPAddress(fmt.Sprintf("192.168.1.%d", (i%254)+1)).
WithUserAgent(fmt.Sprintf("test-agent-%d", i+1)).
Build()
}
return requests
}
func uintPtr(u uint) *uint {
return &u
}
type E2ETestDataFactory struct {
UserRepo repositories.UserRepository
PostRepo repositories.PostRepository
}
func NewE2ETestDataFactory(userRepo repositories.UserRepository, postRepo repositories.PostRepository) *E2ETestDataFactory {
return &E2ETestDataFactory{
UserRepo: userRepo,
PostRepo: postRepo,
}
}
type PostData struct {
Title string
URL string
Content string
}
func (f *E2ETestDataFactory) CreateUserWithPosts(t *testing.T, username, email, password string, posts []PostData) (*TestUser, []*TestPost) {
t.Helper()
user := CreateE2ETestUser(t, f.UserRepo, username, email, password)
var createdPosts []*TestPost
for i, postData := range posts {
url := postData.URL
if url == "" {
url = fmt.Sprintf("https://example.com/post-%d-%d", user.ID, i+1)
}
title := postData.Title
if title == "" {
title = fmt.Sprintf("Test Post %d", i+1)
}
content := postData.Content
if content == "" {
content = fmt.Sprintf("Test content for post %d", i+1)
}
post := &database.Post{
Title: title,
URL: url,
Content: content,
AuthorID: &user.ID,
UpVotes: 0,
DownVotes: 0,
Score: 0,
}
if err := f.PostRepo.Create(post); err != nil {
t.Fatalf("Failed to create test post: %v", err)
}
createdPost, err := f.PostRepo.GetByID(post.ID)
if err != nil {
t.Fatalf("Failed to fetch created post: %v", err)
}
createdPosts = append(createdPosts, &TestPost{
ID: createdPost.ID,
Title: createdPost.Title,
URL: createdPost.URL,
Content: createdPost.Content,
AuthorID: *createdPost.AuthorID,
})
}
return user, createdPosts
}
func (f *E2ETestDataFactory) CreateMultipleUsers(t *testing.T, count int, usernamePrefix, emailPrefix, password string) []*TestUser {
t.Helper()
if count <= 0 {
t.Fatalf("count must be greater than 0, got %d", count)
}
var users []*TestUser
timestamp := time.Now().UnixNano()
for i := 0; i < count; i++ {
uniqueID := timestamp + int64(i)
username := fmt.Sprintf("%s%d", usernamePrefix, uniqueID)
email := fmt.Sprintf("%s%d@example.com", emailPrefix, uniqueID)
userPassword := password
if userPassword == "" {
userPassword = "StrongPass123!"
}
user := CreateE2ETestUser(t, f.UserRepo, username, email, userPassword)
users = append(users, user)
}
return users
}
func (f *E2ETestDataFactory) CreateUserWithDefaultPosts(t *testing.T, username, email, password string, count int) (*TestUser, []*TestPost) {
t.Helper()
if count <= 0 {
count = 3
}
posts := make([]PostData, count)
for i := 0; i < count; i++ {
posts[i] = PostData{
Title: fmt.Sprintf("Default Post %d", i+1),
URL: fmt.Sprintf("https://example.com/default-post-%d", i+1),
Content: fmt.Sprintf("This is default post content number %d", i+1),
}
}
return f.CreateUserWithPosts(t, username, email, password, posts)
}
func (f *E2ETestDataFactory) CreateUsersWithPosts(t *testing.T, count int, usernamePrefix, emailPrefix, password string, postsPerUser []PostData) map[uint]*UserWithPosts {
t.Helper()
if count <= 0 {
t.Fatalf("count must be greater than 0, got %d", count)
}
users := f.CreateMultipleUsers(t, count, usernamePrefix, emailPrefix, password)
result := make(map[uint]*UserWithPosts)
for _, user := range users {
var createdPosts []*TestPost
for i, postData := range postsPerUser {
url := postData.URL
if url == "" {
url = fmt.Sprintf("https://example.com/post-%d-%d", user.ID, i+1)
}
title := postData.Title
if title == "" {
title = fmt.Sprintf("Test Post %d for User %d", i+1, user.ID)
}
content := postData.Content
if content == "" {
content = fmt.Sprintf("Test content for post %d", i+1)
}
post := &database.Post{
Title: title,
URL: url,
Content: content,
AuthorID: &user.ID,
UpVotes: 0,
DownVotes: 0,
Score: 0,
}
if err := f.PostRepo.Create(post); err != nil {
t.Fatalf("Failed to create test post: %v", err)
}
createdPost, err := f.PostRepo.GetByID(post.ID)
if err != nil {
t.Fatalf("Failed to fetch created post: %v", err)
}
createdPosts = append(createdPosts, &TestPost{
ID: createdPost.ID,
Title: createdPost.Title,
URL: createdPost.URL,
Content: createdPost.Content,
AuthorID: *createdPost.AuthorID,
})
}
result[user.ID] = &UserWithPosts{
User: user,
Posts: createdPosts,
}
}
return result
}
type UserWithPosts struct {
User *TestUser
Posts []*TestPost
}
func (f *E2ETestDataFactory) CreatePostForUser(t *testing.T, userID uint, postData PostData) *TestPost {
t.Helper()
url := postData.URL
if url == "" {
url = fmt.Sprintf("https://example.com/post-%d-%d", userID, time.Now().UnixNano())
}
title := postData.Title
if title == "" {
title = "Test Post"
}
content := postData.Content
if content == "" {
content = "Test post content"
}
post := &database.Post{
Title: title,
URL: url,
Content: content,
AuthorID: &userID,
UpVotes: 0,
DownVotes: 0,
Score: 0,
}
if err := f.PostRepo.Create(post); err != nil {
t.Fatalf("Failed to create test post: %v", err)
}
createdPost, err := f.PostRepo.GetByID(post.ID)
if err != nil {
t.Fatalf("Failed to fetch created post: %v", err)
}
return &TestPost{
ID: createdPost.ID,
Title: createdPost.Title,
URL: createdPost.URL,
Content: createdPost.Content,
AuthorID: *createdPost.AuthorID,
}
}