Update department etc
This commit is contained in:
@@ -2,6 +2,7 @@ package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"eslogad-be/internal/appcontext"
|
||||
@@ -309,77 +310,129 @@ func (p *LetterProcessorImpl) SoftDeleteIncomingLetter(ctx context.Context, id u
|
||||
})
|
||||
}
|
||||
|
||||
// CreateDispositions creates a new disposition with modular helper functions
|
||||
func (p *LetterProcessorImpl) CreateDispositions(ctx context.Context, req *contract.CreateLetterDispositionRequest) (*contract.ListDispositionsResponse, error) {
|
||||
var out *contract.ListDispositionsResponse
|
||||
err := p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
userID := appcontext.FromGinContext(txCtx).UserID
|
||||
// Transaction should be handled at service layer
|
||||
// The context passed here should already contain the transaction if needed
|
||||
|
||||
existingDispDepts, err := p.dispositionDeptRepo.GetByLetterAndDepartment(txCtx, req.LetterID, req.FromDepartment)
|
||||
if err == nil && len(existingDispDepts) > 0 {
|
||||
for _, existingDispDept := range existingDispDepts {
|
||||
if existingDispDept.Status == entities.DispositionDepartmentStatusPending {
|
||||
existingDispDept.Status = entities.DispositionDepartmentStatusDispositioned
|
||||
if err := p.dispositionDeptRepo.Update(txCtx, &existingDispDept); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Step 1: Update existing disposition departments
|
||||
if err := p.updateExistingDispositionDepartments(ctx, req.LetterID, req.FromDepartment); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
disp := entities.LetterIncomingDisposition{
|
||||
LetterID: req.LetterID,
|
||||
DepartmentID: &req.FromDepartment,
|
||||
Notes: req.Notes,
|
||||
CreatedBy: userID,
|
||||
}
|
||||
if err := p.dispositionRepo.Create(txCtx, &disp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var dispDepartments []entities.LetterIncomingDispositionDepartment
|
||||
for _, toDept := range req.ToDepartmentIDs {
|
||||
dispDepartments = append(dispDepartments, entities.LetterIncomingDispositionDepartment{
|
||||
LetterIncomingDispositionID: disp.ID,
|
||||
LetterIncomingID: req.LetterID,
|
||||
DepartmentID: toDept,
|
||||
Status: entities.DispositionDepartmentStatusPending,
|
||||
})
|
||||
}
|
||||
|
||||
if err := p.dispositionDeptRepo.CreateBulk(txCtx, dispDepartments); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(req.SelectedActions) > 0 {
|
||||
selections := make([]entities.LetterDispositionActionSelection, 0, len(req.SelectedActions))
|
||||
for _, sel := range req.SelectedActions {
|
||||
selections = append(selections, entities.LetterDispositionActionSelection{
|
||||
DispositionID: disp.ID,
|
||||
ActionID: sel.ActionID,
|
||||
Note: sel.Note,
|
||||
CreatedBy: userID,
|
||||
})
|
||||
}
|
||||
if err := p.dispositionActionSelRepo.CreateBulk(txCtx, selections); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if p.activity != nil {
|
||||
action := "disposition.created"
|
||||
ctxMap := map[string]interface{}{"to_department_id": dispDepartments}
|
||||
if err := p.activity.Log(txCtx, req.LetterID, action, &userID, nil, nil, &disp.ID, nil, nil, ctxMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
out = &contract.ListDispositionsResponse{Dispositions: []contract.DispositionResponse{transformer.DispoToContract(disp)}}
|
||||
return nil
|
||||
})
|
||||
// Step 2: Create the main disposition
|
||||
disp, err := p.createMainDisposition(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
|
||||
// Step 3: Create disposition departments for target departments
|
||||
dispDepartments, err := p.createDispositionDepartments(ctx, disp.ID, req.LetterID, req.ToDepartmentIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Step 4: Create action selections if provided
|
||||
if err := p.createActionSelections(ctx, disp.ID, req.SelectedActions, req.CreatedBy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Step 5: Build and return the response
|
||||
return p.buildDispositionResponse(disp, dispDepartments, req.ToDepartmentIDs), nil
|
||||
}
|
||||
|
||||
// updateExistingDispositionDepartments updates the status of existing disposition departments
|
||||
func (p *LetterProcessorImpl) updateExistingDispositionDepartments(ctx context.Context, letterID uuid.UUID, fromDepartment uuid.UUID) error {
|
||||
existingDispDepts, err := p.dispositionDeptRepo.GetByLetterAndDepartment(ctx, letterID, fromDepartment)
|
||||
if err != nil {
|
||||
// If no existing departments found, that's ok
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, existingDispDept := range existingDispDepts {
|
||||
if existingDispDept.Status == entities.DispositionDepartmentStatusPending {
|
||||
existingDispDept.Status = entities.DispositionDepartmentStatusDispositioned
|
||||
if err := p.dispositionDeptRepo.Update(ctx, &existingDispDept); err != nil {
|
||||
return fmt.Errorf("failed to update existing disposition department: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createMainDisposition creates the primary disposition record
|
||||
func (p *LetterProcessorImpl) createMainDisposition(ctx context.Context, req *contract.CreateLetterDispositionRequest) (*entities.LetterIncomingDisposition, error) {
|
||||
disp := &entities.LetterIncomingDisposition{
|
||||
LetterID: req.LetterID,
|
||||
DepartmentID: &req.FromDepartment,
|
||||
Notes: req.Notes,
|
||||
CreatedBy: req.CreatedBy, // Should be set by service layer
|
||||
}
|
||||
|
||||
if err := p.dispositionRepo.Create(ctx, disp); err != nil {
|
||||
return nil, fmt.Errorf("failed to create disposition: %w", err)
|
||||
}
|
||||
|
||||
return disp, nil
|
||||
}
|
||||
|
||||
// createDispositionDepartments creates disposition department records for target departments
|
||||
func (p *LetterProcessorImpl) createDispositionDepartments(ctx context.Context, dispositionID, letterID uuid.UUID, toDepartmentIDs []uuid.UUID) ([]entities.LetterIncomingDispositionDepartment, error) {
|
||||
if len(toDepartmentIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
dispDepartments := make([]entities.LetterIncomingDispositionDepartment, 0, len(toDepartmentIDs))
|
||||
for _, toDept := range toDepartmentIDs {
|
||||
dispDepartments = append(dispDepartments, entities.LetterIncomingDispositionDepartment{
|
||||
LetterIncomingDispositionID: dispositionID,
|
||||
LetterIncomingID: letterID,
|
||||
DepartmentID: toDept,
|
||||
Status: entities.DispositionDepartmentStatusPending,
|
||||
})
|
||||
}
|
||||
|
||||
if err := p.dispositionDeptRepo.CreateBulk(ctx, dispDepartments); err != nil {
|
||||
return nil, fmt.Errorf("failed to create disposition departments: %w", err)
|
||||
}
|
||||
|
||||
return dispDepartments, nil
|
||||
}
|
||||
|
||||
// createActionSelections creates action selection records for the disposition
|
||||
func (p *LetterProcessorImpl) createActionSelections(ctx context.Context, dispositionID uuid.UUID, selectedActions []contract.CreateDispositionActionSelection, createdBy uuid.UUID) error {
|
||||
if len(selectedActions) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
selections := make([]entities.LetterDispositionActionSelection, 0, len(selectedActions))
|
||||
for _, sel := range selectedActions {
|
||||
selections = append(selections, entities.LetterDispositionActionSelection{
|
||||
DispositionID: dispositionID,
|
||||
ActionID: sel.ActionID,
|
||||
Note: sel.Note,
|
||||
CreatedBy: createdBy,
|
||||
})
|
||||
}
|
||||
|
||||
if err := p.dispositionActionSelRepo.CreateBulk(ctx, selections); err != nil {
|
||||
return fmt.Errorf("failed to create action selections: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildDispositionResponse builds the response for the created disposition
|
||||
func (p *LetterProcessorImpl) buildDispositionResponse(disp *entities.LetterIncomingDisposition, dispDepartments []entities.LetterIncomingDispositionDepartment, toDepartmentIDs []uuid.UUID) *contract.ListDispositionsResponse {
|
||||
response := &contract.ListDispositionsResponse{
|
||||
Dispositions: []contract.DispositionResponse{transformer.DispoToContract(*disp)},
|
||||
}
|
||||
|
||||
// The toDepartmentIDs are available in the dispDepartments for service layer logging
|
||||
// No need to store them in the response as DispositionResponse doesn't have this field
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
func (p *LetterProcessorImpl) ListDispositionsByLetter(ctx context.Context, letterID uuid.UUID) (*contract.ListDispositionsResponse, error) {
|
||||
@@ -449,67 +502,54 @@ func (p *LetterProcessorImpl) GetEnhancedDispositionsByLetter(ctx context.Contex
|
||||
}
|
||||
|
||||
func (p *LetterProcessorImpl) CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error) {
|
||||
var out *contract.LetterDiscussionResponse
|
||||
err := p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
userID := appcontext.FromGinContext(txCtx).UserID
|
||||
mentions := entities.JSONB(nil)
|
||||
if req.Mentions != nil {
|
||||
mentions = entities.JSONB(req.Mentions)
|
||||
}
|
||||
disc := &entities.LetterDiscussion{ID: uuid.New(), LetterID: letterID, ParentID: req.ParentID, UserID: userID, Message: req.Message, Mentions: mentions}
|
||||
if err := p.discussionRepo.Create(txCtx, disc); err != nil {
|
||||
return err
|
||||
}
|
||||
if p.activity != nil {
|
||||
action := "discussion.created"
|
||||
tgt := "discussion"
|
||||
ctxMap := map[string]interface{}{"message": req.Message, "parent_id": req.ParentID}
|
||||
if err := p.activity.Log(txCtx, letterID, action, &userID, nil, &tgt, &disc.ID, nil, nil, ctxMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
out = transformer.DiscussionEntityToContract(disc)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
userID := appcontext.FromGinContext(ctx).UserID
|
||||
|
||||
mentions := entities.JSONB(nil)
|
||||
if req.Mentions != nil {
|
||||
mentions = entities.JSONB(req.Mentions)
|
||||
}
|
||||
return out, nil
|
||||
|
||||
disc := &entities.LetterDiscussion{
|
||||
ID: uuid.New(),
|
||||
LetterID: letterID,
|
||||
ParentID: req.ParentID,
|
||||
UserID: userID,
|
||||
Message: req.Message,
|
||||
Mentions: mentions,
|
||||
}
|
||||
|
||||
if err := p.discussionRepo.Create(ctx, disc); err != nil {
|
||||
return nil, fmt.Errorf("failed to create discussion: %w", err)
|
||||
}
|
||||
|
||||
// Activity logging should be handled at service layer
|
||||
return transformer.DiscussionEntityToContract(disc), nil
|
||||
}
|
||||
|
||||
func (p *LetterProcessorImpl) UpdateDiscussion(ctx context.Context, letterID uuid.UUID, discussionID uuid.UUID, req *contract.UpdateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error) {
|
||||
var out *contract.LetterDiscussionResponse
|
||||
err := p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
disc, err := p.discussionRepo.Get(txCtx, discussionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldMessage := disc.Message
|
||||
disc.Message = req.Message
|
||||
if req.Mentions != nil {
|
||||
disc.Mentions = entities.JSONB(req.Mentions)
|
||||
}
|
||||
now := time.Now()
|
||||
disc.EditedAt = &now
|
||||
if err := p.discussionRepo.Update(txCtx, disc); err != nil {
|
||||
return err
|
||||
}
|
||||
if p.activity != nil {
|
||||
userID := appcontext.FromGinContext(txCtx).UserID
|
||||
action := "discussion.updated"
|
||||
tgt := "discussion"
|
||||
ctxMap := map[string]interface{}{"old_message": oldMessage, "new_message": req.Message}
|
||||
if err := p.activity.Log(txCtx, letterID, action, &userID, nil, &tgt, &disc.ID, nil, nil, ctxMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
out = transformer.DiscussionEntityToContract(disc)
|
||||
return nil
|
||||
})
|
||||
func (p *LetterProcessorImpl) UpdateDiscussion(ctx context.Context, letterID uuid.UUID, discussionID uuid.UUID, req *contract.UpdateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, string, error) {
|
||||
// Transaction should be handled at service layer
|
||||
disc, err := p.discussionRepo.Get(ctx, discussionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", fmt.Errorf("failed to get discussion: %w", err)
|
||||
}
|
||||
return out, nil
|
||||
|
||||
// Store old message for activity logging
|
||||
oldMessage := disc.Message
|
||||
|
||||
// Update discussion fields
|
||||
disc.Message = req.Message
|
||||
if req.Mentions != nil {
|
||||
disc.Mentions = entities.JSONB(req.Mentions)
|
||||
}
|
||||
now := time.Now()
|
||||
disc.EditedAt = &now
|
||||
|
||||
if err := p.discussionRepo.Update(ctx, disc); err != nil {
|
||||
return nil, "", fmt.Errorf("failed to update discussion: %w", err)
|
||||
}
|
||||
|
||||
// Return both the updated discussion and old message for service layer logging
|
||||
return transformer.DiscussionEntityToContract(disc), oldMessage, nil
|
||||
}
|
||||
|
||||
func (p *LetterProcessorImpl) createAttachments(ctx context.Context, letterID uuid.UUID, attachments []contract.CreateIncomingLetterAttachment, userID uuid.UUID) error {
|
||||
|
||||
@@ -78,12 +78,29 @@ func (p *UserProcessorImpl) CreateUser(ctx context.Context, req *contract.Create
|
||||
}
|
||||
}
|
||||
|
||||
// Assign departments if provided
|
||||
if len(req.DepartmentIDs) > 0 {
|
||||
departments := make([]entities.Department, len(req.DepartmentIDs))
|
||||
for i, deptID := range req.DepartmentIDs {
|
||||
departments[i] = entities.Department{ID: deptID}
|
||||
}
|
||||
if err := p.userRepo.UpdateDepartments(ctx, userEntity.ID, departments); err != nil {
|
||||
return nil, fmt.Errorf("failed to assign departments: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if p.novuProcessor != nil {
|
||||
if err := p.novuProcessor.CreateSubscriber(ctx, userEntity); err != nil {
|
||||
_ = err
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the user with departments for response
|
||||
userWithDepts, _ := p.userRepo.GetByIDWithDepartments(ctx, userEntity.ID)
|
||||
if userWithDepts != nil {
|
||||
userEntity = userWithDepts
|
||||
}
|
||||
|
||||
return transformer.EntityToContract(userEntity), nil
|
||||
}
|
||||
|
||||
@@ -107,6 +124,17 @@ func (p *UserProcessorImpl) UpdateUser(ctx context.Context, id uuid.UUID, req *c
|
||||
return nil, fmt.Errorf("failed to update user: %w", err)
|
||||
}
|
||||
|
||||
// Update departments if provided
|
||||
if req.DepartmentIDs != nil {
|
||||
departments := make([]entities.Department, len(*req.DepartmentIDs))
|
||||
for i, deptID := range *req.DepartmentIDs {
|
||||
departments[i] = entities.Department{ID: deptID}
|
||||
}
|
||||
if err := p.userRepo.UpdateDepartments(ctx, updated.ID, departments); err != nil {
|
||||
return nil, fmt.Errorf("failed to update departments: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Update Novu subscriber
|
||||
if p.novuProcessor != nil {
|
||||
if err := p.novuProcessor.UpdateSubscriber(ctx, updated); err != nil {
|
||||
@@ -114,6 +142,12 @@ func (p *UserProcessorImpl) UpdateUser(ctx context.Context, id uuid.UUID, req *c
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the user with departments for response
|
||||
userWithDepts, _ := p.userRepo.GetByIDWithDepartments(ctx, updated.ID)
|
||||
if userWithDepts != nil {
|
||||
updated = userWithDepts
|
||||
}
|
||||
|
||||
return transformer.EntityToContract(updated), nil
|
||||
}
|
||||
|
||||
@@ -184,18 +218,8 @@ func (p *UserProcessorImpl) GetUserByEmail(ctx context.Context, email string) (*
|
||||
return transformer.EntityToContract(user), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) ListUsersWithFilters(ctx context.Context, req *contract.ListUsersRequest) ([]contract.UserResponse, int, error) {
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
limit := req.Limit
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
offset := (page - 1) * limit
|
||||
|
||||
users, totalCount, err := p.userRepo.ListWithFilters(ctx, req.Search, req.RoleCode, req.IsActive, limit, offset)
|
||||
func (p *UserProcessorImpl) ListUsersWithFilters(ctx context.Context, search *string, roleCode *string, isActive *bool, limit, offset int) ([]contract.UserResponse, int, error) {
|
||||
users, totalCount, err := p.userRepo.ListWithFilters(ctx, search, roleCode, isActive, limit, offset)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to get users: %w", err)
|
||||
}
|
||||
@@ -333,12 +357,7 @@ func (p *UserProcessorImpl) UpdateUserProfile(ctx context.Context, userID uuid.U
|
||||
|
||||
// GetActiveUsersForMention retrieves active users for mention purposes with optional username search
|
||||
func (p *UserProcessorImpl) GetActiveUsersForMention(ctx context.Context, search *string, limit int) ([]contract.UserResponse, error) {
|
||||
if limit <= 0 {
|
||||
limit = 50 // Default limit for mention suggestions
|
||||
}
|
||||
if limit > 100 {
|
||||
limit = 100 // Max limit for mention suggestions
|
||||
}
|
||||
// Limit validation is handled in the service layer
|
||||
|
||||
// Set isActive to true to only get active users
|
||||
isActive := true
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"eslogad-be/internal/entities"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// MockUserRepository is a mock implementation of UserRepository
|
||||
type MockUserRepository struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Create(ctx context.Context, user *entities.User) error {
|
||||
args := m.Called(ctx, user)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetByID(ctx context.Context, id uuid.UUID) (*entities.User, error) {
|
||||
args := m.Called(ctx, id)
|
||||
return args.Get(0).(*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetByEmail(ctx context.Context, email string) (*entities.User, error) {
|
||||
args := m.Called(ctx, email)
|
||||
return args.Get(0).(*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetByRole(ctx context.Context, role entities.UserRole) ([]*entities.User, error) {
|
||||
args := m.Called(ctx, role)
|
||||
return args.Get(0).([]*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetActiveUsers(ctx context.Context, organizationID uuid.UUID) ([]*entities.User, error) {
|
||||
args := m.Called(ctx, organizationID)
|
||||
return args.Get(0).([]*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Update(ctx context.Context, user *entities.User) error {
|
||||
args := m.Called(ctx, user)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Delete(ctx context.Context, id uuid.UUID) error {
|
||||
args := m.Called(ctx, id)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) UpdatePassword(ctx context.Context, id uuid.UUID, passwordHash string) error {
|
||||
args := m.Called(ctx, id, passwordHash)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) UpdateActiveStatus(ctx context.Context, id uuid.UUID, isActive bool) error {
|
||||
args := m.Called(ctx, id, isActive)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.User, int64, error) {
|
||||
args := m.Called(ctx, filters, limit, offset)
|
||||
return args.Get(0).([]*entities.User), args.Get(1).(int64), args.Error(2)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Count(ctx context.Context, filters map[string]interface{}) (int64, error) {
|
||||
args := m.Called(ctx, filters)
|
||||
return args.Get(0).(int64), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetRolesByUserID(ctx context.Context, userID uuid.UUID) ([]entities.Role, error) {
|
||||
args := m.Called(ctx, userID)
|
||||
return args.Get(0).([]entities.Role), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetPermissionsByUserID(ctx context.Context, userID uuid.UUID) ([]entities.Permission, error) {
|
||||
args := m.Called(ctx, userID)
|
||||
return args.Get(0).([]entities.Permission), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetDepartmentsByUserID(ctx context.Context, userID uuid.UUID) ([]entities.Department, error) {
|
||||
args := m.Called(ctx, userID)
|
||||
return args.Get(0).([]entities.Department), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) GetRolesByUserIDs(ctx context.Context, userIDs []uuid.UUID) (map[uuid.UUID][]entities.Role, error) {
|
||||
args := m.Called(ctx, userIDs)
|
||||
return args.Get(0).(map[uuid.UUID][]entities.Role), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) ListWithFilters(ctx context.Context, search *string, roleCode *string, isActive *bool, limit, offset int) ([]*entities.User, int64, error) {
|
||||
args := m.Called(ctx, search, roleCode, isActive, limit, offset)
|
||||
return args.Get(0).([]*entities.User), args.Get(1).(int64), args.Error(2)
|
||||
}
|
||||
|
||||
// MockUserProfileRepository is a mock implementation of UserProfileRepository
|
||||
type MockUserProfileRepository struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockUserProfileRepository) GetByUserID(ctx context.Context, userID uuid.UUID) (*entities.UserProfile, error) {
|
||||
args := m.Called(ctx, userID)
|
||||
return args.Get(0).(*entities.UserProfile), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserProfileRepository) Create(ctx context.Context, profile *entities.UserProfile) error {
|
||||
args := m.Called(ctx, profile)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockUserProfileRepository) Upsert(ctx context.Context, profile *entities.UserProfile) error {
|
||||
args := m.Called(ctx, profile)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockUserProfileRepository) Update(ctx context.Context, profile *entities.UserProfile) error {
|
||||
args := m.Called(ctx, profile)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func TestGetActiveUsersForMention(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
search *string
|
||||
limit int
|
||||
mockUsers []*entities.User
|
||||
mockRoles map[uuid.UUID][]entities.Role
|
||||
expectedCount int
|
||||
expectedError bool
|
||||
setupMocks func(*MockUserRepository, *MockUserProfileRepository)
|
||||
}{
|
||||
{
|
||||
name: "success with search",
|
||||
search: stringPtr("john"),
|
||||
limit: 10,
|
||||
mockUsers: []*entities.User{
|
||||
{
|
||||
ID: uuid.New(),
|
||||
Name: "John Doe",
|
||||
Email: "john@example.com",
|
||||
IsActive: true,
|
||||
},
|
||||
},
|
||||
expectedCount: 1,
|
||||
expectedError: false,
|
||||
setupMocks: func(mockRepo *MockUserRepository, mockProfileRepo *MockUserProfileRepository) {
|
||||
mockRepo.On("ListWithFilters", mock.Anything, stringPtr("john"), (*string)(nil), boolPtr(true), 10, 0).
|
||||
Return([]*entities.User{
|
||||
{
|
||||
ID: uuid.New(),
|
||||
Name: "John Doe",
|
||||
Email: "john@example.com",
|
||||
IsActive: true,
|
||||
},
|
||||
}, int64(1), nil)
|
||||
|
||||
mockRepo.On("GetRolesByUserIDs", mock.Anything, mock.AnythingOfType("[]uuid.UUID")).
|
||||
Return(map[uuid.UUID][]entities.Role{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success without search",
|
||||
search: nil,
|
||||
limit: 50,
|
||||
mockUsers: []*entities.User{
|
||||
{
|
||||
ID: uuid.New(),
|
||||
Name: "Jane Doe",
|
||||
Email: "jane@example.com",
|
||||
IsActive: true,
|
||||
},
|
||||
},
|
||||
expectedCount: 1,
|
||||
expectedError: false,
|
||||
setupMocks: func(mockRepo *MockUserRepository, mockProfileRepo *MockUserProfileRepository) {
|
||||
mockRepo.On("ListWithFilters", mock.Anything, (*string)(nil), (*string)(nil), boolPtr(true), 50, 0).
|
||||
Return([]*entities.User{
|
||||
{
|
||||
ID: uuid.New(),
|
||||
Name: "Jane Doe",
|
||||
Email: "jane@example.com",
|
||||
IsActive: true,
|
||||
},
|
||||
}, int64(1), nil)
|
||||
|
||||
mockRepo.On("GetRolesByUserIDs", mock.Anything, mock.AnythingOfType("[]uuid.UUID")).
|
||||
Return(map[uuid.UUID][]entities.Role{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "limit validation - too high",
|
||||
search: nil,
|
||||
limit: 150,
|
||||
mockUsers: []*entities.User{},
|
||||
expectedCount: 0,
|
||||
expectedError: false,
|
||||
setupMocks: func(mockRepo *MockUserRepository, mockProfileRepo *MockUserProfileRepository) {
|
||||
mockRepo.On("ListWithFilters", mock.Anything, (*string)(nil), (*string)(nil), boolPtr(true), 100, 0).
|
||||
Return([]*entities.User{}, int64(0), nil)
|
||||
|
||||
mockRepo.On("GetRolesByUserIDs", mock.Anything, mock.AnythingOfType("[]uuid.UUID")).
|
||||
Return(map[uuid.UUID][]entities.Role{}, nil)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create mocks
|
||||
mockRepo := &MockUserRepository{}
|
||||
mockProfileRepo := &MockUserProfileRepository{}
|
||||
|
||||
// Setup mocks
|
||||
if tt.setupMocks != nil {
|
||||
tt.setupMocks(mockRepo, mockProfileRepo)
|
||||
}
|
||||
|
||||
// Create processor
|
||||
processor := NewUserProcessor(mockRepo, mockProfileRepo)
|
||||
|
||||
// Call method
|
||||
result, err := processor.GetActiveUsersForMention(context.Background(), tt.search, tt.limit)
|
||||
|
||||
// Assertions
|
||||
if tt.expectedError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, tt.expectedCount)
|
||||
}
|
||||
|
||||
// Verify mocks
|
||||
mockRepo.AssertExpectations(t)
|
||||
mockProfileRepo.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
func stringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func boolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
@@ -28,4 +28,7 @@ type UserRepository interface {
|
||||
// New optimized helpers
|
||||
GetRolesByUserIDs(ctx context.Context, userIDs []uuid.UUID) (map[uuid.UUID][]entities.Role, error)
|
||||
ListWithFilters(ctx context.Context, search *string, roleCode *string, isActive *bool, limit, offset int) ([]*entities.User, int64, error)
|
||||
|
||||
GetByIDWithDepartments(ctx context.Context, id uuid.UUID) (*entities.User, error)
|
||||
UpdateDepartments(ctx context.Context, userID uuid.UUID, departments []entities.Department) error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user