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,804 @@
package repositories
import (
"errors"
"strconv"
"strings"
"testing"
"time"
"gorm.io/gorm"
"goyco/internal/database"
)
func TestPostRepository_Create(t *testing.T) {
suite := NewTestSuite(t)
t.Run("successful creation", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
post := suite.CreateTestPost(user.ID, "Test Post", "https://example.com", "Test content")
if suite.GetPostCount() != 1 {
t.Errorf("Expected 1 post, got %d", suite.GetPostCount())
}
if post.ID == 0 {
t.Error("Expected post ID to be assigned")
}
})
t.Run("post with invalid author", func(t *testing.T) {
suite.Reset()
post := &database.Post{
Title: "Test Post",
URL: "https://example.com",
Content: "Test content",
AuthorID: suite.CreateInvalidUserID(),
}
err := suite.PostRepo.Create(post)
if err != nil {
t.Errorf("Unexpected error for invalid author (SQLite allows this): %v", err)
}
})
t.Run("duplicate URL", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
suite.CreateTestPost(user.ID, "Post 1", "https://example.com", "Content 1")
post2 := &database.Post{
Title: "Post 2",
URL: "https://example.com",
Content: "Content 2",
AuthorID: &user.ID,
}
err := suite.PostRepo.Create(post2)
if err == nil {
t.Error("Expected error for duplicate URL")
}
if !errors.Is(err, gorm.ErrDuplicatedKey) && !strings.Contains(err.Error(), "duplicate") && !strings.Contains(err.Error(), "UNIQUE constraint") {
t.Errorf("Expected duplicate key error, got %v", err)
}
})
}
func TestPostRepository_GetByID(t *testing.T) {
suite := NewTestSuite(t)
t.Run("existing post", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
post := suite.CreateTestPost(user.ID, "Test Post", "https://example.com", "Test content")
retrieved, err := suite.PostRepo.GetByID(post.ID)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if retrieved == nil {
t.Fatal("Expected post, got nil")
}
if retrieved.ID != post.ID {
t.Errorf("Expected ID %d, got %d", post.ID, retrieved.ID)
}
if retrieved.Title != post.Title {
t.Errorf("Expected title %s, got %s", post.Title, retrieved.Title)
}
if retrieved.URL != post.URL {
t.Errorf("Expected URL %s, got %s", post.URL, retrieved.URL)
}
if retrieved.AuthorID == nil || post.AuthorID == nil {
t.Error("Expected both AuthorID pointers to be non-nil")
} else if *retrieved.AuthorID != *post.AuthorID {
t.Errorf("Expected author ID %d, got %d", *post.AuthorID, *retrieved.AuthorID)
}
})
t.Run("non-existing post", func(t *testing.T) {
suite.Reset()
_, err := suite.PostRepo.GetByID(999)
if err == nil {
t.Error("Expected error for non-existing post")
}
if err != gorm.ErrRecordNotFound {
t.Errorf("Expected ErrRecordNotFound, got %v", err)
}
})
}
func TestPostRepository_GetAll(t *testing.T) {
suite := NewTestSuite(t)
t.Run("empty database", func(t *testing.T) {
suite.Reset()
posts, err := suite.PostRepo.GetAll(10, 0)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(posts) != 0 {
t.Errorf("Expected 0 posts, got %d", len(posts))
}
})
t.Run("with posts", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
suite.CreateTestPost(user.ID, "Post 1", "https://example.com/1", "Content 1")
suite.CreateTestPost(user.ID, "Post 2", "https://example.com/2", "Content 2")
suite.CreateTestPost(user.ID, "Post 3", "https://example.com/3", "Content 3")
retrieved, err := suite.PostRepo.GetAll(10, 0)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 3 {
t.Errorf("Expected 3 posts, got %d", len(retrieved))
}
})
t.Run("with limit", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser2", "test2@example.com", "password123")
for i := 0; i < 5; i++ {
suite.CreateTestPost(user.ID,
"Post "+strconv.Itoa(i),
"https://example.com/"+strconv.Itoa(i),
"Content "+strconv.Itoa(i))
}
retrieved, err := suite.PostRepo.GetAll(2, 0)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 2 {
t.Errorf("Expected 2 posts, got %d", len(retrieved))
}
})
t.Run("with offset", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser3", "test3@example.com", "password123")
for i := 0; i < 5; i++ {
suite.CreateTestPost(user.ID,
"Post "+strconv.Itoa(i),
"https://example.com/"+strconv.Itoa(i),
"Content "+strconv.Itoa(i))
}
retrieved, err := suite.PostRepo.GetAll(2, 1)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 2 {
t.Errorf("Expected 2 posts, got %d", len(retrieved))
}
})
}
func TestPostRepository_GetByUserID(t *testing.T) {
suite := NewTestSuite(t)
t.Run("user with posts", func(t *testing.T) {
suite.Reset()
user1 := suite.CreateTestUser("user1", "user1@example.com", "password123")
user2 := suite.CreateTestUser("user2", "user2@example.com", "password123")
suite.CreateTestPost(user1.ID, "User1 Post", "https://example.com/1", "Content 1")
suite.CreateTestPost(user2.ID, "User2 Post", "https://example.com/2", "Content 2")
suite.CreateTestPost(user1.ID, "User1 Post 2", "https://example.com/3", "Content 3")
retrieved, err := suite.PostRepo.GetByUserID(user1.ID, 10, 0)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 2 {
t.Errorf("Expected 2 posts for user1, got %d", len(retrieved))
}
for _, post := range retrieved {
if post.AuthorID == nil || *post.AuthorID != user1.ID {
t.Errorf("Expected author ID %d, got %v", user1.ID, post.AuthorID)
}
}
})
t.Run("user without posts", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("noposts", "noposts@example.com", "password123")
retrieved, err := suite.PostRepo.GetByUserID(user.ID, 10, 0)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 0 {
t.Errorf("Expected 0 posts, got %d", len(retrieved))
}
})
}
func TestPostRepository_Update(t *testing.T) {
suite := NewTestSuite(t)
t.Run("successful update", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
post := suite.CreateTestPost(user.ID, "Test Post", "https://example.com", "Test content")
post.Title = "Updated Post"
post.Content = "Updated content"
post.Score = 10
post.UpVotes = 12
post.DownVotes = 2
err := suite.PostRepo.Update(post)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
retrieved, err := suite.PostRepo.GetByID(post.ID)
if err != nil {
t.Fatalf("Failed to retrieve post: %v", err)
}
if retrieved.Title != "Updated Post" {
t.Errorf("Expected title 'Updated Post', got %s", retrieved.Title)
}
if retrieved.Content != "Updated content" {
t.Errorf("Expected content 'Updated content', got %s", retrieved.Content)
}
if retrieved.Score != 10 {
t.Errorf("Expected score 10, got %d", retrieved.Score)
}
if retrieved.UpVotes != 12 {
t.Errorf("Expected up votes 12, got %d", retrieved.UpVotes)
}
if retrieved.DownVotes != 2 {
t.Errorf("Expected down votes 2, got %d", retrieved.DownVotes)
}
})
}
func TestPostRepository_Delete(t *testing.T) {
suite := NewTestSuite(t)
t.Run("successful delete", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
post := suite.CreateTestPost(user.ID, "Test Post", "https://example.com", "Test content")
err := suite.PostRepo.Delete(post.ID)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
_, err = suite.PostRepo.GetByID(post.ID)
if err == nil {
t.Error("Expected error for deleted post")
}
if err != gorm.ErrRecordNotFound {
t.Errorf("Expected ErrRecordNotFound, got %v", err)
}
})
}
func TestPostRepository_Count(t *testing.T) {
suite := NewTestSuite(t)
t.Run("empty database", func(t *testing.T) {
suite.Reset()
count, err := suite.PostRepo.Count()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if count != 0 {
t.Errorf("Expected count 0, got %d", count)
}
})
t.Run("with posts", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
for i := 0; i < 5; i++ {
suite.CreateTestPost(user.ID,
"Post "+strconv.Itoa(i),
"https://example.com/"+strconv.Itoa(i),
"Content "+strconv.Itoa(i))
}
count, err := suite.PostRepo.Count()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if count != 5 {
t.Errorf("Expected count 5, got %d", count)
}
})
}
func TestPostRepository_GetTopPosts(t *testing.T) {
suite := NewTestSuite(t)
t.Run("with posts", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
post1 := &database.Post{
Title: "Low Score",
URL: "https://example.com/1",
Content: "Content 1",
AuthorID: &user.ID,
Score: 1,
}
post2 := &database.Post{
Title: "High Score",
URL: "https://example.com/2",
Content: "Content 2",
AuthorID: &user.ID,
Score: 10,
}
post3 := &database.Post{
Title: "Medium Score",
URL: "https://example.com/3",
Content: "Content 3",
AuthorID: &user.ID,
Score: 5,
}
suite.PostRepo.Create(post1)
suite.PostRepo.Create(post2)
suite.PostRepo.Create(post3)
retrieved, err := suite.PostRepo.GetTopPosts(2)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 2 {
t.Errorf("Expected 2 top posts, got %d", len(retrieved))
}
for i := 0; i < len(retrieved)-1; i++ {
if retrieved[i].Score < retrieved[i+1].Score {
t.Errorf("Posts not in descending order: %d < %d", retrieved[i].Score, retrieved[i+1].Score)
}
}
})
}
func TestPostRepository_GetNewestPosts(t *testing.T) {
suite := NewTestSuite(t)
t.Run("with posts", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
post1 := &database.Post{
Title: "Old Post",
URL: "https://example.com/1",
Content: "Content 1",
AuthorID: &user.ID,
}
post2 := &database.Post{
Title: "New Post",
URL: "https://example.com/2",
Content: "Content 2",
AuthorID: &user.ID,
}
post1.CreatedAt = time.Now().Add(-2 * time.Hour)
post2.CreatedAt = time.Now().Add(-1 * time.Hour)
suite.PostRepo.Create(post1)
suite.PostRepo.Create(post2)
retrieved, err := suite.PostRepo.GetNewestPosts(2)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 2 {
t.Errorf("Expected 2 newest posts, got %d", len(retrieved))
}
if retrieved[0].CreatedAt.Before(retrieved[1].CreatedAt) {
t.Error("Expected posts to be ordered by creation time (newest first)")
}
})
}
func TestPostRepository_Search(t *testing.T) {
suite := NewTestSuite(t)
t.Run("with matching posts", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
suite.CreateTestPost(user.ID, "Golang Tutorial", "https://example.com/1", "Learn Go programming")
suite.CreateTestPost(user.ID, "Python Guide", "https://example.com/2", "Learn Python programming")
suite.CreateTestPost(user.ID, "Go Best Practices", "https://example.com/3", "Advanced Go techniques")
retrieved, err := suite.PostRepo.Search("Go", 10, 0)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 2 {
t.Errorf("Expected 2 posts matching 'Go', got %d", len(retrieved))
}
for _, post := range retrieved {
if post.Title != "Golang Tutorial" && post.Title != "Go Best Practices" {
t.Errorf("Unexpected post in search results: %s", post.Title)
}
}
})
t.Run("case insensitive search", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
suite.CreateTestPost(user.ID, "Golang Tutorial", "https://example.com/1", "Learn Go programming")
retrieved, err := suite.PostRepo.Search("golang", 10, 0)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 1 {
t.Errorf("Expected 1 post matching 'golang', got %d", len(retrieved))
}
if retrieved[0].Title != "Golang Tutorial" {
t.Errorf("Expected 'Golang Tutorial', got %s", retrieved[0].Title)
}
})
t.Run("no matching posts", func(t *testing.T) {
suite.Reset()
retrieved, err := suite.PostRepo.Search("nonexistent", 10, 0)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 0 {
t.Errorf("Expected 0 posts matching 'nonexistent', got %d", len(retrieved))
}
})
t.Run("search with limit and offset", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser2", "test2@example.com", "password123")
for i := 0; i < 5; i++ {
suite.CreateTestPost(user.ID,
"Go Post "+strconv.Itoa(i),
"https://example.com/go"+strconv.Itoa(i),
"Go content "+strconv.Itoa(i))
}
retrieved, err := suite.PostRepo.Search("Go", 2, 0)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 2 {
t.Errorf("Expected 2 posts with limit, got %d", len(retrieved))
}
retrieved, err = suite.PostRepo.Search("Go", 2, 1)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(retrieved) != 2 {
t.Errorf("Expected 2 posts with offset, got %d", len(retrieved))
}
})
}
func TestPostRepository_CountByUserID(t *testing.T) {
suite := NewTestSuite(t)
t.Run("user with posts", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
suite.CreateTestPost(user.ID, "Post 1", "https://example.com/1", "Content 1")
suite.CreateTestPost(user.ID, "Post 2", "https://example.com/2", "Content 2")
suite.CreateTestPost(user.ID, "Post 3", "https://example.com/3", "Content 3")
count, err := suite.PostRepo.CountByUserID(user.ID)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if count != 3 {
t.Errorf("Expected 3 posts, got %d", count)
}
})
t.Run("user without posts", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
count, err := suite.PostRepo.CountByUserID(user.ID)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if count != 0 {
t.Errorf("Expected 0 posts, got %d", count)
}
})
}
func TestPostRepository_WithTx(t *testing.T) {
suite := NewTestSuite(t)
t.Run("transaction repository", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
tx := suite.DB.Begin()
defer tx.Rollback()
txPostRepo := suite.PostRepo.WithTx(tx)
post := &database.Post{
Title: "Transaction Post",
URL: "https://example.com",
Content: "Test content",
AuthorID: &user.ID,
}
err := txPostRepo.Create(post)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
retrieved, err := txPostRepo.GetByID(post.ID)
if err != nil {
t.Fatalf("Expected to find post in transaction, got %v", err)
}
if retrieved.Title != "Transaction Post" {
t.Errorf("Expected title 'Transaction Post', got %s", retrieved.Title)
}
})
}
func TestPostRepository_GetPostsByDeletedUsers(t *testing.T) {
suite := NewTestSuite(t)
t.Run("posts by deleted users", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
post := suite.CreateTestPost(user.ID, "Test Post", "https://example.com", "Test content")
err := suite.UserRepo.Delete(user.ID)
if err != nil {
t.Fatalf("Failed to delete user: %v", err)
}
posts, err := suite.PostRepo.GetPostsByDeletedUsers()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(posts) != 1 {
t.Errorf("Expected 1 post by deleted user, got %d", len(posts))
}
if posts[0].ID != post.ID {
t.Errorf("Expected post ID %d, got %d", post.ID, posts[0].ID)
}
})
t.Run("no posts by deleted users", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
suite.CreateTestPost(user.ID, "Test Post", "https://example.com", "Test content")
posts, err := suite.PostRepo.GetPostsByDeletedUsers()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(posts) != 0 {
t.Errorf("Expected 0 posts by deleted users, got %d", len(posts))
}
})
}
func TestPostRepository_HardDeletePostsByDeletedUsers(t *testing.T) {
suite := NewTestSuite(t)
t.Run("hard delete posts by deleted users", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
post := suite.CreateTestPost(user.ID, "Test Post", "https://example.com", "Test content")
err := suite.UserRepo.Delete(user.ID)
if err != nil {
t.Fatalf("Failed to delete user: %v", err)
}
deletedCount, err := suite.PostRepo.HardDeletePostsByDeletedUsers()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if deletedCount != 1 {
t.Errorf("Expected 1 post deleted, got %d", deletedCount)
}
_, err = suite.PostRepo.GetByID(post.ID)
if err == nil {
t.Error("Expected error for hard deleted post")
}
if err != gorm.ErrRecordNotFound {
t.Errorf("Expected ErrRecordNotFound, got %v", err)
}
})
t.Run("no posts to delete", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
suite.CreateTestPost(user.ID, "Test Post", "https://example.com", "Test content")
deletedCount, err := suite.PostRepo.HardDeletePostsByDeletedUsers()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if deletedCount != 0 {
t.Errorf("Expected 0 posts deleted, got %d", deletedCount)
}
})
}
func TestPostRepository_HardDeleteAll(t *testing.T) {
suite := NewTestSuite(t)
t.Run("hard delete all posts", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser", "test@example.com", "password123")
suite.CreateTestPost(user.ID, "Post 1", "https://example.com/1", "Content 1")
suite.CreateTestPost(user.ID, "Post 2", "https://example.com/2", "Content 2")
deletedCount, err := suite.PostRepo.HardDeleteAll()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if deletedCount != 2 {
t.Errorf("Expected 2 posts deleted, got %d", deletedCount)
}
posts, err := suite.PostRepo.GetAll(10, 0)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(posts) != 0 {
t.Errorf("Expected 0 posts, got %d", len(posts))
}
})
t.Run("hard delete with no posts", func(t *testing.T) {
suite.Reset()
deletedCount, err := suite.PostRepo.HardDeleteAll()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if deletedCount != 0 {
t.Errorf("Expected 0 posts deleted, got %d", deletedCount)
}
})
}
func TestPostRepository_EdgeCases(t *testing.T) {
suite := NewTestSuite(t)
t.Run("empty URL", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser2", "test2@example.com", "password123")
post := &database.Post{
Title: "Test Post",
URL: "",
Content: "Test content",
AuthorID: &user.ID,
}
err := suite.PostRepo.Create(post)
if err != nil {
t.Errorf("Unexpected error for empty URL: %v", err)
}
})
t.Run("invalid URL format", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser3", "test3@example.com", "password123")
post := &database.Post{
Title: "Test Post",
URL: "not-a-url",
Content: "Test content",
AuthorID: &user.ID,
}
err := suite.PostRepo.Create(post)
if err != nil {
t.Errorf("Unexpected error for invalid URL format: %v", err)
}
})
t.Run("very long title", func(t *testing.T) {
suite.Reset()
user := suite.CreateTestUser("testuser4", "test4@example.com", "password123")
longTitle := strings.Repeat("a", 300)
post := &database.Post{
Title: longTitle,
URL: "https://example.com",
Content: "Test content",
AuthorID: &user.ID,
}
err := suite.PostRepo.Create(post)
if err != nil {
t.Errorf("Unexpected error for long title: %v", err)
}
})
}