diff --git a/internal/apimodule/media/mediacreate_test.go b/internal/apimodule/media/mediacreate_test.go index f598260..0c5c533 100644 --- a/internal/apimodule/media/mediacreate_test.go +++ b/internal/apimodule/media/mediacreate_test.go @@ -13,7 +13,6 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -27,48 +26,43 @@ import ( ) type MediaCreateTestSuite struct { + // standard suite interfaces suite.Suite - config *config.Config - mockOauthServer *oauth.MockServer - mockStorage *storage.MockStorage - mediaHandler media.MediaHandler - mastoConverter mastotypes.Converter + config *config.Config + db db.DB + log *logrus.Logger + storage storage.Storage + mastoConverter mastotypes.Converter + mediaHandler media.MediaHandler + oauthServer oauth.Server + + // standard suite models testTokens map[string]*oauth.Token testClients map[string]*oauth.Client testApplications map[string]*gtsmodel.Application testUsers map[string]*gtsmodel.User testAccounts map[string]*gtsmodel.Account - log *logrus.Logger - db db.DB - mediaModule *mediaModule + testAttachments map[string]*gtsmodel.MediaAttachment + + // item being tested + mediaModule *mediaModule } /* TEST INFRASTRUCTURE */ -// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout func (suite *MediaCreateTestSuite) SetupSuite() { - // some of our subsequent entities need a log so create this here - log := logrus.New() - log.SetLevel(logrus.TraceLevel) - suite.log = log - - // Direct config to local postgres instance + // setup standard items suite.config = testrig.NewTestConfig() + suite.db = testrig.NewTestDB() + suite.log = testrig.NewTestLog() + suite.storage = testrig.NewTestStorage() + suite.mastoConverter = testrig.NewTestMastoConverter(suite.db) + suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage) + suite.oauthServer = testrig.NewTestOauthServer(suite.db) - // use an actual database for this, because it's just easier than mocking one out - database, err := db.New(context.Background(), suite.config, log) - if err != nil { - suite.FailNow(err.Error()) - } - suite.db = database - - suite.mockOauthServer = &oauth.MockServer{} - suite.mockStorage = &storage.MockStorage{} - suite.mockStorage.On("StoreFileAt", mock.AnythingOfType("string"), mock.AnythingOfType("[]uint8")).Return(nil) // just pretend to store - suite.mediaHandler = media.New(suite.config, suite.db, suite.mockStorage, log) - suite.mastoConverter = mastotypes.New(suite.config, suite.db) + // setup module being tested suite.mediaModule = New(suite.db, suite.mediaHandler, suite.mastoConverter, suite.config, suite.log).(*mediaModule) } @@ -80,16 +74,18 @@ func (suite *MediaCreateTestSuite) TearDownSuite() { func (suite *MediaCreateTestSuite) SetupTest() { testrig.StandardDBSetup(suite.db) + testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") suite.testTokens = testrig.NewTestTokens() suite.testClients = testrig.NewTestClients() suite.testApplications = testrig.NewTestApplications() suite.testUsers = testrig.NewTestUsers() suite.testAccounts = testrig.NewTestAccounts() + suite.testAttachments = testrig.NewTestAttachments() } -// TearDownTest drops tables to make sure there's no data in the db func (suite *MediaCreateTestSuite) TearDownTest() { testrig.StandardDBTeardown(suite.db) + testrig.StandardStorageTeardown(suite.storage) } /* @@ -98,28 +94,42 @@ func (suite *MediaCreateTestSuite) TearDownTest() { func (suite *MediaCreateTestSuite) TestStatusCreatePOSTImageHandlerSuccessful() { + // set up the context for the request t := suite.testTokens["local_account_1"] oauthToken := oauth.PGTokenToOauthToken(t) - - // setup recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) ctx.Set(oauth.SessionAuthorizedToken, oauthToken) ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) - buf, w, err := testrig.CreateMultipartFormData("file", "../../media/test/test-jpeg.jpg", map[string]string{ + + // see what's in storage *before* the request + storageKeysBeforeRequest, err := suite.storage.ListKeys() + if err != nil { + panic(err) + } + + // create the request + buf, w, err := testrig.CreateMultipartFormData("file", "../../../testrig/media/test-jpeg.jpg", map[string]string{ "description": "this is a test image -- a cool background from somewhere", "focus": "-0.5,0.5", }) if err != nil { panic(err) } - ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), bytes.NewReader(buf.Bytes())) // the endpoint we're hitting ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) + + // do the actual request suite.mediaModule.mediaCreatePOSTHandler(ctx) + // check what's in storage *after* the request + storageKeysAfterRequest, err := suite.storage.ListKeys() + if err != nil { + panic(err) + } + // check response suite.EqualValues(http.StatusAccepted, recorder.Code) @@ -157,6 +167,7 @@ func (suite *MediaCreateTestSuite) TestStatusCreatePOSTImageHandlerSuccessful() assert.NotEmpty(suite.T(), attachmentReply.ID) assert.NotEmpty(suite.T(), attachmentReply.URL) assert.NotEmpty(suite.T(), attachmentReply.PreviewURL) + assert.Equal(suite.T(), len(storageKeysBeforeRequest) + 2, len(storageKeysAfterRequest)) // 2 images should be added to storage: the original and the thumbnail } func TestMediaCreateTestSuite(t *testing.T) { diff --git a/internal/gotosocial/actions.go b/internal/gotosocial/actions.go index 1b3dbf6..2e745e7 100644 --- a/internal/gotosocial/actions.go +++ b/internal/gotosocial/actions.go @@ -31,6 +31,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/apimodule/account" "github.com/superseriousbusiness/gotosocial/internal/apimodule/app" "github.com/superseriousbusiness/gotosocial/internal/apimodule/auth" + mediaModule "github.com/superseriousbusiness/gotosocial/internal/apimodule/media" "github.com/superseriousbusiness/gotosocial/internal/cache" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -54,7 +55,7 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr return fmt.Errorf("error creating router: %s", err) } - storageBackend, err := storage.NewInMem(c, log) + storageBackend, err := storage.NewLocal(c, log) if err != nil { return fmt.Errorf("error creating storage backend: %s", err) } @@ -70,11 +71,13 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr authModule := auth.New(oauthServer, dbService, log) accountModule := account.New(c, dbService, oauthServer, mediaHandler, mastoConverter, log) appsModule := app.New(oauthServer, dbService, mastoConverter, log) + mm := mediaModule.New(dbService, mediaHandler, mastoConverter, c, log) apiModules := []apimodule.ClientAPIModule{ authModule, // this one has to go first so the other modules use its middleware accountModule, appsModule, + mm, } for _, m := range apiModules { diff --git a/internal/mastotypes/mock_Converter.go b/internal/mastotypes/mock_Converter.go index 881bc48..732d933 100644 --- a/internal/mastotypes/mock_Converter.go +++ b/internal/mastotypes/mock_Converter.go @@ -104,3 +104,45 @@ func (_m *MockConverter) AppToMastoSensitive(application *gtsmodel.Application) return r0, r1 } + +// AttachmentToMasto provides a mock function with given fields: attachment +func (_m *MockConverter) AttachmentToMasto(attachment *gtsmodel.MediaAttachment) (mastotypes.Attachment, error) { + ret := _m.Called(attachment) + + var r0 mastotypes.Attachment + if rf, ok := ret.Get(0).(func(*gtsmodel.MediaAttachment) mastotypes.Attachment); ok { + r0 = rf(attachment) + } else { + r0 = ret.Get(0).(mastotypes.Attachment) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*gtsmodel.MediaAttachment) error); ok { + r1 = rf(attachment) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MentionToMasto provides a mock function with given fields: m +func (_m *MockConverter) MentionToMasto(m *gtsmodel.Mention) (mastotypes.Mention, error) { + ret := _m.Called(m) + + var r0 mastotypes.Mention + if rf, ok := ret.Get(0).(func(*gtsmodel.Mention) mastotypes.Mention); ok { + r0 = rf(m) + } else { + r0 = ret.Get(0).(mastotypes.Mention) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*gtsmodel.Mention) error); ok { + r1 = rf(m) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/internal/media/mock_MediaHandler.go b/internal/media/mock_MediaHandler.go index 6f04f1f..1f87555 100644 --- a/internal/media/mock_MediaHandler.go +++ b/internal/media/mock_MediaHandler.go @@ -12,6 +12,29 @@ type MockMediaHandler struct { mock.Mock } +// ProcessAttachment provides a mock function with given fields: img, accountID +func (_m *MockMediaHandler) ProcessAttachment(img []byte, accountID string) (*gtsmodel.MediaAttachment, error) { + ret := _m.Called(img, accountID) + + var r0 *gtsmodel.MediaAttachment + if rf, ok := ret.Get(0).(func([]byte, string) *gtsmodel.MediaAttachment); ok { + r0 = rf(img, accountID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*gtsmodel.MediaAttachment) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func([]byte, string) error); ok { + r1 = rf(img, accountID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SetHeaderOrAvatarForAccountID provides a mock function with given fields: img, accountID, headerOrAvi func (_m *MockMediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*gtsmodel.MediaAttachment, error) { ret := _m.Called(img, accountID, headerOrAvi) diff --git a/internal/storage/inmem.go b/internal/storage/inmem.go index 31c2bd9..976ddd7 100644 --- a/internal/storage/inmem.go +++ b/internal/storage/inmem.go @@ -15,22 +15,41 @@ import ( func NewInMem(c *config.Config, log *logrus.Logger) (Storage, error) { return &inMemStorage{ stored: make(map[string][]byte), + log: log, }, nil } type inMemStorage struct { stored map[string][]byte + log *logrus.Logger } func (s *inMemStorage) StoreFileAt(path string, data []byte) error { + l := s.log.WithField("func", "StoreFileAt") + l.Debugf("storing at path %s", path) s.stored[path] = data return nil } func (s *inMemStorage) RetrieveFileFrom(path string) ([]byte, error) { + l := s.log.WithField("func", "RetrieveFileFrom") + l.Debugf("retrieving from path %s", path) d, ok := s.stored[path] if !ok { return nil, fmt.Errorf("no data found at path %s", path) } return d, nil } + +func (s *inMemStorage)ListKeys() ([]string, error) { + keys := []string{} + for k := range s.stored { + keys = append(keys, k) + } + return keys, nil +} + +func (s *inMemStorage) RemoveFileAt(path string) error { + delete(s.stored, path) + return nil +} diff --git a/internal/storage/local.go b/internal/storage/local.go index 620467d..09a62be 100644 --- a/internal/storage/local.go +++ b/internal/storage/local.go @@ -1,6 +1,11 @@ package storage import ( + "fmt" + "os" + "path/filepath" + "strings" + "github.com/sirupsen/logrus" "github.com/superseriousbusiness/gotosocial/internal/config" ) @@ -8,16 +13,58 @@ import ( // NewLocal returns an implementation of the Storage interface that uses // the local filesystem for storing and retrieving files, attachments, etc. func NewLocal(c *config.Config, log *logrus.Logger) (Storage, error) { - return &localStorage{}, nil + return &localStorage{ + config: c, + log: log, + }, nil } type localStorage struct { + config *config.Config + log *logrus.Logger } func (s *localStorage) StoreFileAt(path string, data []byte) error { + l := s.log.WithField("func", "StoreFileAt") + l.Debugf("storing at path %s", path) + components := strings.Split(path, "/") + dir := strings.Join(components[0:len(components) - 1], "/") + if err := os.MkdirAll(dir, 0777); err != nil { + return fmt.Errorf("error writing file at %s: %s", path, err) + } + if err := os.WriteFile(path, data, 0777); err != nil { + return fmt.Errorf("error writing file at %s: %s", path, err) + } return nil } func (s *localStorage) RetrieveFileFrom(path string) ([]byte, error) { - return nil, nil + l := s.log.WithField("func", "RetrieveFileFrom") + l.Debugf("retrieving from path %s", path) + b, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("error reading file at %s: %s", path, err) + } + return b, nil +} + +func (s *localStorage) ListKeys() ([]string, error) { + keys := []string{} + err := filepath.Walk(s.config.StorageConfig.BasePath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + keys = append(keys, path) + } + return nil + }) + if err != nil { + return nil, err + } + return keys, nil +} + +func (s *localStorage) RemoveFileAt(path string) error { + return os.Remove(path) } diff --git a/internal/storage/mock_Storage.go b/internal/storage/mock_Storage.go index 865d522..2444f03 100644 --- a/internal/storage/mock_Storage.go +++ b/internal/storage/mock_Storage.go @@ -9,6 +9,43 @@ type MockStorage struct { mock.Mock } +// ListKeys provides a mock function with given fields: +func (_m *MockStorage) ListKeys() ([]string, error) { + ret := _m.Called() + + var r0 []string + if rf, ok := ret.Get(0).(func() []string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RemoveFileAt provides a mock function with given fields: path +func (_m *MockStorage) RemoveFileAt(path string) error { + ret := _m.Called(path) + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(path) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // RetrieveFileFrom provides a mock function with given fields: path func (_m *MockStorage) RetrieveFileFrom(path string) ([]byte, error) { ret := _m.Called(path) diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 7c85d0a..493c364 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -25,4 +25,6 @@ package storage type Storage interface { StoreFileAt(path string, data []byte) error RetrieveFileFrom(path string) ([]byte, error) + ListKeys() ([]string, error) + RemoveFileAt(path string) error } diff --git a/testrig/config.go b/testrig/config.go index 1bc0b8b..f7028b1 100644 --- a/testrig/config.go +++ b/testrig/config.go @@ -1,3 +1,21 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package testrig import "github.com/superseriousbusiness/gotosocial/internal/config" diff --git a/testrig/db.go b/testrig/db.go index e930ba2..10025c9 100644 --- a/testrig/db.go +++ b/testrig/db.go @@ -1,3 +1,21 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package testrig import ( diff --git a/testrig/log.go b/testrig/log.go new file mode 100644 index 0000000..0bafc96 --- /dev/null +++ b/testrig/log.go @@ -0,0 +1,28 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package testrig + +import "github.com/sirupsen/logrus" + +// NewTestLog returns a trace level logger for testing +func NewTestLog() *logrus.Logger { + log := logrus.New() + log.SetLevel(logrus.TraceLevel) + return log +} diff --git a/testrig/mastoconverter.go b/testrig/mastoconverter.go new file mode 100644 index 0000000..10bdbdc --- /dev/null +++ b/testrig/mastoconverter.go @@ -0,0 +1,29 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package testrig + +import ( + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/mastotypes" +) + +// NewTestMastoConverter returned a mastotypes converter with the given db and the default test config +func NewTestMastoConverter(db db.DB) mastotypes.Converter { + return mastotypes.New(NewTestConfig(), db) +} diff --git a/testrig/media/welcome-original.jpeg b/testrig/media/welcome-original.jpeg new file mode 100755 index 0000000..1a54437 Binary files /dev/null and b/testrig/media/welcome-original.jpeg differ diff --git a/testrig/media/welcome-small.jpeg b/testrig/media/welcome-small.jpeg new file mode 100755 index 0000000..b1a5851 Binary files /dev/null and b/testrig/media/welcome-small.jpeg differ diff --git a/testrig/mediahandler.go b/testrig/mediahandler.go new file mode 100644 index 0000000..fd79866 --- /dev/null +++ b/testrig/mediahandler.go @@ -0,0 +1,31 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package testrig + +import ( + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/media" + "github.com/superseriousbusiness/gotosocial/internal/storage" +) + +// NewTestMediaHandler returns a media handler with the default test config, the default test logger, +// and the given db and storage. +func NewTestMediaHandler(db db.DB, storage storage.Storage) media.MediaHandler { + return media.New(NewTestConfig(), db, storage, NewTestLog()) +} diff --git a/testrig/oauthserver.go b/testrig/oauthserver.go new file mode 100644 index 0000000..49615ca --- /dev/null +++ b/testrig/oauthserver.go @@ -0,0 +1,29 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package testrig + +import ( + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +// NewTestOauthServer returns an oauth server with the given db, and the default test logger. +func NewTestOauthServer(db db.DB) oauth.Server { + return oauth.New(db, NewTestLog()) +} diff --git a/testrig/storage.go b/testrig/storage.go index 3786b06..a17f620 100644 --- a/testrig/storage.go +++ b/testrig/storage.go @@ -1,24 +1,79 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package testrig import ( - "github.com/sirupsen/logrus" - "github.com/superseriousbusiness/gotosocial/internal/config" + "fmt" + "os" + "strings" + "github.com/superseriousbusiness/gotosocial/internal/storage" ) -// NewTestStorage returns a new in memory storage with the given config -func NewTestStorage(c *config.Config, log *logrus.Logger) storage.Storage { - s, err := storage.NewInMem(c, log) +// NewTestStorage returns a new in memory storage with the default test config +func NewTestStorage() storage.Storage { + s, err := storage.NewInMem(NewTestConfig(), NewTestLog()) if err != nil { panic(err) } return s } -func StandardStorageSetup(s storage.Storage) { - +// StandardStorageSetup populates the storage with standard test entries from the given directory. +func StandardStorageSetup(s storage.Storage, relativePath string) { + stored := NewTestStored() + a := NewTestAttachments() + for k, fileNameTemplate := range stored { + attachmentInfo, ok := a[k] + if !ok { + panic(fmt.Errorf("key %s not found in test attachments", k)) + } + filenameOriginal := strings.Replace(fileNameTemplate, "*", "original", 1) + filenameSmall := strings.Replace(fileNameTemplate, "*", "small", 1) + pathOriginal := attachmentInfo.File.Path + pathSmall := attachmentInfo.Thumbnail.Path + bOriginal, err := os.ReadFile(fmt.Sprintf("%s/%s", relativePath, filenameOriginal)) + if err != nil { + panic(err) + } + if err := s.StoreFileAt(pathOriginal, bOriginal); err != nil { + panic(err) + } + bSmall, err := os.ReadFile(fmt.Sprintf("%s/%s", relativePath, filenameSmall)) + if err != nil { + panic(err) + } + if err := s.StoreFileAt(pathSmall, bSmall); err != nil { + panic(err) + } + } } +// StandardStorageTeardown deletes everything in storage so that it's clean for the next test func StandardStorageTeardown(s storage.Storage) { - + keys, err := s.ListKeys() + if err != nil { + panic(err) + } + for _, k := range keys { + if err := s.RemoveFileAt(k); err != nil { + panic(err) + } + } } diff --git a/testrig/models.go b/testrig/testmodels.go similarity index 87% rename from testrig/models.go rename to testrig/testmodels.go index 9606199..65358eb 100644 --- a/testrig/models.go +++ b/testrig/testmodels.go @@ -1,3 +1,21 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package testrig import ( @@ -10,6 +28,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/oauth" ) +// NewTestTokens returns a map of tokens keyed according to which account the token belongs to. func NewTestTokens() map[string]*oauth.Token { tokens := map[string]*oauth.Token{ "local_account_1": { @@ -26,6 +45,7 @@ func NewTestTokens() map[string]*oauth.Token { return tokens } +// NewTestClients returns a map of Clients keyed according to which account they are used by. func NewTestClients() map[string]*oauth.Client { clients := map[string]*oauth.Client{ "local_account_1": { @@ -38,6 +58,7 @@ func NewTestClients() map[string]*oauth.Client { return clients } +// NewTestApplications returns a map of applications keyed to which number application they are. func NewTestApplications() map[string]*gtsmodel.Application { apps := map[string]*gtsmodel.Application{ "application_1": { @@ -54,6 +75,7 @@ func NewTestApplications() map[string]*gtsmodel.Application { return apps } +// NewTestUsers returns a map of Users keyed by which account belongs to them. func NewTestUsers() map[string]*gtsmodel.User { users := map[string]*gtsmodel.User{ "unconfirmed_account": { @@ -181,6 +203,7 @@ func NewTestUsers() map[string]*gtsmodel.User { return users } +// NewTestAccounts returns a map of accounts keyed by what type of account they are. func NewTestAccounts() map[string]*gtsmodel.Account { accounts := map[string]*gtsmodel.Account{ "unconfirmed_account": { @@ -440,14 +463,66 @@ func NewTestAccounts() map[string]*gtsmodel.Account { return accounts } +// NewTestAttachments returns a map of attachments keyed according to which account +// and status they belong to, and which attachment number of that status they are. func NewTestAttachments() map[string]*gtsmodel.MediaAttachment { return map[string]*gtsmodel.MediaAttachment{ - // "admin_account_status_1": { - - // }, + "admin_account_status_1_attachment_1": { + ID: "b052241b-f30f-4dc6-92fc-2bad0be1f8d8", + StatusID: "502ccd6f-0edf-48d7-9016-2dfa4d3714cd", + URL: "http://localhost:8080/fileserver/8020dbb4-1e7b-4d99-a872-4cf94e64210f/attachment/original/b052241b-f30f-4dc6-92fc-2bad0be1f8d8.jpeg", + RemoteURL: "", + CreatedAt: time.Now().Add(-71 * time.Hour), + UpdatedAt: time.Now().Add(-71 * time.Hour), + Type: gtsmodel.FileTypeImage, + FileMeta: gtsmodel.FileMeta{ + Original: gtsmodel.Original{ + Width: 1200, + Height: 630, + Size: 756000, + Aspect: 1.9047619047619047, + }, + Small: gtsmodel.Small{ + Width: 256, + Height: 134, + Size: 34304, + Aspect: 1.9104477611940298, + }, + }, + AccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f", + Description: "Black and white image of some 50's style text saying: Welcome On Board", + ScheduledStatusID: "", + Blurhash: "LNJRdVM{00Rj%Mayt7j[4nWBofRj", + Processing: 2, + File: gtsmodel.File{ + Path: "/gotosocial/storage/8020dbb4-1e7b-4d99-a872-4cf94e64210f/attachment/original/b052241b-f30f-4dc6-92fc-2bad0be1f8d8.jpeg", + ContentType: "image/jpeg", + FileSize: 62529, + UpdatedAt: time.Now().Add(-71 * time.Hour), + }, + Thumbnail: gtsmodel.Thumbnail{ + Path: "/gotosocial/storage/8020dbb4-1e7b-4d99-a872-4cf94e64210f/attachment/small/b052241b-f30f-4dc6-92fc-2bad0be1f8d8.jpeg", + ContentType: "image/jpeg", + FileSize: 6872, + UpdatedAt: time.Now().Add(-71 * time.Hour), + URL: "http://localhost:8080/fileserver/8020dbb4-1e7b-4d99-a872-4cf94e64210f/attachment/small/b052241b-f30f-4dc6-92fc-2bad0be1f8d8.jpeg", + RemoteURL: "", + }, + Avatar: false, + Header: false, + }, } } +// NewTestStored returns a map of filenames, keyed according to which attachment they pertain to. +func NewTestStored() map[string]string { + return map[string]string { + "admin_account_status_1_attachment_1": "welcome-*.jpeg", + } +} + +// NewTestStatuses returns a map of statuses keyed according to which account +// and status they are. func NewTestStatuses() map[string]*gtsmodel.Status { return map[string]*gtsmodel.Status{ "admin_account_status_1": {