# frozen_string_literal: true describe Chat::ChatChannelFetcher do fab!(:category) { Fabricate(:category, name: "support") } fab!(:private_category) { Fabricate(:private_category, group: Fabricate(:group)) } fab!(:category_channel) { Fabricate(:category_channel, chatable: category, slug: "support") } fab!(:dm_channel1) { Fabricate(:direct_message) } fab!(:dm_channel2) { Fabricate(:direct_message) } fab!(:direct_message_channel1) { Fabricate(:direct_message_channel, chatable: dm_channel1) } fab!(:direct_message_channel2) { Fabricate(:direct_message_channel, chatable: dm_channel2) } fab!(:user1) { Fabricate(:user) } fab!(:user2) { Fabricate(:user) } def guardian Guardian.new(user1) end def memberships UserChatChannelMembership.where(user: user1) end describe ".structured" do it "returns open channel only" do category_channel.user_chat_channel_memberships.create!(user: user1, following: true) channels = subject.structured(guardian)[:public_channels] expect(channels).to contain_exactly(category_channel) category_channel.closed!(Discourse.system_user) channels = subject.structured(guardian)[:public_channels] expect(channels).to be_blank end it "returns followed channel only" do channels = subject.structured(guardian)[:public_channels] expect(channels).to be_blank category_channel.user_chat_channel_memberships.create!(user: user1, following: true) channels = subject.structured(guardian)[:public_channels] expect(channels).to contain_exactly(category_channel) end end describe ".unread_counts" do context "when user is member of the channel" do before do Fabricate(:user_chat_channel_membership, chat_channel: category_channel, user: user1) end context "with unread messages" do before do Fabricate(:chat_message, chat_channel: category_channel, message: "hi", user: user2) Fabricate(:chat_message, chat_channel: category_channel, message: "bonjour", user: user2) end it "returns the correct count" do unread_counts = subject.unread_counts([category_channel], user1) expect(unread_counts[category_channel.id]).to eq(2) end end context "with no unread messages" do it "returns the correct count" do unread_counts = subject.unread_counts([category_channel], user1) expect(unread_counts[category_channel.id]).to eq(0) end end context "when last unread message has been deleted" do fab!(:last_unread) do Fabricate(:chat_message, chat_channel: category_channel, message: "hi", user: user2) end before { last_unread.update!(deleted_at: Time.zone.now) } it "returns the correct count" do unread_counts = subject.unread_counts([category_channel], user1) expect(unread_counts[category_channel.id]).to eq(0) end end end context "when user is not member of the channel" do context "when the channel has new messages" do before do Fabricate(:chat_message, chat_channel: category_channel, message: "hi", user: user2) end it "returns the correct count" do unread_counts = subject.unread_counts([category_channel], user1) expect(unread_counts[category_channel.id]).to eq(0) end end end end describe ".all_secured_channel_ids" do it "returns nothing by default if the user has no memberships" do expect(subject.all_secured_channel_ids(guardian)).to eq([]) end context "when the user has memberships to all the channels" do before do UserChatChannelMembership.create!( user: user1, chat_channel: category_channel, following: true, ) UserChatChannelMembership.create!( user: user1, chat_channel: direct_message_channel1, following: true, desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], ) end it "returns category channel because they are public by default" do expect(subject.all_secured_channel_ids(guardian)).to match_array([category_channel.id]) end it "returns all the channels if the user is a member of the DM channel also" do DirectMessageUser.create!(user: user1, direct_message: dm_channel1) expect(subject.all_secured_channel_ids(guardian)).to match_array( [category_channel.id, direct_message_channel1.id], ) end it "does not include the category channel if the category is a private category the user cannot see" do category_channel.update!(chatable: private_category) expect(subject.all_secured_channel_ids(guardian)).to be_empty GroupUser.create!(group: private_category.groups.last, user: user1) expect(subject.all_secured_channel_ids(guardian)).to match_array([category_channel.id]) end context "when restricted category" do fab!(:group) { Fabricate(:group) } fab!(:group_user) { Fabricate(:group_user, group: group, user: user1) } it "does not include the category channel for member of group with readonly access" do category_channel.update!( chatable: Fabricate( :private_category, group: group, permission_type: CategoryGroup.permission_types[:readonly], ), ) expect(subject.all_secured_channel_ids(guardian)).to be_empty end it "includes the category channel for member of group with create_post access" do category_channel.update!( chatable: Fabricate( :private_category, group: group, permission_type: CategoryGroup.permission_types[:create_post], ), ) expect(subject.all_secured_channel_ids(guardian)).to match_array([category_channel.id]) end it "includes the category channel for member of group with full access" do category_channel.update!( chatable: Fabricate( :private_category, group: group, permission_type: CategoryGroup.permission_types[:full], ), ) expect(subject.all_secured_channel_ids(guardian)).to match_array([category_channel.id]) end end end end describe "#secured_public_channels" do let(:following) { false } it "does not include DM channels" do expect( subject.secured_public_channels(guardian, memberships, following: following).map(&:id), ).to match_array([category_channel.id]) end it "can filter by channel name, or category name" do expect( subject.secured_public_channels( guardian, memberships, following: following, filter: "support", ).map(&:id), ).to match_array([category_channel.id]) category_channel.update!(name: "cool stuff") expect( subject.secured_public_channels( guardian, memberships, following: following, filter: "cool stuff", ).map(&:id), ).to match_array([category_channel.id]) end it "can filter by an array of slugs" do expect( subject.secured_public_channels(guardian, memberships, slugs: ["support"]).map(&:id), ).to match_array([category_channel.id]) end it "returns nothing if the array of slugs is empty" do expect(subject.secured_public_channels(guardian, memberships, slugs: []).map(&:id)).to eq([]) end it "can filter by status" do expect( subject.secured_public_channels(guardian, memberships, status: "closed").map(&:id), ).to match_array([]) category_channel.closed!(Discourse.system_user) expect( subject.secured_public_channels(guardian, memberships, status: "closed").map(&:id), ).to match_array([category_channel.id]) end it "can filter by following" do expect( subject.secured_public_channels(guardian, memberships, following: true).map(&:id), ).to be_blank end it "can filter by not following" do category_channel.user_chat_channel_memberships.create!(user: user1, following: false) another_channel = Fabricate(:category_channel) expect( subject.secured_public_channels(guardian, memberships, following: false).map(&:id), ).to match_array([category_channel.id, another_channel.id]) end it "ensures offset is >= 0" do expect( subject.secured_public_channels(guardian, memberships, offset: -235).map(&:id), ).to match_array([category_channel.id]) end it "ensures limit is > 0" do expect( subject.secured_public_channels(guardian, memberships, limit: -1, offset: 0).map(&:id), ).to match_array([category_channel.id]) end it "ensures limit has a max value" do over_limit = Chat::ChatChannelFetcher::MAX_PUBLIC_CHANNEL_RESULTS + 1 over_limit.times { Fabricate(:category_channel) } expect( subject.secured_public_channels(guardian, memberships, limit: over_limit).length, ).to eq(Chat::ChatChannelFetcher::MAX_PUBLIC_CHANNEL_RESULTS) end it "does not show the user category channels they cannot access" do category_channel.update!(chatable: private_category) expect( subject.secured_public_channels(guardian, memberships, following: following).map(&:id), ).to be_empty end context "when scoping to the user's channel memberships" do let(:following) { true } it "only returns channels where the user is a member and is following the channel" do expect( subject.secured_public_channels(guardian, memberships, following: following).map(&:id), ).to be_empty UserChatChannelMembership.create!( user: user1, chat_channel: category_channel, following: true, ) expect( subject.secured_public_channels(guardian, memberships, following: following).map(&:id), ).to match_array([category_channel.id]) end it "includes the unread count based on mute settings" do UserChatChannelMembership.create!( user: user1, chat_channel: category_channel, following: true, ) Fabricate(:chat_message, user: user2, chat_channel: category_channel) Fabricate(:chat_message, user: user2, chat_channel: category_channel) resolved_memberships = memberships subject.secured_public_channels(guardian, resolved_memberships, following: following) expect( resolved_memberships .find { |membership| membership.chat_channel_id == category_channel.id } .unread_count, ).to eq(2) resolved_memberships.last.update!(muted: true) resolved_memberships = memberships subject.secured_public_channels(guardian, resolved_memberships, following: following) expect( resolved_memberships .find { |membership| membership.chat_channel_id == category_channel.id } .unread_count, ).to eq(0) end end end describe "#secured_direct_message_channels" do it "includes direct message channels the user is a member of ordered by last_message_sent_at" do Fabricate( :user_chat_channel_membership_for_dm, chat_channel: direct_message_channel1, user: user1, following: true, ) DirectMessageUser.create!(direct_message: dm_channel1, user: user1) DirectMessageUser.create!(direct_message: dm_channel1, user: user2) Fabricate( :user_chat_channel_membership_for_dm, chat_channel: direct_message_channel2, user: user1, following: true, ) DirectMessageUser.create!(direct_message: dm_channel2, user: user1) DirectMessageUser.create!(direct_message: dm_channel2, user: user2) direct_message_channel1.update!(last_message_sent_at: 1.day.ago) direct_message_channel2.update!(last_message_sent_at: 1.hour.ago) expect( subject.secured_direct_message_channels(user1.id, memberships, guardian).map(&:id), ).to eq([direct_message_channel2.id, direct_message_channel1.id]) end it "does not include direct message channels where the user is a member but not a direct_message_user" do Fabricate( :user_chat_channel_membership_for_dm, chat_channel: direct_message_channel1, user: user1, following: true, ) DirectMessageUser.create!(direct_message: dm_channel1, user: user2) expect( subject.secured_direct_message_channels(user1.id, memberships, guardian).map(&:id), ).not_to include(direct_message_channel1.id) end it "includes the unread count based on mute settings for the user's channel membership" do membership = Fabricate( :user_chat_channel_membership_for_dm, chat_channel: direct_message_channel1, user: user1, following: true, ) DirectMessageUser.create!(direct_message: dm_channel1, user: user1) DirectMessageUser.create!(direct_message: dm_channel1, user: user2) Fabricate(:chat_message, user: user2, chat_channel: direct_message_channel1) Fabricate(:chat_message, user: user2, chat_channel: direct_message_channel1) resolved_memberships = memberships subject.secured_direct_message_channels(user1.id, resolved_memberships, guardian) target_membership = resolved_memberships.find { |mem| mem.chat_channel_id == direct_message_channel1.id } expect(target_membership.unread_count).to eq(2) resolved_memberships = memberships target_membership = resolved_memberships.find { |mem| mem.chat_channel_id == direct_message_channel1.id } target_membership.update!(muted: true) subject.secured_direct_message_channels(user1.id, resolved_memberships, guardian) expect(target_membership.unread_count).to eq(0) end end describe ".find_with_access_check" do it "raises NotFound if the channel does not exist" do category_channel.destroy! expect { subject.find_with_access_check(category_channel.id, guardian) }.to raise_error( Discourse::NotFound, ) end it "raises InvalidAccess if the user cannot see the channel" do category_channel.update!(chatable: private_category) expect { subject.find_with_access_check(category_channel.id, guardian) }.to raise_error( Discourse::InvalidAccess, ) end it "returns the chat channel if it is found and accessible" do expect(subject.find_with_access_check(category_channel.id, guardian)).to eq(category_channel) end end end