add service

This commit is contained in:
vallyenfail
2026-01-17 17:39:33 +03:00
parent 1376ff9188
commit d959dcca96
82 changed files with 25041 additions and 1 deletions

View File

@@ -0,0 +1,449 @@
package tests
import (
"context"
"errors"
"testing"
"time"
"github.com/gojuno/minimock/v3"
"github.com/stretchr/testify/suite"
"smart-search-back/internal/mocks"
"smart-search-back/internal/model"
"smart-search-back/internal/service"
"smart-search-back/pkg/crypto"
apperrors "smart-search-back/pkg/errors"
"smart-search-back/pkg/jwt"
)
type Suite struct {
suite.Suite
ctx context.Context
authService service.AuthService
userRepo *mocks.UserRepositoryMock
sessionRepo *mocks.SessionRepositoryMock
}
func newSuite(ctx context.Context) *Suite {
return &Suite{ctx: ctx}
}
func TestAuthService(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
suite.Run(t, newSuite(ctx))
}
func (s *Suite) SetupSuite() {
s.ctx = context.Background()
}
func (s *Suite) SetupTest() {
ctrl := minimock.NewController(s.T())
s.userRepo = mocks.NewUserRepositoryMock(ctrl)
s.sessionRepo = mocks.NewSessionRepositoryMock(ctrl)
s.authService = service.NewAuthService(s.userRepo, s.sessionRepo)
}
func createTestUser(password string) *model.User {
return &model.User{
ID: 1,
Email: "test@example.com",
EmailHash: crypto.EmailHash("test@example.com"),
PasswordHash: crypto.PasswordHash(password),
CreatedAt: time.Now(),
}
}
func createTestSession(userID int) *model.Session {
return &model.Session{
ID: 1,
UserID: userID,
AccessToken: "test-access-token",
RefreshToken: "test-refresh-token",
IP: "127.0.0.1",
UserAgent: "test-agent",
CreatedAt: time.Now(),
ExpiresAt: time.Now().Add(30 * 24 * time.Hour),
}
}
func (s *Suite) TestAuthService_Login_Success() {
password := "testpassword"
user := createTestUser(password)
s.userRepo.FindByEmailHashMock.Return(user, nil)
s.sessionRepo.CreateMock.Return(nil)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
password,
"127.0.0.1",
"test-agent",
)
s.NoError(err)
s.NotEmpty(accessToken)
s.NotEmpty(refreshToken)
}
func (s *Suite) TestAuthService_Login_UserNotFound() {
err := apperrors.NewBusinessError(apperrors.UserNotFound, "user not found")
s.userRepo.FindByEmailHashMock.Return(nil, err)
accessToken, refreshToken, loginErr := s.authService.Login(
s.ctx,
"test@example.com",
"password",
"127.0.0.1",
"test-agent",
)
s.Error(loginErr)
s.Empty(accessToken)
s.Empty(refreshToken)
var appErr *apperrors.AppError
s.True(errors.As(loginErr, &appErr))
s.Equal(apperrors.UserNotFound, appErr.Code)
}
func (s *Suite) TestAuthService_Login_DatabaseError_OnFindUser() {
dbErr := apperrors.NewInternalError(apperrors.DatabaseError, "failed to find user", nil)
s.userRepo.FindByEmailHashMock.Return(nil, dbErr)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
"password",
"127.0.0.1",
"test-agent",
)
s.Error(err)
s.Empty(accessToken)
s.Empty(refreshToken)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.InternalErrorType, appErr.Type)
}
func (s *Suite) TestAuthService_Login_InvalidPassword() {
password := "correctpassword"
user := createTestUser(password)
s.userRepo.FindByEmailHashMock.Return(user, nil)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
"wrongpassword",
"127.0.0.1",
"test-agent",
)
s.Error(err)
s.Empty(accessToken)
s.Empty(refreshToken)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.AuthInvalidCredentials, appErr.Code)
s.Contains(appErr.Message, "Invalid email or password")
}
func (s *Suite) TestAuthService_Login_EmptyPassword() {
user := createTestUser("")
s.userRepo.FindByEmailHashMock.Return(user, nil)
s.sessionRepo.CreateMock.Return(nil)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
"",
"127.0.0.1",
"test-agent",
)
s.NoError(err)
s.NotEmpty(accessToken)
s.NotEmpty(refreshToken)
}
func (s *Suite) TestAuthService_Login_EmailWithSpacesAndCase() {
password := "testpassword"
normalizedEmail := "test@example.com"
user := createTestUser(password)
user.EmailHash = crypto.EmailHash(normalizedEmail)
s.userRepo.FindByEmailHashMock.Return(user, nil)
s.sessionRepo.CreateMock.Return(nil)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
" TEST@EXAMPLE.COM ",
password,
"127.0.0.1",
"test-agent",
)
s.NoError(err)
s.NotEmpty(accessToken)
s.NotEmpty(refreshToken)
}
func (s *Suite) TestAuthService_Login_EmptyEmail() {
err := apperrors.NewBusinessError(apperrors.UserNotFound, "user not found")
s.userRepo.FindByEmailHashMock.Return(nil, err)
accessToken, refreshToken, loginErr := s.authService.Login(
s.ctx,
"",
"password",
"127.0.0.1",
"test-agent",
)
s.Error(loginErr)
s.Empty(accessToken)
s.Empty(refreshToken)
}
func (s *Suite) TestAuthService_Login_SessionCreateError() {
password := "testpassword"
user := createTestUser(password)
s.userRepo.FindByEmailHashMock.Return(user, nil)
dbErr := apperrors.NewInternalError(apperrors.DatabaseError, "failed to create session", nil)
s.sessionRepo.CreateMock.Return(dbErr)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
password,
"127.0.0.1",
"test-agent",
)
s.Error(err)
s.Empty(accessToken)
s.Empty(refreshToken)
}
func (s *Suite) TestAuthService_Login_EmptyIPAndUserAgent() {
password := "testpassword"
user := createTestUser(password)
s.userRepo.FindByEmailHashMock.Return(user, nil)
s.sessionRepo.CreateMock.Return(nil)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
password,
"",
"",
)
s.NoError(err)
s.NotEmpty(accessToken)
s.NotEmpty(refreshToken)
}
func (s *Suite) TestAuthService_Refresh_Success() {
session := createTestSession(1)
s.sessionRepo.FindByRefreshTokenMock.Return(session, nil)
s.sessionRepo.UpdateAccessTokenMock.Return(nil)
accessToken, err := s.authService.Refresh(s.ctx, "test-refresh-token")
s.NoError(err)
s.NotEmpty(accessToken)
}
func (s *Suite) TestAuthService_Refresh_RefreshInvalid() {
err := apperrors.NewBusinessError(apperrors.RefreshInvalid, "refresh token is invalid or expired")
s.sessionRepo.FindByRefreshTokenMock.Return(nil, err)
accessToken, refreshErr := s.authService.Refresh(s.ctx, "invalid-token")
s.Error(refreshErr)
s.Empty(accessToken)
var appErr *apperrors.AppError
s.True(errors.As(refreshErr, &appErr))
s.Equal(apperrors.RefreshInvalid, appErr.Code)
}
func (s *Suite) TestAuthService_Refresh_DatabaseError_OnFindSession() {
dbErr := apperrors.NewInternalError(apperrors.DatabaseError, "failed to find session", nil)
s.sessionRepo.FindByRefreshTokenMock.Return(nil, dbErr)
accessToken, err := s.authService.Refresh(s.ctx, "test-refresh-token")
s.Error(err)
s.Empty(accessToken)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.InternalErrorType, appErr.Type)
}
func (s *Suite) TestAuthService_Refresh_EmptyToken() {
err := apperrors.NewBusinessError(apperrors.RefreshInvalid, "refresh token is invalid or expired")
s.sessionRepo.FindByRefreshTokenMock.Return(nil, err)
accessToken, refreshErr := s.authService.Refresh(s.ctx, "")
s.Error(refreshErr)
s.Empty(accessToken)
}
func (s *Suite) TestAuthService_Refresh_UpdateAccessTokenError() {
session := createTestSession(1)
s.sessionRepo.FindByRefreshTokenMock.Return(session, nil)
dbErr := apperrors.NewInternalError(apperrors.DatabaseError, "failed to update access token", nil)
s.sessionRepo.UpdateAccessTokenMock.Return(dbErr)
accessToken, err := s.authService.Refresh(s.ctx, "test-refresh-token")
s.Error(err)
s.Empty(accessToken)
}
func (s *Suite) TestAuthService_Refresh_UserIDZero() {
session := createTestSession(0)
s.sessionRepo.FindByRefreshTokenMock.Return(session, nil)
s.sessionRepo.UpdateAccessTokenMock.Return(nil)
accessToken, err := s.authService.Refresh(s.ctx, "test-refresh-token")
s.NoError(err)
s.NotEmpty(accessToken)
}
func (s *Suite) TestAuthService_Validate_Success() {
s.T().Parallel()
userID := 1
accessToken, err := jwt.GenerateAccessToken(userID)
s.NoError(err)
validatedUserID, validateErr := s.authService.Validate(s.ctx, accessToken)
s.NoError(validateErr)
s.Equal(userID, validatedUserID)
}
func (s *Suite) TestAuthService_Validate_EmptyToken() {
s.T().Parallel()
userID, err := s.authService.Validate(s.ctx, "")
s.Error(err)
s.Equal(0, userID)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.AuthInvalidToken, appErr.Code)
}
func (s *Suite) TestAuthService_Validate_InvalidTokenFormat() {
s.T().Parallel()
userID, err := s.authService.Validate(s.ctx, "invalid.token.format")
s.Error(err)
s.Equal(0, userID)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.AuthInvalidToken, appErr.Code)
}
func (s *Suite) TestAuthService_Validate_RefreshTokenInsteadOfAccess() {
s.T().Parallel()
userID := 1
refreshToken, err := jwt.GenerateRefreshToken(userID)
s.NoError(err)
validatedUserID, validateErr := s.authService.Validate(s.ctx, refreshToken)
s.Error(validateErr)
s.Equal(0, validatedUserID)
var appErr *apperrors.AppError
s.True(errors.As(validateErr, &appErr))
s.Equal(apperrors.AuthInvalidToken, appErr.Code)
s.Contains(appErr.Message, "not an access token")
}
func (s *Suite) TestAuthService_Validate_UserIDZero() {
s.T().Parallel()
accessToken, err := jwt.GenerateAccessToken(0)
s.NoError(err)
validatedUserID, validateErr := s.authService.Validate(s.ctx, accessToken)
s.NoError(validateErr)
s.Equal(0, validatedUserID)
}
func (s *Suite) TestAuthService_Validate_InvalidSignature() {
s.T().Parallel()
invalidToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidHlwZSI6ImFjY2VzcyIsImlhdCI6MTYwOTQ1NjgwMCwiZXhwIjoxNjA5NDY1ODAwfQ.invalid-signature"
userID, err := s.authService.Validate(s.ctx, invalidToken)
s.Error(err)
s.Equal(0, userID)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.AuthInvalidToken, appErr.Code)
}
func (s *Suite) TestAuthService_Logout_Success() {
s.sessionRepo.RevokeMock.Return(nil)
err := s.authService.Logout(s.ctx, "test-refresh-token")
s.NoError(err)
}
func (s *Suite) TestAuthService_Logout_DatabaseError() {
dbErr := apperrors.NewInternalError(apperrors.DatabaseError, "failed to revoke session", nil)
s.sessionRepo.RevokeMock.Return(dbErr)
err := s.authService.Logout(s.ctx, "test-refresh-token")
s.Error(err)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.InternalErrorType, appErr.Type)
}
func (s *Suite) TestAuthService_Logout_EmptyToken() {
s.sessionRepo.RevokeMock.Return(nil)
err := s.authService.Logout(s.ctx, "")
s.NoError(err)
}
func (s *Suite) TestAuthService_Logout_NonExistentToken() {
s.sessionRepo.RevokeMock.Return(nil)
err := s.authService.Logout(s.ctx, "non-existent-token")
s.NoError(err)
}