To gitea and beyond, let's go(-yco)
This commit is contained in:
998
internal/testutils/mocks.go
Normal file
998
internal/testutils/mocks.go
Normal file
@@ -0,0 +1,998 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"goyco/internal/database"
|
||||
"goyco/internal/repositories"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type MockEmailSender struct {
|
||||
sendFunc func(to, subject, body string) error
|
||||
lastVerificationToken string
|
||||
lastDeletionToken string
|
||||
lastPasswordResetToken string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) Send(to, subject, body string) error {
|
||||
if m.sendFunc != nil {
|
||||
return m.sendFunc(to, subject, body)
|
||||
}
|
||||
|
||||
if len(body) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
normalized := strings.ToLower(strings.TrimSpace(subject))
|
||||
|
||||
token := extractTokenFromBody(body)
|
||||
|
||||
switch {
|
||||
case strings.Contains(normalized, "resend") && strings.Contains(normalized, "confirm"):
|
||||
m.SetVerificationToken(defaultIfEmpty(token, "test-verification-token"))
|
||||
case strings.Contains(normalized, "confirm your goyco account") || strings.Contains(normalized, "confirm your account"):
|
||||
m.SetVerificationToken(defaultIfEmpty(token, "test-verification-token"))
|
||||
case strings.Contains(normalized, "confirm") && strings.Contains(normalized, "email"):
|
||||
m.SetVerificationToken(defaultIfEmpty(token, "test-verification-token"))
|
||||
case strings.Contains(normalized, "confirm your new email"):
|
||||
m.SetVerificationToken(defaultIfEmpty(token, "test-verification-token"))
|
||||
case strings.Contains(normalized, "account deletion"):
|
||||
m.SetDeletionToken(defaultIfEmpty(token, "test-deletion-token"))
|
||||
case strings.Contains(normalized, "password reset") || strings.Contains(normalized, "reset your") || strings.Contains(normalized, "reset password"):
|
||||
m.SetPasswordResetToken(defaultIfEmpty(token, "test-password-reset-token"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) GetLastVerificationToken() string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.lastVerificationToken
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) GetLastDeletionToken() string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.lastDeletionToken
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) GetLastPasswordResetToken() string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.lastPasswordResetToken
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) Reset() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.lastVerificationToken = ""
|
||||
m.lastDeletionToken = ""
|
||||
m.lastPasswordResetToken = ""
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) VerificationToken() string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.lastVerificationToken
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) SetVerificationToken(token string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.lastVerificationToken = token
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) DeletionToken() string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.lastDeletionToken
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) PasswordResetToken() string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.lastPasswordResetToken
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) SetDeletionToken(token string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.lastDeletionToken = token
|
||||
}
|
||||
|
||||
func (m *MockEmailSender) SetPasswordResetToken(token string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.lastPasswordResetToken = token
|
||||
}
|
||||
|
||||
func defaultIfEmpty(value, fallback string) string {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return fallback
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func extractTokenFromBody(body string) string {
|
||||
index := strings.Index(body, "token=")
|
||||
if index == -1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
tokenPart := body[index+len("token="):]
|
||||
|
||||
if delimIdx := strings.IndexAny(tokenPart, "&\"'\\\r\n <>"); delimIdx != -1 {
|
||||
tokenPart = tokenPart[:delimIdx]
|
||||
}
|
||||
|
||||
trimmed := strings.Trim(tokenPart, "\"' ")
|
||||
if trimmed == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
unescaped, err := url.QueryUnescape(trimmed)
|
||||
if err != nil {
|
||||
return trimmed
|
||||
}
|
||||
|
||||
return unescaped
|
||||
}
|
||||
|
||||
type MockTitleFetcher struct {
|
||||
fetchFunc func(ctx context.Context, url string) (string, error)
|
||||
title string
|
||||
err error
|
||||
}
|
||||
|
||||
func (m *MockTitleFetcher) FetchTitle(ctx context.Context, url string) (string, error) {
|
||||
if m.fetchFunc != nil {
|
||||
return m.fetchFunc(ctx, url)
|
||||
}
|
||||
if m.err != nil {
|
||||
return "", m.err
|
||||
}
|
||||
return m.title, nil
|
||||
}
|
||||
|
||||
func (m *MockTitleFetcher) SetTitle(title string) {
|
||||
m.title = title
|
||||
m.err = nil
|
||||
}
|
||||
|
||||
func (m *MockTitleFetcher) SetError(err error) {
|
||||
m.err = err
|
||||
m.title = ""
|
||||
}
|
||||
|
||||
type MockUserRepository struct {
|
||||
users map[uint]*database.User
|
||||
usersByUsername map[string]*database.User
|
||||
usersByEmail map[string]*database.User
|
||||
usersByVerificationToken map[string]*database.User
|
||||
usersByPasswordResetToken map[string]*database.User
|
||||
deletedUsers map[uint]*database.User
|
||||
nextID uint
|
||||
createErr error
|
||||
getByIDErr error
|
||||
getByUsernameErr error
|
||||
getByEmailErr error
|
||||
getByVerificationTokenErr error
|
||||
getByPasswordResetTokenErr error
|
||||
updateErr error
|
||||
deleteErr error
|
||||
mu sync.RWMutex
|
||||
|
||||
GetAllFunc func(limit, offset int) ([]database.User, error)
|
||||
GetDeletedUsersFunc func() ([]database.User, error)
|
||||
HardDeleteAllFunc func() (int64, error)
|
||||
|
||||
GetErr error
|
||||
DeleteErr error
|
||||
|
||||
Users map[uint]*database.User
|
||||
DeletedUsers map[uint]*database.User
|
||||
}
|
||||
|
||||
func NewMockUserRepository() *MockUserRepository {
|
||||
return &MockUserRepository{
|
||||
users: make(map[uint]*database.User),
|
||||
usersByUsername: make(map[string]*database.User),
|
||||
usersByEmail: make(map[string]*database.User),
|
||||
usersByVerificationToken: make(map[string]*database.User),
|
||||
usersByPasswordResetToken: make(map[string]*database.User),
|
||||
deletedUsers: make(map[uint]*database.User),
|
||||
nextID: 1,
|
||||
Users: make(map[uint]*database.User),
|
||||
DeletedUsers: make(map[uint]*database.User),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Create(user *database.User) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.createErr != nil {
|
||||
return m.createErr
|
||||
}
|
||||
|
||||
user.ID = m.nextID
|
||||
m.nextID++
|
||||
|
||||
now := time.Now()
|
||||
user.CreatedAt = now
|
||||
user.UpdatedAt = now
|
||||
|
||||
userCopy := *user
|
||||
m.users[user.ID] = &userCopy
|
||||
m.usersByUsername[user.Username] = &userCopy
|
||||
m.usersByEmail[user.Email] = &userCopy
|
||||
m.Users[user.ID] = &userCopy
|
||||
|
||||
if user.EmailVerificationToken != "" {
|
||||
m.usersByVerificationToken[user.EmailVerificationToken] = &userCopy
|
||||
}
|
||||
if user.PasswordResetToken != "" {
|
||||
m.usersByPasswordResetToken[user.PasswordResetToken] = &userCopy
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetByID(id uint) (*database.User, error) {
|
||||
if m.GetErr != nil {
|
||||
return nil, m.GetErr
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if m.getByIDErr != nil {
|
||||
return nil, m.getByIDErr
|
||||
}
|
||||
|
||||
if user, ok := m.users[id]; ok {
|
||||
userCopy := *user
|
||||
return &userCopy, nil
|
||||
}
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetByUsername(username string) (*database.User, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if m.getByUsernameErr != nil {
|
||||
return nil, m.getByUsernameErr
|
||||
}
|
||||
|
||||
if user, ok := m.usersByUsername[username]; ok {
|
||||
userCopy := *user
|
||||
return &userCopy, nil
|
||||
}
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetByUsernameIncludingDeleted(username string) (*database.User, error) {
|
||||
return m.GetByUsername(username)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetByIDIncludingDeleted(id uint) (*database.User, error) {
|
||||
return m.GetByID(id)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetByEmail(email string) (*database.User, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if m.getByEmailErr != nil {
|
||||
return nil, m.getByEmailErr
|
||||
}
|
||||
|
||||
if user, ok := m.usersByEmail[email]; ok {
|
||||
userCopy := *user
|
||||
return &userCopy, nil
|
||||
}
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetByVerificationToken(token string) (*database.User, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if m.getByVerificationTokenErr != nil {
|
||||
return nil, m.getByVerificationTokenErr
|
||||
}
|
||||
|
||||
if user, ok := m.usersByVerificationToken[token]; ok {
|
||||
userCopy := *user
|
||||
return &userCopy, nil
|
||||
}
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetByPasswordResetToken(token string) (*database.User, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if m.getByPasswordResetTokenErr != nil {
|
||||
return nil, m.getByPasswordResetTokenErr
|
||||
}
|
||||
|
||||
if user, ok := m.usersByPasswordResetToken[token]; ok {
|
||||
userCopy := *user
|
||||
return &userCopy, nil
|
||||
}
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetAll(limit, offset int) ([]database.User, error) {
|
||||
if m.GetErr != nil {
|
||||
return nil, m.GetErr
|
||||
}
|
||||
if m.GetAllFunc != nil {
|
||||
return m.GetAllFunc(limit, offset)
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
var users []database.User
|
||||
count := 0
|
||||
for _, user := range m.users {
|
||||
if count >= offset && count < offset+limit {
|
||||
users = append(users, *user)
|
||||
}
|
||||
count++
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Update(user *database.User) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.updateErr != nil {
|
||||
return m.updateErr
|
||||
}
|
||||
|
||||
if _, ok := m.users[user.ID]; !ok {
|
||||
return gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
user.UpdatedAt = time.Now()
|
||||
|
||||
userCopy := *user
|
||||
m.users[user.ID] = &userCopy
|
||||
m.usersByUsername[user.Username] = &userCopy
|
||||
m.usersByEmail[user.Email] = &userCopy
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Delete(id uint) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.DeleteErr != nil {
|
||||
return m.DeleteErr
|
||||
}
|
||||
|
||||
if user, ok := m.users[id]; ok {
|
||||
delete(m.users, id)
|
||||
delete(m.usersByUsername, user.Username)
|
||||
delete(m.usersByEmail, user.Email)
|
||||
return nil
|
||||
}
|
||||
return gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) HardDelete(id uint) error {
|
||||
return m.Delete(id)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) SoftDeleteWithPosts(id uint) error {
|
||||
return m.Delete(id)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetPosts(userID uint, limit, offset int) ([]database.Post, error) {
|
||||
return []database.Post{}, nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Lock(id uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Unlock(id uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetDeletedUsers() ([]database.User, error) {
|
||||
if m.GetDeletedUsersFunc != nil {
|
||||
return m.GetDeletedUsersFunc()
|
||||
}
|
||||
return []database.User{}, nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) HardDeleteAll() (int64, error) {
|
||||
if m.HardDeleteAllFunc != nil {
|
||||
return m.HardDeleteAllFunc()
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
count := int64(len(m.users))
|
||||
m.users = make(map[uint]*database.User)
|
||||
m.usersByUsername = make(map[string]*database.User)
|
||||
m.usersByEmail = make(map[string]*database.User)
|
||||
m.usersByVerificationToken = make(map[string]*database.User)
|
||||
m.usersByPasswordResetToken = make(map[string]*database.User)
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Count() (int64, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return int64(len(m.users)), nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) WithTx(tx *gorm.DB) repositories.UserRepository {
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) SetCreateError(err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.createErr = err
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) SetGetByIDError(err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.getByIDErr = err
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) SetGetByUsernameError(err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.getByUsernameErr = err
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) SetGetByEmailError(err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.getByEmailErr = err
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) SetUpdateError(err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.updateErr = err
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) SetDeleteError(err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.deleteErr = err
|
||||
}
|
||||
|
||||
type MockPostRepository struct {
|
||||
createFunc func(*database.Post) error
|
||||
getByIDFunc func(uint) (*database.Post, error)
|
||||
getAllFunc func(int, int) ([]database.Post, error)
|
||||
getByUserIDFunc func(uint, int, int) ([]database.Post, error)
|
||||
updateFunc func(*database.Post) error
|
||||
deleteFunc func(uint) error
|
||||
countFunc func() (int64, error)
|
||||
countByUserIDFunc func(uint) (int64, error)
|
||||
getTopPostsFunc func(int) ([]database.Post, error)
|
||||
getNewestPostsFunc func(int) ([]database.Post, error)
|
||||
searchFunc func(string, int, int) ([]database.Post, error)
|
||||
getPostsByDeletedUsersFunc func() ([]database.Post, error)
|
||||
hardDeletePostsByDeletedUsersFunc func() (int64, error)
|
||||
hardDeleteAllFunc func() (int64, error)
|
||||
withTxFunc func(*gorm.DB) repositories.PostRepository
|
||||
|
||||
GetPostsByDeletedUsersFunc func() ([]database.Post, error)
|
||||
HardDeletePostsByDeletedUsersFunc func() (int64, error)
|
||||
HardDeleteAllFunc func() (int64, error)
|
||||
CountFunc func() (int64, error)
|
||||
|
||||
posts map[uint]*database.Post
|
||||
nextID uint
|
||||
mu sync.RWMutex
|
||||
|
||||
SearchCalls []SearchCall
|
||||
|
||||
GetErr error
|
||||
DeleteErr error
|
||||
SearchErr error
|
||||
|
||||
Posts map[uint]*database.Post
|
||||
}
|
||||
|
||||
type SearchCall struct {
|
||||
Query string
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
func NewMockPostRepository() *MockPostRepository {
|
||||
return &MockPostRepository{
|
||||
posts: make(map[uint]*database.Post),
|
||||
nextID: 1,
|
||||
Posts: make(map[uint]*database.Post),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) Create(post *database.Post) error {
|
||||
if m.createFunc != nil {
|
||||
return m.createFunc(post)
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
post.ID = m.nextID
|
||||
m.nextID++
|
||||
|
||||
postCopy := *post
|
||||
m.posts[post.ID] = &postCopy
|
||||
m.Posts[post.ID] = &postCopy
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) GetByID(id uint) (*database.Post, error) {
|
||||
if m.GetErr != nil {
|
||||
return nil, m.GetErr
|
||||
}
|
||||
if m.getByIDFunc != nil {
|
||||
return m.getByIDFunc(id)
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if post, ok := m.posts[id]; ok {
|
||||
postCopy := *post
|
||||
return &postCopy, nil
|
||||
}
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) GetAll(limit, offset int) ([]database.Post, error) {
|
||||
if m.GetErr != nil {
|
||||
return nil, m.GetErr
|
||||
}
|
||||
if m.getAllFunc != nil {
|
||||
return m.getAllFunc(limit, offset)
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
var posts []database.Post
|
||||
count := 0
|
||||
for _, post := range m.posts {
|
||||
if count >= offset && count < offset+limit {
|
||||
posts = append(posts, *post)
|
||||
}
|
||||
count++
|
||||
}
|
||||
return posts, nil
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) GetByUserID(userID uint, limit, offset int) ([]database.Post, error) {
|
||||
if m.GetErr != nil {
|
||||
return nil, m.GetErr
|
||||
}
|
||||
if m.getByUserIDFunc != nil {
|
||||
return m.getByUserIDFunc(userID, limit, offset)
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
var posts []database.Post
|
||||
count := 0
|
||||
for _, post := range m.posts {
|
||||
if post.AuthorID != nil && *post.AuthorID == userID {
|
||||
if count >= offset && count < offset+limit {
|
||||
posts = append(posts, *post)
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
return posts, nil
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) Update(post *database.Post) error {
|
||||
if m.updateFunc != nil {
|
||||
return m.updateFunc(post)
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if _, ok := m.posts[post.ID]; !ok {
|
||||
return gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
postCopy := *post
|
||||
m.posts[post.ID] = &postCopy
|
||||
m.Posts[post.ID] = &postCopy
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) Delete(id uint) error {
|
||||
if m.DeleteErr != nil {
|
||||
return m.DeleteErr
|
||||
}
|
||||
if m.deleteFunc != nil {
|
||||
return m.deleteFunc(id)
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if _, ok := m.posts[id]; !ok {
|
||||
return gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
delete(m.posts, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) Count() (int64, error) {
|
||||
if m.CountFunc != nil {
|
||||
return m.CountFunc()
|
||||
}
|
||||
if m.countFunc != nil {
|
||||
return m.countFunc()
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return int64(len(m.posts)), nil
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) CountByUserID(userID uint) (int64, error) {
|
||||
if m.countByUserIDFunc != nil {
|
||||
return m.countByUserIDFunc(userID)
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
count := int64(0)
|
||||
for _, post := range m.posts {
|
||||
if post.AuthorID != nil && *post.AuthorID == userID {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) GetTopPosts(limit int) ([]database.Post, error) {
|
||||
if m.getTopPostsFunc != nil {
|
||||
return m.getTopPostsFunc(limit)
|
||||
}
|
||||
return m.GetAll(limit, 0)
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) GetNewestPosts(limit int) ([]database.Post, error) {
|
||||
if m.getNewestPostsFunc != nil {
|
||||
return m.getNewestPostsFunc(limit)
|
||||
}
|
||||
return m.GetAll(limit, 0)
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) Search(query string, limit, offset int) ([]database.Post, error) {
|
||||
if m.SearchErr != nil {
|
||||
return nil, m.SearchErr
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
m.SearchCalls = append(m.SearchCalls, SearchCall{
|
||||
Query: query,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
})
|
||||
m.mu.Unlock()
|
||||
|
||||
if m.searchFunc != nil {
|
||||
return m.searchFunc(query, limit, offset)
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
var posts []database.Post
|
||||
count := 0
|
||||
for _, post := range m.posts {
|
||||
if containsIgnoreCase(post.Title, query) || containsIgnoreCase(post.Content, query) {
|
||||
if count >= offset && count < offset+limit {
|
||||
posts = append(posts, *post)
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
return posts, nil
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) WithTx(tx *gorm.DB) repositories.PostRepository {
|
||||
if m.withTxFunc != nil {
|
||||
return m.withTxFunc(tx)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) GetPostsByDeletedUsers() ([]database.Post, error) {
|
||||
if m.GetPostsByDeletedUsersFunc != nil {
|
||||
return m.GetPostsByDeletedUsersFunc()
|
||||
}
|
||||
if m.getPostsByDeletedUsersFunc != nil {
|
||||
return m.getPostsByDeletedUsersFunc()
|
||||
}
|
||||
return []database.Post{}, nil
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) HardDeletePostsByDeletedUsers() (int64, error) {
|
||||
if m.HardDeletePostsByDeletedUsersFunc != nil {
|
||||
return m.HardDeletePostsByDeletedUsersFunc()
|
||||
}
|
||||
if m.hardDeletePostsByDeletedUsersFunc != nil {
|
||||
return m.hardDeletePostsByDeletedUsersFunc()
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (m *MockPostRepository) HardDeleteAll() (int64, error) {
|
||||
if m.HardDeleteAllFunc != nil {
|
||||
return m.HardDeleteAllFunc()
|
||||
}
|
||||
if m.hardDeleteAllFunc != nil {
|
||||
return m.hardDeleteAllFunc()
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
count := int64(len(m.posts))
|
||||
m.posts = make(map[uint]*database.Post)
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func containsIgnoreCase(s, substr string) bool {
|
||||
return len(s) >= len(substr)
|
||||
}
|
||||
|
||||
type MockVoteRepository struct {
|
||||
votes map[uint]*database.Vote
|
||||
byUserPost map[string]*database.Vote
|
||||
nextID uint
|
||||
createErr error
|
||||
updateErr error
|
||||
deleteErr error
|
||||
mu sync.RWMutex
|
||||
|
||||
DeleteErr error
|
||||
}
|
||||
|
||||
func NewMockVoteRepository() *MockVoteRepository {
|
||||
return &MockVoteRepository{
|
||||
votes: make(map[uint]*database.Vote),
|
||||
byUserPost: make(map[string]*database.Vote),
|
||||
nextID: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) Create(vote *database.Vote) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.createErr != nil {
|
||||
return m.createErr
|
||||
}
|
||||
|
||||
var key string
|
||||
if vote.UserID != nil {
|
||||
key = m.key(*vote.UserID, vote.PostID)
|
||||
} else {
|
||||
key = fmt.Sprintf("anon-%d", vote.PostID)
|
||||
}
|
||||
if existingVote, exists := m.byUserPost[key]; exists {
|
||||
existingVote.Type = vote.Type
|
||||
existingVote.UpdatedAt = vote.UpdatedAt
|
||||
vote.ID = existingVote.ID
|
||||
return nil
|
||||
}
|
||||
|
||||
vote.ID = m.nextID
|
||||
m.nextID++
|
||||
|
||||
voteCopy := *vote
|
||||
m.votes[vote.ID] = &voteCopy
|
||||
m.byUserPost[key] = &voteCopy
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) CreateOrUpdate(vote *database.Vote) error {
|
||||
return m.Create(vote)
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) GetByID(id uint) (*database.Vote, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if vote, ok := m.votes[id]; ok {
|
||||
voteCopy := *vote
|
||||
return &voteCopy, nil
|
||||
}
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) GetByUserAndPost(userID, postID uint) (*database.Vote, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
key := m.key(userID, postID)
|
||||
if vote, ok := m.byUserPost[key]; ok {
|
||||
voteCopy := *vote
|
||||
return &voteCopy, nil
|
||||
}
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) GetByVoteHash(voteHash string) (*database.Vote, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
for _, vote := range m.votes {
|
||||
if vote.VoteHash != nil && *vote.VoteHash == voteHash {
|
||||
voteCopy := *vote
|
||||
return &voteCopy, nil
|
||||
}
|
||||
}
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) GetByPostID(postID uint) ([]database.Vote, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
var votes []database.Vote
|
||||
for _, vote := range m.votes {
|
||||
if vote.PostID == postID {
|
||||
votes = append(votes, *vote)
|
||||
}
|
||||
}
|
||||
return votes, nil
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) GetByUserID(userID uint) ([]database.Vote, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
var votes []database.Vote
|
||||
for _, vote := range m.votes {
|
||||
if vote.UserID != nil && *vote.UserID == userID {
|
||||
votes = append(votes, *vote)
|
||||
}
|
||||
}
|
||||
return votes, nil
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) Update(vote *database.Vote) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.updateErr != nil {
|
||||
return m.updateErr
|
||||
}
|
||||
|
||||
if _, ok := m.votes[vote.ID]; !ok {
|
||||
return gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
voteCopy := *vote
|
||||
m.votes[vote.ID] = &voteCopy
|
||||
var key string
|
||||
if vote.UserID != nil {
|
||||
key = m.key(*vote.UserID, vote.PostID)
|
||||
} else {
|
||||
key = fmt.Sprintf("anon-%d", vote.PostID)
|
||||
}
|
||||
m.byUserPost[key] = &voteCopy
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) Delete(id uint) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.DeleteErr != nil {
|
||||
return m.DeleteErr
|
||||
}
|
||||
|
||||
if vote, ok := m.votes[id]; ok {
|
||||
delete(m.votes, id)
|
||||
var key string
|
||||
if vote.UserID != nil {
|
||||
key = m.key(*vote.UserID, vote.PostID)
|
||||
} else {
|
||||
key = fmt.Sprintf("anon-%d", vote.PostID)
|
||||
}
|
||||
delete(m.byUserPost, key)
|
||||
return nil
|
||||
}
|
||||
return gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) CountByPostID(postID uint) (int64, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
count := int64(0)
|
||||
for _, vote := range m.votes {
|
||||
if vote.PostID == postID {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) CountByUserID(userID uint) (int64, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
count := int64(0)
|
||||
for _, vote := range m.votes {
|
||||
if vote.UserID != nil && *vote.UserID == userID {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) Count() (int64, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return int64(len(m.votes)), nil
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) WithTx(tx *gorm.DB) repositories.VoteRepository {
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) key(userID, postID uint) string {
|
||||
return fmt.Sprintf("%d-%d", userID, postID)
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) SetCreateError(err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.createErr = err
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) SetUpdateError(err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.updateErr = err
|
||||
}
|
||||
|
||||
func (m *MockVoteRepository) SetDeleteError(err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.deleteErr = err
|
||||
}
|
||||
Reference in New Issue
Block a user