package handler import ( "bytes" "context" "encoding/json" "errors" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "io" "net/http" "net/http/httptest" "testing" "time" "git.urec56.ru/urec/chat_back_go/internal/domain" mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks" mock_service "git.urec56.ru/urec/chat_back_go/internal/service/mocks" mock_http "git.urec56.ru/urec/chat_back_go/internal/transport/rest/handler/mocks" ) func TestHandler_GetUsers(t *testing.T) { type servBehavior func(s *mock_service.MockServ, username string, users []domain.User, err error) type logBehavior func(l *mock_logger.MockLog, path string, err error) testTable := []struct { name string username string servBehavior servBehavior servUsers []domain.User servErr error logBehavior logBehavior logErr error writeErr error expectedUsers map[string][]domain.User expectedStatusCode int }{ { name: "ok_1", username: "urec", servBehavior: func(s *mock_service.MockServ, username string, users []domain.User, err error) { s.EXPECT().GetAll(username).Return(users, err) }, servUsers: []domain.User{ { ID: 1, Username: "urec", Email: "mail@mail.ru", AvatarImage: "image", BlackPhoenix: true, DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, }, logBehavior: func(l *mock_logger.MockLog, path string, err error) {}, expectedUsers: map[string][]domain.User{ "users": { { ID: 1, Username: "urec", Email: "mail@mail.ru", AvatarImage: "image", BlackPhoenix: true, DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, }, }, expectedStatusCode: http.StatusOK, }, { name: "ok_2", servBehavior: func(s *mock_service.MockServ, username string, users []domain.User, err error) { s.EXPECT().GetAll(username).Return(users, err) }, servUsers: []domain.User{ { ID: 1, Username: "urec", Email: "mail@mail.ru", AvatarImage: "image", BlackPhoenix: true, DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, }, logBehavior: func(l *mock_logger.MockLog, path string, err error) {}, expectedUsers: map[string][]domain.User{ "users": { { ID: 1, Username: "urec", Email: "mail@mail.ru", AvatarImage: "image", BlackPhoenix: true, DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, }, }, expectedStatusCode: http.StatusOK, }, { name: "serv_error", servBehavior: func(s *mock_service.MockServ, username string, users []domain.User, err error) { s.EXPECT().GetAll(username).Return(users, err) }, servErr: domain.AnyError, logBehavior: func(l *mock_logger.MockLog, path string, err error) { l.EXPECT().Infof("[%s] getting users: %s", path, err.Error()) }, logErr: domain.AnyError, expectedStatusCode: http.StatusInternalServerError, }, { name: "response_writing_error", servBehavior: func(s *mock_service.MockServ, username string, users []domain.User, err error) { s.EXPECT().GetAll(username).Return(users, err) }, logBehavior: func(l *mock_logger.MockLog, path string, err error) { l.EXPECT().Errorf("[%s] writing response: %s", path, err.Error()) }, logErr: domain.AnyError, writeErr: domain.AnyError, expectedStatusCode: http.StatusInternalServerError, }, } for _, tc := range testTable { t.Run(tc.name, func(t *testing.T) { c := gomock.NewController(t) defer c.Finish() reqPath := "/users" log := mock_logger.NewMockLog(c) serv := mock_service.NewMockServ(c) tc.logBehavior(log, reqPath, tc.logErr) tc.servBehavior(serv, tc.username, tc.servUsers, tc.servErr) req := httptest.NewRequest(http.MethodGet, reqPath, nil) w := mock_http.NewRecorder(tc.writeErr) q := req.URL.Query() q.Add("username", tc.username) req.URL.RawQuery = q.Encode() h := &Handler{serv: serv, l: log} h.GetUsers(w, req) resp := w.Result() b, err := io.ReadAll(resp.Body) assert.NoError(t, err) var u map[string][]domain.User if tc.expectedStatusCode == http.StatusOK { err = json.Unmarshal(b, &u) assert.NoError(t, err) } assert.Equal(t, tc.expectedUsers, u) assert.Equal(t, tc.expectedStatusCode, resp.StatusCode) }) } } func TestHandler_CheckExisting(t *testing.T) { type servBehavior func(s *mock_service.MockServ, username, email string, user domain.User, err error) type logBehavior func(l *mock_logger.MockLog, path string, err error) testTable := []struct { name string reqBody domain.UserFilter servBehavior servBehavior logBehavior logBehavior servUser domain.User servErr error logErr error isDecodeError bool expectedStatusCode int }{ { name: "ok", reqBody: domain.UserFilter{Username: "urecs", Email: "mail@atod.com"}, servBehavior: func(s *mock_service.MockServ, username, email string, user domain.User, err error) { s.EXPECT().FindOne(username, email).Return(user, err) }, logBehavior: func(l *mock_logger.MockLog, path string, err error) {}, expectedStatusCode: http.StatusOK, }, { name: "decoding_body_error", servBehavior: func(s *mock_service.MockServ, username, email string, user domain.User, err error) {}, logBehavior: func(l *mock_logger.MockLog, path string, err error) {}, isDecodeError: true, expectedStatusCode: http.StatusUnprocessableEntity, }, { name: "validation_error", reqBody: domain.UserFilter{Username: "u"}, servBehavior: func(s *mock_service.MockServ, username, email string, user domain.User, err error) {}, logBehavior: func(l *mock_logger.MockLog, path string, err error) { l.EXPECT().Infof("[%s] validation: %s", path, err.Error()) }, logErr: errors.New("Key: 'UserFilter.Username' Error:Field validation for 'Username' failed on the 'min' tag"), expectedStatusCode: http.StatusUnprocessableEntity, }, { name: "serv_error", reqBody: domain.UserFilter{Username: "urec"}, servBehavior: func(s *mock_service.MockServ, username, email string, user domain.User, err error) { s.EXPECT().FindOne(username, email).Return(user, err) }, logBehavior: func(l *mock_logger.MockLog, path string, err error) { l.EXPECT().Infof("[%s] serv.FindOne error: %s", path, err.Error()) }, servErr: domain.AnyError, logErr: domain.AnyError, expectedStatusCode: http.StatusInternalServerError, }, { name: "user_already_exists", reqBody: domain.UserFilter{Username: "urec"}, servUser: domain.User{ID: 1}, servBehavior: func(s *mock_service.MockServ, username, email string, user domain.User, err error) { s.EXPECT().FindOne(username, email).Return(user, err) }, logBehavior: func(l *mock_logger.MockLog, path string, err error) {}, expectedStatusCode: http.StatusConflict, }, } for _, tc := range testTable { t.Run(tc.name, func(t *testing.T) { c := gomock.NewController(t) defer c.Finish() reqPath := "/check_existing_user" log := mock_logger.NewMockLog(c) serv := mock_service.NewMockServ(c) tc.logBehavior(log, reqPath, tc.logErr) tc.servBehavior(serv, tc.reqBody.Username, tc.reqBody.Email, tc.servUser, tc.servErr) var body []byte if !tc.isDecodeError { b, err := json.Marshal(tc.reqBody) assert.NoError(t, err) body = b } else { b := make([]byte, 0) body = b } req := httptest.NewRequest(http.MethodPost, reqPath, bytes.NewBuffer(body)) w := mock_http.NewRecorder(nil) h := &Handler{serv: serv, l: log} h.CheckExisting(w, req) resp := w.Result() assert.Equal(t, tc.expectedStatusCode, resp.StatusCode) }) } } func TestHandler_CheckExistingPassword(t *testing.T) { reqPath := "/check_existing_password" req := httptest.NewRequest(http.MethodPost, reqPath, nil) w := httptest.NewRecorder() h := &Handler{} for i := 0; i < 10; i++ { h.CheckExistingPassword(w, req) } } func TestHandler_Register(t *testing.T) { type servBehavior func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) type logBehavior func(l *mock_logger.MockLog, path string, err error) testTable := []struct { name string reqBody domain.UserRegister servBehavior servBehavior logBehavior logBehavior servToken string servErr error logErr error isDecodeError bool writeErr error expectedStatusCode int expectedBody map[string]string }{ { name: "ok", reqBody: domain.UserRegister{ Username: "urecs", Email: "mail@atod.com", Password: "password", Password2: "password", DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) { s.EXPECT().Register(userData).Return(token, err) }, logBehavior: func(l *mock_logger.MockLog, path string, err error) {}, servToken: "token", expectedStatusCode: http.StatusCreated, expectedBody: map[string]string{"authorization": "Bearer token"}, }, { name: "user_already_exists", reqBody: domain.UserRegister{ Username: "urec", Email: "mail@atod.com", Password: "password", Password2: "password", DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) { s.EXPECT().Register(userData).Return(token, err) }, logBehavior: func(l *mock_logger.MockLog, path string, err error) {}, servErr: domain.UserAlreadyExistsError, expectedStatusCode: http.StatusConflict, }, { name: "decoding_body_error", servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) {}, logBehavior: func(l *mock_logger.MockLog, path string, err error) {}, isDecodeError: true, expectedStatusCode: http.StatusUnprocessableEntity, }, { name: "validation_error", reqBody: domain.UserRegister{ Email: "mail@atod.com", Password: "password", Password2: "password", DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) {}, logBehavior: func(l *mock_logger.MockLog, path string, err error) { l.EXPECT().Infof("[%s] validation: %s", path, err.Error()) }, logErr: errors.New("Key: 'UserRegister.Username' Error:Field validation for 'Username' failed on the 'min' tag"), expectedStatusCode: http.StatusUnprocessableEntity, }, { name: "serv_error", reqBody: domain.UserRegister{ Username: "urec", Email: "mail@atod.com", Password: "password", Password2: "password", DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) { s.EXPECT().Register(userData).Return(token, err) }, logBehavior: func(l *mock_logger.MockLog, path string, err error) { l.EXPECT().Infof("[%s] serv.Register: %s", path, err.Error()) }, servErr: domain.AnyError, logErr: domain.AnyError, expectedStatusCode: http.StatusInternalServerError, }, { name: "response_writing_error", reqBody: domain.UserRegister{ Username: "urec", Email: "mail@atod.com", Password: "password", Password2: "password", DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) { s.EXPECT().Register(userData).Return(token, err) }, logBehavior: func(l *mock_logger.MockLog, path string, err error) { l.EXPECT().Errorf("[%s] writing response: %s", path, err.Error()) }, writeErr: domain.AnyError, logErr: domain.AnyError, expectedStatusCode: http.StatusCreated, }, } for _, tc := range testTable { t.Run(tc.name, func(t *testing.T) { c := gomock.NewController(t) defer c.Finish() reqPath := "/register" log := mock_logger.NewMockLog(c) serv := mock_service.NewMockServ(c) tc.logBehavior(log, reqPath, tc.logErr) tc.servBehavior(serv, tc.reqBody, tc.servToken, tc.servErr) var body []byte if !tc.isDecodeError { b, err := json.Marshal(tc.reqBody) assert.NoError(t, err) body = b } else { b := make([]byte, 0) body = b } req := httptest.NewRequest(http.MethodPost, reqPath, bytes.NewBuffer(body)) w := mock_http.NewRecorder(tc.writeErr) h := &Handler{serv: serv, l: log} h.Register(w, req) resp := w.Result() b, err := io.ReadAll(resp.Body) assert.NoError(t, err) var authToken map[string]string if tc.expectedStatusCode == http.StatusCreated && tc.writeErr == nil { err = json.Unmarshal(b, &authToken) assert.NoError(t, err) } assert.Equal(t, tc.expectedBody, authToken) assert.Equal(t, tc.expectedStatusCode, resp.StatusCode) }) } } func TestHandler_ResendEmailVerification(t *testing.T) { c := gomock.NewController(t) defer c.Finish() log := mock_logger.NewMockLog(c) serv := mock_service.NewMockServ(c) req := httptest.NewRequest("", "/chats", nil) w := httptest.NewRecorder() h := &Handler{serv: serv, l: log} h.ResendEmailVerification(w, req) _ = w.Result() } func TestHandler_EmailVerification(t *testing.T) { c := gomock.NewController(t) defer c.Finish() log := mock_logger.NewMockLog(c) serv := mock_service.NewMockServ(c) req := httptest.NewRequest("", "/chats", nil) w := httptest.NewRecorder() h := &Handler{serv: serv, l: log} h.EmailVerification(w, req) _ = w.Result() } func TestHandler_Login(t *testing.T) { c := gomock.NewController(t) defer c.Finish() log := mock_logger.NewMockLog(c) serv := mock_service.NewMockServ(c) req := httptest.NewRequest("", "/chats", nil) w := httptest.NewRecorder() h := &Handler{serv: serv, l: log} h.Login(w, req) _ = w.Result() } func TestHandler_Get(t *testing.T) { type logBehavior func(l *mock_logger.MockLog, path string, err error) testTable := []struct { name string ctxUser domain.User addingCtxUser bool logBehavior logBehavior logErr error writeErr error expectedUser domain.User expectedStatusCode int }{ { name: "ok", ctxUser: domain.User{ ID: 1, Username: "urec", Email: "mail@mail.ru", AvatarImage: "image", BlackPhoenix: true, DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, addingCtxUser: true, logBehavior: func(l *mock_logger.MockLog, path string, err error) {}, expectedUser: domain.User{ ID: 1, Username: "urec", Email: "mail@mail.ru", AvatarImage: "image", BlackPhoenix: true, DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, expectedStatusCode: http.StatusOK, }, { name: "incorrect_user_in_ctx", logBehavior: func(l *mock_logger.MockLog, path string, err error) { l.EXPECT().Errorf("[%s] extracting user from ctx", path) }, expectedStatusCode: http.StatusInternalServerError, }, { name: "response_writing_error", ctxUser: domain.User{ ID: 1, Username: "urec", Email: "mail@mail.ru", AvatarImage: "image", BlackPhoenix: true, DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, }, addingCtxUser: true, logBehavior: func(l *mock_logger.MockLog, path string, err error) { l.EXPECT().Errorf("[%s] writing response: %s", path, err.Error()) }, logErr: domain.AnyError, writeErr: domain.AnyError, expectedStatusCode: http.StatusInternalServerError, }, } for _, tc := range testTable { t.Run(tc.name, func(t *testing.T) { c := gomock.NewController(t) defer c.Finish() reqPath := "/users/me" log := mock_logger.NewMockLog(c) tc.logBehavior(log, reqPath, tc.logErr) req := httptest.NewRequest(http.MethodGet, reqPath, nil) w := mock_http.NewRecorder(tc.writeErr) ctx := req.Context() if tc.addingCtxUser { ctx = context.WithValue(ctx, "user", tc.ctxUser) } req = req.WithContext(ctx) h := &Handler{l: log} h.Get(w, req) resp := w.Result() b, err := io.ReadAll(resp.Body) assert.NoError(t, err) var u domain.User if tc.expectedStatusCode == http.StatusOK { err = json.Unmarshal(b, &u) assert.NoError(t, err) } assert.Equal(t, tc.expectedUser, u) assert.Equal(t, tc.expectedStatusCode, resp.StatusCode) }) } } func TestHandler_GetAvatarsHistory(t *testing.T) { c := gomock.NewController(t) defer c.Finish() log := mock_logger.NewMockLog(c) serv := mock_service.NewMockServ(c) req := httptest.NewRequest("", "/chats", nil) w := httptest.NewRecorder() h := &Handler{serv: serv, l: log} h.GetAvatarsHistory(w, req) _ = w.Result() } func TestHandler_SendConfirmationCode(t *testing.T) { c := gomock.NewController(t) defer c.Finish() log := mock_logger.NewMockLog(c) serv := mock_service.NewMockServ(c) req := httptest.NewRequest("", "/chats", nil) w := httptest.NewRecorder() h := &Handler{serv: serv, l: log} h.SendConfirmationCode(w, req) _ = w.Result() } func TestHandler_ChangeUserData(t *testing.T) { c := gomock.NewController(t) defer c.Finish() log := mock_logger.NewMockLog(c) serv := mock_service.NewMockServ(c) req := httptest.NewRequest("", "/chats", nil) w := httptest.NewRecorder() h := &Handler{serv: serv, l: log} h.ChangeUserData(w, req) _ = w.Result() }