github.com/mattermost/mattermost-server/v5@v5.39.3/store/storetest/thread_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 "context" 8 "testing" 9 "time" 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 TestThreadStore(t *testing.T, ss store.Store, s SqlStore) { 19 t.Run("ThreadSQLOperations", func(t *testing.T) { testThreadSQLOperations(t, ss, s) }) 20 t.Run("ThreadStorePopulation", func(t *testing.T) { testThreadStorePopulation(t, ss) }) 21 t.Run("ThreadStorePermanentDeleteBatchForRetentionPolicies", func(t *testing.T) { 22 testThreadStorePermanentDeleteBatchForRetentionPolicies(t, ss) 23 }) 24 t.Run("ThreadStorePermanentDeleteBatchThreadMembershipsForRetentionPolicies", func(t *testing.T) { 25 testThreadStorePermanentDeleteBatchThreadMembershipsForRetentionPolicies(t, ss) 26 }) 27 } 28 29 func testThreadStorePopulation(t *testing.T, ss store.Store) { 30 makeSomePosts := func() []*model.Post { 31 32 u1 := model.User{ 33 Email: MakeEmail(), 34 Username: model.NewId(), 35 } 36 37 u, err := ss.User().Save(&u1) 38 require.NoError(t, err) 39 40 c, err2 := ss.Channel().Save(&model.Channel{ 41 DisplayName: model.NewId(), 42 Type: model.CHANNEL_OPEN, 43 Name: model.NewId(), 44 }, 999) 45 require.NoError(t, err2) 46 47 _, err44 := ss.Channel().SaveMember(&model.ChannelMember{ 48 ChannelId: c.Id, 49 UserId: u1.Id, 50 NotifyProps: model.GetDefaultChannelNotifyProps(), 51 MsgCount: 0, 52 }) 53 require.NoError(t, err44) 54 o := model.Post{} 55 o.ChannelId = c.Id 56 o.UserId = u.Id 57 o.Message = "zz" + model.NewId() + "b" 58 59 otmp, err3 := ss.Post().Save(&o) 60 require.NoError(t, err3) 61 o2 := model.Post{} 62 o2.ChannelId = c.Id 63 o2.UserId = model.NewId() 64 o2.RootId = otmp.Id 65 o2.Message = "zz" + model.NewId() + "b" 66 67 o3 := model.Post{} 68 o3.ChannelId = c.Id 69 o3.UserId = u.Id 70 o3.RootId = otmp.Id 71 o3.Message = "zz" + model.NewId() + "b" 72 73 o4 := model.Post{} 74 o4.ChannelId = c.Id 75 o4.UserId = model.NewId() 76 o4.Message = "zz" + model.NewId() + "b" 77 78 newPosts, errIdx, err3 := ss.Post().SaveMultiple([]*model.Post{&o2, &o3, &o4}) 79 80 olist, _ := ss.Post().Get(context.Background(), otmp.Id, true, false, false, "") 81 o1 := olist.Posts[olist.Order[0]] 82 83 newPosts = append([]*model.Post{o1}, newPosts...) 84 require.NoError(t, err3, "couldn't save item") 85 require.Equal(t, -1, errIdx) 86 require.Len(t, newPosts, 4) 87 require.Equal(t, int64(2), newPosts[0].ReplyCount) 88 require.Equal(t, int64(2), newPosts[1].ReplyCount) 89 require.Equal(t, int64(2), newPosts[2].ReplyCount) 90 require.Equal(t, int64(0), newPosts[3].ReplyCount) 91 92 return newPosts 93 } 94 t.Run("Save replies creates a thread", func(t *testing.T) { 95 newPosts := makeSomePosts() 96 thread, err := ss.Thread().Get(newPosts[0].Id) 97 require.NoError(t, err, "couldn't get thread") 98 require.NotNil(t, thread) 99 require.Equal(t, int64(2), thread.ReplyCount) 100 require.ElementsMatch(t, model.StringArray{newPosts[0].UserId, newPosts[1].UserId}, thread.Participants) 101 102 o5 := model.Post{} 103 o5.ChannelId = model.NewId() 104 o5.UserId = model.NewId() 105 o5.RootId = newPosts[0].Id 106 o5.Message = "zz" + model.NewId() + "b" 107 108 _, _, err = ss.Post().SaveMultiple([]*model.Post{&o5}) 109 require.NoError(t, err, "couldn't save item") 110 111 thread, err = ss.Thread().Get(newPosts[0].Id) 112 require.NoError(t, err, "couldn't get thread") 113 require.NotNil(t, thread) 114 require.Equal(t, int64(3), thread.ReplyCount) 115 require.ElementsMatch(t, model.StringArray{newPosts[0].UserId, newPosts[1].UserId, o5.UserId}, thread.Participants) 116 }) 117 118 t.Run("Delete a reply updates count on a thread", func(t *testing.T) { 119 newPosts := makeSomePosts() 120 thread, err := ss.Thread().Get(newPosts[0].Id) 121 require.NoError(t, err, "couldn't get thread") 122 require.NotNil(t, thread) 123 require.Equal(t, int64(2), thread.ReplyCount) 124 require.ElementsMatch(t, model.StringArray{newPosts[0].UserId, newPosts[1].UserId}, thread.Participants) 125 126 err = ss.Post().Delete(newPosts[1].Id, 1234, model.NewId()) 127 require.NoError(t, err, "couldn't delete post") 128 129 thread, err = ss.Thread().Get(newPosts[0].Id) 130 require.NoError(t, err, "couldn't get thread") 131 require.NotNil(t, thread) 132 require.Equal(t, int64(1), thread.ReplyCount) 133 require.ElementsMatch(t, model.StringArray{newPosts[0].UserId, newPosts[1].UserId}, thread.Participants) 134 }) 135 136 t.Run("Update reply should update the UpdateAt of the thread", func(t *testing.T) { 137 rootPost := model.Post{} 138 rootPost.RootId = model.NewId() 139 rootPost.ChannelId = model.NewId() 140 rootPost.UserId = model.NewId() 141 rootPost.Message = "zz" + model.NewId() + "b" 142 143 replyPost := model.Post{} 144 replyPost.ChannelId = rootPost.ChannelId 145 replyPost.UserId = model.NewId() 146 replyPost.Message = "zz" + model.NewId() + "b" 147 replyPost.RootId = rootPost.RootId 148 149 newPosts, _, err := ss.Post().SaveMultiple([]*model.Post{&rootPost, &replyPost}) 150 require.NoError(t, err) 151 152 thread1, err := ss.Thread().Get(newPosts[0].RootId) 153 require.NoError(t, err) 154 155 rrootPost, err := ss.Post().GetSingle(rootPost.Id, false) 156 require.NoError(t, err) 157 require.Equal(t, rrootPost.UpdateAt, rootPost.UpdateAt) 158 159 replyPost2 := model.Post{} 160 replyPost2.ChannelId = rootPost.ChannelId 161 replyPost2.UserId = model.NewId() 162 replyPost2.Message = "zz" + model.NewId() + "b" 163 replyPost2.RootId = rootPost.Id 164 165 replyPost3 := model.Post{} 166 replyPost3.ChannelId = rootPost.ChannelId 167 replyPost3.UserId = model.NewId() 168 replyPost3.Message = "zz" + model.NewId() + "b" 169 replyPost3.RootId = rootPost.Id 170 171 _, _, err = ss.Post().SaveMultiple([]*model.Post{&replyPost2, &replyPost3}) 172 require.NoError(t, err) 173 174 rrootPost2, err := ss.Post().GetSingle(rootPost.Id, false) 175 require.NoError(t, err) 176 require.Greater(t, rrootPost2.UpdateAt, rrootPost.UpdateAt) 177 178 thread2, err := ss.Thread().Get(rootPost.Id) 179 require.NoError(t, err) 180 require.Greater(t, thread2.LastReplyAt, thread1.LastReplyAt) 181 }) 182 183 t.Run("Deleting reply should update the thread", func(t *testing.T) { 184 rootPost := model.Post{} 185 rootPost.RootId = model.NewId() 186 rootPost.ChannelId = model.NewId() 187 rootPost.UserId = model.NewId() 188 rootPost.Message = "zz" + model.NewId() + "b" 189 190 replyPost := model.Post{} 191 replyPost.ChannelId = rootPost.ChannelId 192 replyPost.UserId = model.NewId() 193 replyPost.Message = "zz" + model.NewId() + "b" 194 replyPost.RootId = rootPost.RootId 195 196 newPosts, _, err := ss.Post().SaveMultiple([]*model.Post{&rootPost, &replyPost}) 197 require.NoError(t, err) 198 199 thread1, err := ss.Thread().Get(newPosts[0].RootId) 200 require.NoError(t, err) 201 require.EqualValues(t, thread1.ReplyCount, 2) 202 require.Len(t, thread1.Participants, 2) 203 204 err = ss.Post().Delete(replyPost.Id, 123, model.NewId()) 205 require.NoError(t, err) 206 207 thread2, err := ss.Thread().Get(rootPost.RootId) 208 require.NoError(t, err) 209 require.EqualValues(t, thread2.ReplyCount, 1) 210 require.Len(t, thread2.Participants, 2) 211 }) 212 213 t.Run("Deleting root post should delete the thread", func(t *testing.T) { 214 rootPost := model.Post{} 215 rootPost.ChannelId = model.NewId() 216 rootPost.UserId = model.NewId() 217 rootPost.Message = "zz" + model.NewId() + "b" 218 219 newPosts1, _, err := ss.Post().SaveMultiple([]*model.Post{&rootPost}) 220 require.NoError(t, err) 221 222 replyPost := model.Post{} 223 replyPost.ChannelId = rootPost.ChannelId 224 replyPost.UserId = model.NewId() 225 replyPost.Message = "zz" + model.NewId() + "b" 226 replyPost.RootId = newPosts1[0].Id 227 228 _, _, err = ss.Post().SaveMultiple([]*model.Post{&replyPost}) 229 require.NoError(t, err) 230 231 thread1, err := ss.Thread().Get(newPosts1[0].Id) 232 require.NoError(t, err) 233 require.EqualValues(t, thread1.ReplyCount, 1) 234 require.Len(t, thread1.Participants, 2) 235 236 err = ss.Post().PermanentDeleteByUser(rootPost.UserId) 237 require.NoError(t, err) 238 239 thread2, _ := ss.Thread().Get(rootPost.Id) 240 require.Nil(t, thread2) 241 }) 242 243 t.Run("Thread last updated is changed when channel is updated after UpdateLastViewedAtPost", func(t *testing.T) { 244 newPosts := makeSomePosts() 245 opts := store.ThreadMembershipOpts{ 246 Following: true, 247 IncrementMentions: false, 248 UpdateFollowing: true, 249 UpdateViewedTimestamp: false, 250 UpdateParticipants: false, 251 } 252 _, e := ss.Thread().MaintainMembership(newPosts[0].UserId, newPosts[0].Id, opts) 253 require.NoError(t, e) 254 m, err1 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id) 255 require.NoError(t, err1) 256 m.LastUpdated -= 1000 257 _, err := ss.Thread().UpdateMembership(m) 258 require.NoError(t, err) 259 260 _, err = ss.Channel().UpdateLastViewedAtPost(newPosts[0], newPosts[0].UserId, 0, 0, true, true) 261 require.NoError(t, err) 262 263 assert.Eventually(t, func() bool { 264 m2, err2 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id) 265 require.NoError(t, err2) 266 return m2.LastUpdated > m.LastUpdated 267 }, time.Second, 10*time.Millisecond) 268 }) 269 270 t.Run("Thread last updated is changed when channel is updated after IncrementMentionCount", func(t *testing.T) { 271 newPosts := makeSomePosts() 272 273 opts := store.ThreadMembershipOpts{ 274 Following: true, 275 IncrementMentions: false, 276 UpdateFollowing: true, 277 UpdateViewedTimestamp: false, 278 UpdateParticipants: false, 279 } 280 _, e := ss.Thread().MaintainMembership(newPosts[0].UserId, newPosts[0].Id, opts) 281 require.NoError(t, e) 282 m, err1 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id) 283 require.NoError(t, err1) 284 m.LastUpdated -= 1000 285 _, err := ss.Thread().UpdateMembership(m) 286 require.NoError(t, err) 287 288 err = ss.Channel().IncrementMentionCount(newPosts[0].ChannelId, newPosts[0].UserId, true, false) 289 require.NoError(t, err) 290 291 assert.Eventually(t, func() bool { 292 m2, err2 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id) 293 require.NoError(t, err2) 294 return m2.LastUpdated > m.LastUpdated 295 }, time.Second, 10*time.Millisecond) 296 }) 297 298 t.Run("Thread last updated is changed when channel is updated after UpdateLastViewedAt", func(t *testing.T) { 299 newPosts := makeSomePosts() 300 opts := store.ThreadMembershipOpts{ 301 Following: true, 302 IncrementMentions: false, 303 UpdateFollowing: true, 304 UpdateViewedTimestamp: false, 305 UpdateParticipants: false, 306 } 307 _, e := ss.Thread().MaintainMembership(newPosts[0].UserId, newPosts[0].Id, opts) 308 require.NoError(t, e) 309 m, err1 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id) 310 require.NoError(t, err1) 311 m.LastUpdated -= 1000 312 _, err := ss.Thread().UpdateMembership(m) 313 require.NoError(t, err) 314 315 _, err = ss.Channel().UpdateLastViewedAt([]string{newPosts[0].ChannelId}, newPosts[0].UserId, true) 316 require.NoError(t, err) 317 318 assert.Eventually(t, func() bool { 319 m2, err2 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id) 320 require.NoError(t, err2) 321 return m2.LastUpdated > m.LastUpdated 322 }, time.Second, 10*time.Millisecond) 323 }) 324 325 t.Run("Thread membership 'viewed' timestamp is updated properly", func(t *testing.T) { 326 newPosts := makeSomePosts() 327 328 opts := store.ThreadMembershipOpts{ 329 Following: true, 330 IncrementMentions: false, 331 UpdateFollowing: true, 332 UpdateViewedTimestamp: false, 333 UpdateParticipants: false, 334 } 335 tm, e := ss.Thread().MaintainMembership(newPosts[0].UserId, newPosts[0].Id, opts) 336 require.NoError(t, e) 337 require.Equal(t, int64(0), tm.LastViewed) 338 339 opts.UpdateViewedTimestamp = true 340 _, e = ss.Thread().MaintainMembership(newPosts[0].UserId, newPosts[0].Id, opts) 341 require.NoError(t, e) 342 m2, err2 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id) 343 require.NoError(t, err2) 344 require.Greater(t, m2.LastViewed, int64(0)) 345 }) 346 347 t.Run("Thread membership 'viewed' timestamp is updated properly for new membership", func(t *testing.T) { 348 newPosts := makeSomePosts() 349 350 opts := store.ThreadMembershipOpts{ 351 Following: true, 352 IncrementMentions: false, 353 UpdateFollowing: false, 354 UpdateViewedTimestamp: true, 355 UpdateParticipants: false, 356 } 357 tm, e := ss.Thread().MaintainMembership(newPosts[0].UserId, newPosts[0].Id, opts) 358 require.NoError(t, e) 359 require.NotEqual(t, int64(0), tm.LastViewed) 360 }) 361 362 t.Run("Thread last updated is changed when channel is updated after UpdateLastViewedAtPost for mark unread", func(t *testing.T) { 363 newPosts := makeSomePosts() 364 opts := store.ThreadMembershipOpts{ 365 Following: true, 366 IncrementMentions: false, 367 UpdateFollowing: true, 368 UpdateViewedTimestamp: false, 369 UpdateParticipants: false, 370 } 371 _, e := ss.Thread().MaintainMembership(newPosts[0].UserId, newPosts[0].Id, opts) 372 require.NoError(t, e) 373 m, err1 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id) 374 require.NoError(t, err1) 375 m.LastUpdated += 1000 376 _, err := ss.Thread().UpdateMembership(m) 377 require.NoError(t, err) 378 379 _, err = ss.Channel().UpdateLastViewedAtPost(newPosts[0], newPosts[0].UserId, 0, 0, true, true) 380 require.NoError(t, err) 381 382 assert.Eventually(t, func() bool { 383 m2, err2 := ss.Thread().GetMembershipForUser(newPosts[0].UserId, newPosts[0].Id) 384 require.NoError(t, err2) 385 return m2.LastUpdated < m.LastUpdated 386 }, time.Second, 10*time.Millisecond) 387 }) 388 389 t.Run("Updating post does not make thread unread", func(t *testing.T) { 390 newPosts := makeSomePosts() 391 opts := store.ThreadMembershipOpts{ 392 Following: true, 393 IncrementMentions: false, 394 UpdateFollowing: true, 395 UpdateViewedTimestamp: false, 396 UpdateParticipants: false, 397 } 398 m, err := ss.Thread().MaintainMembership(newPosts[0].UserId, newPosts[0].Id, opts) 399 require.NoError(t, err) 400 th, err := ss.Thread().GetThreadForUser("", m, false) 401 require.NoError(t, err) 402 require.Equal(t, int64(2), th.UnreadReplies) 403 404 m.LastViewed = newPosts[2].UpdateAt + 1 405 _, err = ss.Thread().UpdateMembership(m) 406 require.NoError(t, err) 407 th, err = ss.Thread().GetThreadForUser("", m, false) 408 require.NoError(t, err) 409 require.Equal(t, int64(0), th.UnreadReplies) 410 411 editedPost := newPosts[2].Clone() 412 editedPost.Message = "This is an edited post" 413 _, err = ss.Post().Update(editedPost, newPosts[2]) 414 require.NoError(t, err) 415 416 th, err = ss.Thread().GetThreadForUser("", m, false) 417 require.NoError(t, err) 418 require.Equal(t, int64(0), th.UnreadReplies) 419 }) 420 } 421 422 func testThreadSQLOperations(t *testing.T, ss store.Store, s SqlStore) { 423 t.Run("Save", func(t *testing.T) { 424 threadToSave := &model.Thread{ 425 PostId: model.NewId(), 426 ChannelId: model.NewId(), 427 LastReplyAt: 10, 428 ReplyCount: 5, 429 Participants: model.StringArray{model.NewId(), model.NewId()}, 430 } 431 _, err := ss.Thread().Save(threadToSave) 432 require.NoError(t, err) 433 434 th, err := ss.Thread().Get(threadToSave.PostId) 435 require.NoError(t, err) 436 require.Equal(t, threadToSave, th) 437 }) 438 } 439 440 func threadStoreCreateReply(t *testing.T, ss store.Store, channelID, postID string, createAt int64) *model.Post { 441 reply, err := ss.Post().Save(&model.Post{ 442 ChannelId: channelID, 443 UserId: model.NewId(), 444 CreateAt: createAt, 445 RootId: postID, 446 ParentId: postID, 447 }) 448 require.NoError(t, err) 449 return reply 450 } 451 452 func testThreadStorePermanentDeleteBatchForRetentionPolicies(t *testing.T, ss store.Store) { 453 const limit = 1000 454 team, err := ss.Team().Save(&model.Team{ 455 DisplayName: "DisplayName", 456 Name: "team" + model.NewId(), 457 Email: MakeEmail(), 458 Type: model.TEAM_OPEN, 459 }) 460 require.NoError(t, err) 461 channel, err := ss.Channel().Save(&model.Channel{ 462 TeamId: team.Id, 463 DisplayName: "DisplayName", 464 Name: "channel" + model.NewId(), 465 Type: model.CHANNEL_OPEN, 466 }, -1) 467 require.NoError(t, err) 468 469 post, err := ss.Post().Save(&model.Post{ 470 ChannelId: channel.Id, 471 UserId: model.NewId(), 472 }) 473 require.NoError(t, err) 474 threadStoreCreateReply(t, ss, channel.Id, post.Id, 2000) 475 476 thread, err := ss.Thread().Get(post.Id) 477 require.NoError(t, err) 478 479 channelPolicy, err := ss.RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{ 480 RetentionPolicy: model.RetentionPolicy{ 481 DisplayName: "DisplayName", 482 PostDuration: model.NewInt64(30), 483 }, 484 ChannelIDs: []string{channel.Id}, 485 }) 486 require.NoError(t, err) 487 488 nowMillis := thread.LastReplyAt + *channelPolicy.PostDuration*24*60*60*1000 + 1 489 _, _, err = ss.Thread().PermanentDeleteBatchForRetentionPolicies(nowMillis, 0, limit, model.RetentionPolicyCursor{}) 490 require.NoError(t, err) 491 thread, err = ss.Thread().Get(post.Id) 492 assert.NoError(t, err) 493 assert.Nil(t, thread, "thread should have been deleted by channel policy") 494 495 // create a new thread 496 threadStoreCreateReply(t, ss, channel.Id, post.Id, 2000) 497 thread, err = ss.Thread().Get(post.Id) 498 require.NoError(t, err) 499 500 // Create a team policy which is stricter than the channel policy 501 teamPolicy, err := ss.RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{ 502 RetentionPolicy: model.RetentionPolicy{ 503 DisplayName: "DisplayName", 504 PostDuration: model.NewInt64(20), 505 }, 506 TeamIDs: []string{team.Id}, 507 }) 508 require.NoError(t, err) 509 510 nowMillis = thread.LastReplyAt + *teamPolicy.PostDuration*24*60*60*1000 + 1 511 _, _, err = ss.Thread().PermanentDeleteBatchForRetentionPolicies(nowMillis, 0, limit, model.RetentionPolicyCursor{}) 512 require.NoError(t, err) 513 _, err = ss.Thread().Get(post.Id) 514 require.NoError(t, err, "channel policy should have overridden team policy") 515 516 // Delete channel policy and re-run team policy 517 err = ss.RetentionPolicy().Delete(channelPolicy.ID) 518 require.NoError(t, err) 519 _, _, err = ss.Thread().PermanentDeleteBatchForRetentionPolicies(nowMillis, 0, limit, model.RetentionPolicyCursor{}) 520 require.NoError(t, err) 521 thread, err = ss.Thread().Get(post.Id) 522 assert.NoError(t, err) 523 assert.Nil(t, thread, "thread should have been deleted by team policy") 524 } 525 526 func testThreadStorePermanentDeleteBatchThreadMembershipsForRetentionPolicies(t *testing.T, ss store.Store) { 527 const limit = 1000 528 userID := model.NewId() 529 createThreadMembership := func(userID, postID string) *model.ThreadMembership { 530 opts := store.ThreadMembershipOpts{ 531 Following: true, 532 IncrementMentions: false, 533 UpdateFollowing: true, 534 UpdateViewedTimestamp: false, 535 UpdateParticipants: false, 536 } 537 _, err := ss.Thread().MaintainMembership(userID, postID, opts) 538 require.NoError(t, err) 539 threadMembership, err := ss.Thread().GetMembershipForUser(userID, postID) 540 require.NoError(t, err) 541 return threadMembership 542 } 543 team, err := ss.Team().Save(&model.Team{ 544 DisplayName: "DisplayName", 545 Name: "team" + model.NewId(), 546 Email: MakeEmail(), 547 Type: model.TEAM_OPEN, 548 }) 549 require.NoError(t, err) 550 channel, err := ss.Channel().Save(&model.Channel{ 551 TeamId: team.Id, 552 DisplayName: "DisplayName", 553 Name: "channel" + model.NewId(), 554 Type: model.CHANNEL_OPEN, 555 }, -1) 556 require.NoError(t, err) 557 post, err := ss.Post().Save(&model.Post{ 558 ChannelId: channel.Id, 559 UserId: model.NewId(), 560 }) 561 require.NoError(t, err) 562 threadStoreCreateReply(t, ss, channel.Id, post.Id, 2000) 563 564 threadMembership := createThreadMembership(userID, post.Id) 565 566 channelPolicy, err := ss.RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{ 567 RetentionPolicy: model.RetentionPolicy{ 568 DisplayName: "DisplayName", 569 PostDuration: model.NewInt64(30), 570 }, 571 ChannelIDs: []string{channel.Id}, 572 }) 573 require.NoError(t, err) 574 575 nowMillis := threadMembership.LastUpdated + *channelPolicy.PostDuration*24*60*60*1000 + 1 576 _, _, err = ss.Thread().PermanentDeleteBatchThreadMembershipsForRetentionPolicies(nowMillis, 0, limit, model.RetentionPolicyCursor{}) 577 require.NoError(t, err) 578 _, err = ss.Thread().GetMembershipForUser(userID, post.Id) 579 require.Error(t, err, "thread membership should have been deleted by channel policy") 580 581 // create a new thread membership 582 threadMembership = createThreadMembership(userID, post.Id) 583 584 // Create a team policy which is stricter than the channel policy 585 teamPolicy, err := ss.RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{ 586 RetentionPolicy: model.RetentionPolicy{ 587 DisplayName: "DisplayName", 588 PostDuration: model.NewInt64(20), 589 }, 590 TeamIDs: []string{team.Id}, 591 }) 592 require.NoError(t, err) 593 594 nowMillis = threadMembership.LastUpdated + *teamPolicy.PostDuration*24*60*60*1000 + 1 595 _, _, err = ss.Thread().PermanentDeleteBatchThreadMembershipsForRetentionPolicies(nowMillis, 0, limit, model.RetentionPolicyCursor{}) 596 require.NoError(t, err) 597 _, err = ss.Thread().GetMembershipForUser(userID, post.Id) 598 require.NoError(t, err, "channel policy should have overridden team policy") 599 600 // Delete channel policy and re-run team policy 601 err = ss.RetentionPolicy().Delete(channelPolicy.ID) 602 require.NoError(t, err) 603 _, _, err = ss.Thread().PermanentDeleteBatchThreadMembershipsForRetentionPolicies(nowMillis, 0, limit, model.RetentionPolicyCursor{}) 604 require.NoError(t, err) 605 _, err = ss.Thread().GetMembershipForUser(userID, post.Id) 606 require.Error(t, err, "thread membership should have been deleted by team policy") 607 608 // create a new thread membership 609 createThreadMembership(userID, post.Id) 610 611 // Delete team policy and thread 612 err = ss.RetentionPolicy().Delete(teamPolicy.ID) 613 require.NoError(t, err) 614 err = ss.Thread().Delete(post.Id) 615 require.NoError(t, err) 616 617 deleted, err := ss.Thread().DeleteOrphanedRows(1000) 618 require.NoError(t, err) 619 require.NotZero(t, deleted) 620 _, err = ss.Thread().GetMembershipForUser(userID, post.Id) 621 require.Error(t, err, "thread membership should have been deleted because thread no longer exists") 622 }