mirror of
https://github.com/gotify/server.git
synced 2026-06-22 03:52:45 +08:00
704 lines
20 KiB
Go
704 lines
20 KiB
Go
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_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)
|
|
}
|