package repository import ( "context" "database/sql/driver" "git.sch9.ru/new_gate/ms-auth/internal/models" "git.sch9.ru/new_gate/ms-auth/pkg" "github.com/DATA-DOG/go-sqlmock" "github.com/jmoiron/sqlx" "github.com/stretchr/testify/require" "testing" "time" ) func TestCaller_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) t.Run("valid user creation", func(t *testing.T) { rows := sqlmock.NewRows([]string{"id"}).AddRow(1) username := "testuser" password := "testpassword" role := models.RoleAdmin mock.ExpectQuery(sqlxDB.Rebind(createUser)).WithArgs( username, AnyString{}, role, ).WillReturnRows(rows) _, err = userRepo.C().CreateUser(context.Background(), username, password, role) require.NoError(t, err) }) t.Run("invalid user creation (invalid username)", func(t *testing.T) { username := "test" password := "testpassword" role := models.RoleAdmin _, err = userRepo.C().CreateUser(context.Background(), username, password, role) require.ErrorIs(t, err, pkg.ErrBadInput) }) t.Run("invalid user creation (invalid password)", func(t *testing.T) { username := "testuser" password := "test" role := models.RoleAdmin _, err = userRepo.C().CreateUser(context.Background(), username, password, role) require.ErrorIs(t, err, pkg.ErrBadInput) }) t.Run("invalid user creation (invalid role)", func(t *testing.T) { username := "testuser" password := "testpassword" _, err = userRepo.C().CreateUser(context.Background(), username, password, 123) require.ErrorIs(t, err, pkg.ErrBadInput) }) } func TestCaller_ReadUserByUsername(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) t.Run("valid user read", func(t *testing.T) { user := &models.User{ Id: 1, Username: "testuser", HashedPassword: "hashedtestpassword", CreatedAt: time.Now(), ModifiedAt: time.Now(), Role: models.RoleAdmin, } rows := sqlmock.NewRows( []string{ "id", "username", "hashed_pwd", "created_at", "modified_at", "role", }).AddRow( user.Id, user.Username, user.HashedPassword, user.CreatedAt, user.ModifiedAt, user.Role, ) mock.ExpectQuery(sqlxDB.Rebind(readUserByUsername)).WithArgs( user.Username, ).WillReturnRows(rows) readUser, err := userRepo.C().ReadUserByUsername(context.Background(), user.Username) require.NoError(t, err) require.Equal(t, user, readUser) }) } 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) t.Run("valid user read", func(t *testing.T) { user := &models.User{ Id: 1, Username: "testuser", HashedPassword: "hashedtestpassword", CreatedAt: time.Now(), ModifiedAt: time.Now(), Role: models.RoleAdmin, } rows := sqlmock.NewRows( []string{ "id", "username", "hashed_pwd", "created_at", "modified_at", "role", }).AddRow( user.Id, user.Username, user.HashedPassword, user.CreatedAt, user.ModifiedAt, user.Role, ) mock.ExpectQuery(sqlxDB.Rebind(readUserById)).WithArgs( user.Id, ).WillReturnRows(rows) readUser, err := userRepo.C().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) t.Run("valid user update", func(t *testing.T) { user := &models.User{ Id: 1, Username: "testuser", HashedPassword: "hashedtestpassword", CreatedAt: time.Now(), ModifiedAt: time.Now(), Role: models.RoleAdmin, } mock.ExpectExec(sqlxDB.Rebind(updateUser)).WithArgs( user.Username, user.Role, user.Id, ).WillReturnResult(sqlmock.NewResult(1, 1)) err := userRepo.C().UpdateUser(context.Background(), user.Id, AsStringP(user.Username), AsRoleP(user.Role)) require.NoError(t, err) }) } func TestCaller_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) t.Run("valid user delete", func(t *testing.T) { mock.ExpectExec(sqlxDB.Rebind(deleteUser)).WithArgs( 1, ).WillReturnResult(sqlmock.NewResult(1, 1)) err := userRepo.C().DeleteUser(context.Background(), 1) require.NoError(t, err) }) } 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 } func AsStringP(str string) *string { return &str }