servefile_test.go (8882B)
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 fileserver_test 19 20 import ( 21 "context" 22 "io/ioutil" 23 "net/http" 24 "net/http/httptest" 25 "testing" 26 27 "github.com/stretchr/testify/suite" 28 "github.com/superseriousbusiness/gotosocial/internal/api/fileserver" 29 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 30 "github.com/superseriousbusiness/gotosocial/internal/media" 31 "github.com/superseriousbusiness/gotosocial/testrig" 32 ) 33 34 type ServeFileTestSuite struct { 35 FileserverTestSuite 36 } 37 38 // GetFile is just a convenience function to save repetition in this test suite. 39 // It takes the required params to serve a file, calls the handler, and returns 40 // the http status code, the response headers, and the parsed body bytes. 41 func (suite *ServeFileTestSuite) GetFile( 42 accountID string, 43 mediaType media.Type, 44 mediaSize media.Size, 45 filename string, 46 ) (code int, headers http.Header, body []byte) { 47 recorder := httptest.NewRecorder() 48 49 ctx, _ := testrig.CreateGinTestContext(recorder, nil) 50 ctx.Request = httptest.NewRequest(http.MethodGet, "http://localhost:8080/whatever", nil) 51 ctx.Request.Header.Set("accept", "*/*") 52 ctx.AddParam(fileserver.AccountIDKey, accountID) 53 ctx.AddParam(fileserver.MediaTypeKey, string(mediaType)) 54 ctx.AddParam(fileserver.MediaSizeKey, string(mediaSize)) 55 ctx.AddParam(fileserver.FileNameKey, filename) 56 57 suite.fileServer.ServeFile(ctx) 58 code = recorder.Code 59 headers = recorder.Result().Header 60 61 var err error 62 body, err = ioutil.ReadAll(recorder.Body) 63 if err != nil { 64 suite.FailNow(err.Error()) 65 } 66 67 return 68 } 69 70 // UncacheAttachment is a convenience function that uncaches the targetAttachment by 71 // removing its associated files from storage, and updating the database. 72 func (suite *ServeFileTestSuite) UncacheAttachment(targetAttachment *gtsmodel.MediaAttachment) { 73 ctx := context.Background() 74 75 cached := false 76 targetAttachment.Cached = &cached 77 78 if err := suite.db.UpdateByID(ctx, targetAttachment, targetAttachment.ID, "cached"); err != nil { 79 suite.FailNow(err.Error()) 80 } 81 if err := suite.storage.Delete(ctx, targetAttachment.File.Path); err != nil { 82 suite.FailNow(err.Error()) 83 } 84 if err := suite.storage.Delete(ctx, targetAttachment.Thumbnail.Path); err != nil { 85 suite.FailNow(err.Error()) 86 } 87 } 88 89 func (suite *ServeFileTestSuite) TestServeOriginalLocalFileOK() { 90 targetAttachment := >smodel.MediaAttachment{} 91 *targetAttachment = *suite.testAttachments["admin_account_status_1_attachment_1"] 92 fileInStorage, err := suite.storage.Get(context.Background(), targetAttachment.File.Path) 93 if err != nil { 94 suite.FailNow(err.Error()) 95 } 96 97 code, headers, body := suite.GetFile( 98 targetAttachment.AccountID, 99 media.TypeAttachment, 100 media.SizeOriginal, 101 targetAttachment.ID+".jpg", 102 ) 103 104 suite.Equal(http.StatusOK, code) 105 suite.Equal("image/jpeg", headers.Get("content-type")) 106 suite.Equal(fileInStorage, body) 107 } 108 109 func (suite *ServeFileTestSuite) TestServeSmallLocalFileOK() { 110 targetAttachment := >smodel.MediaAttachment{} 111 *targetAttachment = *suite.testAttachments["admin_account_status_1_attachment_1"] 112 fileInStorage, err := suite.storage.Get(context.Background(), targetAttachment.Thumbnail.Path) 113 if err != nil { 114 suite.FailNow(err.Error()) 115 } 116 117 code, headers, body := suite.GetFile( 118 targetAttachment.AccountID, 119 media.TypeAttachment, 120 media.SizeSmall, 121 targetAttachment.ID+".jpg", 122 ) 123 124 suite.Equal(http.StatusOK, code) 125 suite.Equal("image/jpeg", headers.Get("content-type")) 126 suite.Equal(fileInStorage, body) 127 } 128 129 func (suite *ServeFileTestSuite) TestServeOriginalRemoteFileOK() { 130 targetAttachment := >smodel.MediaAttachment{} 131 *targetAttachment = *suite.testAttachments["remote_account_1_status_1_attachment_1"] 132 fileInStorage, err := suite.storage.Get(context.Background(), targetAttachment.File.Path) 133 if err != nil { 134 suite.FailNow(err.Error()) 135 } 136 137 code, headers, body := suite.GetFile( 138 targetAttachment.AccountID, 139 media.TypeAttachment, 140 media.SizeOriginal, 141 targetAttachment.ID+".jpg", 142 ) 143 144 suite.Equal(http.StatusOK, code) 145 suite.Equal("image/jpeg", headers.Get("content-type")) 146 suite.Equal(fileInStorage, body) 147 } 148 149 func (suite *ServeFileTestSuite) TestServeSmallRemoteFileOK() { 150 targetAttachment := >smodel.MediaAttachment{} 151 *targetAttachment = *suite.testAttachments["remote_account_1_status_1_attachment_1"] 152 fileInStorage, err := suite.storage.Get(context.Background(), targetAttachment.Thumbnail.Path) 153 if err != nil { 154 suite.FailNow(err.Error()) 155 } 156 157 code, headers, body := suite.GetFile( 158 targetAttachment.AccountID, 159 media.TypeAttachment, 160 media.SizeSmall, 161 targetAttachment.ID+".jpg", 162 ) 163 164 suite.Equal(http.StatusOK, code) 165 suite.Equal("image/jpeg", headers.Get("content-type")) 166 suite.Equal(fileInStorage, body) 167 } 168 169 func (suite *ServeFileTestSuite) TestServeOriginalRemoteFileRecache() { 170 targetAttachment := >smodel.MediaAttachment{} 171 *targetAttachment = *suite.testAttachments["remote_account_1_status_1_attachment_1"] 172 fileInStorage, err := suite.storage.Get(context.Background(), targetAttachment.File.Path) 173 if err != nil { 174 suite.FailNow(err.Error()) 175 } 176 177 // uncache the attachment so we'll have to refetch it from the 'remote' instance 178 suite.UncacheAttachment(targetAttachment) 179 180 code, headers, body := suite.GetFile( 181 targetAttachment.AccountID, 182 media.TypeAttachment, 183 media.SizeOriginal, 184 targetAttachment.ID+".jpg", 185 ) 186 187 suite.Equal(http.StatusOK, code) 188 suite.Equal("image/jpeg", headers.Get("content-type")) 189 suite.Equal(fileInStorage, body) 190 } 191 192 func (suite *ServeFileTestSuite) TestServeSmallRemoteFileRecache() { 193 targetAttachment := >smodel.MediaAttachment{} 194 *targetAttachment = *suite.testAttachments["remote_account_1_status_1_attachment_1"] 195 fileInStorage, err := suite.storage.Get(context.Background(), targetAttachment.Thumbnail.Path) 196 if err != nil { 197 suite.FailNow(err.Error()) 198 } 199 200 // uncache the attachment so we'll have to refetch it from the 'remote' instance 201 suite.UncacheAttachment(targetAttachment) 202 203 code, headers, body := suite.GetFile( 204 targetAttachment.AccountID, 205 media.TypeAttachment, 206 media.SizeSmall, 207 targetAttachment.ID+".jpg", 208 ) 209 210 suite.Equal(http.StatusOK, code) 211 suite.Equal("image/jpeg", headers.Get("content-type")) 212 suite.Equal(fileInStorage, body) 213 } 214 215 func (suite *ServeFileTestSuite) TestServeOriginalRemoteFileRecacheNotFound() { 216 targetAttachment := >smodel.MediaAttachment{} 217 *targetAttachment = *suite.testAttachments["remote_account_1_status_1_attachment_1"] 218 219 // uncache the attachment *and* set the remote URL to something that will return a 404 220 suite.UncacheAttachment(targetAttachment) 221 targetAttachment.RemoteURL = "http://nothing.at.this.url/weeeeeeeee" 222 if err := suite.db.UpdateByID(context.Background(), targetAttachment, targetAttachment.ID, "remote_url"); err != nil { 223 suite.FailNow(err.Error()) 224 } 225 226 code, _, _ := suite.GetFile( 227 targetAttachment.AccountID, 228 media.TypeAttachment, 229 media.SizeOriginal, 230 targetAttachment.ID+".jpg", 231 ) 232 233 suite.Equal(http.StatusNotFound, code) 234 } 235 236 func (suite *ServeFileTestSuite) TestServeSmallRemoteFileRecacheNotFound() { 237 targetAttachment := >smodel.MediaAttachment{} 238 *targetAttachment = *suite.testAttachments["remote_account_1_status_1_attachment_1"] 239 240 // uncache the attachment *and* set the remote URL to something that will return a 404 241 suite.UncacheAttachment(targetAttachment) 242 targetAttachment.RemoteURL = "http://nothing.at.this.url/weeeeeeeee" 243 if err := suite.db.UpdateByID(context.Background(), targetAttachment, targetAttachment.ID, "remote_url"); err != nil { 244 suite.FailNow(err.Error()) 245 } 246 247 code, _, _ := suite.GetFile( 248 targetAttachment.AccountID, 249 media.TypeAttachment, 250 media.SizeSmall, 251 targetAttachment.ID+".jpg", 252 ) 253 254 suite.Equal(http.StatusNotFound, code) 255 } 256 257 // Callers trying to get some random-ass file that doesn't exist should just get a 404 258 func (suite *ServeFileTestSuite) TestServeFileNotFound() { 259 code, _, _ := suite.GetFile( 260 "01GMMY4G9B0QEG0PQK5Q5JGJWZ", 261 media.TypeAttachment, 262 media.SizeOriginal, 263 "01GMMY68Y7E5DJ3CA3Y9SS8524.jpg", 264 ) 265 266 suite.Equal(http.StatusNotFound, code) 267 } 268 269 func TestServeFileTestSuite(t *testing.T) { 270 suite.Run(t, new(ServeFileTestSuite)) 271 }