mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
MM-43598: Bulk export not exporting non-thread DMs from deactivated users (#30427)
This commit is contained in:
parent
0c8e30da4d
commit
e7e80634d6
1 changed files with 149 additions and 0 deletions
|
|
@ -1586,3 +1586,152 @@ func TestExportSchemes(t *testing.T) {
|
|||
require.Equal(t, customTeamGuestRole.BuiltIn, importedTeamGuestRole.BuiltIn)
|
||||
})
|
||||
}
|
||||
|
||||
// TestExportDeactivatedUserDMs specifically tests the MM-43598 bug
|
||||
// by validating that direct messages from deactivated users are exported correctly
|
||||
func TestExportDeactivatedUserDMs(t *testing.T) {
|
||||
th1 := Setup(t).InitBasic()
|
||||
defer th1.TearDown()
|
||||
|
||||
// Create a DM Channel
|
||||
user2 := th1.BasicUser2
|
||||
dmChannel := th1.CreateDmChannel(user2)
|
||||
|
||||
// 1. First basic user (active) sends a message to user2 (who will later be deactivated)
|
||||
initialMessage := "initial_message_from_basic_user"
|
||||
initialPost := &model.Post{
|
||||
ChannelId: dmChannel.Id,
|
||||
Message: initialMessage,
|
||||
UserId: th1.BasicUser.Id,
|
||||
}
|
||||
initialPostCreated, appErr := th1.App.CreatePost(th1.Context, initialPost, dmChannel, model.CreatePostFlags{SetOnline: true})
|
||||
require.Nil(t, appErr)
|
||||
|
||||
// 2. Have user2 reply with TWO types of replies:
|
||||
|
||||
// 2a. User2 replies in a thread (to the initial message)
|
||||
threadedReplyMessage := "threaded_reply_from_user2"
|
||||
threadedReply := &model.Post{
|
||||
ChannelId: dmChannel.Id,
|
||||
Message: threadedReplyMessage,
|
||||
UserId: user2.Id,
|
||||
RootId: initialPostCreated.Id, // This makes it a threaded reply
|
||||
}
|
||||
_, appErr = th1.App.CreatePost(th1.Context, threadedReply, dmChannel, model.CreatePostFlags{SetOnline: true})
|
||||
require.Nil(t, appErr)
|
||||
|
||||
// 2b. User2 sends a standalone reply (NOT in a thread)
|
||||
nonThreadedReplyMessage := "non_threaded_reply_from_user2"
|
||||
nonThreadedReply := &model.Post{
|
||||
ChannelId: dmChannel.Id,
|
||||
Message: nonThreadedReplyMessage,
|
||||
UserId: user2.Id,
|
||||
// No RootId, making it a standalone message, not a thread reply
|
||||
}
|
||||
_, appErr = th1.App.CreatePost(th1.Context, nonThreadedReply, dmChannel, model.CreatePostFlags{SetOnline: true})
|
||||
require.Nil(t, appErr)
|
||||
|
||||
// 3. Now deactivate user2
|
||||
_, err := th1.App.UpdateActive(th1.Context, user2, false)
|
||||
require.Nil(t, err)
|
||||
|
||||
// 4. Export data
|
||||
var b bytes.Buffer
|
||||
appErr = th1.App.BulkExport(th1.Context, &b, "somePath", nil, model.BulkExportOpts{})
|
||||
require.Nil(t, appErr)
|
||||
|
||||
// 5. Make a copy of the buffer for export validation
|
||||
var exportDataCopy bytes.Buffer
|
||||
_, nErr := exportDataCopy.Write(b.Bytes())
|
||||
require.NoError(t, nErr)
|
||||
|
||||
// 6. Validate export data directly to ensure both types of replies are present
|
||||
scanner := bufio.NewScanner(&exportDataCopy)
|
||||
foundThreadedReply := false
|
||||
foundNonThreadedReply := false
|
||||
|
||||
for scanner.Scan() {
|
||||
var line imports.LineImportData
|
||||
err := json.Unmarshal(scanner.Bytes(), &line)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check for direct posts
|
||||
if line.Type == "direct_post" && line.DirectPost != nil {
|
||||
// Check for the non-threaded reply (the standalone message)
|
||||
if line.DirectPost.Message != nil && *line.DirectPost.Message == nonThreadedReplyMessage {
|
||||
foundNonThreadedReply = true
|
||||
// Verify username is correctly preserved in export
|
||||
require.Equal(t, user2.Username, *line.DirectPost.User,
|
||||
"Deactivated user's username should be preserved in export for non-threaded reply")
|
||||
}
|
||||
|
||||
// Check for the thread starter and its replies
|
||||
if line.DirectPost.Message != nil && *line.DirectPost.Message == initialMessage {
|
||||
// Check if the threaded reply is in the replies array
|
||||
if line.DirectPost.Replies != nil {
|
||||
for _, reply := range *line.DirectPost.Replies {
|
||||
if reply.Message != nil && *reply.Message == threadedReplyMessage {
|
||||
foundThreadedReply = true
|
||||
require.Equal(t, user2.Username, *reply.User,
|
||||
"Deactivated user's username should be preserved in export for threaded reply")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is key for testing MM-43598
|
||||
require.True(t, foundNonThreadedReply,
|
||||
"Non-threaded reply from deactivated user must be present in export data")
|
||||
require.True(t, foundThreadedReply,
|
||||
"Threaded reply from deactivated user must be present in export data")
|
||||
|
||||
// 7. Import data into a new instance
|
||||
th2 := Setup(t)
|
||||
defer th2.TearDown()
|
||||
|
||||
i, appErr := th2.App.BulkImport(th2.Context, &b, nil, false, 5)
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, 0, i)
|
||||
|
||||
// 8. Verify the DM channel was imported
|
||||
channels, nErr := th2.App.Srv().Store().Channel().GetAllDirectChannelsForExportAfter(1000, "00000000", false)
|
||||
require.NoError(t, nErr)
|
||||
require.Equal(t, 1, len(channels), "Direct channel should be imported")
|
||||
|
||||
// 9. Verify all posts were imported
|
||||
posts, nErr := th2.App.Srv().Store().Post().GetPosts(model.GetPostsOptions{
|
||||
ChannelId: channels[0].Id,
|
||||
PerPage: 1000,
|
||||
}, false, nil)
|
||||
require.NoError(t, nErr)
|
||||
|
||||
// We should have exactly 3 posts
|
||||
require.Equal(t, 3, len(posts.Posts), "Should have imported exactly 3 posts")
|
||||
|
||||
// 10. Specifically check that both types of replies are present
|
||||
foundThreadedReplyInImport := false
|
||||
foundNonThreadedReplyInImport := false
|
||||
|
||||
for _, post := range posts.Posts {
|
||||
if post.Message == threadedReplyMessage {
|
||||
foundThreadedReplyInImport = true
|
||||
// Verify this is a reply in a thread
|
||||
require.NotEmpty(t, post.RootId, "Threaded reply should have a RootId")
|
||||
}
|
||||
if post.Message == nonThreadedReplyMessage {
|
||||
foundNonThreadedReplyInImport = true
|
||||
// Verify this is NOT a reply in a thread
|
||||
require.Empty(t, post.RootId, "Non-threaded reply should not have a RootId")
|
||||
}
|
||||
}
|
||||
|
||||
// This directly tests the issue in MM-43598
|
||||
require.True(t, foundNonThreadedReplyInImport,
|
||||
"Non-threaded reply from deactivated user should be imported")
|
||||
require.True(t, foundThreadedReplyInImport,
|
||||
"Threaded reply from deactivated user should be imported")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue