mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 05:11:34 +00:00
Some checks failed
- Updated database models and repositories to replace uint IDs with UUIDs. - Modified test fixtures to generate and use UUIDs for authors, translations, users, and works. - Adjusted mock implementations to align with the new UUID structure. - Ensured all relevant functions and methods are updated to handle UUIDs correctly. - Added necessary imports for UUID handling in various files.
285 lines
9.9 KiB
Go
285 lines
9.9 KiB
Go
package translation_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"gorm.io/gorm"
|
|
"tercul/internal/app/authz"
|
|
"tercul/internal/app/translation"
|
|
"tercul/internal/domain"
|
|
platform_auth "tercul/internal/platform/auth"
|
|
"tercul/internal/testutil"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
// MockAuthorRepository is a mock implementation of the AuthorRepository interface.
|
|
type mockAuthorRepository struct{ mock.Mock }
|
|
|
|
func (m *mockAuthorRepository) Create(ctx context.Context, entity *domain.Author) error {
|
|
args := m.Called(ctx, entity)
|
|
return args.Error(0)
|
|
}
|
|
func (m *mockAuthorRepository) CreateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Author) error {
|
|
args := m.Called(ctx, tx, entity)
|
|
return args.Error(0)
|
|
}
|
|
func (m *mockAuthorRepository) GetByID(ctx context.Context, id uuid.UUID) (*domain.Author, error) {
|
|
args := m.Called(ctx, id)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*domain.Author), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Author, error) {
|
|
args := m.Called(ctx, id, options)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*domain.Author), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) Update(ctx context.Context, entity *domain.Author) error {
|
|
args := m.Called(ctx, entity)
|
|
return args.Error(0)
|
|
}
|
|
func (m *mockAuthorRepository) UpdateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Author) error {
|
|
args := m.Called(ctx, tx, entity)
|
|
return args.Error(0)
|
|
}
|
|
func (m *mockAuthorRepository) Delete(ctx context.Context, id uint) error {
|
|
args := m.Called(ctx, id)
|
|
return args.Error(0)
|
|
}
|
|
func (m *mockAuthorRepository) DeleteInTx(ctx context.Context, tx *gorm.DB, id uint) error {
|
|
args := m.Called(ctx, tx, id)
|
|
return args.Error(0)
|
|
}
|
|
func (m *mockAuthorRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Author], error) {
|
|
args := m.Called(ctx, page, pageSize)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*domain.PaginatedResult[domain.Author]), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]domain.Author, error) {
|
|
args := m.Called(ctx, options)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]domain.Author), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) ListAll(ctx context.Context) ([]domain.Author, error) {
|
|
args := m.Called(ctx)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]domain.Author), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) Count(ctx context.Context) (int64, error) {
|
|
args := m.Called(ctx)
|
|
return args.Get(0).(int64), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
|
|
args := m.Called(ctx, options)
|
|
return args.Get(0).(int64), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*domain.Author, error) {
|
|
args := m.Called(ctx, preloads, id)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*domain.Author), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Author, error) {
|
|
args := m.Called(ctx, batchSize, offset)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]domain.Author), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) Exists(ctx context.Context, id uint) (bool, error) {
|
|
args := m.Called(ctx, id)
|
|
return args.Bool(0), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) BeginTx(ctx context.Context) (*gorm.DB, error) {
|
|
args := m.Called(ctx)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*gorm.DB), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) WithTx(ctx context.Context, fn func(tx *gorm.DB) error) error {
|
|
return fn(nil)
|
|
}
|
|
func (m *mockAuthorRepository) FindByName(ctx context.Context, name string) (*domain.Author, error) {
|
|
args := m.Called(ctx, name)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*domain.Author), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Author, error) {
|
|
args := m.Called(ctx, workID)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]domain.Author), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) ListByBookID(ctx context.Context, bookID uint) ([]domain.Author, error) {
|
|
args := m.Called(ctx, bookID)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]domain.Author), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) ListByCountryID(ctx context.Context, countryID uint) ([]domain.Author, error) {
|
|
args := m.Called(ctx, countryID)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]domain.Author), args.Error(1)
|
|
}
|
|
func (m *mockAuthorRepository) GetWithTranslations(ctx context.Context, id uint) (*domain.Author, error) {
|
|
args := m.Called(ctx, id)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*domain.Author), args.Error(1)
|
|
}
|
|
|
|
type TranslationCommandsTestSuite struct {
|
|
suite.Suite
|
|
mockWorkRepo *testutil.MockWorkRepository
|
|
mockTranslationRepo *testutil.MockTranslationRepository
|
|
mockAuthorRepo *mockAuthorRepository
|
|
mockUserRepo *testutil.MockUserRepository
|
|
authzSvc *authz.Service
|
|
cmd *translation.TranslationCommands
|
|
adminCtx context.Context
|
|
userCtx context.Context
|
|
adminUser *domain.User
|
|
regularUser *domain.User
|
|
}
|
|
|
|
func (s *TranslationCommandsTestSuite) SetupTest() {
|
|
s.mockWorkRepo = new(testutil.MockWorkRepository)
|
|
s.mockTranslationRepo = new(testutil.MockTranslationRepository)
|
|
s.mockAuthorRepo = new(mockAuthorRepository)
|
|
s.mockUserRepo = new(testutil.MockUserRepository)
|
|
s.authzSvc = authz.NewService(s.mockWorkRepo, s.mockAuthorRepo, s.mockUserRepo, s.mockTranslationRepo)
|
|
s.cmd = translation.NewTranslationCommands(s.mockTranslationRepo, s.authzSvc)
|
|
|
|
s.adminUser = &domain.User{BaseModel: domain.BaseModel{ID: 1}, Role: domain.UserRoleAdmin, Username: "admin"}
|
|
s.regularUser = &domain.User{BaseModel: domain.BaseModel{ID: 2}, Role: domain.UserRoleContributor, Username: "contributor"}
|
|
|
|
s.adminCtx = context.WithValue(context.Background(), platform_auth.ClaimsContextKey, &platform_auth.Claims{
|
|
UserID: s.adminUser.ID,
|
|
Role: string(s.adminUser.Role),
|
|
})
|
|
s.userCtx = context.WithValue(context.Background(), platform_auth.ClaimsContextKey, &platform_auth.Claims{
|
|
UserID: s.regularUser.ID,
|
|
Role: string(s.regularUser.Role),
|
|
})
|
|
}
|
|
|
|
func (s *TranslationCommandsTestSuite) TestCreateOrUpdateTranslation() {
|
|
testWork := &domain.Work{
|
|
TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: 1}},
|
|
}
|
|
testAuthor := &domain.Author{
|
|
TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: 1}},
|
|
Name: s.regularUser.Username,
|
|
}
|
|
baseInput := translation.CreateOrUpdateTranslationInput{
|
|
Title: "Test Title",
|
|
Content: "Test content",
|
|
Language: "es",
|
|
TranslatableID: testWork.ID,
|
|
TranslatableType: "works",
|
|
}
|
|
|
|
s.Run("should create translation for admin", func() {
|
|
s.SetupTest()
|
|
input := baseInput
|
|
// Arrange
|
|
s.mockWorkRepo.On("GetByID", mock.Anything, testWork.ID).Return(testWork, nil).Once()
|
|
s.mockTranslationRepo.On("Upsert", mock.Anything, mock.AnythingOfType("*domain.Translation")).Return(nil).Once()
|
|
|
|
// Act
|
|
result, err := s.cmd.CreateOrUpdateTranslation(s.adminCtx, input)
|
|
|
|
// Assert
|
|
s.NoError(err)
|
|
s.NotNil(result)
|
|
s.Equal(input.Title, result.Title)
|
|
s.Equal(s.adminUser.ID, *result.TranslatorID)
|
|
s.mockWorkRepo.AssertExpectations(s.T())
|
|
s.mockTranslationRepo.AssertExpectations(s.T())
|
|
})
|
|
|
|
s.Run("should create translation for author", func() {
|
|
s.SetupTest()
|
|
input := baseInput
|
|
// Arrange
|
|
s.mockUserRepo.On("GetByID", mock.Anything, s.regularUser.ID).Return(s.regularUser, nil).Once()
|
|
s.mockAuthorRepo.On("FindByName", mock.Anything, s.regularUser.Username).Return(testAuthor, nil).Once()
|
|
s.mockWorkRepo.On("GetByID", mock.Anything, testWork.ID).Return(testWork, nil).Once()
|
|
s.mockWorkRepo.On("IsAuthor", mock.Anything, testWork.ID, testAuthor.ID).Return(true, nil).Once()
|
|
s.mockTranslationRepo.On("Upsert", mock.Anything, mock.AnythingOfType("*domain.Translation")).Return(nil).Once()
|
|
|
|
// Act
|
|
result, err := s.cmd.CreateOrUpdateTranslation(s.userCtx, input)
|
|
|
|
// Assert
|
|
s.NoError(err)
|
|
s.NotNil(result)
|
|
s.Equal(input.Title, result.Title)
|
|
s.Equal(s.regularUser.ID, *result.TranslatorID)
|
|
s.mockUserRepo.AssertExpectations(s.T())
|
|
s.mockAuthorRepo.AssertExpectations(s.T())
|
|
s.mockWorkRepo.AssertExpectations(s.T())
|
|
s.mockTranslationRepo.AssertExpectations(s.T())
|
|
})
|
|
|
|
s.Run("should fail if user is not authorized", func() {
|
|
s.SetupTest()
|
|
input := baseInput
|
|
// Arrange
|
|
s.mockUserRepo.On("GetByID", mock.Anything, s.regularUser.ID).Return(s.regularUser, nil).Once()
|
|
s.mockAuthorRepo.On("FindByName", mock.Anything, s.regularUser.Username).Return(testAuthor, nil).Once()
|
|
s.mockWorkRepo.On("GetByID", mock.Anything, testWork.ID).Return(testWork, nil).Once()
|
|
s.mockWorkRepo.On("IsAuthor", mock.Anything, testWork.ID, testAuthor.ID).Return(false, nil).Once()
|
|
|
|
// Act
|
|
_, err := s.cmd.CreateOrUpdateTranslation(s.userCtx, input)
|
|
|
|
// Assert
|
|
s.Error(err)
|
|
s.ErrorIs(err, domain.ErrForbidden)
|
|
s.mockUserRepo.AssertExpectations(s.T())
|
|
s.mockAuthorRepo.AssertExpectations(s.T())
|
|
s.mockWorkRepo.AssertExpectations(s.T())
|
|
})
|
|
|
|
s.Run("should fail on validation error for empty language", func() {
|
|
s.SetupTest()
|
|
// Arrange
|
|
input := baseInput
|
|
input.Language = ""
|
|
|
|
// Act
|
|
_, err := s.cmd.CreateOrUpdateTranslation(s.userCtx, input)
|
|
|
|
// Assert
|
|
s.Error(err)
|
|
assert.ErrorContains(s.T(), err, "language cannot be empty")
|
|
})
|
|
}
|
|
|
|
func TestTranslationCommands(t *testing.T) {
|
|
suite.Run(t, new(TranslationCommandsTestSuite))
|
|
}
|