FIX: Update first_pm_unread_at of user's groups without unread.

FIX: Update first_pm_unread_at of user’s groups without unread.

If a user always read all group messages, we will never update the first_pm_unread_at column since the previous query will not return the group_user. Instead, we should update first_pm_unread_at to the current timestamp if the user has read everything.

Follow-up to 9b75d95fc616ea51181d622182b0f74dea8694ac

diff --git a/app/models/group_user.rb b/app/models/group_user.rb
index d83b060..820bbcc 100644
--- a/app/models/group_user.rb
+++ b/app/models/group_user.rb
@@ -24,7 +24,7 @@ class GroupUser < ActiveRecord::Base
   end
 
   def self.update_first_unread_pm(last_seen, limit: 10_000)
-    DB.exec(<<~SQL, archetype: Archetype.private_message, last_seen: last_seen, limit: limit)
+    DB.exec(<<~SQL, archetype: Archetype.private_message, last_seen: last_seen, limit: limit, now: 10.minutes.ago)
     UPDATE group_users gu
     SET first_unread_pm_at = Y.min_date
     FROM (
@@ -34,23 +34,30 @@ class GroupUser < ActiveRecord::Base
         X.min_date
       FROM (
         SELECT
-          gu2.group_id,
-          gu2.user_id,
-          MIN(t.updated_at) min_date
-        FROM group_users gu2
-        INNER JOIN topic_allowed_groups tag ON tag.group_id = gu2.group_id
-        INNER JOIN topics t ON t.id = tag.topic_id
-        INNER JOIN users u ON u.id = gu2.user_id
-        LEFT JOIN topic_users tu ON t.id = tu.topic_id AND tu.user_id = gu2.user_id
-        WHERE t.deleted_at IS NULL
-        AND t.archetype = :archetype
-        AND tu.last_read_post_number < CASE
-                                       WHEN u.admin OR u.moderator
-                                       THEN t.highest_staff_post_number
-                                       ELSE t.highest_post_number
-                                       END
-        AND (COALESCE(tu.notification_level, 1) >= 2)
-        GROUP BY gu2.user_id, gu2.group_id
+          gu.group_id,
+          gu.user_id,
+          COALESCE(Z.min_date, :now) min_date
+        FROM group_users gu
+        LEFT JOIN (
+          SELECT
+            gu2.group_id,
+            gu2.user_id,
+            MIN(t.updated_at) min_date
+          FROM group_users gu2
+          INNER JOIN topic_allowed_groups tag ON tag.group_id = gu2.group_id
+          INNER JOIN topics t ON t.id = tag.topic_id
+          INNER JOIN users u ON u.id = gu2.user_id
+          LEFT JOIN topic_users tu ON t.id = tu.topic_id AND tu.user_id = gu2.user_id
+          WHERE t.deleted_at IS NULL
+          AND t.archetype = :archetype
+          AND tu.last_read_post_number < CASE
+                                         WHEN u.admin OR u.moderator
+                                         THEN t.highest_staff_post_number
+                                         ELSE t.highest_post_number
+                                         END
+          AND (COALESCE(tu.notification_level, 1) >= 2)
+          GROUP BY gu2.user_id, gu2.group_id
+        ) AS Z ON Z.user_id = gu.user_id AND Z.group_id = gu.group_id
       ) AS X
       WHERE X.user_id IN (
         SELECT id
diff --git a/spec/models/group_user_spec.rb b/spec/models/group_user_spec.rb
index fca2c2c..2f7a53a 100644
--- a/spec/models/group_user_spec.rb
+++ b/spec/models/group_user_spec.rb
@@ -163,6 +163,7 @@ describe GroupUser do
 
   describe '#ensure_consistency!' do
     fab!(:group) { Fabricate(:group) }
+    fab!(:group_2) { Fabricate(:group) }
 
     fab!(:pm_post) { Fabricate(:private_message_post) }
 
@@ -173,6 +174,7 @@ describe GroupUser do
     fab!(:user) do
       Fabricate(:user, last_seen_at: Time.zone.now).tap do |u|
         group.add(u)
+        group_2.add(u)
 
         TopicUser.change(u.id, pm_topic.id,
           notification_level: TopicUser.notification_levels[:tracking],
@@ -213,11 +215,13 @@ describe GroupUser do
         topic_id: pm_topic.id
       )
 
-      GroupUser.ensure_consistency!
+      expect { GroupUser.ensure_consistency! }
+        .to_not change { group.group_users.find_by(user_id: user_3.id).first_unread_pm_at }
 
+      expect(post.topic.updated_at).to_not eq(10.minutes.ago)
       expect(group.group_users.find_by(user_id: user.id).first_unread_pm_at).to eq_time(post.topic.updated_at)
-      expect(group.group_users.find_by(user_id: user_2.id).first_unread_pm_at).to_not eq_time(post.topic.updated_at)
-      expect(group.group_users.find_by(user_id: user_3.id).first_unread_pm_at).to_not eq_time(post.topic.updated_at)
+      expect(group_2.group_users.find_by(user_id: user.id).first_unread_pm_at).to eq_time(10.minutes.ago)
+      expect(group.group_users.find_by(user_id: user_2.id).first_unread_pm_at).to eq_time(10.minutes.ago)
     end
   end
 end

GitHub sha: f27de87b