mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-29 09:12:26 -05:00
# Description This is quite a complex database migration that updates the `statuses.thread_id` column to be notnull, in order that statuses always be threaded, which will be useful in various pieces of upcoming work. This is unfortunately a migration that acts over the entire statuses table, and is quite complex in order to ensure that all existing statuses get correctly threaded together, and where possible fix any issues of statuses in the same thread having incorrect thread_ids. TODO: - ~~update testrig models to all be threaded~~ - ~~update code to ensure thread_id is always set~~ - ~~run on **a copy** of an sqlite production database~~ - ~~run on **a copy** of a postgres production database~~ ## Checklist - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [x] I/we have commented the added code, particularly in hard-to-understand areas. - [ ] I/we have made any necessary changes to documentation. - [x] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes. - [x] I/we have run `go fmt ./...` and `golangci-lint run`. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4160 Co-authored-by: kim <grufwub@gmail.com> Co-committed-by: kim <grufwub@gmail.com>
389 lines
8.8 KiB
Go
389 lines
8.8 KiB
Go
// GoToSocial
|
|
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
//
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
package testrig
|
|
|
|
import (
|
|
"context"
|
|
|
|
"code.superseriousbusiness.org/gotosocial/internal/db"
|
|
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
|
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
|
"codeberg.org/gruf/go-kv"
|
|
)
|
|
|
|
var testModels = []interface{}{
|
|
>smodel.Account{},
|
|
>smodel.AccountNote{},
|
|
>smodel.AccountSettings{},
|
|
>smodel.AccountToEmoji{},
|
|
>smodel.Application{},
|
|
>smodel.Block{},
|
|
>smodel.DomainBlock{},
|
|
>smodel.EmailDomainBlock{},
|
|
>smodel.Filter{},
|
|
>smodel.FilterKeyword{},
|
|
>smodel.FilterStatus{},
|
|
>smodel.Follow{},
|
|
>smodel.FollowRequest{},
|
|
>smodel.InteractionRequest{},
|
|
>smodel.List{},
|
|
>smodel.ListEntry{},
|
|
>smodel.Marker{},
|
|
>smodel.MediaAttachment{},
|
|
>smodel.Mention{},
|
|
>smodel.Poll{},
|
|
>smodel.PollVote{},
|
|
>smodel.Status{},
|
|
>smodel.StatusToEmoji{},
|
|
>smodel.StatusToTag{},
|
|
>smodel.StatusEdit{},
|
|
>smodel.StatusFave{},
|
|
>smodel.StatusBookmark{},
|
|
>smodel.Tag{},
|
|
>smodel.Thread{},
|
|
>smodel.ThreadMute{},
|
|
>smodel.User{},
|
|
>smodel.UserMute{},
|
|
>smodel.VAPIDKeyPair{},
|
|
>smodel.WebPushSubscription{},
|
|
>smodel.Emoji{},
|
|
>smodel.Instance{},
|
|
>smodel.Notification{},
|
|
>smodel.RouterSession{},
|
|
>smodel.Token{},
|
|
>smodel.EmojiCategory{},
|
|
>smodel.Tombstone{},
|
|
>smodel.Report{},
|
|
>smodel.Rule{},
|
|
>smodel.WorkerTask{},
|
|
}
|
|
|
|
// NewTestDB returns a new initialized, empty database for testing.
|
|
//
|
|
// If the environment variable GTS_DB_ADDRESS is set, it will take that
|
|
// value as the database address instead.
|
|
//
|
|
// If the environment variable GTS_DB_TYPE is set, it will take that
|
|
// value as the database type instead.
|
|
//
|
|
// If the environment variable GTS_DB_PORT is set, it will take that
|
|
// value as the port instead.
|
|
func NewTestDB(state *state.State) db.DB {
|
|
state.Caches.Init()
|
|
|
|
testDB, err := bundb.NewBunDBService(context.Background(), state)
|
|
if err != nil {
|
|
log.Panic(nil, err)
|
|
}
|
|
|
|
state.DB = testDB
|
|
|
|
return testDB
|
|
}
|
|
|
|
// CreateTestTables creates prerequisite test tables in the database, but doesn't populate them.
|
|
func CreateTestTables(db db.DB) {
|
|
ctx := context.Background()
|
|
for _, m := range testModels {
|
|
if err := db.CreateTable(ctx, m); err != nil {
|
|
log.Panicf(ctx, "error creating table for %+v: %s", m, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// StandardDBSetup populates a given db with all the necessary tables/models for perfoming tests.
|
|
//
|
|
// The accounts parameter is provided in case the db should be populated with a certain set of accounts.
|
|
// If accounts is nil, then the standard test accounts will be used.
|
|
//
|
|
// When testing http signatures, you should pass into this function the same accounts map that you generated
|
|
// signatures with, otherwise this function will randomly generate new keys for accounts and signature
|
|
// verification will fail.
|
|
func StandardDBSetup(db db.DB, accounts map[string]*gtsmodel.Account) {
|
|
if db == nil {
|
|
log.Panic(nil, "db setup: db was nil")
|
|
}
|
|
|
|
CreateTestTables(db)
|
|
|
|
ctx := context.Background()
|
|
|
|
for _, v := range NewTestTokens() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestApplications() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestBlocks() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestReports() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestRules() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestDomainBlocks() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestInstances() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestUsers() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
if accounts == nil {
|
|
for _, v := range NewTestAccounts() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
} else {
|
|
for _, v := range accounts {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestAccountSettings() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestAttachments() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestStatuses() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.PanicKVs(ctx, kv.Fields{
|
|
{"error", err},
|
|
{"status", v},
|
|
}...)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestEmojis() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestEmojiCategories() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestStatusToEmojis() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestTags() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestStatusToTags() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestMentions() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestFaves() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestFollows() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestLists() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestListEntries() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestNotifications() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestTombstones() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestBookmarks() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestAccountNotes() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestMarkers() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestThreads() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestPolls() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestPollVotes() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestFilters() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestFilterKeywords() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestFilterStatuses() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestUserMutes() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestWebPushSubscriptions() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(nil, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestInteractionRequests() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
for _, v := range NewTestStatusEdits() {
|
|
if err := db.Put(ctx, v); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
}
|
|
|
|
if err := db.CreateInstanceAccount(ctx); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
|
|
if err := db.CreateInstanceInstance(ctx); err != nil {
|
|
log.Panic(ctx, err)
|
|
}
|
|
|
|
// Generates and stores a VAPID key pair as a side effect.
|
|
if _, err := db.GetVAPIDKeyPair(ctx); err != nil {
|
|
log.Panic(nil, err)
|
|
}
|
|
|
|
log.Debug(ctx, "testing db setup complete")
|
|
}
|
|
|
|
// StandardDBTeardown drops all the standard testing tables/models from the database to ensure it's clean for the next test.
|
|
func StandardDBTeardown(db db.DB) {
|
|
ctx := context.Background()
|
|
if db == nil {
|
|
return
|
|
}
|
|
defer db.Close()
|
|
for _, m := range testModels {
|
|
if err := db.DropTable(ctx, m); err != nil {
|
|
log.Error(ctx, err)
|
|
}
|
|
}
|
|
}
|