package service import ( "context" "testing" "git.logidex.ru/fakz9/logidex-id/internal/api/user/domain" db "git.logidex.ru/fakz9/logidex-id/internal/db/generated" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) // MockUserRepository implements domain.UserRepository type MockUserRepository struct { mock.Mock } func (m *MockUserRepository) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*db.User, error) { args := m.Called(ctx, phoneNumber) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*db.User), args.Error(1) } func (m *MockUserRepository) GetUserByUuid(ctx context.Context, uuid string) (*db.User, error) { args := m.Called(ctx, uuid) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*db.User), args.Error(1) } func (m *MockUserRepository) CreateUser(ctx context.Context, phoneNumber string) (*db.User, error) { args := m.Called(ctx, phoneNumber) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*db.User), args.Error(1) } func (m *MockUserRepository) VerifyUser(ctx context.Context, uuid string) (*db.User, error) { args := m.Called(ctx, uuid) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*db.User), args.Error(1) } func TestNewUserService(t *testing.T) { mockRepo := &MockUserRepository{} service := NewUserService(mockRepo) assert.NotNil(t, service) assert.Implements(t, (*UserService)(nil), service) } func TestUserService_GetUserByPhoneNumber(t *testing.T) { mockRepo := &MockUserRepository{} service := &userService{repo: mockRepo} ctx := context.Background() phoneNumber := "+79161234567" testUuid := uuid.New() tests := []struct { name string setupMock func() expectedUser *domain.User wantErr bool errType interface{} }{ { name: "user found", setupMock: func() { dbUser := &db.User{ Uuid: testUuid, PhoneNumber: phoneNumber, } mockRepo.On("GetUserByPhoneNumber", ctx, phoneNumber).Return(dbUser, nil).Once() }, expectedUser: &domain.User{ Uuid: testUuid.String(), PhoneNumber: phoneNumber, }, wantErr: false, }, { name: "user not found", setupMock: func() { mockRepo.On("GetUserByPhoneNumber", ctx, phoneNumber).Return(nil, nil).Once() }, wantErr: true, errType: domain.ErrUserNotFound{}, }, { name: "repository error", setupMock: func() { mockRepo.On("GetUserByPhoneNumber", ctx, phoneNumber).Return(nil, assert.AnError).Once() }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Reset mock mockRepo.ExpectedCalls = nil tt.setupMock() result, err := service.GetUserByPhoneNumber(ctx, phoneNumber) if tt.wantErr { assert.Error(t, err) assert.Nil(t, result) if tt.errType != nil { assert.IsType(t, tt.errType, err) } } else { assert.NoError(t, err) assert.Equal(t, tt.expectedUser, result) } mockRepo.AssertExpectations(t) }) } } func TestUserService_GetUserByUuid(t *testing.T) { mockRepo := &MockUserRepository{} service := &userService{repo: mockRepo} ctx := context.Background() testUuid := uuid.New() uuidString := testUuid.String() phoneNumber := "+79161234567" tests := []struct { name string setupMock func() expectedUser *domain.User wantErr bool errType interface{} }{ { name: "user found", setupMock: func() { dbUser := &db.User{ Uuid: testUuid, PhoneNumber: phoneNumber, } mockRepo.On("GetUserByUuid", ctx, uuidString).Return(dbUser, nil).Once() }, expectedUser: &domain.User{ Uuid: uuidString, PhoneNumber: phoneNumber, }, wantErr: false, }, { name: "user not found", setupMock: func() { mockRepo.On("GetUserByUuid", ctx, uuidString).Return(nil, nil).Once() }, wantErr: true, errType: domain.ErrUserNotFound{}, }, { name: "repository error", setupMock: func() { mockRepo.On("GetUserByUuid", ctx, uuidString).Return(nil, assert.AnError).Once() }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Reset mock mockRepo.ExpectedCalls = nil tt.setupMock() result, err := service.GetUserByUuid(ctx, uuidString) if tt.wantErr { assert.Error(t, err) assert.Nil(t, result) if tt.errType != nil { assert.IsType(t, tt.errType, err) } } else { assert.NoError(t, err) assert.Equal(t, tt.expectedUser, result) } mockRepo.AssertExpectations(t) }) } } func TestUserService_CreateUser(t *testing.T) { mockRepo := &MockUserRepository{} service := &userService{repo: mockRepo} ctx := context.Background() phoneNumber := "+79161234567" testUuid := uuid.New() tests := []struct { name string setupMock func() expectedUser *domain.User wantErr bool }{ { name: "user created successfully", setupMock: func() { dbUser := &db.User{ Uuid: testUuid, PhoneNumber: phoneNumber, } mockRepo.On("CreateUser", ctx, phoneNumber).Return(dbUser, nil).Once() }, expectedUser: &domain.User{ Uuid: testUuid.String(), PhoneNumber: phoneNumber, }, wantErr: false, }, { name: "repository error", setupMock: func() { mockRepo.On("CreateUser", ctx, phoneNumber).Return(nil, assert.AnError).Once() }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Reset mock mockRepo.ExpectedCalls = nil tt.setupMock() result, err := service.CreateUser(ctx, phoneNumber) if tt.wantErr { assert.Error(t, err) assert.Nil(t, result) } else { assert.NoError(t, err) assert.Equal(t, tt.expectedUser, result) } mockRepo.AssertExpectations(t) }) } } func TestUserService_VerifyUser(t *testing.T) { mockRepo := &MockUserRepository{} service := &userService{repo: mockRepo} ctx := context.Background() testUuid := uuid.New() uuidString := testUuid.String() phoneNumber := "+79161234567" tests := []struct { name string setupMock func() expectedUser *domain.User wantErr bool }{ { name: "user verified successfully", setupMock: func() { dbUser := &db.User{ Uuid: testUuid, PhoneNumber: phoneNumber, } mockRepo.On("VerifyUser", ctx, uuidString).Return(dbUser, nil).Once() }, expectedUser: &domain.User{ Uuid: uuidString, PhoneNumber: phoneNumber, }, wantErr: false, }, { name: "repository error", setupMock: func() { mockRepo.On("VerifyUser", ctx, uuidString).Return(nil, assert.AnError).Once() }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Reset mock mockRepo.ExpectedCalls = nil tt.setupMock() result, err := service.VerifyUser(ctx, uuidString) if tt.wantErr { assert.Error(t, err) assert.Nil(t, result) } else { assert.NoError(t, err) assert.Equal(t, tt.expectedUser, result) } mockRepo.AssertExpectations(t) }) } } // Test edge cases and error scenarios func TestUserService_ErrorMessages(t *testing.T) { mockRepo := &MockUserRepository{} service := &userService{repo: mockRepo} ctx := context.Background() phoneNumber := "+79161234567" t.Run("GetUserByPhoneNumber returns proper error message", func(t *testing.T) { mockRepo.On("GetUserByPhoneNumber", ctx, phoneNumber).Return(nil, nil).Once() _, err := service.GetUserByPhoneNumber(ctx, phoneNumber) assert.Error(t, err) userErr, ok := err.(domain.ErrUserNotFound) assert.True(t, ok) assert.Equal(t, phoneNumber, userErr.PhoneNumber) assert.Contains(t, err.Error(), phoneNumber) mockRepo.AssertExpectations(t) }) }