github.com/mattermost/mattermost-server/v5@v5.39.3/store/storetest/channel_member_history_store.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package storetest 5 6 import ( 7 "testing" 8 9 "math" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 "github.com/mattermost/mattermost-server/v5/model" 15 "github.com/mattermost/mattermost-server/v5/store" 16 ) 17 18 func TestChannelMemberHistoryStore(t *testing.T, ss store.Store) { 19 t.Run("TestLogJoinEvent", func(t *testing.T) { testLogJoinEvent(t, ss) }) 20 t.Run("TestLogLeaveEvent", func(t *testing.T) { testLogLeaveEvent(t, ss) }) 21 t.Run("TestGetUsersInChannelAtChannelMemberHistory", func(t *testing.T) { testGetUsersInChannelAtChannelMemberHistory(t, ss) }) 22 t.Run("TestGetUsersInChannelAtChannelMembers", func(t *testing.T) { testGetUsersInChannelAtChannelMembers(t, ss) }) 23 t.Run("TestPermanentDeleteBatch", func(t *testing.T) { testPermanentDeleteBatch(t, ss) }) 24 t.Run("TestPermanentDeleteBatchForRetentionPolicies", func(t *testing.T) { testPermanentDeleteBatchForRetentionPolicies(t, ss) }) 25 } 26 27 func testLogJoinEvent(t *testing.T, ss store.Store) { 28 // create a test channel 29 ch := model.Channel{ 30 TeamId: model.NewId(), 31 DisplayName: "Display " + model.NewId(), 32 Name: "zz" + model.NewId() + "b", 33 Type: model.CHANNEL_OPEN, 34 } 35 channel, err := ss.Channel().Save(&ch, -1) 36 require.NoError(t, err) 37 38 // and a test user 39 user := model.User{ 40 Email: MakeEmail(), 41 Nickname: model.NewId(), 42 Username: model.NewId(), 43 } 44 userPtr, err := ss.User().Save(&user) 45 require.NoError(t, err) 46 user = *userPtr 47 48 // log a join event 49 err = ss.ChannelMemberHistory().LogJoinEvent(user.Id, channel.Id, model.GetMillis()) 50 assert.NoError(t, err) 51 } 52 53 func testLogLeaveEvent(t *testing.T, ss store.Store) { 54 // create a test channel 55 ch := model.Channel{ 56 TeamId: model.NewId(), 57 DisplayName: "Display " + model.NewId(), 58 Name: "zz" + model.NewId() + "b", 59 Type: model.CHANNEL_OPEN, 60 } 61 channel, err := ss.Channel().Save(&ch, -1) 62 require.NoError(t, err) 63 64 // and a test user 65 user := model.User{ 66 Email: MakeEmail(), 67 Nickname: model.NewId(), 68 Username: model.NewId(), 69 } 70 userPtr, err := ss.User().Save(&user) 71 require.NoError(t, err) 72 user = *userPtr 73 74 // log a join event, followed by a leave event 75 err = ss.ChannelMemberHistory().LogJoinEvent(user.Id, channel.Id, model.GetMillis()) 76 assert.NoError(t, err) 77 78 err = ss.ChannelMemberHistory().LogLeaveEvent(user.Id, channel.Id, model.GetMillis()) 79 assert.NoError(t, err) 80 } 81 82 func testGetUsersInChannelAtChannelMemberHistory(t *testing.T, ss store.Store) { 83 // create a test channel 84 ch := &model.Channel{ 85 TeamId: model.NewId(), 86 DisplayName: "Display " + model.NewId(), 87 Name: "zz" + model.NewId() + "b", 88 Type: model.CHANNEL_OPEN, 89 } 90 channel, err := ss.Channel().Save(ch, -1) 91 require.NoError(t, err) 92 93 // and a test user 94 user := model.User{ 95 Email: MakeEmail(), 96 Nickname: model.NewId(), 97 Username: model.NewId(), 98 } 99 userPtr, err := ss.User().Save(&user) 100 require.NoError(t, err) 101 user = *userPtr 102 103 // the user was previously in the channel a long time ago, before the export period starts 104 // the existence of this record makes it look like the MessageExport feature has been active for awhile, and prevents 105 // us from looking in the ChannelMembers table for data that isn't found in the ChannelMemberHistory table 106 leaveTime := model.GetMillis() - 20000 107 joinTime := leaveTime - 10000 108 err = ss.ChannelMemberHistory().LogJoinEvent(user.Id, channel.Id, joinTime) 109 require.NoError(t, err) 110 err = ss.ChannelMemberHistory().LogLeaveEvent(user.Id, channel.Id, leaveTime) 111 require.NoError(t, err) 112 113 // log a join event 114 leaveTime = model.GetMillis() 115 joinTime = leaveTime - 10000 116 err = ss.ChannelMemberHistory().LogJoinEvent(user.Id, channel.Id, joinTime) 117 require.NoError(t, err) 118 119 // case 1: user joins and leaves the channel before the export period begins 120 channelMembers, err := ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime-500, joinTime-100, channel.Id) 121 require.NoError(t, err) 122 assert.Empty(t, channelMembers) 123 124 // case 2: user joins the channel after the export period begins, but has not yet left the channel when the export period ends 125 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime-100, joinTime+500, channel.Id) 126 require.NoError(t, err) 127 assert.Len(t, channelMembers, 1) 128 assert.Equal(t, channel.Id, channelMembers[0].ChannelId) 129 assert.Equal(t, user.Id, channelMembers[0].UserId) 130 assert.Equal(t, user.Email, channelMembers[0].UserEmail) 131 assert.Equal(t, user.Username, channelMembers[0].Username) 132 assert.Equal(t, joinTime, channelMembers[0].JoinTime) 133 assert.Nil(t, channelMembers[0].LeaveTime) 134 135 // case 3: user joins the channel before the export period begins, but has not yet left the channel when the export period ends 136 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime+100, joinTime+500, channel.Id) 137 require.NoError(t, err) 138 assert.Len(t, channelMembers, 1) 139 assert.Equal(t, channel.Id, channelMembers[0].ChannelId) 140 assert.Equal(t, user.Id, channelMembers[0].UserId) 141 assert.Equal(t, user.Email, channelMembers[0].UserEmail) 142 assert.Equal(t, user.Username, channelMembers[0].Username) 143 assert.Equal(t, joinTime, channelMembers[0].JoinTime) 144 assert.Nil(t, channelMembers[0].LeaveTime) 145 146 // add a leave time for the user 147 err = ss.ChannelMemberHistory().LogLeaveEvent(user.Id, channel.Id, leaveTime) 148 require.NoError(t, err) 149 150 // case 4: user joins the channel before the export period begins, but has not yet left the channel when the export period ends 151 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime+100, leaveTime-100, channel.Id) 152 require.NoError(t, err) 153 assert.Len(t, channelMembers, 1) 154 assert.Equal(t, channel.Id, channelMembers[0].ChannelId) 155 assert.Equal(t, user.Id, channelMembers[0].UserId) 156 assert.Equal(t, user.Email, channelMembers[0].UserEmail) 157 assert.Equal(t, user.Username, channelMembers[0].Username) 158 assert.Equal(t, joinTime, channelMembers[0].JoinTime) 159 assert.Equal(t, leaveTime, *channelMembers[0].LeaveTime) 160 161 // case 5: user joins the channel after the export period begins, and leaves the channel before the export period ends 162 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime-100, leaveTime+100, channel.Id) 163 require.NoError(t, err) 164 assert.Len(t, channelMembers, 1) 165 assert.Equal(t, channel.Id, channelMembers[0].ChannelId) 166 assert.Equal(t, user.Id, channelMembers[0].UserId) 167 assert.Equal(t, user.Email, channelMembers[0].UserEmail) 168 assert.Equal(t, user.Username, channelMembers[0].Username) 169 assert.Equal(t, joinTime, channelMembers[0].JoinTime) 170 assert.Equal(t, leaveTime, *channelMembers[0].LeaveTime) 171 172 // case 6: user has joined and left the channel long before the export period begins 173 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(leaveTime+100, leaveTime+200, channel.Id) 174 require.NoError(t, err) 175 assert.Empty(t, channelMembers) 176 } 177 178 func testGetUsersInChannelAtChannelMembers(t *testing.T, ss store.Store) { 179 // create a test channel 180 channel := &model.Channel{ 181 TeamId: model.NewId(), 182 DisplayName: "Display " + model.NewId(), 183 Name: "zz" + model.NewId() + "b", 184 Type: model.CHANNEL_OPEN, 185 } 186 channel, err := ss.Channel().Save(channel, -1) 187 require.NoError(t, err) 188 189 // and a test user 190 user := model.User{ 191 Email: MakeEmail(), 192 Nickname: model.NewId(), 193 Username: model.NewId(), 194 } 195 userPtr, err := ss.User().Save(&user) 196 require.NoError(t, err) 197 user = *userPtr 198 199 // clear any existing ChannelMemberHistory data that might interfere with our test 200 var tableDataTruncated = false 201 for !tableDataTruncated { 202 var count int64 203 count, _, err = ss.ChannelMemberHistory().PermanentDeleteBatchForRetentionPolicies( 204 0, model.GetMillis(), 1000, model.RetentionPolicyCursor{}) 205 require.NoError(t, err, "Failed to truncate ChannelMemberHistory contents") 206 tableDataTruncated = count == int64(0) 207 } 208 209 // in this test, we're pretending that Message Export was not activated during the export period, so there's no data 210 // available in the ChannelMemberHistory table. Instead, we'll fall back to the ChannelMembers table for a rough approximation 211 joinTime := int64(1000) 212 leaveTime := joinTime + 5000 213 _, err = ss.Channel().SaveMember(&model.ChannelMember{ 214 ChannelId: channel.Id, 215 UserId: user.Id, 216 NotifyProps: model.GetDefaultChannelNotifyProps(), 217 }) 218 require.NoError(t, err) 219 220 // in every single case, the user will be included in the export, because ChannelMembers says they were in the channel at some point in 221 // the past, even though the time that they were actually in the channel doesn't necessarily overlap with the export period 222 223 // case 1: user joins and leaves the channel before the export period begins 224 channelMembers, err := ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime-500, joinTime-100, channel.Id) 225 require.NoError(t, err) 226 assert.Len(t, channelMembers, 1) 227 assert.Equal(t, channel.Id, channelMembers[0].ChannelId) 228 assert.Equal(t, user.Id, channelMembers[0].UserId) 229 assert.Equal(t, user.Email, channelMembers[0].UserEmail) 230 assert.Equal(t, user.Username, channelMembers[0].Username) 231 assert.Equal(t, joinTime-500, channelMembers[0].JoinTime) 232 assert.Equal(t, joinTime-100, *channelMembers[0].LeaveTime) 233 234 // case 2: user joins the channel after the export period begins, but has not yet left the channel when the export period ends 235 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime-100, joinTime+500, channel.Id) 236 require.NoError(t, err) 237 assert.Len(t, channelMembers, 1) 238 assert.Equal(t, channel.Id, channelMembers[0].ChannelId) 239 assert.Equal(t, user.Id, channelMembers[0].UserId) 240 assert.Equal(t, user.Email, channelMembers[0].UserEmail) 241 assert.Equal(t, user.Username, channelMembers[0].Username) 242 assert.Equal(t, joinTime-100, channelMembers[0].JoinTime) 243 assert.Equal(t, joinTime+500, *channelMembers[0].LeaveTime) 244 245 // case 3: user joins the channel before the export period begins, but has not yet left the channel when the export period ends 246 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime+100, joinTime+500, channel.Id) 247 require.NoError(t, err) 248 assert.Len(t, channelMembers, 1) 249 assert.Equal(t, channel.Id, channelMembers[0].ChannelId) 250 assert.Equal(t, user.Id, channelMembers[0].UserId) 251 assert.Equal(t, user.Email, channelMembers[0].UserEmail) 252 assert.Equal(t, user.Username, channelMembers[0].Username) 253 assert.Equal(t, joinTime+100, channelMembers[0].JoinTime) 254 assert.Equal(t, joinTime+500, *channelMembers[0].LeaveTime) 255 256 // case 4: user joins the channel before the export period begins, but has not yet left the channel when the export period ends 257 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime+100, leaveTime-100, channel.Id) 258 require.NoError(t, err) 259 assert.Len(t, channelMembers, 1) 260 assert.Equal(t, channel.Id, channelMembers[0].ChannelId) 261 assert.Equal(t, user.Id, channelMembers[0].UserId) 262 assert.Equal(t, user.Email, channelMembers[0].UserEmail) 263 assert.Equal(t, user.Username, channelMembers[0].Username) 264 assert.Equal(t, joinTime+100, channelMembers[0].JoinTime) 265 assert.Equal(t, leaveTime-100, *channelMembers[0].LeaveTime) 266 267 // case 5: user joins the channel after the export period begins, and leaves the channel before the export period ends 268 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime-100, leaveTime+100, channel.Id) 269 require.NoError(t, err) 270 assert.Len(t, channelMembers, 1) 271 assert.Equal(t, channel.Id, channelMembers[0].ChannelId) 272 assert.Equal(t, user.Id, channelMembers[0].UserId) 273 assert.Equal(t, user.Email, channelMembers[0].UserEmail) 274 assert.Equal(t, user.Username, channelMembers[0].Username) 275 assert.Equal(t, joinTime-100, channelMembers[0].JoinTime) 276 assert.Equal(t, leaveTime+100, *channelMembers[0].LeaveTime) 277 278 // case 6: user has joined and left the channel long before the export period begins 279 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(leaveTime+100, leaveTime+200, channel.Id) 280 require.NoError(t, err) 281 assert.Len(t, channelMembers, 1) 282 assert.Equal(t, channel.Id, channelMembers[0].ChannelId) 283 assert.Equal(t, user.Id, channelMembers[0].UserId) 284 assert.Equal(t, user.Email, channelMembers[0].UserEmail) 285 assert.Equal(t, user.Username, channelMembers[0].Username) 286 assert.Equal(t, leaveTime+100, channelMembers[0].JoinTime) 287 assert.Equal(t, leaveTime+200, *channelMembers[0].LeaveTime) 288 } 289 290 func testPermanentDeleteBatch(t *testing.T, ss store.Store) { 291 // create a test channel 292 channel := &model.Channel{ 293 TeamId: model.NewId(), 294 DisplayName: "Display " + model.NewId(), 295 Name: "zz" + model.NewId() + "b", 296 Type: model.CHANNEL_OPEN, 297 } 298 channel, err := ss.Channel().Save(channel, -1) 299 require.NoError(t, err) 300 301 // and two test users 302 user := model.User{ 303 Email: MakeEmail(), 304 Nickname: model.NewId(), 305 Username: model.NewId(), 306 } 307 userPtr, err := ss.User().Save(&user) 308 require.NoError(t, err) 309 user = *userPtr 310 311 user2 := model.User{ 312 Email: MakeEmail(), 313 Nickname: model.NewId(), 314 Username: model.NewId(), 315 } 316 user2Ptr, err := ss.User().Save(&user2) 317 require.NoError(t, err) 318 user2 = *user2Ptr 319 320 // user1 joins and leaves the channel 321 leaveTime := model.GetMillis() 322 joinTime := leaveTime - 10000 323 err = ss.ChannelMemberHistory().LogJoinEvent(user.Id, channel.Id, joinTime) 324 require.NoError(t, err) 325 err = ss.ChannelMemberHistory().LogLeaveEvent(user.Id, channel.Id, leaveTime) 326 require.NoError(t, err) 327 328 // user2 joins the channel but never leaves 329 err = ss.ChannelMemberHistory().LogJoinEvent(user2.Id, channel.Id, joinTime) 330 require.NoError(t, err) 331 332 // in between the join time and the leave time, both users were members of the channel 333 channelMembers, err := ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime+10, leaveTime-10, channel.Id) 334 require.NoError(t, err) 335 assert.Len(t, channelMembers, 2) 336 337 // the permanent delete should delete at least one record 338 rowsDeleted, _, err := ss.ChannelMemberHistory().PermanentDeleteBatchForRetentionPolicies( 339 0, leaveTime+1, math.MaxInt64, model.RetentionPolicyCursor{}) 340 require.NoError(t, err) 341 assert.NotEqual(t, int64(0), rowsDeleted) 342 343 // after the delete, there should be one less member in the channel 344 channelMembers, err = ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime+10, leaveTime-10, channel.Id) 345 require.NoError(t, err) 346 assert.Len(t, channelMembers, 1) 347 assert.Equal(t, user2.Id, channelMembers[0].UserId) 348 } 349 350 func testPermanentDeleteBatchForRetentionPolicies(t *testing.T, ss store.Store) { 351 const limit = 1000 352 team, err := ss.Team().Save(&model.Team{ 353 DisplayName: "DisplayName", 354 Name: "team" + model.NewId(), 355 Email: MakeEmail(), 356 Type: model.TEAM_OPEN, 357 }) 358 require.NoError(t, err) 359 channel, err := ss.Channel().Save(&model.Channel{ 360 TeamId: team.Id, 361 DisplayName: "DisplayName", 362 Name: "channel" + model.NewId(), 363 Type: model.CHANNEL_OPEN, 364 }, -1) 365 require.NoError(t, err) 366 userID := model.NewId() 367 368 joinTime := int64(1000) 369 leaveTime := int64(1500) 370 err = ss.ChannelMemberHistory().LogJoinEvent(userID, channel.Id, joinTime) 371 require.NoError(t, err) 372 err = ss.ChannelMemberHistory().LogLeaveEvent(userID, channel.Id, leaveTime) 373 require.NoError(t, err) 374 375 channelPolicy, err := ss.RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{ 376 RetentionPolicy: model.RetentionPolicy{ 377 DisplayName: "DisplayName", 378 PostDuration: model.NewInt64(30), 379 }, 380 ChannelIDs: []string{channel.Id}, 381 }) 382 require.NoError(t, err) 383 384 nowMillis := leaveTime + *channelPolicy.PostDuration*24*60*60*1000 + 1 385 _, _, err = ss.ChannelMemberHistory().PermanentDeleteBatchForRetentionPolicies( 386 nowMillis, 0, limit, model.RetentionPolicyCursor{}) 387 require.NoError(t, err) 388 result, err := ss.ChannelMemberHistory().GetUsersInChannelDuring(joinTime, leaveTime, channel.Id) 389 require.NoError(t, err) 390 require.Empty(t, result, "history should have been deleted by channel policy") 391 }