gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

manager_test.go (45727B)


      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 	"fmt"
     24 	"io"
     25 	"os"
     26 	"path"
     27 	"testing"
     28 	"time"
     29 
     30 	"codeberg.org/gruf/go-store/v2/storage"
     31 	"github.com/stretchr/testify/suite"
     32 	gtsmodel "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
     33 	"github.com/superseriousbusiness/gotosocial/internal/media"
     34 	"github.com/superseriousbusiness/gotosocial/internal/state"
     35 	gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
     36 	"github.com/superseriousbusiness/gotosocial/testrig"
     37 )
     38 
     39 type ManagerTestSuite struct {
     40 	MediaStandardTestSuite
     41 }
     42 
     43 func (suite *ManagerTestSuite) TestEmojiProcessBlocking() {
     44 	ctx := context.Background()
     45 
     46 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
     47 		// load bytes from a test image
     48 		b, err := os.ReadFile("./test/rainbow-original.png")
     49 		if err != nil {
     50 			panic(err)
     51 		}
     52 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
     53 	}
     54 
     55 	emojiID := "01GDQ9G782X42BAMFASKP64343"
     56 	emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
     57 
     58 	processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, "rainbow_test", emojiID, emojiURI, nil, false)
     59 	suite.NoError(err)
     60 
     61 	// do a blocking call to fetch the emoji
     62 	emoji, err := processingEmoji.LoadEmoji(ctx)
     63 	suite.NoError(err)
     64 	suite.NotNil(emoji)
     65 
     66 	// make sure it's got the stuff set on it that we expect
     67 	suite.Equal(emojiID, emoji.ID)
     68 
     69 	// file meta should be correctly derived from the image
     70 	suite.Equal("image/png", emoji.ImageContentType)
     71 	suite.Equal("image/png", emoji.ImageStaticContentType)
     72 	suite.Equal(36702, emoji.ImageFileSize)
     73 
     74 	// now make sure the emoji is in the database
     75 	dbEmoji, err := suite.db.GetEmojiByID(ctx, emojiID)
     76 	suite.NoError(err)
     77 	suite.NotNil(dbEmoji)
     78 
     79 	// make sure the processed emoji file is in storage
     80 	processedFullBytes, err := suite.storage.Get(ctx, emoji.ImagePath)
     81 	suite.NoError(err)
     82 	suite.NotEmpty(processedFullBytes)
     83 
     84 	// load the processed bytes from our test folder, to compare
     85 	processedFullBytesExpected, err := os.ReadFile("./test/rainbow-original.png")
     86 	suite.NoError(err)
     87 	suite.NotEmpty(processedFullBytesExpected)
     88 
     89 	// the bytes in storage should be what we expected
     90 	suite.Equal(processedFullBytesExpected, processedFullBytes)
     91 
     92 	// now do the same for the thumbnail and make sure it's what we expected
     93 	processedStaticBytes, err := suite.storage.Get(ctx, emoji.ImageStaticPath)
     94 	suite.NoError(err)
     95 	suite.NotEmpty(processedStaticBytes)
     96 
     97 	processedStaticBytesExpected, err := os.ReadFile("./test/rainbow-static.png")
     98 	suite.NoError(err)
     99 	suite.NotEmpty(processedStaticBytesExpected)
    100 
    101 	suite.Equal(processedStaticBytesExpected, processedStaticBytes)
    102 }
    103 
    104 func (suite *ManagerTestSuite) TestEmojiProcessBlockingRefresh() {
    105 	ctx := context.Background()
    106 
    107 	// we're going to 'refresh' the remote 'yell' emoji by changing the image url to the pixellated gts logo
    108 	originalEmoji := suite.testEmojis["yell"]
    109 
    110 	emojiToUpdate := &gtsmodel.Emoji{}
    111 	*emojiToUpdate = *originalEmoji
    112 	newImageRemoteURL := "http://fossbros-anonymous.io/some/image/path.png"
    113 
    114 	oldEmojiImagePath := emojiToUpdate.ImagePath
    115 	oldEmojiImageStaticPath := emojiToUpdate.ImageStaticPath
    116 
    117 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    118 		b, err := os.ReadFile("./test/gts_pixellated-original.png")
    119 		if err != nil {
    120 			panic(err)
    121 		}
    122 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
    123 	}
    124 
    125 	emojiID := emojiToUpdate.ID
    126 	emojiURI := emojiToUpdate.URI
    127 
    128 	processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, "yell", emojiID, emojiURI, &media.AdditionalEmojiInfo{
    129 		CreatedAt:      &emojiToUpdate.CreatedAt,
    130 		Domain:         &emojiToUpdate.Domain,
    131 		ImageRemoteURL: &newImageRemoteURL,
    132 	}, true)
    133 	suite.NoError(err)
    134 
    135 	// do a blocking call to fetch the emoji
    136 	emoji, err := processingEmoji.LoadEmoji(ctx)
    137 	suite.NoError(err)
    138 	suite.NotNil(emoji)
    139 
    140 	// make sure it's got the stuff set on it that we expect
    141 	suite.Equal(emojiID, emoji.ID)
    142 
    143 	// file meta should be correctly derived from the image
    144 	suite.Equal("image/png", emoji.ImageContentType)
    145 	suite.Equal("image/png", emoji.ImageStaticContentType)
    146 	suite.Equal(10296, emoji.ImageFileSize)
    147 
    148 	// now make sure the emoji is in the database
    149 	dbEmoji, err := suite.db.GetEmojiByID(ctx, emojiID)
    150 	suite.NoError(err)
    151 	suite.NotNil(dbEmoji)
    152 
    153 	// make sure the processed emoji file is in storage
    154 	processedFullBytes, err := suite.storage.Get(ctx, emoji.ImagePath)
    155 	suite.NoError(err)
    156 	suite.NotEmpty(processedFullBytes)
    157 
    158 	// load the processed bytes from our test folder, to compare
    159 	processedFullBytesExpected, err := os.ReadFile("./test/gts_pixellated-original.png")
    160 	suite.NoError(err)
    161 	suite.NotEmpty(processedFullBytesExpected)
    162 
    163 	// the bytes in storage should be what we expected
    164 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    165 
    166 	// now do the same for the thumbnail and make sure it's what we expected
    167 	processedStaticBytes, err := suite.storage.Get(ctx, emoji.ImageStaticPath)
    168 	suite.NoError(err)
    169 	suite.NotEmpty(processedStaticBytes)
    170 
    171 	processedStaticBytesExpected, err := os.ReadFile("./test/gts_pixellated-static.png")
    172 	suite.NoError(err)
    173 	suite.NotEmpty(processedStaticBytesExpected)
    174 
    175 	suite.Equal(processedStaticBytesExpected, processedStaticBytes)
    176 
    177 	// most fields should be different on the emoji now from what they were before
    178 	suite.Equal(originalEmoji.ID, dbEmoji.ID)
    179 	suite.NotEqual(originalEmoji.ImageRemoteURL, dbEmoji.ImageRemoteURL)
    180 	suite.NotEqual(originalEmoji.ImageURL, dbEmoji.ImageURL)
    181 	suite.NotEqual(originalEmoji.ImageStaticURL, dbEmoji.ImageStaticURL)
    182 	suite.NotEqual(originalEmoji.ImageFileSize, dbEmoji.ImageFileSize)
    183 	suite.NotEqual(originalEmoji.ImageStaticFileSize, dbEmoji.ImageStaticFileSize)
    184 	suite.NotEqual(originalEmoji.ImagePath, dbEmoji.ImagePath)
    185 	suite.NotEqual(originalEmoji.ImageStaticPath, dbEmoji.ImageStaticPath)
    186 	suite.NotEqual(originalEmoji.ImageStaticPath, dbEmoji.ImageStaticPath)
    187 	suite.NotEqual(originalEmoji.UpdatedAt, dbEmoji.UpdatedAt)
    188 	suite.NotEqual(originalEmoji.ImageUpdatedAt, dbEmoji.ImageUpdatedAt)
    189 
    190 	// the old image files should no longer be in storage
    191 	_, err = suite.storage.Get(ctx, oldEmojiImagePath)
    192 	suite.ErrorIs(err, storage.ErrNotFound)
    193 	_, err = suite.storage.Get(ctx, oldEmojiImageStaticPath)
    194 	suite.ErrorIs(err, storage.ErrNotFound)
    195 }
    196 
    197 func (suite *ManagerTestSuite) TestEmojiProcessBlockingTooLarge() {
    198 	ctx := context.Background()
    199 
    200 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    201 		// load bytes from a test image
    202 		b, err := os.ReadFile("./test/big-panda.gif")
    203 		if err != nil {
    204 			panic(err)
    205 		}
    206 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
    207 	}
    208 
    209 	emojiID := "01GDQ9G782X42BAMFASKP64343"
    210 	emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
    211 
    212 	processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, "big_panda", emojiID, emojiURI, nil, false)
    213 	suite.NoError(err)
    214 
    215 	// do a blocking call to fetch the emoji
    216 	emoji, err := processingEmoji.LoadEmoji(ctx)
    217 	suite.EqualError(err, "given emoji size 630kiB greater than max allowed 50.0kiB")
    218 	suite.Nil(emoji)
    219 }
    220 
    221 func (suite *ManagerTestSuite) TestEmojiProcessBlockingTooLargeNoSizeGiven() {
    222 	ctx := context.Background()
    223 
    224 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    225 		// load bytes from a test image
    226 		b, err := os.ReadFile("./test/big-panda.gif")
    227 		if err != nil {
    228 			panic(err)
    229 		}
    230 		return io.NopCloser(bytes.NewBuffer(b)), -1, nil
    231 	}
    232 
    233 	emojiID := "01GDQ9G782X42BAMFASKP64343"
    234 	emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
    235 
    236 	processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, "big_panda", emojiID, emojiURI, nil, false)
    237 	suite.NoError(err)
    238 
    239 	// do a blocking call to fetch the emoji
    240 	emoji, err := processingEmoji.LoadEmoji(ctx)
    241 	suite.EqualError(err, "calculated emoji size 630kiB greater than max allowed 50.0kiB")
    242 	suite.Nil(emoji)
    243 }
    244 
    245 func (suite *ManagerTestSuite) TestEmojiProcessBlockingNoFileSizeGiven() {
    246 	ctx := context.Background()
    247 
    248 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    249 		// load bytes from a test image
    250 		b, err := os.ReadFile("./test/rainbow-original.png")
    251 		if err != nil {
    252 			panic(err)
    253 		}
    254 		return io.NopCloser(bytes.NewBuffer(b)), -1, nil
    255 	}
    256 
    257 	emojiID := "01GDQ9G782X42BAMFASKP64343"
    258 	emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
    259 
    260 	// process the media with no additional info provided
    261 	processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, "rainbow_test", emojiID, emojiURI, nil, false)
    262 	suite.NoError(err)
    263 
    264 	// do a blocking call to fetch the emoji
    265 	emoji, err := processingEmoji.LoadEmoji(ctx)
    266 	suite.NoError(err)
    267 	suite.NotNil(emoji)
    268 
    269 	// make sure it's got the stuff set on it that we expect
    270 	suite.Equal(emojiID, emoji.ID)
    271 
    272 	// file meta should be correctly derived from the image
    273 	suite.Equal("image/png", emoji.ImageContentType)
    274 	suite.Equal("image/png", emoji.ImageStaticContentType)
    275 	suite.Equal(36702, emoji.ImageFileSize)
    276 
    277 	// now make sure the emoji is in the database
    278 	dbEmoji, err := suite.db.GetEmojiByID(ctx, emojiID)
    279 	suite.NoError(err)
    280 	suite.NotNil(dbEmoji)
    281 
    282 	// make sure the processed emoji file is in storage
    283 	processedFullBytes, err := suite.storage.Get(ctx, emoji.ImagePath)
    284 	suite.NoError(err)
    285 	suite.NotEmpty(processedFullBytes)
    286 
    287 	// load the processed bytes from our test folder, to compare
    288 	processedFullBytesExpected, err := os.ReadFile("./test/rainbow-original.png")
    289 	suite.NoError(err)
    290 	suite.NotEmpty(processedFullBytesExpected)
    291 
    292 	// the bytes in storage should be what we expected
    293 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    294 
    295 	// now do the same for the thumbnail and make sure it's what we expected
    296 	processedStaticBytes, err := suite.storage.Get(ctx, emoji.ImageStaticPath)
    297 	suite.NoError(err)
    298 	suite.NotEmpty(processedStaticBytes)
    299 
    300 	processedStaticBytesExpected, err := os.ReadFile("./test/rainbow-static.png")
    301 	suite.NoError(err)
    302 	suite.NotEmpty(processedStaticBytesExpected)
    303 
    304 	suite.Equal(processedStaticBytesExpected, processedStaticBytes)
    305 }
    306 
    307 func (suite *ManagerTestSuite) TestSimpleJpegProcessBlocking() {
    308 	ctx := context.Background()
    309 
    310 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    311 		// load bytes from a test image
    312 		b, err := os.ReadFile("./test/test-jpeg.jpg")
    313 		if err != nil {
    314 			panic(err)
    315 		}
    316 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
    317 	}
    318 
    319 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
    320 
    321 	// process the media with no additional info provided
    322 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
    323 	suite.NoError(err)
    324 	// fetch the attachment id from the processing media
    325 	attachmentID := processingMedia.AttachmentID()
    326 
    327 	// do a blocking call to fetch the attachment
    328 	attachment, err := processingMedia.LoadAttachment(ctx)
    329 	suite.NoError(err)
    330 	suite.NotNil(attachment)
    331 
    332 	// make sure it's got the stuff set on it that we expect
    333 	// the attachment ID and accountID we expect
    334 	suite.Equal(attachmentID, attachment.ID)
    335 	suite.Equal(accountID, attachment.AccountID)
    336 
    337 	// file meta should be correctly derived from the image
    338 	suite.EqualValues(gtsmodel.Original{
    339 		Width: 1920, Height: 1080, Size: 2073600, Aspect: 1.7777777777777777,
    340 	}, attachment.FileMeta.Original)
    341 	suite.EqualValues(gtsmodel.Small{
    342 		Width: 512, Height: 288, Size: 147456, Aspect: 1.7777777777777777,
    343 	}, attachment.FileMeta.Small)
    344 	suite.Equal("image/jpeg", attachment.File.ContentType)
    345 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
    346 	suite.Equal(269739, attachment.File.FileSize)
    347 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)
    348 
    349 	// now make sure the attachment is in the database
    350 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
    351 	suite.NoError(err)
    352 	suite.NotNil(dbAttachment)
    353 
    354 	// make sure the processed file is in storage
    355 	processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
    356 	suite.NoError(err)
    357 	suite.NotEmpty(processedFullBytes)
    358 
    359 	// load the processed bytes from our test folder, to compare
    360 	processedFullBytesExpected, err := os.ReadFile("./test/test-jpeg-processed.jpg")
    361 	suite.NoError(err)
    362 	suite.NotEmpty(processedFullBytesExpected)
    363 
    364 	// the bytes in storage should be what we expected
    365 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    366 
    367 	// now do the same for the thumbnail and make sure it's what we expected
    368 	processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
    369 	suite.NoError(err)
    370 	suite.NotEmpty(processedThumbnailBytes)
    371 
    372 	processedThumbnailBytesExpected, err := os.ReadFile("./test/test-jpeg-thumbnail.jpg")
    373 	suite.NoError(err)
    374 	suite.NotEmpty(processedThumbnailBytesExpected)
    375 
    376 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
    377 }
    378 
    379 func (suite *ManagerTestSuite) TestSlothVineProcessBlocking() {
    380 	ctx := context.Background()
    381 
    382 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    383 		// load bytes from a test video
    384 		b, err := os.ReadFile("./test/test-mp4-original.mp4")
    385 		if err != nil {
    386 			panic(err)
    387 		}
    388 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
    389 	}
    390 
    391 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
    392 
    393 	// process the media with no additional info provided
    394 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
    395 	suite.NoError(err)
    396 	// fetch the attachment id from the processing media
    397 	attachmentID := processingMedia.AttachmentID()
    398 
    399 	// do a blocking call to fetch the attachment
    400 	attachment, err := processingMedia.LoadAttachment(ctx)
    401 	suite.NoError(err)
    402 	suite.NotNil(attachment)
    403 
    404 	// make sure it's got the stuff set on it that we expect
    405 	// the attachment ID and accountID we expect
    406 	suite.Equal(attachmentID, attachment.ID)
    407 	suite.Equal(accountID, attachment.AccountID)
    408 
    409 	// file meta should be correctly derived from the video
    410 	suite.Equal(338, attachment.FileMeta.Original.Width)
    411 	suite.Equal(240, attachment.FileMeta.Original.Height)
    412 	suite.Equal(81120, attachment.FileMeta.Original.Size)
    413 	suite.EqualValues(1.4083333, attachment.FileMeta.Original.Aspect)
    414 	suite.EqualValues(6.640907, *attachment.FileMeta.Original.Duration)
    415 	suite.EqualValues(29.000029, *attachment.FileMeta.Original.Framerate)
    416 	suite.EqualValues(0x59e74, *attachment.FileMeta.Original.Bitrate)
    417 	suite.EqualValues(gtsmodel.Small{
    418 		Width: 338, Height: 240, Size: 81120, Aspect: 1.4083333333333334,
    419 	}, attachment.FileMeta.Small)
    420 	suite.Equal("video/mp4", attachment.File.ContentType)
    421 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
    422 	suite.Equal(312413, attachment.File.FileSize)
    423 	suite.Equal("L00000fQfQfQfQfQfQfQfQfQfQfQ", attachment.Blurhash)
    424 
    425 	// now make sure the attachment is in the database
    426 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
    427 	suite.NoError(err)
    428 	suite.NotNil(dbAttachment)
    429 
    430 	// make sure the processed file is in storage
    431 	processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
    432 	suite.NoError(err)
    433 	suite.NotEmpty(processedFullBytes)
    434 
    435 	// load the processed bytes from our test folder, to compare
    436 	processedFullBytesExpected, err := os.ReadFile("./test/test-mp4-processed.mp4")
    437 	suite.NoError(err)
    438 	suite.NotEmpty(processedFullBytesExpected)
    439 
    440 	// the bytes in storage should be what we expected
    441 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    442 
    443 	// now do the same for the thumbnail and make sure it's what we expected
    444 	processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
    445 	suite.NoError(err)
    446 	suite.NotEmpty(processedThumbnailBytes)
    447 
    448 	processedThumbnailBytesExpected, err := os.ReadFile("./test/test-mp4-thumbnail.jpg")
    449 	suite.NoError(err)
    450 	suite.NotEmpty(processedThumbnailBytesExpected)
    451 
    452 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
    453 }
    454 
    455 func (suite *ManagerTestSuite) TestLongerMp4ProcessBlocking() {
    456 	ctx := context.Background()
    457 
    458 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    459 		// load bytes from a test video
    460 		b, err := os.ReadFile("./test/longer-mp4-original.mp4")
    461 		if err != nil {
    462 			panic(err)
    463 		}
    464 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
    465 	}
    466 
    467 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
    468 
    469 	// process the media with no additional info provided
    470 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
    471 	suite.NoError(err)
    472 	// fetch the attachment id from the processing media
    473 	attachmentID := processingMedia.AttachmentID()
    474 
    475 	// do a blocking call to fetch the attachment
    476 	attachment, err := processingMedia.LoadAttachment(ctx)
    477 	suite.NoError(err)
    478 	suite.NotNil(attachment)
    479 
    480 	// make sure it's got the stuff set on it that we expect
    481 	// the attachment ID and accountID we expect
    482 	suite.Equal(attachmentID, attachment.ID)
    483 	suite.Equal(accountID, attachment.AccountID)
    484 
    485 	// file meta should be correctly derived from the video
    486 	suite.Equal(600, attachment.FileMeta.Original.Width)
    487 	suite.Equal(330, attachment.FileMeta.Original.Height)
    488 	suite.Equal(198000, attachment.FileMeta.Original.Size)
    489 	suite.EqualValues(1.8181819, attachment.FileMeta.Original.Aspect)
    490 	suite.EqualValues(16.6, *attachment.FileMeta.Original.Duration)
    491 	suite.EqualValues(10, *attachment.FileMeta.Original.Framerate)
    492 	suite.EqualValues(0xc8fb, *attachment.FileMeta.Original.Bitrate)
    493 	suite.EqualValues(gtsmodel.Small{
    494 		Width: 512, Height: 281, Size: 143872, Aspect: 1.822064,
    495 	}, attachment.FileMeta.Small)
    496 	suite.Equal("video/mp4", attachment.File.ContentType)
    497 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
    498 	suite.Equal(109549, attachment.File.FileSize)
    499 	suite.Equal("L00000fQfQfQfQfQfQfQfQfQfQfQ", attachment.Blurhash)
    500 
    501 	// now make sure the attachment is in the database
    502 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
    503 	suite.NoError(err)
    504 	suite.NotNil(dbAttachment)
    505 
    506 	// make sure the processed file is in storage
    507 	processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
    508 	suite.NoError(err)
    509 	suite.NotEmpty(processedFullBytes)
    510 
    511 	// load the processed bytes from our test folder, to compare
    512 	processedFullBytesExpected, err := os.ReadFile("./test/longer-mp4-processed.mp4")
    513 	suite.NoError(err)
    514 	suite.NotEmpty(processedFullBytesExpected)
    515 
    516 	// the bytes in storage should be what we expected
    517 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    518 
    519 	// now do the same for the thumbnail and make sure it's what we expected
    520 	processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
    521 	suite.NoError(err)
    522 	suite.NotEmpty(processedThumbnailBytes)
    523 
    524 	processedThumbnailBytesExpected, err := os.ReadFile("./test/longer-mp4-thumbnail.jpg")
    525 	suite.NoError(err)
    526 	suite.NotEmpty(processedThumbnailBytesExpected)
    527 
    528 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
    529 }
    530 
    531 func (suite *ManagerTestSuite) TestBirdnestMp4ProcessBlocking() {
    532 	ctx := context.Background()
    533 
    534 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    535 		// load bytes from a test video
    536 		b, err := os.ReadFile("./test/birdnest-original.mp4")
    537 		if err != nil {
    538 			panic(err)
    539 		}
    540 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
    541 	}
    542 
    543 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
    544 
    545 	// process the media with no additional info provided
    546 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
    547 	suite.NoError(err)
    548 	// fetch the attachment id from the processing media
    549 	attachmentID := processingMedia.AttachmentID()
    550 
    551 	// do a blocking call to fetch the attachment
    552 	attachment, err := processingMedia.LoadAttachment(ctx)
    553 	suite.NoError(err)
    554 	suite.NotNil(attachment)
    555 
    556 	// make sure it's got the stuff set on it that we expect
    557 	// the attachment ID and accountID we expect
    558 	suite.Equal(attachmentID, attachment.ID)
    559 	suite.Equal(accountID, attachment.AccountID)
    560 
    561 	// file meta should be correctly derived from the video
    562 	suite.Equal(404, attachment.FileMeta.Original.Width)
    563 	suite.Equal(720, attachment.FileMeta.Original.Height)
    564 	suite.Equal(290880, attachment.FileMeta.Original.Size)
    565 	suite.EqualValues(0.5611111, attachment.FileMeta.Original.Aspect)
    566 	suite.EqualValues(9.822041, *attachment.FileMeta.Original.Duration)
    567 	suite.EqualValues(30, *attachment.FileMeta.Original.Framerate)
    568 	suite.EqualValues(0x117c79, *attachment.FileMeta.Original.Bitrate)
    569 	suite.EqualValues(gtsmodel.Small{
    570 		Width: 287, Height: 512, Size: 146944, Aspect: 0.5605469,
    571 	}, attachment.FileMeta.Small)
    572 	suite.Equal("video/mp4", attachment.File.ContentType)
    573 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
    574 	suite.Equal(1409577, attachment.File.FileSize)
    575 	suite.Equal("L00000fQfQfQfQfQfQfQfQfQfQfQ", attachment.Blurhash)
    576 
    577 	// now make sure the attachment is in the database
    578 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
    579 	suite.NoError(err)
    580 	suite.NotNil(dbAttachment)
    581 
    582 	// make sure the processed file is in storage
    583 	processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
    584 	suite.NoError(err)
    585 	suite.NotEmpty(processedFullBytes)
    586 
    587 	// load the processed bytes from our test folder, to compare
    588 	processedFullBytesExpected, err := os.ReadFile("./test/birdnest-processed.mp4")
    589 	suite.NoError(err)
    590 	suite.NotEmpty(processedFullBytesExpected)
    591 
    592 	// the bytes in storage should be what we expected
    593 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    594 
    595 	// now do the same for the thumbnail and make sure it's what we expected
    596 	processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
    597 	suite.NoError(err)
    598 	suite.NotEmpty(processedThumbnailBytes)
    599 
    600 	processedThumbnailBytesExpected, err := os.ReadFile("./test/birdnest-thumbnail.jpg")
    601 	suite.NoError(err)
    602 	suite.NotEmpty(processedThumbnailBytesExpected)
    603 
    604 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
    605 }
    606 
    607 func (suite *ManagerTestSuite) TestNotAnMp4ProcessBlocking() {
    608 	// try to load an 'mp4' that's actually an mkv in disguise
    609 
    610 	ctx := context.Background()
    611 
    612 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    613 		// load bytes from a test video
    614 		b, err := os.ReadFile("./test/not-an.mp4")
    615 		if err != nil {
    616 			panic(err)
    617 		}
    618 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
    619 	}
    620 
    621 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
    622 
    623 	// pre processing should go fine but...
    624 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
    625 	suite.NoError(err)
    626 
    627 	// we should get an error while loading
    628 	attachment, err := processingMedia.LoadAttachment(ctx)
    629 	suite.EqualError(err, "error decoding video: error determining video metadata: [width height framerate]")
    630 	suite.Nil(attachment)
    631 }
    632 
    633 func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingNoContentLengthGiven() {
    634 	ctx := context.Background()
    635 
    636 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    637 		// load bytes from a test image
    638 		b, err := os.ReadFile("./test/test-jpeg.jpg")
    639 		if err != nil {
    640 			panic(err)
    641 		}
    642 		// give length as -1 to indicate unknown
    643 		return io.NopCloser(bytes.NewBuffer(b)), -1, nil
    644 	}
    645 
    646 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
    647 
    648 	// process the media with no additional info provided
    649 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
    650 	suite.NoError(err)
    651 	// fetch the attachment id from the processing media
    652 	attachmentID := processingMedia.AttachmentID()
    653 
    654 	// do a blocking call to fetch the attachment
    655 	attachment, err := processingMedia.LoadAttachment(ctx)
    656 	suite.NoError(err)
    657 	suite.NotNil(attachment)
    658 
    659 	// make sure it's got the stuff set on it that we expect
    660 	// the attachment ID and accountID we expect
    661 	suite.Equal(attachmentID, attachment.ID)
    662 	suite.Equal(accountID, attachment.AccountID)
    663 
    664 	// file meta should be correctly derived from the image
    665 	suite.EqualValues(gtsmodel.Original{
    666 		Width: 1920, Height: 1080, Size: 2073600, Aspect: 1.7777777777777777,
    667 	}, attachment.FileMeta.Original)
    668 	suite.EqualValues(gtsmodel.Small{
    669 		Width: 512, Height: 288, Size: 147456, Aspect: 1.7777777777777777,
    670 	}, attachment.FileMeta.Small)
    671 	suite.Equal("image/jpeg", attachment.File.ContentType)
    672 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
    673 	suite.Equal(269739, attachment.File.FileSize)
    674 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)
    675 
    676 	// now make sure the attachment is in the database
    677 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
    678 	suite.NoError(err)
    679 	suite.NotNil(dbAttachment)
    680 
    681 	// make sure the processed file is in storage
    682 	processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
    683 	suite.NoError(err)
    684 	suite.NotEmpty(processedFullBytes)
    685 
    686 	// load the processed bytes from our test folder, to compare
    687 	processedFullBytesExpected, err := os.ReadFile("./test/test-jpeg-processed.jpg")
    688 	suite.NoError(err)
    689 	suite.NotEmpty(processedFullBytesExpected)
    690 
    691 	// the bytes in storage should be what we expected
    692 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    693 
    694 	// now do the same for the thumbnail and make sure it's what we expected
    695 	processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
    696 	suite.NoError(err)
    697 	suite.NotEmpty(processedThumbnailBytes)
    698 
    699 	processedThumbnailBytesExpected, err := os.ReadFile("./test/test-jpeg-thumbnail.jpg")
    700 	suite.NoError(err)
    701 	suite.NotEmpty(processedThumbnailBytesExpected)
    702 
    703 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
    704 }
    705 
    706 func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingReadCloser() {
    707 	ctx := context.Background()
    708 
    709 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    710 		// open test image as a file
    711 		f, err := os.Open("./test/test-jpeg.jpg")
    712 		if err != nil {
    713 			panic(err)
    714 		}
    715 		// give length as -1 to indicate unknown
    716 		return f, -1, nil
    717 	}
    718 
    719 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
    720 
    721 	// process the media with no additional info provided
    722 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
    723 	suite.NoError(err)
    724 	// fetch the attachment id from the processing media
    725 	attachmentID := processingMedia.AttachmentID()
    726 
    727 	// do a blocking call to fetch the attachment
    728 	attachment, err := processingMedia.LoadAttachment(ctx)
    729 	suite.NoError(err)
    730 	suite.NotNil(attachment)
    731 
    732 	// make sure it's got the stuff set on it that we expect
    733 	// the attachment ID and accountID we expect
    734 	suite.Equal(attachmentID, attachment.ID)
    735 	suite.Equal(accountID, attachment.AccountID)
    736 
    737 	// file meta should be correctly derived from the image
    738 	suite.EqualValues(gtsmodel.Original{
    739 		Width: 1920, Height: 1080, Size: 2073600, Aspect: 1.7777777777777777,
    740 	}, attachment.FileMeta.Original)
    741 	suite.EqualValues(gtsmodel.Small{
    742 		Width: 512, Height: 288, Size: 147456, Aspect: 1.7777777777777777,
    743 	}, attachment.FileMeta.Small)
    744 	suite.Equal("image/jpeg", attachment.File.ContentType)
    745 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
    746 	suite.Equal(269739, attachment.File.FileSize)
    747 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)
    748 
    749 	// now make sure the attachment is in the database
    750 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
    751 	suite.NoError(err)
    752 	suite.NotNil(dbAttachment)
    753 
    754 	// make sure the processed file is in storage
    755 	processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
    756 	suite.NoError(err)
    757 	suite.NotEmpty(processedFullBytes)
    758 
    759 	// load the processed bytes from our test folder, to compare
    760 	processedFullBytesExpected, err := os.ReadFile("./test/test-jpeg-processed.jpg")
    761 	suite.NoError(err)
    762 	suite.NotEmpty(processedFullBytesExpected)
    763 
    764 	// the bytes in storage should be what we expected
    765 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    766 
    767 	// now do the same for the thumbnail and make sure it's what we expected
    768 	processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
    769 	suite.NoError(err)
    770 	suite.NotEmpty(processedThumbnailBytes)
    771 
    772 	processedThumbnailBytesExpected, err := os.ReadFile("./test/test-jpeg-thumbnail.jpg")
    773 	suite.NoError(err)
    774 	suite.NotEmpty(processedThumbnailBytesExpected)
    775 
    776 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
    777 }
    778 
    779 func (suite *ManagerTestSuite) TestPngNoAlphaChannelProcessBlocking() {
    780 	ctx := context.Background()
    781 
    782 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    783 		// load bytes from a test image
    784 		b, err := os.ReadFile("./test/test-png-noalphachannel.png")
    785 		if err != nil {
    786 			panic(err)
    787 		}
    788 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
    789 	}
    790 
    791 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
    792 
    793 	// process the media with no additional info provided
    794 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
    795 	suite.NoError(err)
    796 	// fetch the attachment id from the processing media
    797 	attachmentID := processingMedia.AttachmentID()
    798 
    799 	// do a blocking call to fetch the attachment
    800 	attachment, err := processingMedia.LoadAttachment(ctx)
    801 	suite.NoError(err)
    802 	suite.NotNil(attachment)
    803 
    804 	// make sure it's got the stuff set on it that we expect
    805 	// the attachment ID and accountID we expect
    806 	suite.Equal(attachmentID, attachment.ID)
    807 	suite.Equal(accountID, attachment.AccountID)
    808 
    809 	// file meta should be correctly derived from the image
    810 	suite.EqualValues(gtsmodel.Original{
    811 		Width: 186, Height: 187, Size: 34782, Aspect: 0.9946524064171123,
    812 	}, attachment.FileMeta.Original)
    813 	suite.EqualValues(gtsmodel.Small{
    814 		Width: 186, Height: 187, Size: 34782, Aspect: 0.9946524064171123,
    815 	}, attachment.FileMeta.Small)
    816 	suite.Equal("image/png", attachment.File.ContentType)
    817 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
    818 	suite.Equal(17471, attachment.File.FileSize)
    819 	suite.Equal("LFQT7e.A%O%4?co$M}M{_1W9~TxV", attachment.Blurhash)
    820 
    821 	// now make sure the attachment is in the database
    822 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
    823 	suite.NoError(err)
    824 	suite.NotNil(dbAttachment)
    825 
    826 	// make sure the processed file is in storage
    827 	processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
    828 	suite.NoError(err)
    829 	suite.NotEmpty(processedFullBytes)
    830 
    831 	// load the processed bytes from our test folder, to compare
    832 	processedFullBytesExpected, err := os.ReadFile("./test/test-png-noalphachannel-processed.png")
    833 	suite.NoError(err)
    834 	suite.NotEmpty(processedFullBytesExpected)
    835 
    836 	// the bytes in storage should be what we expected
    837 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    838 
    839 	// now do the same for the thumbnail and make sure it's what we expected
    840 	processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
    841 	suite.NoError(err)
    842 	suite.NotEmpty(processedThumbnailBytes)
    843 
    844 	processedThumbnailBytesExpected, err := os.ReadFile("./test/test-png-noalphachannel-thumbnail.jpg")
    845 	suite.NoError(err)
    846 	suite.NotEmpty(processedThumbnailBytesExpected)
    847 
    848 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
    849 }
    850 
    851 func (suite *ManagerTestSuite) TestPngAlphaChannelProcessBlocking() {
    852 	ctx := context.Background()
    853 
    854 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    855 		// load bytes from a test image
    856 		b, err := os.ReadFile("./test/test-png-alphachannel.png")
    857 		if err != nil {
    858 			panic(err)
    859 		}
    860 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
    861 	}
    862 
    863 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
    864 
    865 	// process the media with no additional info provided
    866 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
    867 	suite.NoError(err)
    868 	// fetch the attachment id from the processing media
    869 	attachmentID := processingMedia.AttachmentID()
    870 
    871 	// do a blocking call to fetch the attachment
    872 	attachment, err := processingMedia.LoadAttachment(ctx)
    873 	suite.NoError(err)
    874 	suite.NotNil(attachment)
    875 
    876 	// make sure it's got the stuff set on it that we expect
    877 	// the attachment ID and accountID we expect
    878 	suite.Equal(attachmentID, attachment.ID)
    879 	suite.Equal(accountID, attachment.AccountID)
    880 
    881 	// file meta should be correctly derived from the image
    882 	suite.EqualValues(gtsmodel.Original{
    883 		Width: 186, Height: 187, Size: 34782, Aspect: 0.9946524064171123,
    884 	}, attachment.FileMeta.Original)
    885 	suite.EqualValues(gtsmodel.Small{
    886 		Width: 186, Height: 187, Size: 34782, Aspect: 0.9946524064171123,
    887 	}, attachment.FileMeta.Small)
    888 	suite.Equal("image/png", attachment.File.ContentType)
    889 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
    890 	suite.Equal(18904, attachment.File.FileSize)
    891 	suite.Equal("LFQT7e.A%O%4?co$M}M{_1W9~TxV", attachment.Blurhash)
    892 
    893 	// now make sure the attachment is in the database
    894 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
    895 	suite.NoError(err)
    896 	suite.NotNil(dbAttachment)
    897 
    898 	// make sure the processed file is in storage
    899 	processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
    900 	suite.NoError(err)
    901 	suite.NotEmpty(processedFullBytes)
    902 
    903 	// load the processed bytes from our test folder, to compare
    904 	processedFullBytesExpected, err := os.ReadFile("./test/test-png-alphachannel-processed.png")
    905 	suite.NoError(err)
    906 	suite.NotEmpty(processedFullBytesExpected)
    907 
    908 	// the bytes in storage should be what we expected
    909 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    910 
    911 	// now do the same for the thumbnail and make sure it's what we expected
    912 	processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
    913 	suite.NoError(err)
    914 	suite.NotEmpty(processedThumbnailBytes)
    915 
    916 	processedThumbnailBytesExpected, err := os.ReadFile("./test/test-png-alphachannel-thumbnail.jpg")
    917 	suite.NoError(err)
    918 	suite.NotEmpty(processedThumbnailBytesExpected)
    919 
    920 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
    921 }
    922 
    923 func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithCallback() {
    924 	ctx := context.Background()
    925 
    926 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
    927 		// load bytes from a test image
    928 		b, err := os.ReadFile("./test/test-jpeg.jpg")
    929 		if err != nil {
    930 			panic(err)
    931 		}
    932 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
    933 	}
    934 
    935 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
    936 
    937 	// process the media with no additional info provided
    938 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
    939 	suite.NoError(err)
    940 	// fetch the attachment id from the processing media
    941 	attachmentID := processingMedia.AttachmentID()
    942 
    943 	// do a blocking call to fetch the attachment
    944 	attachment, err := processingMedia.LoadAttachment(ctx)
    945 	suite.NoError(err)
    946 	suite.NotNil(attachment)
    947 
    948 	// make sure it's got the stuff set on it that we expect
    949 	// the attachment ID and accountID we expect
    950 	suite.Equal(attachmentID, attachment.ID)
    951 	suite.Equal(accountID, attachment.AccountID)
    952 
    953 	// file meta should be correctly derived from the image
    954 	suite.EqualValues(gtsmodel.Original{
    955 		Width: 1920, Height: 1080, Size: 2073600, Aspect: 1.7777777777777777,
    956 	}, attachment.FileMeta.Original)
    957 	suite.EqualValues(gtsmodel.Small{
    958 		Width: 512, Height: 288, Size: 147456, Aspect: 1.7777777777777777,
    959 	}, attachment.FileMeta.Small)
    960 	suite.Equal("image/jpeg", attachment.File.ContentType)
    961 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
    962 	suite.Equal(269739, attachment.File.FileSize)
    963 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)
    964 
    965 	// now make sure the attachment is in the database
    966 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
    967 	suite.NoError(err)
    968 	suite.NotNil(dbAttachment)
    969 
    970 	// make sure the processed file is in storage
    971 	processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
    972 	suite.NoError(err)
    973 	suite.NotEmpty(processedFullBytes)
    974 
    975 	// load the processed bytes from our test folder, to compare
    976 	processedFullBytesExpected, err := os.ReadFile("./test/test-jpeg-processed.jpg")
    977 	suite.NoError(err)
    978 	suite.NotEmpty(processedFullBytesExpected)
    979 
    980 	// the bytes in storage should be what we expected
    981 	suite.Equal(processedFullBytesExpected, processedFullBytes)
    982 
    983 	// now do the same for the thumbnail and make sure it's what we expected
    984 	processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
    985 	suite.NoError(err)
    986 	suite.NotEmpty(processedThumbnailBytes)
    987 
    988 	processedThumbnailBytesExpected, err := os.ReadFile("./test/test-jpeg-thumbnail.jpg")
    989 	suite.NoError(err)
    990 	suite.NotEmpty(processedThumbnailBytesExpected)
    991 
    992 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
    993 }
    994 
    995 func (suite *ManagerTestSuite) TestSimpleJpegProcessAsync() {
    996 	ctx, cncl := context.WithTimeout(context.Background(), time.Second*30)
    997 	defer cncl()
    998 
    999 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
   1000 		// load bytes from a test image
   1001 		b, err := os.ReadFile("./test/test-jpeg.jpg")
   1002 		if err != nil {
   1003 			panic(err)
   1004 		}
   1005 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
   1006 	}
   1007 
   1008 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
   1009 
   1010 	// process the media with no additional info provided
   1011 	processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
   1012 	suite.NoError(err)
   1013 
   1014 	// fetch the attachment id from the processing media
   1015 	attachmentID := processingMedia.AttachmentID()
   1016 
   1017 	// wait for processing to complete
   1018 	var attachment *gtsmodel.MediaAttachment
   1019 	if !testrig.WaitFor(func() bool {
   1020 		attachment, err = suite.db.GetAttachmentByID(ctx, attachmentID)
   1021 		return err == nil && attachment != nil
   1022 	}) {
   1023 		suite.FailNow("timed out waiting for attachment to process")
   1024 	}
   1025 
   1026 	// make sure it's got the stuff set on it that we expect
   1027 	// the attachment ID and accountID we expect
   1028 	suite.Equal(attachmentID, attachment.ID)
   1029 	suite.Equal(accountID, attachment.AccountID)
   1030 
   1031 	// file meta should be correctly derived from the image
   1032 	suite.EqualValues(gtsmodel.Original{
   1033 		Width: 1920, Height: 1080, Size: 2073600, Aspect: 1.7777777777777777,
   1034 	}, attachment.FileMeta.Original)
   1035 	suite.EqualValues(gtsmodel.Small{
   1036 		Width: 512, Height: 288, Size: 147456, Aspect: 1.7777777777777777,
   1037 	}, attachment.FileMeta.Small)
   1038 	suite.Equal("image/jpeg", attachment.File.ContentType)
   1039 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
   1040 	suite.Equal(269739, attachment.File.FileSize)
   1041 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)
   1042 
   1043 	// now make sure the attachment is in the database
   1044 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
   1045 	suite.NoError(err)
   1046 	suite.NotNil(dbAttachment)
   1047 
   1048 	// make sure the processed file is in storage
   1049 	processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
   1050 	suite.NoError(err)
   1051 	suite.NotEmpty(processedFullBytes)
   1052 
   1053 	// load the processed bytes from our test folder, to compare
   1054 	processedFullBytesExpected, err := os.ReadFile("./test/test-jpeg-processed.jpg")
   1055 	suite.NoError(err)
   1056 	suite.NotEmpty(processedFullBytesExpected)
   1057 
   1058 	// the bytes in storage should be what we expected
   1059 	suite.Equal(processedFullBytesExpected, processedFullBytes)
   1060 
   1061 	// now do the same for the thumbnail and make sure it's what we expected
   1062 	processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
   1063 	suite.NoError(err)
   1064 	suite.NotEmpty(processedThumbnailBytes)
   1065 
   1066 	processedThumbnailBytesExpected, err := os.ReadFile("./test/test-jpeg-thumbnail.jpg")
   1067 	suite.NoError(err)
   1068 	suite.NotEmpty(processedThumbnailBytesExpected)
   1069 
   1070 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
   1071 }
   1072 
   1073 func (suite *ManagerTestSuite) TestSimpleJpegQueueSpamming() {
   1074 	// in this test, we spam the manager queue with 50 new media requests, just to see how it holds up
   1075 	ctx := context.Background()
   1076 
   1077 	b, err := os.ReadFile("./test/test-jpeg.jpg")
   1078 	if err != nil {
   1079 		panic(err)
   1080 	}
   1081 
   1082 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
   1083 		// load bytes from a test image
   1084 		return io.NopCloser(bytes.NewReader(b)), int64(len(b)), nil
   1085 	}
   1086 
   1087 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
   1088 
   1089 	spam := 50
   1090 	inProcess := []*media.ProcessingMedia{}
   1091 	for i := 0; i < spam; i++ {
   1092 		// process the media with no additional info provided
   1093 		processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
   1094 		suite.NoError(err)
   1095 		inProcess = append(inProcess, processingMedia)
   1096 	}
   1097 
   1098 	for _, processingMedia := range inProcess {
   1099 		// fetch the attachment id from the processing media
   1100 		attachmentID := processingMedia.AttachmentID()
   1101 
   1102 		// do a blocking call to fetch the attachment
   1103 		attachment, err := processingMedia.LoadAttachment(ctx)
   1104 		suite.NoError(err)
   1105 		suite.NotNil(attachment)
   1106 
   1107 		// make sure it's got the stuff set on it that we expect
   1108 		// the attachment ID and accountID we expect
   1109 		suite.Equal(attachmentID, attachment.ID)
   1110 		suite.Equal(accountID, attachment.AccountID)
   1111 
   1112 		// file meta should be correctly derived from the image
   1113 		suite.EqualValues(gtsmodel.Original{
   1114 			Width: 1920, Height: 1080, Size: 2073600, Aspect: 1.7777777777777777,
   1115 		}, attachment.FileMeta.Original)
   1116 		suite.EqualValues(gtsmodel.Small{
   1117 			Width: 512, Height: 288, Size: 147456, Aspect: 1.7777777777777777,
   1118 		}, attachment.FileMeta.Small)
   1119 		suite.Equal("image/jpeg", attachment.File.ContentType)
   1120 		suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
   1121 		suite.Equal(269739, attachment.File.FileSize)
   1122 		suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)
   1123 
   1124 		// now make sure the attachment is in the database
   1125 		dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
   1126 		suite.NoError(err)
   1127 		suite.NotNil(dbAttachment)
   1128 
   1129 		// make sure the processed file is in storage
   1130 		processedFullBytes, err := suite.storage.Get(ctx, attachment.File.Path)
   1131 		suite.NoError(err)
   1132 		suite.NotEmpty(processedFullBytes)
   1133 
   1134 		// load the processed bytes from our test folder, to compare
   1135 		processedFullBytesExpected, err := os.ReadFile("./test/test-jpeg-processed.jpg")
   1136 		suite.NoError(err)
   1137 		suite.NotEmpty(processedFullBytesExpected)
   1138 
   1139 		// the bytes in storage should be what we expected
   1140 		suite.Equal(processedFullBytesExpected, processedFullBytes)
   1141 
   1142 		// now do the same for the thumbnail and make sure it's what we expected
   1143 		processedThumbnailBytes, err := suite.storage.Get(ctx, attachment.Thumbnail.Path)
   1144 		suite.NoError(err)
   1145 		suite.NotEmpty(processedThumbnailBytes)
   1146 
   1147 		processedThumbnailBytesExpected, err := os.ReadFile("./test/test-jpeg-thumbnail.jpg")
   1148 		suite.NoError(err)
   1149 		suite.NotEmpty(processedThumbnailBytesExpected)
   1150 
   1151 		suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
   1152 	}
   1153 }
   1154 
   1155 func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithDiskStorage() {
   1156 	ctx := context.Background()
   1157 
   1158 	data := func(_ context.Context) (io.ReadCloser, int64, error) {
   1159 		// load bytes from a test image
   1160 		b, err := os.ReadFile("./test/test-jpeg.jpg")
   1161 		if err != nil {
   1162 			panic(err)
   1163 		}
   1164 		return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
   1165 	}
   1166 
   1167 	accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
   1168 
   1169 	temp := fmt.Sprintf("%s/gotosocial-test", os.TempDir())
   1170 	defer os.RemoveAll(temp)
   1171 
   1172 	disk, err := storage.OpenDisk(temp, &storage.DiskConfig{
   1173 		LockFile: path.Join(temp, "store.lock"),
   1174 	})
   1175 	if err != nil {
   1176 		panic(err)
   1177 	}
   1178 
   1179 	var state state.State
   1180 
   1181 	state.Workers.Start()
   1182 	defer state.Workers.Stop()
   1183 
   1184 	storage := &gtsstorage.Driver{
   1185 		Storage: disk,
   1186 	}
   1187 	state.Storage = storage
   1188 	state.DB = suite.db
   1189 
   1190 	diskManager := media.NewManager(&state)
   1191 	suite.manager = diskManager
   1192 
   1193 	// process the media with no additional info provided
   1194 	processingMedia, err := diskManager.ProcessMedia(ctx, data, accountID, nil)
   1195 	suite.NoError(err)
   1196 	// fetch the attachment id from the processing media
   1197 	attachmentID := processingMedia.AttachmentID()
   1198 
   1199 	// do a blocking call to fetch the attachment
   1200 	attachment, err := processingMedia.LoadAttachment(ctx)
   1201 	suite.NoError(err)
   1202 	suite.NotNil(attachment)
   1203 
   1204 	// make sure it's got the stuff set on it that we expect
   1205 	// the attachment ID and accountID we expect
   1206 	suite.Equal(attachmentID, attachment.ID)
   1207 	suite.Equal(accountID, attachment.AccountID)
   1208 
   1209 	// file meta should be correctly derived from the image
   1210 	suite.EqualValues(gtsmodel.Original{
   1211 		Width: 1920, Height: 1080, Size: 2073600, Aspect: 1.7777777777777777,
   1212 	}, attachment.FileMeta.Original)
   1213 	suite.EqualValues(gtsmodel.Small{
   1214 		Width: 512, Height: 288, Size: 147456, Aspect: 1.7777777777777777,
   1215 	}, attachment.FileMeta.Small)
   1216 	suite.Equal("image/jpeg", attachment.File.ContentType)
   1217 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)
   1218 	suite.Equal(269739, attachment.File.FileSize)
   1219 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)
   1220 
   1221 	// now make sure the attachment is in the database
   1222 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
   1223 	suite.NoError(err)
   1224 	suite.NotNil(dbAttachment)
   1225 
   1226 	// make sure the processed file is in storage
   1227 	processedFullBytes, err := storage.Get(ctx, attachment.File.Path)
   1228 	suite.NoError(err)
   1229 	suite.NotEmpty(processedFullBytes)
   1230 
   1231 	// load the processed bytes from our test folder, to compare
   1232 	processedFullBytesExpected, err := os.ReadFile("./test/test-jpeg-processed.jpg")
   1233 	suite.NoError(err)
   1234 	suite.NotEmpty(processedFullBytesExpected)
   1235 
   1236 	// the bytes in storage should be what we expected
   1237 	suite.Equal(processedFullBytesExpected, processedFullBytes)
   1238 
   1239 	// now do the same for the thumbnail and make sure it's what we expected
   1240 	processedThumbnailBytes, err := storage.Get(ctx, attachment.Thumbnail.Path)
   1241 	suite.NoError(err)
   1242 	suite.NotEmpty(processedThumbnailBytes)
   1243 
   1244 	processedThumbnailBytesExpected, err := os.ReadFile("./test/test-jpeg-thumbnail.jpg")
   1245 	suite.NoError(err)
   1246 	suite.NotEmpty(processedThumbnailBytesExpected)
   1247 
   1248 	suite.Equal(processedThumbnailBytesExpected, processedThumbnailBytes)
   1249 }
   1250 
   1251 func TestManagerTestSuite(t *testing.T) {
   1252 	suite.Run(t, &ManagerTestSuite{})
   1253 }