package merch import ( "bytes" "encoding/json" "fmt" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "io" "merch-api/pkg/utils" "net/http" "net/http/httptest" "testing" ) const userId int64 = 789 const validUuid string = "d28f5a94-ccf9-4eb6-bac3-53b065ba0d0c" const invalidUuid string = "invalid-uuid" func setupTestRouter(mockRepo Repository) *gin.Engine { ut := utils.New() tService := newService(mockRepo, ut) tController := newController(tService, ut) handler := &Handler{ controller: tController, } gin.SetMode(gin.TestMode) router := gin.New() router.Use( func(c *gin.Context) { c.Set("userId", userId) c.Next() }) handler.RegisterRoutes(router.Group(""), nil) return router } // Labels func TestCreateLabelSuccess(t *testing.T) { mockRepo := new(MockRepository) mockRepo. On("createLabel", mock.Anything, userId, mock.Anything). Return(nil) router := setupTestRouter(mockRepo) label := LabelDTO{ Name: "Success", Color: "1", BgColor: "2", } payload, err := json.Marshal(label) if err != nil { t.Fatal(err) } body := bytes.NewBuffer(payload) req, _ := http.NewRequest("POST", "/merch/labels", body) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) mockRepo.AssertExpectations(t) } func TestGetLabelsSuccess(t *testing.T) { expectedList := []LabelsList{ {LabelUuid: "123", Name: "First", Color: "1", BgColor: "2"}, {LabelUuid: "456", Name: "Second", Color: "3", BgColor: "4"}, } mockRepo := new(MockRepository) mockRepo. On("getLabels", mock.Anything, userId). Return(expectedList, nil) router := setupTestRouter(mockRepo) req, _ := http.NewRequest("GET", "/merch/labels", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) var response []LabelsList err := json.Unmarshal(w.Body.Bytes(), &response) if err != nil { t.Fatal(err) } assert.Equal(t, expectedList, response) mockRepo.AssertExpectations(t) } func TestUpdateLabelSuccess(t *testing.T) { mockRepo := new(MockRepository) mockRepo. On("updateLabel", mock.Anything, userId, mock.MatchedBy(func(arg any) bool { label, ok := arg.(*Label) if !ok { return false } return label.Uuid == "u123" && label.Name == "Test Label1" }), ). Return(nil) router := setupTestRouter(mockRepo) l := &LabelDTO{ Name: "Test Label1", Color: "1", BgColor: "2", } payload, err := json.Marshal(l) if err != nil { t.Fatal(err) } body := bytes.NewBuffer(payload) req, _ := http.NewRequest("PUT", "/merch/labels/u123", body) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) } func TestDeleteLabelSuccess(t *testing.T) { mockRepo := new(MockRepository) expectedUuid := mock.MatchedBy(func(arg any) bool { u, ok := arg.(string) if !ok { return false } return u == "u123" }) mockRepo. On("deleteLabel", mock.Anything, userId, expectedUuid, mock.Anything). Return(nil) router := setupTestRouter(mockRepo) req, _ := http.NewRequest("DELETE", "/merch/labels/u123", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) } func TestAttachLabelSuccess(t *testing.T) { mockRepo := new(MockRepository) expected := mock.MatchedBy(func(arg any) bool { val, ok := arg.(*LabelLink) if !ok { return false } return val.LabelUuid == "l123" }) mockRepo. On("attachLabel", mock.Anything, userId, expected, mock.Anything). Return(nil) router := setupTestRouter(mockRepo) l := LabelLink{ MerchUuid: "m123", LabelUuid: "l123", } payload, err := json.Marshal(l) if err != nil { t.Fatal(err) } body := bytes.NewBuffer(payload) req, _ := http.NewRequest("POST", "/merch/labels/attach", body) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) mockRepo.AssertExpectations(t) } func TestDetachLabelSuccess(t *testing.T) { mockRepo := new(MockRepository) expected := mock.MatchedBy(func(arg any) bool { val, ok := arg.(*LabelLink) if !ok { return false } return val.LabelUuid == "l123" }) mockRepo. On("detachLabel", mock.Anything, userId, expected, mock.Anything). Return(nil) router := setupTestRouter(mockRepo) l := LabelLink{ MerchUuid: "m123", LabelUuid: "l123", } payload, err := json.Marshal(l) if err != nil { t.Fatal(err) } body := bytes.NewBuffer(payload) req, _ := http.NewRequest("POST", "/merch/labels/detach", body) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) mockRepo.AssertExpectations(t) } func TestGetMerchLabelsSuccess(t *testing.T) { mockRepo := new(MockRepository) expected := []string{"l123", "l456", "l789"} mockRepo. On("getAttachedLabelsByUuid", mock.Anything, userId, "m123"). Return(expected, nil) router := setupTestRouter(mockRepo) req, _ := http.NewRequest("GET", "/merch/labels/m123", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) var response []string err := json.Unmarshal(w.Body.Bytes(), &response) if err != nil { t.Fatal(err) } assert.Equal(t, expected, response) mockRepo.AssertExpectations(t) } // Merch func TestCreateMerchSuccess(t *testing.T) { type testCases struct { name string inputDTO newMerchDTO expectOrigins bool expectedCode int } cases := []testCases{ { name: "Success - with origins", inputDTO: newMerchDTO{ Name: "Test name1", Links: []originLink{ {Origin: "origin 1", Link: "link1"}, {Origin: "origin 2", Link: "link2"}, {Origin: "origin 3", Link: "link3"}, {Origin: "origin 4", Link: "link4"}, {Origin: "origin 5", Link: "link5"}, {Origin: "origin 6", Link: "link6"}, {Origin: "origin 7", Link: "link7"}, }, }, expectOrigins: true, expectedCode: 201, }, { name: "Success - without origins", inputDTO: newMerchDTO{ Name: "Test name2", }, expectOrigins: false, expectedCode: 201, }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { mockRepo := new(MockRepository) if tt.expectOrigins { mockRepo. On("getOrigins", mock.Anything). Return([]Origin{ {Id: 1, Name: "origin 1"}, {Id: 2, Name: "origin 2"}, {Id: 3, Name: "origin 3"}, {Id: 4, Name: "origin 4"}, {Id: 5, Name: "origin 5"}, {Id: 6, Name: "origin 6"}, {Id: 7, Name: "origin 7"}, }, nil) } merchExpected := mock.MatchedBy(func(arg any) bool { model, ok := arg.(*Merch) if !ok { return false } if model.CreatedAt.IsZero() { return false } if model.UpdatedAt.Valid == false { return false } if model.UpdatedAt.Time.IsZero() { return false } if model.UpdatedAt.Time.Before(model.CreatedAt) { return false } if model.DeletedAt.Valid != false { return false } if model.MerchUuid == "" || len(model.MerchUuid) != 36 { return false } if model.UserId != userId { return false } return true }) extraExpected := mock.MatchedBy(func(arg any) bool { _, ok := arg.([]ExtraData) if !ok { return false } return true }) mockRepo. On("createMerch", mock.Anything, merchExpected, extraExpected). Return(nil) router := setupTestRouter(mockRepo) payload, err := json.Marshal(&tt.inputDTO) if err != nil { t.Fatal(err) } body := bytes.NewBuffer(payload) req, _ := http.NewRequest("POST", "/merch/create", body) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, tt.expectedCode, w.Code) mockRepo.AssertExpectations(t) }) } } func TestGetManySuccess(t *testing.T) { type testCase struct { name string getManyReturn []getMerchInternal expectLabels bool labelsReceive []int64 labelsReturn map[int64][]string expectedCode int expectedResult []listResponse } cases := []testCase{ { name: "Success with merch", getManyReturn: []getMerchInternal{ {Id: 1, MerchUuid: validUuid, Name: "merch1"}, {Id: 2, MerchUuid: validUuid, Name: "merch2"}, {Id: 3, MerchUuid: validUuid, Name: "merch3"}, }, expectLabels: true, labelsReceive: []int64{1, 2, 3}, labelsReturn: map[int64][]string{1: {"l1", "l2", "l3"}, 2: {"l4", "l5", "l6"}, 3: {"l7", "l8", "l9"}}, expectedCode: 200, expectedResult: []listResponse{ {MerchUuid: validUuid, Name: "merch1", Labels: []string{"l1", "l2", "l3"}}, {MerchUuid: validUuid, Name: "merch2", Labels: []string{"l4", "l5", "l6"}}, {MerchUuid: validUuid, Name: "merch3", Labels: []string{"l7", "l8", "l9"}}, }, }, { name: "Success no merch - empty merch", getManyReturn: []getMerchInternal{}, expectLabels: false, expectedCode: 204, }, { name: "Success no merch - nil merch", getManyReturn: nil, expectLabels: false, expectedCode: 204, }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { mockRepo := new(MockRepository) mockRepo. On("getMany", mock.Anything, userId). Return(tt.getManyReturn, nil) if tt.expectLabels { mockRepo. On("getManyAttachedLabelsByList", mock.Anything, userId, tt.labelsReceive). Return(tt.labelsReturn, nil) } router := setupTestRouter(mockRepo) req, _ := http.NewRequest("GET", "/merch/list", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, tt.expectedCode, w.Code) if tt.expectedCode == 200 { var response []listResponse err := json.Unmarshal(w.Body.Bytes(), &response) if err != nil { t.Fatal(err) } assert.Equal(t, tt.expectedResult, response) } mockRepo.AssertExpectations(t) }) } } func TestUpdateMerch(t *testing.T) { type testCase struct { name string pathUuid string payload *updateMerchDTO updateResponse *merchDTO expectCode int } cases := []testCase{ { name: "Success", pathUuid: validUuid, payload: &updateMerchDTO{MerchUuid: validUuid, Name: "merch1"}, updateResponse: &merchDTO{MerchUuid: validUuid, Name: "merch1"}, expectCode: 200, }, { name: "Bad request - empty payload", pathUuid: validUuid, expectCode: 400, }, { name: "Bad request - no path uuid", payload: &updateMerchDTO{MerchUuid: validUuid, Name: "merch1"}, pathUuid: invalidUuid, expectCode: 400, }, { name: "Bad request - no payload and no path uuid", payload: nil, pathUuid: invalidUuid, expectCode: 400, }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { mockRepo := new(MockRepository) var input io.Reader = nil if tt.expectCode == 200 { mockRepo. On("updateMerch", mock.Anything, userId, tt.payload). Return(tt.updateResponse, nil) payload, err := json.Marshal(tt.payload) if err != nil { t.Fatal(err) } input = bytes.NewReader(payload) } router := setupTestRouter(mockRepo) url := fmt.Sprintf("/merch/%s", tt.pathUuid) req, _ := http.NewRequest("PUT", url, input) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, tt.expectCode, w.Code) if tt.expectCode == 200 { var response merchDTO err := json.Unmarshal(w.Body.Bytes(), &response) if err != nil { t.Fatal(err) } assert.Equal(t, tt.updateResponse, &response) } mockRepo.AssertExpectations(t) }) } }