getfile_test.go (7522B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package media_test 19 20 import ( 21 "bytes" 22 "context" 23 "io" 24 "path" 25 "testing" 26 27 "github.com/stretchr/testify/suite" 28 apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" 29 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 30 "github.com/superseriousbusiness/gotosocial/internal/media" 31 "github.com/superseriousbusiness/gotosocial/testrig" 32 ) 33 34 type GetFileTestSuite struct { 35 MediaStandardTestSuite 36 } 37 38 func (suite *GetFileTestSuite) TestGetRemoteFileCached() { 39 ctx := context.Background() 40 41 testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"] 42 fileName := path.Base(testAttachment.File.Path) 43 requestingAccount := suite.testAccounts["local_account_1"] 44 45 content, errWithCode := suite.mediaProcessor.GetFile(ctx, requestingAccount, &apimodel.GetContentRequestForm{ 46 AccountID: testAttachment.AccountID, 47 MediaType: string(media.TypeAttachment), 48 MediaSize: string(media.SizeOriginal), 49 FileName: fileName, 50 }) 51 52 suite.NoError(errWithCode) 53 suite.NotNil(content) 54 b, err := io.ReadAll(content.Content) 55 suite.NoError(err) 56 57 if closer, ok := content.Content.(io.Closer); ok { 58 suite.NoError(closer.Close()) 59 } 60 61 suite.Equal(suite.testRemoteAttachments[testAttachment.RemoteURL].Data, b) 62 suite.Equal(suite.testRemoteAttachments[testAttachment.RemoteURL].ContentType, content.ContentType) 63 suite.EqualValues(len(suite.testRemoteAttachments[testAttachment.RemoteURL].Data), content.ContentLength) 64 } 65 66 func (suite *GetFileTestSuite) TestGetRemoteFileUncached() { 67 ctx := context.Background() 68 69 // uncache the file from local 70 testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"] 71 testAttachment.Cached = testrig.FalseBool() 72 err := suite.db.UpdateByID(ctx, testAttachment, testAttachment.ID, "cached") 73 suite.NoError(err) 74 err = suite.storage.Delete(ctx, testAttachment.File.Path) 75 suite.NoError(err) 76 err = suite.storage.Delete(ctx, testAttachment.Thumbnail.Path) 77 suite.NoError(err) 78 79 // now fetch it 80 fileName := path.Base(testAttachment.File.Path) 81 requestingAccount := suite.testAccounts["local_account_1"] 82 83 content, errWithCode := suite.mediaProcessor.GetFile(ctx, requestingAccount, &apimodel.GetContentRequestForm{ 84 AccountID: testAttachment.AccountID, 85 MediaType: string(media.TypeAttachment), 86 MediaSize: string(media.SizeOriginal), 87 FileName: fileName, 88 }) 89 90 suite.NoError(errWithCode) 91 suite.NotNil(content) 92 b, err := io.ReadAll(content.Content) 93 suite.NoError(err) 94 suite.NoError(content.Content.Close()) 95 96 suite.Equal(suite.testRemoteAttachments[testAttachment.RemoteURL].Data, b) 97 suite.Equal(suite.testRemoteAttachments[testAttachment.RemoteURL].ContentType, content.ContentType) 98 suite.EqualValues(len(suite.testRemoteAttachments[testAttachment.RemoteURL].Data), content.ContentLength) 99 100 // the attachment should be updated in the database 101 var dbAttachment *gtsmodel.MediaAttachment 102 if !testrig.WaitFor(func() bool { 103 dbAttachment, err = suite.db.GetAttachmentByID(ctx, testAttachment.ID) 104 return dbAttachment != nil 105 }) { 106 suite.FailNow("timed out waiting for updated attachment") 107 } 108 109 suite.NoError(err) 110 suite.True(*dbAttachment.Cached) 111 112 // the file should be back in storage at the same path as before 113 refreshedBytes, err := suite.storage.Get(ctx, testAttachment.File.Path) 114 suite.NoError(err) 115 suite.Equal(suite.testRemoteAttachments[testAttachment.RemoteURL].Data, refreshedBytes) 116 } 117 118 func (suite *GetFileTestSuite) TestGetRemoteFileUncachedInterrupted() { 119 ctx := context.Background() 120 121 // uncache the file from local 122 testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"] 123 testAttachment.Cached = testrig.FalseBool() 124 err := suite.db.UpdateByID(ctx, testAttachment, testAttachment.ID, "cached") 125 suite.NoError(err) 126 err = suite.storage.Delete(ctx, testAttachment.File.Path) 127 suite.NoError(err) 128 err = suite.storage.Delete(ctx, testAttachment.Thumbnail.Path) 129 suite.NoError(err) 130 131 // now fetch it 132 fileName := path.Base(testAttachment.File.Path) 133 requestingAccount := suite.testAccounts["local_account_1"] 134 135 content, errWithCode := suite.mediaProcessor.GetFile(ctx, requestingAccount, &apimodel.GetContentRequestForm{ 136 AccountID: testAttachment.AccountID, 137 MediaType: string(media.TypeAttachment), 138 MediaSize: string(media.SizeOriginal), 139 FileName: fileName, 140 }) 141 142 suite.NoError(errWithCode) 143 suite.NotNil(content) 144 145 // only read the first kilobyte and then stop 146 b := make([]byte, 0, 1024) 147 if !testrig.WaitFor(func() bool { 148 read, err := io.CopyN(bytes.NewBuffer(b), content.Content, 1024) 149 return err == nil && read == 1024 150 }) { 151 suite.FailNow("timed out trying to read first 1024 bytes") 152 } 153 154 // close the reader 155 suite.NoError(content.Content.Close()) 156 157 // the attachment should still be updated in the database even though the caller hung up 158 if !testrig.WaitFor(func() bool { 159 dbAttachment, _ := suite.db.GetAttachmentByID(ctx, testAttachment.ID) 160 return *dbAttachment.Cached 161 }) { 162 suite.FailNow("timed out waiting for attachment to be updated") 163 } 164 165 // the file should be back in storage at the same path as before 166 refreshedBytes, err := suite.storage.Get(ctx, testAttachment.File.Path) 167 suite.NoError(err) 168 suite.Equal(suite.testRemoteAttachments[testAttachment.RemoteURL].Data, refreshedBytes) 169 } 170 171 func (suite *GetFileTestSuite) TestGetRemoteFileThumbnailUncached() { 172 ctx := context.Background() 173 testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"] 174 175 // fetch the existing thumbnail bytes from storage first 176 thumbnailBytes, err := suite.storage.Get(ctx, testAttachment.Thumbnail.Path) 177 suite.NoError(err) 178 179 // uncache the file from local 180 testAttachment.Cached = testrig.FalseBool() 181 err = suite.db.UpdateByID(ctx, testAttachment, testAttachment.ID, "cached") 182 suite.NoError(err) 183 err = suite.storage.Delete(ctx, testAttachment.File.Path) 184 suite.NoError(err) 185 err = suite.storage.Delete(ctx, testAttachment.Thumbnail.Path) 186 suite.NoError(err) 187 188 // now fetch the thumbnail 189 fileName := path.Base(testAttachment.File.Path) 190 requestingAccount := suite.testAccounts["local_account_1"] 191 192 content, errWithCode := suite.mediaProcessor.GetFile(ctx, requestingAccount, &apimodel.GetContentRequestForm{ 193 AccountID: testAttachment.AccountID, 194 MediaType: string(media.TypeAttachment), 195 MediaSize: string(media.SizeSmall), 196 FileName: fileName, 197 }) 198 199 suite.NoError(errWithCode) 200 suite.NotNil(content) 201 b, err := io.ReadAll(content.Content) 202 suite.NoError(err) 203 suite.NoError(content.Content.Close()) 204 205 suite.Equal(thumbnailBytes, b) 206 suite.Equal("image/jpeg", content.ContentType) 207 suite.EqualValues(testAttachment.Thumbnail.FileSize, content.ContentLength) 208 } 209 210 func TestGetFileTestSuite(t *testing.T) { 211 suite.Run(t, &GetFileTestSuite{}) 212 }