Files
gochat/internal/repository/article_repo_test.go
T
2026-06-04 15:44:48 +08:00

502 lines
14 KiB
Go

package repository
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/gochat/gochat/internal/model"
)
func newTestArticle(portalID uint, title string) *model.Article {
return &model.Article{
PortalID: portalID,
Title: title,
Slug: title + "-slug",
Content: "Test article content",
Status: string(model.ArticleStatusDraft),
Locale: "en",
}
}
// ========== Create ==========
func TestArticleRepo_Create(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
article := newTestArticle(1, "TestArticle")
err := repo.Create(context.Background(), article)
require.NoError(t, err)
assert.NotZero(t, article.ID)
assert.Equal(t, uint(1), article.PortalID)
assert.Equal(t, string(model.ArticleStatusDraft), article.Status)
}
// ========== GetByID ==========
func TestArticleRepo_GetByID(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
article := newTestArticle(1, "FindByIDArticle")
err := repo.Create(context.Background(), article)
require.NoError(t, err)
found, err := repo.GetByID(context.Background(), article.ID)
require.NoError(t, err)
assert.Equal(t, article.ID, found.ID)
assert.Equal(t, article.Title, found.Title)
}
func TestArticleRepo_GetByID_NotFound(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
found, err := repo.GetByID(context.Background(), 99999)
assert.Error(t, err)
assert.Nil(t, found)
}
// ========== Update ==========
func TestArticleRepo_Update(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
article := newTestArticle(1, "BeforeUpdate")
err := repo.Create(context.Background(), article)
require.NoError(t, err)
article.Title = "AfterUpdate"
article.Status = string(model.ArticleStatusPublished)
err = repo.Update(context.Background(), article)
require.NoError(t, err)
found, err := repo.GetByID(context.Background(), article.ID)
require.NoError(t, err)
assert.Equal(t, "AfterUpdate", found.Title)
assert.Equal(t, string(model.ArticleStatusPublished), found.Status)
}
// ========== Delete ==========
func TestArticleRepo_Delete(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
article := newTestArticle(1, "DeleteArticle")
err := repo.Create(context.Background(), article)
require.NoError(t, err)
err = repo.Delete(context.Background(), article.ID)
require.NoError(t, err)
found, err := repo.GetByID(context.Background(), article.ID)
assert.Error(t, err)
assert.Nil(t, found)
}
// ========== FindByPortalID ==========
func TestArticleRepo_FindByPortalID(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
portalID := uint(10)
for i := 0; i < 3; i++ {
a := newTestArticle(portalID, "Article"+string(rune('A'+i)))
err := repo.Create(context.Background(), a)
require.NoError(t, err)
}
articles, count, err := repo.FindByPortalID(context.Background(), portalID, 0, 10)
require.NoError(t, err)
assert.Equal(t, int64(3), count)
assert.Len(t, articles, 3)
}
// ========== FindByCategoryID ==========
func TestArticleRepo_FindByCategoryID(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
categoryID := uint(5)
for i := 0; i < 2; i++ {
a := newTestArticle(1, "CatArticle"+string(rune('A'+i)))
a.CategoryID = &categoryID
err := repo.Create(context.Background(), a)
require.NoError(t, err)
}
articles, count, err := repo.FindByCategoryID(context.Background(), categoryID, 0, 10)
require.NoError(t, err)
assert.Equal(t, int64(2), count)
assert.Len(t, articles, 2)
for _, a := range articles {
assert.Equal(t, categoryID, *a.CategoryID)
}
}
// ========== FindByFolderID ==========
func TestArticleRepo_FindByFolderID(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
folderID := uint(7)
for i := 0; i < 2; i++ {
a := newTestArticle(1, "FolderArticle"+string(rune('A'+i)))
a.FolderID = &folderID
err := repo.Create(context.Background(), a)
require.NoError(t, err)
}
// Also create one without a folder
noFolder := newTestArticle(1, "NoFolderArticle")
require.NoError(t, repo.Create(context.Background(), noFolder))
articles, count, err := repo.FindByFolderID(context.Background(), folderID, 0, 10)
require.NoError(t, err)
assert.Equal(t, int64(2), count)
assert.Len(t, articles, 2)
for _, a := range articles {
assert.Equal(t, folderID, *a.FolderID)
}
}
// ========== FindByStatus ==========
func TestArticleRepo_FindByStatus(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
portalID := uint(10)
// Create 2 draft articles
for i := 0; i < 2; i++ {
a := newTestArticle(portalID, "DraftArticle"+string(rune('A'+i)))
err := repo.Create(context.Background(), a)
require.NoError(t, err)
}
// Create 1 published article
published := newTestArticle(portalID, "PublishedArticle")
published.Status = string(model.ArticleStatusPublished)
err := repo.Create(context.Background(), published)
require.NoError(t, err)
articles, count, err := repo.FindByStatus(context.Background(), portalID, model.ArticleStatusDraft, 0, 10)
require.NoError(t, err)
assert.Equal(t, int64(2), count)
assert.Len(t, articles, 2)
for _, a := range articles {
assert.Equal(t, string(model.ArticleStatusDraft), a.Status)
}
}
// ========== FindBySlug ==========
func TestArticleRepo_FindBySlug(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
article := newTestArticle(1, "SlugArticle")
err := repo.Create(context.Background(), article)
require.NoError(t, err)
found, err := repo.FindBySlug(context.Background(), article.Slug)
require.NoError(t, err)
assert.Equal(t, article.ID, found.ID)
assert.Equal(t, article.Slug, found.Slug)
}
func TestArticleRepo_FindBySlug_NotFound(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
found, err := repo.FindBySlug(context.Background(), "nonexistent-slug")
assert.Error(t, err)
assert.Nil(t, found)
}
// ========== Search ==========
func TestArticleRepo_Search_ByQuery(t *testing.T) {
skipIfSQLite(t) // ILIKE requires PG
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
portalID := uint(10)
a1 := newTestArticle(portalID, "Getting Started Guide")
a1.Description = "A guide to help you get started"
require.NoError(t, repo.Create(context.Background(), a1))
a2 := newTestArticle(portalID, "Advanced Configuration")
a2.Content = "Configure your advanced settings"
require.NoError(t, repo.Create(context.Background(), a2))
a3 := newTestArticle(portalID, "Troubleshooting Guide")
require.NoError(t, repo.Create(context.Background(), a3))
params := ArticleSearchParams{
PortalID: portalID,
Query: "guide",
Offset: 0,
Limit: 10,
}
articles, count, err := repo.Search(context.Background(), params)
require.NoError(t, err)
// Should match "Getting Started Guide" and "Troubleshooting Guide"
assert.Equal(t, int64(2), count)
assert.Len(t, articles, 2)
}
func TestArticleRepo_Search_ByStatus(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
portalID := uint(10)
for i := 0; i < 3; i++ {
a := newTestArticle(portalID, "DraftSearch"+string(rune('A'+i)))
a.Status = string(model.ArticleStatusDraft)
require.NoError(t, repo.Create(context.Background(), a))
}
pub := newTestArticle(portalID, "PublishedSearch")
pub.Status = string(model.ArticleStatusPublished)
require.NoError(t, repo.Create(context.Background(), pub))
params := ArticleSearchParams{
PortalID: portalID,
Status: string(model.ArticleStatusDraft),
Offset: 0,
Limit: 10,
}
articles, count, err := repo.Search(context.Background(), params)
require.NoError(t, err)
assert.Equal(t, int64(3), count)
assert.Len(t, articles, 3)
}
func TestArticleRepo_Search_ByLocale(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
portalID := uint(10)
en := newTestArticle(portalID, "English Article")
en.Locale = "en"
require.NoError(t, repo.Create(context.Background(), en))
es := newTestArticle(portalID, "Spanish Article")
es.Locale = "es"
require.NoError(t, repo.Create(context.Background(), es))
params := ArticleSearchParams{
PortalID: portalID,
Locale: "en",
Offset: 0,
Limit: 10,
}
articles, count, err := repo.Search(context.Background(), params)
require.NoError(t, err)
assert.Equal(t, int64(1), count)
assert.Len(t, articles, 1)
assert.Equal(t, "en", articles[0].Locale)
}
func TestArticleRepo_Search_EmptyParams(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
portalID := uint(10)
for i := 0; i < 5; i++ {
a := newTestArticle(portalID, "AllArticles"+string(rune('A'+i)))
require.NoError(t, repo.Create(context.Background(), a))
}
params := ArticleSearchParams{
PortalID: portalID,
Offset: 0,
Limit: 10,
}
articles, count, err := repo.Search(context.Background(), params)
require.NoError(t, err)
assert.Equal(t, int64(5), count)
assert.Len(t, articles, 5)
}
// ========== StatusCounts ==========
func TestArticleRepo_StatusCounts(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
portalID := uint(10)
// Create 3 draft articles
for i := 0; i < 3; i++ {
a := newTestArticle(portalID, "DraftCount"+string(rune('A'+i)))
a.Status = string(model.ArticleStatusDraft)
require.NoError(t, repo.Create(context.Background(), a))
}
// Create 2 published articles
for i := 0; i < 2; i++ {
a := newTestArticle(portalID, "PublishedCount"+string(rune('A'+i)))
a.Status = string(model.ArticleStatusPublished)
require.NoError(t, repo.Create(context.Background(), a))
}
// Create 1 archived article
a := newTestArticle(portalID, "ArchivedCount")
a.Status = string(model.ArticleStatusArchived)
require.NoError(t, repo.Create(context.Background(), a))
counts, err := repo.StatusCounts(context.Background(), portalID)
require.NoError(t, err)
assert.Equal(t, int64(6), counts["all_count"])
assert.Equal(t, int64(3), counts["draft_count"])
assert.Equal(t, int64(2), counts["published_count"])
assert.Equal(t, int64(1), counts["archived_count"])
}
func TestArticleRepo_StatusCounts_EmptyPortal(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
counts, err := repo.StatusCounts(context.Background(), 99999)
require.NoError(t, err)
assert.Equal(t, int64(0), counts["all_count"])
assert.Equal(t, int64(0), counts["draft_count"])
assert.Equal(t, int64(0), counts["published_count"])
assert.Equal(t, int64(0), counts["archived_count"])
}
// ========== UpdatePositions ==========
func TestArticleRepo_UpdatePositions(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
portalID := uint(10)
a1 := newTestArticle(portalID, "PositionArticle1")
require.NoError(t, repo.Create(context.Background(), a1))
a2 := newTestArticle(portalID, "PositionArticle2")
require.NoError(t, repo.Create(context.Background(), a2))
positions := map[uint]int{
a1.ID: 5,
a2.ID: 10,
}
err := repo.UpdatePositions(context.Background(), positions)
require.NoError(t, err)
found1, err := repo.GetByID(context.Background(), a1.ID)
require.NoError(t, err)
assert.Equal(t, 5, found1.Position)
found2, err := repo.GetByID(context.Background(), a2.ID)
require.NoError(t, err)
assert.Equal(t, 10, found2.Position)
}
// ========== BulkUpdateStatus ==========
func TestArticleRepo_BulkUpdateStatus(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
portalID := uint(10)
var ids []uint
for i := 0; i < 3; i++ {
a := newTestArticle(portalID, "BulkStatus"+string(rune('A'+i)))
a.Status = string(model.ArticleStatusDraft)
require.NoError(t, repo.Create(context.Background(), a))
ids = append(ids, a.ID)
}
err := repo.BulkUpdateStatus(context.Background(), ids, string(model.ArticleStatusPublished))
require.NoError(t, err)
for _, id := range ids {
found, err := repo.GetByID(context.Background(), id)
require.NoError(t, err)
assert.Equal(t, string(model.ArticleStatusPublished), found.Status)
}
}
func TestArticleRepo_BulkUpdateStatus_EmptyIDs(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
err := repo.BulkUpdateStatus(context.Background(), []uint{}, string(model.ArticleStatusPublished))
require.NoError(t, err)
}
// ========== BulkDelete ==========
func TestArticleRepo_BulkDelete(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
portalID := uint(10)
var ids []uint
for i := 0; i < 3; i++ {
a := newTestArticle(portalID, "BulkDelete"+string(rune('A'+i)))
require.NoError(t, repo.Create(context.Background(), a))
ids = append(ids, a.ID)
}
// Create one that should NOT be deleted
keeper := newTestArticle(portalID, "KeeperArticle")
require.NoError(t, repo.Create(context.Background(), keeper))
err := repo.BulkDelete(context.Background(), ids)
require.NoError(t, err)
for _, id := range ids {
found, err := repo.GetByID(context.Background(), id)
assert.Error(t, err)
assert.Nil(t, found)
}
// Keeper should still exist
found, err := repo.GetByID(context.Background(), keeper.ID)
require.NoError(t, err)
assert.Equal(t, keeper.ID, found.ID)
}
func TestArticleRepo_BulkDelete_EmptyIDs(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
err := repo.BulkDelete(context.Background(), []uint{})
require.NoError(t, err)
}
// ========== IncrementViews ==========
func TestArticleRepo_IncrementViews(t *testing.T) {
db := setupTestDB(t, &model.Article{})
repo := NewArticleRepo(db)
article := newTestArticle(1, "ViewArticle")
require.NoError(t, repo.Create(context.Background(), article))
assert.Equal(t, 0, article.Views)
// Increment twice
err := repo.IncrementViews(context.Background(), article.ID)
require.NoError(t, err)
err = repo.IncrementViews(context.Background(), article.ID)
require.NoError(t, err)
found, err := repo.GetByID(context.Background(), article.ID)
require.NoError(t, err)
assert.Equal(t, 2, found.Views)
}