package api import ( "bytes" "encoding/json" "errors" "fmt" "io" "mime/multipart" "net/http/httptest" "os" "strings" "testing" "github.com/gin-gonic/gin" "github.com/gotify/server/v2/auth" "github.com/gotify/server/v2/mode" "github.com/gotify/server/v2/model" "github.com/gotify/server/v2/test" "github.com/gotify/server/v2/test/testdb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) func TestApplicationSuite(t *testing.T) { suite.Run(t, new(ApplicationSuite)) } type ApplicationSuite struct { suite.Suite db *testdb.Database a *ApplicationAPI ctx *gin.Context imageDir *test.TmpDir recorder *httptest.ResponseRecorder } func (s *ApplicationSuite) BeforeTest(suiteName, testName string) { mode.Set(mode.TestDev) s.recorder = httptest.NewRecorder() s.db = testdb.NewDB(s.T()) s.ctx, _ = gin.CreateTestContext(s.recorder) tmpDir := test.NewTmpDir("gotify_applicationsuite") s.imageDir = &tmpDir withURL(s.ctx, "http", "example.com") s.a = &ApplicationAPI{DB: s.db, ImageDir: s.imageDir.Path() + "/"} } func (s *ApplicationSuite) AfterTest(suiteName, testName string) { s.imageDir.Clean() s.db.Close() } func (s *ApplicationSuite) Test_CreateApplication_mapAllParameters() { s.db.User(5) test.WithUser(s.ctx, 5) s.withFormData("name=custom_name&description=description_text&sortKey=a5") s.a.CreateApplication(s.ctx) expected := &model.Application{ ID: 1, UserID: 5, Name: "custom_name", Description: "description_text", SortKey: "a5", CreatedAt: testdb.Now, } assert.Equal(s.T(), 200, s.recorder.Code) if app, err := s.db.GetApplicationByID(1); assert.NoError(s.T(), err) { expected.Token = app.Token assert.Equal(s.T(), expected, app) } } func (s *ApplicationSuite) Test_ensureApplicationHasCorrectJsonRepresentation() { actual := &model.Application{ ID: 1, UserID: 2, Token: "Aasdasfgeeg", Name: "myapp", Description: "mydesc", Image: "asd", Internal: true, LastUsed: nil, SortKey: "a1", CreatedAt: testdb.Now, } test.JSONEquals(s.T(), actual, `{"id":1,"token":"Aasdasfgeeg","name":"myapp","description":"mydesc", "image": "asd", "internal":true, "defaultPriority":0, "createdAt":"2020-01-01T00:00:00Z", "lastUsed":null, "sortKey":"a1"}`) } func (s *ApplicationSuite) Test_CreateApplication_expectBadRequestOnEmptyName() { s.db.User(5) test.WithUser(s.ctx, 5) s.withFormData("name=&description=description_text") s.a.CreateApplication(s.ctx) assert.Equal(s.T(), 400, s.recorder.Code) if app, err := s.db.GetApplicationsByUser(5); assert.NoError(s.T(), err) { assert.Empty(s.T(), app) } } func (s *ApplicationSuite) Test_CreateApplication_ignoresReadOnlyPropertiesInParams() { s.db.User(5) test.WithUser(s.ctx, 5) s.withJSON(&model.Application{ Name: "name", Description: "description", ID: 333, Internal: true, Token: "token", Image: "adfdf", SortKey: "a5", }) s.a.CreateApplication(s.ctx) expected := &model.Application{ ID: 1, Name: "name", Description: "description", Internal: false, Image: "static/defaultapp.png", SortKey: "a5", CreatedAt: testdb.Now, } assert.Equal(s.T(), 200, s.recorder.Code) bodyBytes, err := io.ReadAll(s.recorder.Body) assert.Nil(s.T(), err) var got model.Application assert.Nil(s.T(), json.Unmarshal(bodyBytes, &got)) expected.Token = got.Token assert.Equal(s.T(), expected, &got) tokenParsed, err := auth.ParseEnhancedToken(got.Token) assert.Nil(s.T(), err) if app, err := s.db.GetApplicationByID(1); assert.NoError(s.T(), err) { assert.Equal(s.T(), app.Token, tokenParsed.PublicForm()) } } func (s *ApplicationSuite) Test_UpdateApplicationSecurity_regenerateToken() { s.db.User(5).App(1) test.WithUser(s.ctx, 5) oldToken, err := s.db.GetApplicationByID(1) assert.NoError(s.T(), err) s.ctx.Request = httptest.NewRequest("PUT", "/application/1/security", bytes.NewBufferString(`{"regenerateToken": true}`)) s.ctx.Request.Header.Set("Content-Type", "application/json") s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.UpdateApplicationSecurity(s.ctx) assert.Equal(s.T(), 200, s.recorder.Code) bodyBytes, err := io.ReadAll(s.recorder.Body) assert.NoError(s.T(), err) var got model.SecurityUpdateActionResponse assert.NoError(s.T(), json.Unmarshal(bodyBytes, &got)) assert.Equal(s.T(), &model.SecurityUpdateActionResponse{ RegenerateToken: &model.RegenerateTokenResponse{ Token: got.RegenerateToken.Token, }, }, &got) newToken, err := s.db.GetApplicationByID(1) assert.NoError(s.T(), err) assert.NotEqual(s.T(), oldToken.Token, newToken.Token) } func (s *ApplicationSuite) Test_UpdateApplicationSecurity_isNoOpIfNilAction() { s.db.User(5).App(1) test.WithUser(s.ctx, 5) oldToken, err := s.db.GetApplicationByID(1) assert.NoError(s.T(), err) s.ctx.Request = httptest.NewRequest("PUT", "/application/1/security", bytes.NewBufferString(`{}`)) s.ctx.Request.Header.Set("Content-Type", "application/json") s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.UpdateApplicationSecurity(s.ctx) assert.Equal(s.T(), 200, s.recorder.Code) bodyBytes, err := io.ReadAll(s.recorder.Body) assert.NoError(s.T(), err) assert.Equal(s.T(), "{}", string(bodyBytes)) newToken, err := s.db.GetApplicationByID(1) assert.NoError(s.T(), err) assert.Equal(s.T(), oldToken, newToken) } func (s *ApplicationSuite) Test_UpdateApplicationSecurity_expectNotFoundOnCurrentUserIsNotOwner() { s.db.User(2) s.db.User(5).App(1) test.WithUser(s.ctx, 2) oldToken, err := s.db.GetApplicationByID(1) assert.NoError(s.T(), err) s.ctx.Request = httptest.NewRequest("PUT", "/application/1/security", bytes.NewBufferString(`{}`)) s.ctx.Request.Header.Set("Content-Type", "application/json") s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.UpdateApplicationSecurity(s.ctx) assert.Equal(s.T(), 404, s.recorder.Code) newToken, err := s.db.GetApplicationByID(1) assert.NoError(s.T(), err) assert.Equal(s.T(), oldToken, newToken) } func (s *ApplicationSuite) Test_DeleteApplication_expectNotFoundOnCurrentUserIsNotOwner() { s.db.User(2) s.db.User(5).App(5) test.WithUser(s.ctx, 2) s.ctx.Request = httptest.NewRequest("DELETE", "/token/5", nil) s.ctx.Params = gin.Params{{Key: "id", Value: "5"}} s.a.DeleteApplication(s.ctx) assert.Equal(s.T(), 404, s.recorder.Code) s.db.AssertAppExist(5) } func (s *ApplicationSuite) Test_CreateApplication_onlyRequiredParameters() { s.db.User(5) test.WithUser(s.ctx, 5) s.withFormData("name=custom_name") s.a.CreateApplication(s.ctx) expected := &model.Application{ID: 1, Name: "custom_name", SortKey: "a0", CreatedAt: testdb.Now, Image: "static/defaultapp.png"} assert.Equal(s.T(), 200, s.recorder.Code) bodyBytes, err := io.ReadAll(s.recorder.Body) assert.Nil(s.T(), err) var got model.Application assert.Nil(s.T(), json.Unmarshal(bodyBytes, &got)) expected.Token = got.Token assert.Equal(s.T(), expected, &got) tokenParsed, err := auth.ParseEnhancedToken(got.Token) assert.Nil(s.T(), err) if app, err := s.db.GetApplicationByID(1); assert.NoError(s.T(), err) { assert.Equal(s.T(), app.Token, tokenParsed.PublicForm()) } } func (s *ApplicationSuite) Test_CreateApplication_returnsApplicationWithID() { s.db.User(5) test.WithUser(s.ctx, 5) s.withFormData("name=custom_name") s.a.CreateApplication(s.ctx) expected := &model.Application{ ID: 1, Name: "custom_name", Image: "static/defaultapp.png", SortKey: "a0", CreatedAt: testdb.Now, } assert.Equal(s.T(), 200, s.recorder.Code) bodyBytes, err := io.ReadAll(s.recorder.Body) assert.Nil(s.T(), err) var got model.Application assert.Nil(s.T(), json.Unmarshal(bodyBytes, &got)) expected.Token = got.Token assert.Equal(s.T(), expected, &got) tokenParsed, err := auth.ParseEnhancedToken(got.Token) assert.Nil(s.T(), err) if app, err := s.db.GetApplicationByID(1); assert.NoError(s.T(), err) { assert.Equal(s.T(), app.Token, tokenParsed.PublicForm()) } } func (s *ApplicationSuite) Test_CreateApplication_withExistingToken() { s.db.User(5) s.db.User(6).App(1) test.WithUser(s.ctx, 5) s.withFormData("name=custom_name") s.a.CreateApplication(s.ctx) expected := &model.Application{ID: 2, Name: "custom_name", UserID: 5, SortKey: "a0", CreatedAt: testdb.Now} assert.Equal(s.T(), 200, s.recorder.Code) if app, err := s.db.GetApplicationByID(2); assert.NoError(s.T(), err) { expected.Token = app.Token assert.Equal(s.T(), expected, app) } } func (s *ApplicationSuite) Test_Sorting() { s.db.User(5) test.WithUser(s.ctx, 5) s.withFormData("name=one") s.a.CreateApplication(s.ctx) test.WithUser(s.ctx, 5) s.withFormData("name=two") s.a.CreateApplication(s.ctx) test.WithUser(s.ctx, 5) s.withFormData("name=three") s.a.CreateApplication(s.ctx) apps, err := s.db.GetApplicationsByUser(5) require.NoError(s.T(), err) require.Len(s.T(), apps, 3) assert.Equal(s.T(), apps[0].Name, "one") assert.Equal(s.T(), apps[0].SortKey, "a0") assert.Equal(s.T(), apps[1].Name, "two") assert.Equal(s.T(), apps[1].SortKey, "a1") assert.Equal(s.T(), apps[2].Name, "three") assert.Equal(s.T(), apps[2].SortKey, "a2") s.withFormData("name=one&description=&sortKey=a1V") s.ctx.Params = gin.Params{{Key: "id", Value: fmt.Sprint(apps[0].ID)}} s.a.UpdateApplication(s.ctx) apps, err = s.db.GetApplicationsByUser(5) require.NoError(s.T(), err) require.Len(s.T(), apps, 3) assert.Equal(s.T(), apps[0].Name, "two") assert.Equal(s.T(), apps[0].SortKey, "a1") assert.Equal(s.T(), apps[1].Name, "one") assert.Equal(s.T(), apps[1].SortKey, "a1V") assert.Equal(s.T(), apps[2].Name, "three") assert.Equal(s.T(), apps[2].SortKey, "a2") } func (s *ApplicationSuite) Test_GetApplications() { userBuilder := s.db.User(5) first := userBuilder.NewAppWithToken(1, "perfper") second := userBuilder.NewAppWithToken(2, "asdasd") test.WithUser(s.ctx, 5) s.ctx.Request = httptest.NewRequest("GET", "/tokens", nil) s.a.GetApplications(s.ctx) assert.Equal(s.T(), 200, s.recorder.Code) first.Image = "static/defaultapp.png" second.Image = "static/defaultapp.png" first.Token = "" second.Token = "" test.BodyEquals(s.T(), []*model.Application{first, second}, s.recorder) } func (s *ApplicationSuite) Test_GetApplications_WithImage() { userBuilder := s.db.User(5) first := userBuilder.NewAppWithToken(1, "perfper") second := userBuilder.NewAppWithToken(2, "asdasd") first.Image = "abcd.jpg" s.db.UpdateApplication(first) test.WithUser(s.ctx, 5) s.ctx.Request = httptest.NewRequest("GET", "/tokens", nil) s.a.GetApplications(s.ctx) assert.Equal(s.T(), 200, s.recorder.Code) first.Image = "image/abcd.jpg" second.Image = "static/defaultapp.png" first.Token = "" second.Token = "" test.BodyEquals(s.T(), []*model.Application{first, second}, s.recorder) } func (s *ApplicationSuite) Test_DeleteApplication_internal_expectBadRequest() { s.db.User(5).InternalApp(10) test.WithUser(s.ctx, 5) s.ctx.Request = httptest.NewRequest("DELETE", "/token/", nil) s.ctx.Params = gin.Params{{Key: "id", Value: "10"}} s.a.DeleteApplication(s.ctx) assert.Equal(s.T(), 400, s.recorder.Code) } func (s *ApplicationSuite) Test_DeleteApplication_expectNotFound() { s.db.User(5) test.WithUser(s.ctx, 5) s.ctx.Request = httptest.NewRequest("DELETE", "/token/", nil) s.ctx.Params = gin.Params{{Key: "id", Value: "4"}} s.a.DeleteApplication(s.ctx) assert.Equal(s.T(), 404, s.recorder.Code) } func (s *ApplicationSuite) Test_DeleteApplication() { s.db.User(5).App(1) test.WithUser(s.ctx, 5) s.ctx.Request = httptest.NewRequest("DELETE", "/token/", nil) s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.DeleteApplication(s.ctx) assert.Equal(s.T(), 200, s.recorder.Code) s.db.AssertAppNotExist(1) } func (s *ApplicationSuite) Test_UploadAppImage_NoImageProvided_expectBadRequest() { s.db.User(5).App(1) var b bytes.Buffer writer := multipart.NewWriter(&b) writer.Close() s.ctx.Request = httptest.NewRequest("POST", "/irrelevant", &b) s.ctx.Request.Header.Set("Content-Type", writer.FormDataContentType()) test.WithUser(s.ctx, 5) s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.UploadApplicationImage(s.ctx) assert.Equal(s.T(), 400, s.recorder.Code) assert.Equal(s.T(), s.ctx.Errors[0].Err, errors.New("file with key 'file' must be present")) } func (s *ApplicationSuite) Test_UploadAppImage_OtherErrors_expectServerError() { s.db.User(5).App(1) var b bytes.Buffer writer := multipart.NewWriter(&b) defer writer.Close() s.ctx.Request = httptest.NewRequest("POST", "/irrelevant", &b) s.ctx.Request.Header.Set("Content-Type", writer.FormDataContentType()) test.WithUser(s.ctx, 5) s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.UploadApplicationImage(s.ctx) assert.Equal(s.T(), 500, s.recorder.Code) assert.Error(s.T(), s.ctx.Errors[0].Err, "multipart: NextPart: EOF") } func (s *ApplicationSuite) Test_UploadAppImage_WithImageFile_expectSuccess() { s.db.User(5).App(1) cType, buffer, err := upload(map[string]*os.File{"file": mustOpen("../test/assets/image.png")}) assert.Nil(s.T(), err) s.ctx.Request = httptest.NewRequest("POST", "/irrelevant", &buffer) s.ctx.Request.Header.Set("Content-Type", cType) test.WithUser(s.ctx, 5) s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.UploadApplicationImage(s.ctx) if app, err := s.db.GetApplicationByID(1); assert.NoError(s.T(), err) { imgName := app.Image assert.Equal(s.T(), 200, s.recorder.Code) _, err = os.Stat(s.imageDir.Path(imgName)) assert.Nil(s.T(), err) s.a.DeleteApplication(s.ctx) _, err = os.Stat(imgName) assert.True(s.T(), os.IsNotExist(err)) } } func (s *ApplicationSuite) Test_UploadAppImage_WithImageFile_DeleteExstingImage() { existingImageName := "existing.png" s.db.User(5) s.db.CreateApplication(&model.Application{UserID: 5, ID: 1, Image: existingImageName}) fakeImage(s.T(), s.imageDir.Path(existingImageName)) cType, buffer, err := upload(map[string]*os.File{"file": mustOpen("../test/assets/image.png")}) assert.Nil(s.T(), err) s.ctx.Request = httptest.NewRequest("POST", "/irrelevant", &buffer) s.ctx.Request.Header.Set("Content-Type", cType) test.WithUser(s.ctx, 5) s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.UploadApplicationImage(s.ctx) assert.Equal(s.T(), 200, s.recorder.Code) listing, err := os.ReadDir(s.imageDir.Path()) assert.Nil(s.T(), err) assert.Len(s.T(), listing, 1) } func (s *ApplicationSuite) Test_UploadAppImage_WithTextFile_expectBadRequest() { s.db.User(5).App(1) cType, buffer, err := upload(map[string]*os.File{"file": mustOpen("../test/assets/text.txt")}) assert.Nil(s.T(), err) s.ctx.Request = httptest.NewRequest("POST", "/irrelevant", &buffer) s.ctx.Request.Header.Set("Content-Type", cType) test.WithUser(s.ctx, 5) s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.UploadApplicationImage(s.ctx) assert.Equal(s.T(), 400, s.recorder.Code) assert.Equal(s.T(), s.ctx.Errors[0].Err, errors.New("file must be an image")) } func (s *ApplicationSuite) Test_UploadAppImage_WithHtmlFileHavingImageHeader() { s.db.User(5).App(1) cType, buffer, err := upload(map[string]*os.File{"file": mustOpen("../test/assets/image-header-with.html")}) assert.Nil(s.T(), err) s.ctx.Request = httptest.NewRequest("POST", "/irrelevant", &buffer) s.ctx.Request.Header.Set("Content-Type", cType) test.WithUser(s.ctx, 5) s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.UploadApplicationImage(s.ctx) assert.Equal(s.T(), 400, s.recorder.Code) assert.Equal(s.T(), s.ctx.Errors[0].Err, errors.New("invalid file extension")) } func (s *ApplicationSuite) Test_UploadAppImage_expectNotFound() { s.db.User(5) test.WithUser(s.ctx, 5) s.ctx.Request = httptest.NewRequest("POST", "/irrelevant", nil) s.ctx.Params = gin.Params{{Key: "id", Value: "4"}} s.a.UploadApplicationImage(s.ctx) assert.Equal(s.T(), 404, s.recorder.Code) } func (s *ApplicationSuite) Test_RemoveAppImage_expectNotFound() { s.db.User(5) test.WithUser(s.ctx, 5) s.ctx.Request = httptest.NewRequest("DELETE", "/irrelevant", nil) s.ctx.Params = gin.Params{{Key: "id", Value: "4"}} s.a.RemoveApplicationImage(s.ctx) assert.Equal(s.T(), 404, s.recorder.Code) } func (s *ApplicationSuite) Test_RemoveAppImage_noCustomizedImage() { s.db.User(5).App(1) test.WithUser(s.ctx, 5) s.ctx.Request = httptest.NewRequest("DELETE", "/irrelevant", nil) s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.RemoveApplicationImage(s.ctx) assert.Equal(s.T(), 400, s.recorder.Code) } func (s *ApplicationSuite) Test_RemoveAppImage_expectSuccess() { s.db.User(5) imageFile := "existing.png" s.db.CreateApplication(&model.Application{UserID: 5, ID: 1, Image: imageFile}) fakeImage(s.T(), s.imageDir.Path(imageFile)) test.WithUser(s.ctx, 5) s.ctx.Request = httptest.NewRequest("DELETE", "/irrelevant", nil) s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} s.a.RemoveApplicationImage(s.ctx) _, err := os.Stat(s.imageDir.Path(imageFile)) assert.True(s.T(), os.IsNotExist(err)) assert.Equal(s.T(), 200, s.recorder.Code) } func (s *ApplicationSuite) Test_UpdateApplicationNameAndDescription_expectSuccess() { s.db.User(5).NewAppWithToken(2, "app-2") test.WithUser(s.ctx, 5) s.withFormData("name=new_name&description=new_description_text") s.ctx.Params = gin.Params{{Key: "id", Value: "2"}} s.a.UpdateApplication(s.ctx) expected := &model.Application{ ID: 2, Token: "app-2", UserID: 5, Name: "new_name", Description: "new_description_text", SortKey: "a0", CreatedAt: testdb.Now, } assert.Equal(s.T(), 200, s.recorder.Code) if app, err := s.db.GetApplicationByID(2); assert.NoError(s.T(), err) { assert.Equal(s.T(), expected, app) } } func (s *ApplicationSuite) Test_UpdateApplicationName_expectSuccess() { s.db.User(5).NewAppWithToken(2, "app-2") test.WithUser(s.ctx, 5) s.withFormData("name=new_name") s.ctx.Params = gin.Params{{Key: "id", Value: "2"}} s.a.UpdateApplication(s.ctx) expected := &model.Application{ ID: 2, Token: "app-2", UserID: 5, Name: "new_name", Description: "", SortKey: "a0", CreatedAt: testdb.Now, } assert.Equal(s.T(), 200, s.recorder.Code) if app, err := s.db.GetApplicationByID(2); assert.NoError(s.T(), err) { assert.Equal(s.T(), expected, app) } } func (s *ApplicationSuite) Test_UpdateApplicationDefaultPriority_expectSuccess() { s.db.User(5).NewAppWithToken(2, "app-2") test.WithUser(s.ctx, 5) s.withFormData("name=name&description=&defaultPriority=4") s.ctx.Params = gin.Params{{Key: "id", Value: "2"}} s.a.UpdateApplication(s.ctx) expected := &model.Application{ ID: 2, Token: "app-2", UserID: 5, Name: "name", Description: "", DefaultPriority: 4, SortKey: "a0", CreatedAt: testdb.Now, } assert.Equal(s.T(), 200, s.recorder.Code) if app, err := s.db.GetApplicationByID(2); assert.NoError(s.T(), err) { assert.Equal(s.T(), expected, app) } } func (s *ApplicationSuite) Test_UpdateApplication_preservesImageAndSortKey() { app := s.db.User(5).NewAppWithToken(2, "app-2") app.Image = "existing.png" app.SortKey = "a5" assert.Nil(s.T(), s.db.UpdateApplication(app)) test.WithUser(s.ctx, 5) s.withFormData("name=new_name") s.ctx.Params = gin.Params{{Key: "id", Value: "2"}} s.a.UpdateApplication(s.ctx) assert.Equal(s.T(), 200, s.recorder.Code) if app, err := s.db.GetApplicationByID(2); assert.NoError(s.T(), err) { assert.Equal(s.T(), "existing.png", app.Image) assert.Equal(s.T(), "a5", app.SortKey) } } func (s *ApplicationSuite) Test_UpdateApplication_setEmptyDescription() { app := s.db.User(5).NewAppWithToken(2, "app-2") app.Description = "my desc" assert.Nil(s.T(), s.db.UpdateApplication(app)) test.WithUser(s.ctx, 5) s.withFormData("name=new_name&desc=") s.ctx.Params = gin.Params{{Key: "id", Value: "2"}} s.a.UpdateApplication(s.ctx) assert.Equal(s.T(), 200, s.recorder.Code) if app, err := s.db.GetApplicationByID(2); assert.NoError(s.T(), err) { assert.Equal(s.T(), "", app.Description) } } func (s *ApplicationSuite) Test_UpdateApplication_expectNotFound() { test.WithUser(s.ctx, 5) s.ctx.Params = gin.Params{{Key: "id", Value: "2"}} s.a.UpdateApplication(s.ctx) assert.Equal(s.T(), 404, s.recorder.Code) } func (s *ApplicationSuite) Test_UpdateApplication_WithMissingAttributes_expectBadRequest() { test.WithUser(s.ctx, 5) s.a.UpdateApplication(s.ctx) assert.Equal(s.T(), 400, s.recorder.Code) } func (s *ApplicationSuite) Test_UpdateApplication_WithoutPermission_expectNotFound() { s.db.User(5).NewAppWithToken(2, "app-2") test.WithUser(s.ctx, 4) s.ctx.Params = gin.Params{{Key: "id", Value: "2"}} s.a.UpdateApplication(s.ctx) assert.Equal(s.T(), 404, s.recorder.Code) } func (s *ApplicationSuite) Test_UpdateApplication_duplicateSortKey() { user := s.db.User(5) user.App(1) // sortKey=a0 user.App(2) // sortKey=a1 s.withFormData("name=new_name&sortKey=a0") test.WithUser(s.ctx, 5) s.ctx.Params = gin.Params{{Key: "id", Value: "2"}} s.a.UpdateApplication(s.ctx) assert.EqualError(s.T(), s.ctx.Errors[0].Err, "sort key is not unique") assert.Equal(s.T(), 400, s.recorder.Code) } func (s *ApplicationSuite) withFormData(formData string) { s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader(formData)) s.ctx.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded") } func (s *ApplicationSuite) withJSON(value any) { jsonVal, _ := json.Marshal(value) s.ctx.Request = httptest.NewRequest("POST", "/application", bytes.NewBuffer(jsonVal)) s.ctx.Request.Header.Set("Content-Type", "application/json") } // A modified version of https://stackoverflow.com/a/20397167/4244993 from Attila O. func upload(values map[string]*os.File) (contentType string, buffer bytes.Buffer, err error) { w := multipart.NewWriter(&buffer) for key, r := range values { var fw io.Writer if fw, err = w.CreateFormFile(key, r.Name()); err != nil { return contentType, buffer, err } if _, err = io.Copy(fw, r); err != nil { return contentType, buffer, err } } contentType = w.FormDataContentType() w.Close() return contentType, buffer, err } func mustOpen(f string) *os.File { r, err := os.Open(f) if err != nil { panic(err) } return r } func fakeImage(t *testing.T, path string) { data, err := os.ReadFile("../test/assets/image.png") assert.Nil(t, err) // Write data to dst err = os.WriteFile(path, data, 0o644) assert.Nil(t, err) }