tercul-backend/internal/repositories/cached_work_repository_test.go
Damir Mukimov fa336cacf3
wip
2025-09-01 00:43:59 +02:00

251 lines
6.5 KiB
Go

package repositories_test
import (
"context"
"encoding/json"
"errors"
models2 "tercul/internal/models"
repositories2 "tercul/internal/repositories"
"testing"
"time"
"github.com/stretchr/testify/suite"
"tercul/internal/testutil"
)
// ErrCacheMiss is returned when a key is not found in the cache
var ErrCacheMiss = errors.New("cache miss")
// MarshalValue marshals a value to JSON
func MarshalValue(value interface{}) ([]byte, error) {
return json.Marshal(value)
}
// UnmarshalValue unmarshals a value from JSON
func UnmarshalValue(data []byte, value interface{}) error {
return json.Unmarshal(data, value)
}
// testCache is a simple in-memory cache for testing
type testCache struct {
data map[string][]byte
}
func (c *testCache) Get(ctx context.Context, key string, value interface{}) error {
data, ok := c.data[key]
if !ok {
return ErrCacheMiss
}
return UnmarshalValue(data, value)
}
func (c *testCache) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
data, err := MarshalValue(value)
if err != nil {
return err
}
c.data[key] = data
return nil
}
func (c *testCache) Delete(ctx context.Context, key string) error {
delete(c.data, key)
return nil
}
func (c *testCache) Clear(ctx context.Context) error {
c.data = make(map[string][]byte)
return nil
}
func (c *testCache) GetMulti(ctx context.Context, keys []string) (map[string][]byte, error) {
result := make(map[string][]byte)
for _, key := range keys {
if data, ok := c.data[key]; ok {
result[key] = data
}
}
return result, nil
}
func (c *testCache) SetMulti(ctx context.Context, items map[string]interface{}, expiration time.Duration) error {
for key, value := range items {
data, err := MarshalValue(value)
if err != nil {
return err
}
c.data[key] = data
}
return nil
}
// MockWorkRepository for testing
type MockWorkRepository struct {
works []*models2.Work
}
func NewMockWorkRepository() *MockWorkRepository {
return &MockWorkRepository{works: []*models2.Work{}}
}
func (m *MockWorkRepository) AddWork(work *models2.Work) {
work.ID = uint(len(m.works) + 1)
m.works = append(m.works, work)
}
func (m *MockWorkRepository) GetByID(id uint) (*models2.Work, error) {
for _, w := range m.works {
if w.ID == id {
return w, nil
}
}
return nil, errors.New("not found")
}
func (m *MockWorkRepository) FindByTitle(title string) ([]*models2.Work, error) {
var result []*models2.Work
for _, w := range m.works {
if len(title) == 0 || (len(w.Title) >= len(title) && w.Title[:len(title)] == title) {
result = append(result, w)
}
}
return result, nil
}
func (m *MockWorkRepository) FindByLanguage(language string, page, pageSize int) (*repositories2.PaginatedResult[*models2.Work], error) {
var filtered []*models2.Work
for _, w := range m.works {
if w.Language == language {
filtered = append(filtered, w)
}
}
total := int64(len(filtered))
start := (page - 1) * pageSize
end := start + pageSize
if start > len(filtered) {
return &repositories2.PaginatedResult[*models2.Work]{Items: []*models2.Work{}, TotalCount: total}, nil
}
if end > len(filtered) {
end = len(filtered)
}
return &repositories2.PaginatedResult[*models2.Work]{Items: filtered[start:end], TotalCount: total}, nil
}
func (m *MockWorkRepository) Count() (int64, error) {
return int64(len(m.works)), nil
}
// CachedWorkRepositorySuite is a test suite for the CachedWorkRepository
// Refactored to use MockWorkRepository and in-memory cache
type CachedWorkRepositorySuite struct {
suite.Suite
baseRepo *testutil.UnifiedMockWorkRepository
cache *testCache
repo *repositories2.CachedWorkRepository
}
func (s *CachedWorkRepositorySuite) SetupSuite() {
// No DB setup required
}
func (s *CachedWorkRepositorySuite) SetupTest() {
s.baseRepo = testutil.NewUnifiedMockWorkRepository()
s.cache = &testCache{data: make(map[string][]byte)}
s.repo = repositories2.NewCachedWorkRepository(
s.baseRepo,
s.cache,
nil,
30*time.Minute,
)
}
// createTestWork creates a test work and adds it to the mock repo
func (s *CachedWorkRepositorySuite) createTestWork(title, language string) *models2.Work {
work := &models2.Work{
TranslatableModel: models2.TranslatableModel{BaseModel: models2.BaseModel{ID: 0}, Language: language},
Title: title,
Description: "Test description",
Status: "published",
}
s.baseRepo.AddWork(work)
return work
}
// TestGetByID tests the GetByID method with cache miss and hit
func (s *CachedWorkRepositorySuite) TestGetByID() {
work := s.createTestWork("Test Work", "en")
result1, err := s.repo.GetByID(context.Background(), work.ID)
s.Require().NoError(err)
s.Require().NotNil(result1)
s.Equal(work.ID, result1.ID)
s.Equal(work.Title, result1.Title)
result2, err := s.repo.GetByID(context.Background(), work.ID)
s.Require().NoError(err)
s.Require().NotNil(result2)
s.Equal(work.ID, result2.ID)
s.Equal(work.Title, result2.Title)
s.Equal(result1.ID, result2.ID)
s.Equal(result1.Title, result2.Title)
}
// TestFindByTitle tests the FindByTitle method
func (s *CachedWorkRepositorySuite) TestFindByTitle() {
work1 := s.createTestWork("Test Work 1", "en")
work2 := s.createTestWork("Test Work 2", "en")
_ = s.createTestWork("Another Work", "en")
works1, err := s.repo.FindByTitle(context.Background(), "Test")
s.Require().NoError(err)
s.Require().Len(works1, 2)
works2, err := s.repo.FindByTitle(context.Background(), "Test")
s.Require().NoError(err)
s.Require().Len(works2, 2)
foundWork1 := false
foundWork2 := false
for _, work := range works2 {
if work.ID == work1.ID {
foundWork1 = true
}
if work.ID == work2.ID {
foundWork2 = true
}
}
s.True(foundWork1)
s.True(foundWork2)
}
// TestFindByLanguage tests the FindByLanguage method
func (s *CachedWorkRepositorySuite) TestFindByLanguage() {
s.createTestWork("Work 1", "en")
s.createTestWork("Work 2", "en")
s.createTestWork("Work 3", "fr")
s.createTestWork("Work 4", "fr")
s.createTestWork("Work 5", "es")
result1, err := s.repo.FindByLanguage(context.Background(), "en", 1, 10)
s.Require().NoError(err)
s.Require().NotNil(result1)
s.Equal(int64(2), result1.TotalCount)
s.Equal(2, len(result1.Items))
result2, err := s.repo.FindByLanguage(context.Background(), "en", 1, 10)
s.Require().NoError(err)
s.Require().NotNil(result2)
s.Equal(int64(2), result2.TotalCount)
s.Equal(2, len(result2.Items))
}
// TestCachedWorkRepositorySuite runs the test suite
func TestCachedWorkRepositorySuite(t *testing.T) {
suite.Run(t, new(CachedWorkRepositorySuite))
}