test(avatar): deleting a user avatar is idempotent

If the avatar file in storage does not exist, it is not an error and
the database can be updated.

See 1be797faba Fix bug on avatar
This commit is contained in:
Earl Warren 2024-06-05 09:10:42 +02:00
parent 8b5642949a
commit d2c4d833f4
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
4 changed files with 62 additions and 29 deletions

View file

@ -10,30 +10,30 @@ import (
"os" "os"
) )
var UninitializedStorage = discardStorage("uninitialized storage") var UninitializedStorage = DiscardStorage("uninitialized storage")
type discardStorage string type DiscardStorage string
func (s discardStorage) Open(_ string) (Object, error) { func (s DiscardStorage) Open(_ string) (Object, error) {
return nil, fmt.Errorf("%s", s) return nil, fmt.Errorf("%s", s)
} }
func (s discardStorage) Save(_ string, _ io.Reader, _ int64) (int64, error) { func (s DiscardStorage) Save(_ string, _ io.Reader, _ int64) (int64, error) {
return 0, fmt.Errorf("%s", s) return 0, fmt.Errorf("%s", s)
} }
func (s discardStorage) Stat(_ string) (os.FileInfo, error) { func (s DiscardStorage) Stat(_ string) (os.FileInfo, error) {
return nil, fmt.Errorf("%s", s) return nil, fmt.Errorf("%s", s)
} }
func (s discardStorage) Delete(_ string) error { func (s DiscardStorage) Delete(_ string) error {
return fmt.Errorf("%s", s) return fmt.Errorf("%s", s)
} }
func (s discardStorage) URL(_, _ string) (*url.URL, error) { func (s DiscardStorage) URL(_, _ string) (*url.URL, error) {
return nil, fmt.Errorf("%s", s) return nil, fmt.Errorf("%s", s)
} }
func (s discardStorage) IterateObjects(_ string, _ func(string, Object) error) error { func (s DiscardStorage) IterateObjects(_ string, _ func(string, Object) error) error {
return fmt.Errorf("%s", s) return fmt.Errorf("%s", s)
} }

View file

@ -11,9 +11,9 @@ import (
) )
func Test_discardStorage(t *testing.T) { func Test_discardStorage(t *testing.T) {
tests := []discardStorage{ tests := []DiscardStorage{
UninitializedStorage, UninitializedStorage,
discardStorage("empty"), DiscardStorage("empty"),
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(string(tt), func(t *testing.T) { t.Run(string(tt), func(t *testing.T) {

View file

@ -170,7 +170,7 @@ func initAvatars() (err error) {
func initAttachments() (err error) { func initAttachments() (err error) {
if !setting.Attachment.Enabled { if !setting.Attachment.Enabled {
Attachments = discardStorage("Attachment isn't enabled") Attachments = DiscardStorage("Attachment isn't enabled")
return nil return nil
} }
log.Info("Initialising Attachment storage with type: %s", setting.Attachment.Storage.Type) log.Info("Initialising Attachment storage with type: %s", setting.Attachment.Storage.Type)
@ -180,7 +180,7 @@ func initAttachments() (err error) {
func initLFS() (err error) { func initLFS() (err error) {
if !setting.LFS.StartServer { if !setting.LFS.StartServer {
LFS = discardStorage("LFS isn't enabled") LFS = DiscardStorage("LFS isn't enabled")
return nil return nil
} }
log.Info("Initialising LFS storage with type: %s", setting.LFS.Storage.Type) log.Info("Initialising LFS storage with type: %s", setting.LFS.Storage.Type)
@ -202,7 +202,7 @@ func initRepoArchives() (err error) {
func initPackages() (err error) { func initPackages() (err error) {
if !setting.Packages.Enabled { if !setting.Packages.Enabled {
Packages = discardStorage("Packages isn't enabled") Packages = DiscardStorage("Packages isn't enabled")
return nil return nil
} }
log.Info("Initialising Packages storage with type: %s", setting.Packages.Storage.Type) log.Info("Initialising Packages storage with type: %s", setting.Packages.Storage.Type)
@ -212,8 +212,8 @@ func initPackages() (err error) {
func initActions() (err error) { func initActions() (err error) {
if !setting.Actions.Enabled { if !setting.Actions.Enabled {
Actions = discardStorage("Actions isn't enabled") Actions = DiscardStorage("Actions isn't enabled")
ActionsArtifacts = discardStorage("ActionsArtifacts isn't enabled") ActionsArtifacts = DiscardStorage("ActionsArtifacts isn't enabled")
return nil return nil
} }
log.Info("Initialising Actions storage with type: %s", setting.Actions.LogStorage.Type) log.Info("Initialising Actions storage with type: %s", setting.Actions.LogStorage.Type)

View file

@ -7,6 +7,7 @@ import (
"bytes" "bytes"
"image" "image"
"image/png" "image/png"
"os"
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@ -18,30 +19,62 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
type alreadyDeletedStorage struct {
storage.DiscardStorage
}
func (s alreadyDeletedStorage) Delete(_ string) error {
return os.ErrNotExist
}
func TestUserDeleteAvatar(t *testing.T) { func TestUserDeleteAvatar(t *testing.T) {
myImage := image.NewRGBA(image.Rect(0, 0, 1, 1)) myImage := image.NewRGBA(image.Rect(0, 0, 1, 1))
var buff bytes.Buffer var buff bytes.Buffer
png.Encode(&buff, myImage) png.Encode(&buff, myImage)
assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
err := UploadAvatar(db.DefaultContext, user, buff.Bytes())
assert.NoError(t, err)
verification := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.NotEqual(t, "", verification.Avatar)
t.Run("AtomicStorageFailure", func(t *testing.T) { t.Run("AtomicStorageFailure", func(t *testing.T) {
defer test.MockVariableValue[storage.ObjectStorage](&storage.Avatars, storage.UninitializedStorage)() defer test.MockProtect[storage.ObjectStorage](&storage.Avatars)()
assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
err := UploadAvatar(db.DefaultContext, user, buff.Bytes())
assert.NoError(t, err)
verification := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.NotEqual(t, "", verification.Avatar)
// fail to delete ...
storage.Avatars = storage.UninitializedStorage
err = DeleteAvatar(db.DefaultContext, user) err = DeleteAvatar(db.DefaultContext, user)
assert.Error(t, err) assert.Error(t, err)
verification := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
// ... the avatar is not removed from the database
verification = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.True(t, verification.UseCustomAvatar) assert.True(t, verification.UseCustomAvatar)
// already deleted ...
storage.Avatars = alreadyDeletedStorage{}
err = DeleteAvatar(db.DefaultContext, user)
assert.NoError(t, err)
// ... the avatar is removed from the database
verification = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.Equal(t, "", verification.Avatar)
}) })
err = DeleteAvatar(db.DefaultContext, user) t.Run("Success", func(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
verification = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) err := UploadAvatar(db.DefaultContext, user, buff.Bytes())
assert.Equal(t, "", verification.Avatar) assert.NoError(t, err)
verification := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.NotEqual(t, "", verification.Avatar)
err = DeleteAvatar(db.DefaultContext, user)
assert.NoError(t, err)
verification = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.Equal(t, "", verification.Avatar)
})
} }