package repository import ( "context" "database/sql/driver" "git.sch9.ru/new_gate/ms-auth/pkg/utils" "github.com/DATA-DOG/go-sqlmock" "github.com/jmoiron/sqlx" "github.com/stretchr/testify/require" "go.uber.org/zap" "testing" "time" "git.sch9.ru/new_gate/ms-auth/internal/models" ) func TestUsersRepository_CreateUser(t *testing.T) { t.Parallel() db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) require.NoError(t, err) defer db.Close() sqlxDB := sqlx.NewDb(db, "sqlmock") defer sqlxDB.Close() userRepo := NewUserRepository(sqlxDB, zap.NewNop()) t.Run("valid user creation", func(t *testing.T) { rows := sqlmock.NewRows([]string{"id"}).AddRow(1) user := &models.User{ Username: utils.AsStringP("testuser"), Password: utils.AsStringP("testpassword"), Email: utils.AsStringP("test@example.com"), Role: AsRoleP(models.RoleAdmin), } mock.ExpectQuery(sqlxDB.Rebind(createUser)).WithArgs( user.Username, AnyString{}, user.Email, AnyTime{}, user.Role, ).WillReturnRows(rows) _, err = userRepo.CreateUser(context.Background(), user) require.NoError(t, err) }) // TODO: add more tests // invalid username // invalid password // invalid email // invalid role // password hashing error // database query error // database scan error // etc } func TestUsersRepository_ReadUserById(t *testing.T) { t.Parallel() db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) require.NoError(t, err) defer db.Close() sqlxDB := sqlx.NewDb(db, "sqlmock") defer sqlxDB.Close() userRepo := NewUserRepository(sqlxDB, zap.NewNop()) t.Run("valid user read", func(t *testing.T) { user := &models.User{ Id: utils.AsInt32P(1), Username: utils.AsStringP("testuser"), Password: utils.AsStringP("testpassword"), Email: utils.AsStringP("test@example.com"), ExpiresAt: utils.AsTimeP(time.Now().Add(1 * time.Hour)), CreatedAt: utils.AsTimeP(time.Now()), UpdatedAt: utils.AsTimeP(time.Now()), Role: AsRoleP(models.RoleAdmin), } rows := sqlmock.NewRows( []string{ "id", "username", "hashed_pwd", "email", "expires_at", "created_at", "updated_at", "role", }).AddRow( *user.Id, *user.Username, *user.Password, *user.Email, *user.ExpiresAt, *user.CreatedAt, *user.UpdatedAt, *user.Role, ) mock.ExpectQuery(sqlxDB.Rebind(readUserById)).WithArgs( *user.Id, ).WillReturnRows(rows) readUser, err := userRepo.ReadUserById(context.Background(), *user.Id) require.NoError(t, err) require.Equal(t, user, readUser) }) } func TestUsersRepository_UpdateUser(t *testing.T) { t.Parallel() db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) require.NoError(t, err) defer db.Close() sqlxDB := sqlx.NewDb(db, "sqlmock") defer sqlxDB.Close() userRepo := NewUserRepository(sqlxDB, zap.NewNop()) t.Run("valid user update", func(t *testing.T) { user := &models.User{ Id: utils.AsInt32P(1), Username: utils.AsStringP("testuser"), Password: utils.AsStringP("testpassword"), Email: utils.AsStringP("test@example.com"), ExpiresAt: utils.AsTimeP(time.Now().Add(1 * time.Hour)), Role: AsRoleP(models.RoleAdmin), } require.NoError(t, err) mock.ExpectExec(sqlxDB.Rebind(updateUser)). WithArgs( *user.Username, AnyString{}, *user.Email, *user.ExpiresAt, *user.Role, *user.Id, ).WillReturnResult(sqlmock.NewResult(1, 1)) err = userRepo.UpdateUser(context.Background(), user) require.NoError(t, err) }) // TODO: add more tests } func TestUsersRepository_DeleteUser(t *testing.T) { t.Parallel() db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) require.NoError(t, err) defer db.Close() sqlxDB := sqlx.NewDb(db, "sqlmock") defer sqlxDB.Close() userRepo := NewUserRepository(sqlxDB, zap.NewNop()) t.Run("valid user deletion", func(t *testing.T) { user := &models.User{ Id: utils.AsInt32P(1), } mock.ExpectExec(sqlxDB.Rebind(deleteUser)).WithArgs(*user.Id).WillReturnResult(sqlmock.NewResult(1, 1)) err = userRepo.DeleteUser(context.Background(), *user.Id) require.NoError(t, err) }) // TODO: add more tests } func AsRoleP(r models.Role) *models.Role { return &r } type AnyTime struct{} // Match satisfies sqlmock.Argument interface func (a AnyTime) Match(v driver.Value) bool { _, ok := v.(time.Time) return ok } type AnyString struct{} func (a AnyString) Match(v driver.Value) bool { _, ok := v.(string) return ok }