Files
tamabox/internal/db/users_outbox_test.go
T

143 lines
6.4 KiB
Go

package db
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/tamakyi/TamaBox/internal/conf"
)
func createOutboxTestUser(t *testing.T, ctx context.Context, store UsersStore, email, domain string) *User {
t.Helper()
require.NoError(t, store.Create(ctx, CreateUserOptions{
Name: domain,
Password: "test-only-password",
Email: email,
Domain: domain,
}))
user, err := store.GetByEmail(ctx, email)
require.NoError(t, err)
return user
}
func TestPurgeUserContentEnqueuesOwnedR2Objects(t *testing.T) {
setR2TestConfig(t)
conn, _ := newTestDB(t)
ctx := context.Background()
users := NewUsersStore(conn)
owner := createOutboxTestUser(t, ctx, users, "[email protected]", "outbox-owner")
other := createOutboxTestUser(t, ctx, users, "[email protected]", "outbox-other")
require.NoError(t, conn.Model(owner).Updates(map[string]interface{}{
"avatar": "https://cdn.example.test/assets/profile-owned.webp", "background": "https://cdn.example.test/assets/default-background.webp", "background_image": "https://cdn.example.test/assets/profile-shared.webp",
}).Error)
require.NoError(t, conn.Model(other).Update("background_image", "https://cdn.example.test/assets/profile-shared.webp").Error)
questions := NewQuestionsStore(conn)
question, err := questions.Create(ctx, CreateQuestionOptions{UserID: owner.ID, Content: "owned"})
require.NoError(t, err)
otherQuestion, err := questions.Create(ctx, CreateQuestionOptions{UserID: other.ID, Content: "other"})
require.NoError(t, err)
images := NewUploadImagesStore(conn)
owned, err := images.Create(ctx, CreateUploadImageOptions{QuestionID: question.ID, UploaderUserID: owner.ID, Key: "owned.webp"})
require.NoError(t, err)
_, err = images.Create(ctx, CreateUploadImageOptions{QuestionID: question.ID, UploaderUserID: owner.ID, Key: "owned.webp"})
require.NoError(t, err)
legacy, err := images.Create(ctx, CreateUploadImageOptions{
QuestionID: question.ID, UploaderUserID: owner.ID, Key: "https://cdn.example.test/assets/legacy.webp",
})
require.NoError(t, err)
external, err := images.Create(ctx, CreateUploadImageOptions{
QuestionID: question.ID, UploaderUserID: owner.ID, Key: "https://external.example.net/external.webp",
})
require.NoError(t, err)
shared, err := images.Create(ctx, CreateUploadImageOptions{QuestionID: question.ID, UploaderUserID: owner.ID, Key: "shared.webp"})
require.NoError(t, err)
require.NoError(t, conn.WithContext(ctx).Create(&UploadImageQuestion{
UploadImageID: shared.ID, QuestionID: otherQuestion.ID,
}).Error)
conversation, message, err := NewConversationStore(conn).CreateWithFirstMessage(ctx, CreateConversationOptions{
OwnerID: owner.ID, AskerID: other.ID, MaxMessages: 20,
}, other.ID, "private")
require.NoError(t, err)
require.NotNil(t, conversation)
messageImages := NewMessageImageStore(conn)
for _, opts := range []CreateMessageImageOptions{
{MessageID: message.ID, ObjectKey: "message-key.webp", URL: "https://cdn.example.test/assets/message-key.webp"},
{MessageID: message.ID, ObjectKey: "owned.webp", URL: "https://cdn.example.test/assets/owned.webp"},
{MessageID: message.ID, URL: "https://cdn.example.test/assets/message-legacy.webp"},
{MessageID: message.ID, URL: "https://external.example.net/message-external.webp"},
} {
_, err = messageImages.Create(ctx, opts)
require.NoError(t, err)
}
require.NoError(t, users.Deactivate(ctx, owner.ID))
require.Zero(t, uploadImageCount(t, conn, owned.ID))
require.Zero(t, uploadImageCount(t, conn, legacy.ID))
require.Zero(t, uploadImageCount(t, conn, external.ID))
require.Equal(t, int64(1), uploadImageCount(t, conn, shared.ID))
var sharedLinks int64
require.NoError(t, conn.WithContext(ctx).Model(&UploadImageQuestion{}).
Where("upload_image_id = ? AND question_id = ?", shared.ID, otherQuestion.ID).Count(&sharedLinks).Error)
require.Equal(t, int64(1), sharedLinks)
due, err := NewR2DeleteTaskStore(conn).ListDue(ctx, 20)
require.NoError(t, err)
keys := make([]string, 0, len(due))
for _, task := range due {
keys = append(keys, task.Key)
}
require.ElementsMatch(t, []string{
"owned.webp", "legacy.webp", "message-key.webp", "message-legacy.webp", "profile-owned.webp", "profile-shared.webp",
}, keys)
require.NotContains(t, keys, "shared.webp")
require.NotContains(t, keys, "external.webp")
require.NotContains(t, keys, "message-external.webp")
require.NotContains(t, keys, "default-background.webp")
var deactivated User
require.NoError(t, conn.Unscoped().First(&deactivated, owner.ID).Error)
require.Empty(t, deactivated.Avatar)
require.Empty(t, deactivated.Background)
require.Empty(t, deactivated.BackgroundImage)
other, err = users.GetByID(ctx, other.ID)
require.NoError(t, err)
require.Equal(t, "https://cdn.example.test/assets/profile-shared.webp", other.BackgroundImage)
referenced, err := NewR2DeleteTaskStore(conn).HasReferences(ctx, conf.Upload.ImageBucket, "profile-shared.webp")
require.NoError(t, err)
require.True(t, referenced)
referenced, err = NewR2DeleteTaskStore(conn).HasReferences(ctx, conf.Upload.ImageBucket, "default-background.webp")
require.NoError(t, err)
require.True(t, referenced)
}
func TestPurgeUserContentOutboxFailureRollsBack(t *testing.T) {
setR2TestConfig(t)
conn, _ := newTestDB(t)
ctx := context.Background()
users := NewUsersStore(conn)
owner := createOutboxTestUser(t, ctx, users, "[email protected]", "outbox-rollback")
question, err := NewQuestionsStore(conn).Create(ctx, CreateQuestionOptions{UserID: owner.ID, Content: "rollback"})
require.NoError(t, err)
image, err := NewUploadImagesStore(conn).Create(ctx, CreateUploadImageOptions{
QuestionID: question.ID, UploaderUserID: owner.ID, Key: "rollback.webp",
})
require.NoError(t, err)
require.NoError(t, conn.Migrator().DropTable(&R2DeleteTask{}))
require.Error(t, users.Deactivate(ctx, owner.ID))
require.Equal(t, int64(1), uploadImageCount(t, conn, image.ID))
var links int64
require.NoError(t, conn.WithContext(ctx).Model(&UploadImageQuestion{}).
Where("upload_image_id = ? AND question_id = ?", image.ID, question.ID).Count(&links).Error)
require.Equal(t, int64(1), links)
var questions int64
require.NoError(t, conn.WithContext(ctx).Unscoped().Model(&Question{}).Where("id = ?", question.ID).Count(&questions).Error)
require.Equal(t, int64(1), questions)
var user User
require.NoError(t, conn.WithContext(ctx).First(&user, owner.ID).Error)
require.False(t, user.DeletedAt.Valid)
}