tercul-backend/internal/app/translation/commands_test.go
Damir Mukimov d50722dad5
Some checks failed
Test / Integration Tests (push) Successful in 4s
Build / Build Binary (push) Failing after 2m9s
Docker Build / Build Docker Image (push) Failing after 2m32s
Test / Unit Tests (push) Failing after 3m12s
Lint / Go Lint (push) Failing after 1m0s
Refactor ID handling to use UUIDs across the application
- 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.
2025-12-27 00:33:34 +01:00

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))
}