# frozen_string_literal: true describe Chat do before do SiteSetting.clean_up_uploads = true SiteSetting.clean_orphan_uploads_grace_period_hours = 1 Jobs::CleanUpUploads.new.reset_last_cleanup! SiteSetting.chat_enabled = true end describe "register_upload_unused" do fab!(:chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:category)) } fab!(:user) fab!(:upload) { Fabricate(:upload, user: user, created_at: 1.month.ago) } fab!(:unused_upload) { Fabricate(:upload, user: user, created_at: 1.month.ago) } let!(:chat_message) do Fabricate( :chat_message, chat_channel: chat_channel, user: user, message: "Hello world!", uploads: [upload], ) end it "marks uploads with reference to ChatMessage via UploadReference in use" do unused_upload expect { Jobs::CleanUpUploads.new.execute({}) }.to change { Upload.count }.by(-1) expect(Upload.exists?(id: upload.id)).to eq(true) expect(Upload.exists?(id: unused_upload.id)).to eq(false) end end describe "register_upload_in_use" do fab!(:chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:category)) } fab!(:user) fab!(:message_upload) { Fabricate(:upload, user: user, created_at: 1.month.ago) } fab!(:draft_upload) { Fabricate(:upload, user: user, created_at: 1.month.ago) } fab!(:unused_upload) { Fabricate(:upload, user: user, created_at: 1.month.ago) } let!(:chat_message) do Fabricate( :chat_message, chat_channel: chat_channel, user: user, message: "Hello world! #{message_upload.sha1}", ) end let!(:draft_message) do Chat::Draft.create!( user: user, chat_channel: chat_channel, data: "{\"value\":\"hello world \",\"uploads\":[\"#{draft_upload.sha1}\"],\"replyToMsg\":null}", ) end it "marks uploads with reference to ChatMessage via UploadReference in use" do draft_upload unused_upload expect { Jobs::CleanUpUploads.new.execute({}) }.to change { Upload.count }.by(-1) expect(Upload.exists?(id: message_upload.id)).to eq(true) expect(Upload.exists?(id: draft_upload.id)).to eq(true) expect(Upload.exists?(id: unused_upload.id)).to eq(false) end end describe "user card serializer extension #can_chat_user" do fab!(:target_user) { Fabricate(:user) } let!(:user) { Fabricate(:user) } let!(:guardian) { Guardian.new(user) } let(:serializer) { UserCardSerializer.new(target_user, scope: guardian) } fab!(:group) context "when chat enabled" do before { SiteSetting.chat_enabled = true } it "returns true if the target user and the guardian user is in the Chat.allowed_group_ids" do SiteSetting.chat_allowed_groups = group.id SiteSetting.direct_message_enabled_groups = group.id GroupUser.create(user: target_user, group: group) GroupUser.create(user: user, group: group) expect(serializer.can_chat_user).to eq(true) end it "returns false if the target user but not the guardian user is in the Chat.allowed_group_ids" do SiteSetting.chat_allowed_groups = group.id GroupUser.create(user: target_user, group: group) expect(serializer.can_chat_user).to eq(false) end it "returns false if the guardian user but not the target user is in the Chat.allowed_group_ids" do SiteSetting.chat_allowed_groups = group.id GroupUser.create(user: user, group: group) expect(serializer.can_chat_user).to eq(false) end context "when guardian user is same as target user" do let!(:guardian) { Guardian.new(target_user) } it "returns false" do expect(serializer.can_chat_user).to eq(false) end end context "when guardian user is anon" do let!(:guardian) { Guardian.new } it "returns false" do expect(serializer.can_chat_user).to eq(false) end end context "when both users are in Chat.allowed_group_ids" do before do SiteSetting.chat_allowed_groups = group.id SiteSetting.direct_message_enabled_groups = group.id GroupUser.create(user: target_user, group: group) GroupUser.create(user: user, group: group) end it "returns true when both users are valid" do expect(serializer.can_chat_user).to eq(true) end it "returns false if current user has chat disabled" do user.user_option.update!(chat_enabled: false) expect(serializer.can_chat_user).to eq(false) end it "returns false if target user has chat disabled" do target_user.user_option.update!(chat_enabled: false) expect(serializer.can_chat_user).to eq(false) end it "returns false if user is not in dm allowed group" do SiteSetting.direct_message_enabled_groups = 3 expect(serializer.can_chat_user).to eq(false) end end end context "when chat not enabled" do before { SiteSetting.chat_enabled = false } it "returns false" do expect(serializer.can_chat_user).to eq(false) end end end describe "chat oneboxes" do fab!(:chat_channel) { Fabricate(:category_channel) } fab!(:user) fab!(:chat_message) do Fabricate(:chat_message, chat_channel: chat_channel, user: user, message: "Hello world!") end let(:chat_url) { "#{Discourse.base_url}/chat/c/-/#{chat_channel.id}" } context "when inline" do it "renders channel" do results = InlineOneboxer.new([chat_url], skip_cache: true).process expect(results).to be_present expect(results[0][:url]).to eq(chat_url) expect(results[0][:title]).to eq("Chat ##{chat_channel.name}") end it "renders messages" do results = InlineOneboxer.new(["#{chat_url}/#{chat_message.id}"], skip_cache: true).process expect(results).to be_present expect(results[0][:url]).to eq("#{chat_url}/#{chat_message.id}") expect(results[0][:title]).to eq( "Message ##{chat_message.id} by #{chat_message.user.username} – ##{chat_channel.name}", ) end end end describe "auto-joining users to a channel" do fab!(:chatters_group) { Fabricate(:group) } fab!(:user) { Fabricate(:user, last_seen_at: 15.minutes.ago) } let!(:channel) { Fabricate(:category_channel, auto_join_users: true, chatable: category) } before { Jobs.run_immediately! } def assert_user_following_state(user, channel, following:) membership = Chat::UserChatChannelMembership.find_by(user: user, chat_channel: channel) following ? (expect(membership.following).to eq(true)) : (expect(membership).to be_nil) end describe "when a user is added to a group with access to a channel through a category" do let!(:category) { Fabricate(:private_category, group: chatters_group) } it "joins the user to the channel if auto-join is enabled" do chatters_group.add(user) assert_user_following_state(user, channel, following: true) end it "does nothing if auto-join is disabled" do channel.update!(auto_join_users: false) assert_user_following_state(user, channel, following: false) end end describe "when a user is created" do fab!(:category) let(:user) { Fabricate(:user, last_seen_at: nil, first_seen_at: nil) } it "queues a job to auto-join the user the first time they log in" do user.update_last_seen! assert_user_following_state(user, channel, following: true) end it "does nothing if it's not the first time we see the user" do user.update!(first_seen_at: 2.minute.ago) user.update_last_seen! assert_user_following_state(user, channel, following: false) end it "does nothing if auto-join is disabled" do channel.update!(auto_join_users: false) user.update_last_seen! assert_user_following_state(user, channel, following: false) end end describe "when category permissions change" do fab!(:category) let(:chatters_group_permission) do { chatters_group.name => CategoryGroup.permission_types[:full] } end describe "given permissions to a new group" do it "adds the user to the channel" do chatters_group.add(user) category.update!(permissions: chatters_group_permission) assert_user_following_state(user, channel, following: true) end it "does nothing if there is no channel for the category" do another_category = Fabricate(:category) another_category.update!(permissions: chatters_group_permission) assert_user_following_state(user, channel, following: false) end end end end describe "secure uploads compatibility" do fab!(:user) it "disables chat uploads if secure uploads changes from disabled to enabled" do enable_secure_uploads expect(SiteSetting.chat_allow_uploads).to eq(false) last_history = UserHistory.last expect(last_history.action).to eq(UserHistory.actions[:change_site_setting]) expect(last_history.previous_value).to eq("true") expect(last_history.new_value).to eq("false") expect(last_history.subject).to eq("chat_allow_uploads") expect(last_history.context).to eq("Disabled because secure_uploads is enabled") end context "when the global setting allow_unsecure_chat_uploads is true" do fab!(:filename) { "small.pdf" } fab!(:file) { file_from_fixtures(filename, "pdf") } before { global_setting :allow_unsecure_chat_uploads, true } it "does not disable chat uploads" do expect { enable_secure_uploads }.not_to change { UserHistory.count } expect(SiteSetting.chat_allow_uploads).to eq(true) end it "does not mark chat uploads as secure" do filename = "small.pdf" file = file_from_fixtures(filename, "pdf") enable_secure_uploads upload = UploadCreator.new(file, filename, type: "chat-composer").create_for(user.id) expect(upload.secure).to eq(false) end context "when login_required is true" do before { SiteSetting.login_required = true } it "does not mark chat uploads as secure" do enable_secure_uploads upload = UploadCreator.new(file, filename, type: "chat-composer").create_for(user.id) expect(upload.secure).to eq(false) end end end end describe "current_user_serializer#has_joinable_public_channels" do before do SiteSetting.chat_enabled = true SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:everyone] end fab!(:user) let(:serializer) { CurrentUserSerializer.new(user, scope: Guardian.new(user)) } context "when no channels exist" do it "returns false" do expect(serializer.has_joinable_public_channels).to eq(false) end end context "when no joinable channel exist" do fab!(:channel) { Fabricate(:chat_channel) } before do Fabricate(:user_chat_channel_membership, user: user, chat_channel: channel, following: true) end it "returns false" do expect(serializer.has_joinable_public_channels).to eq(false) end end context "when no public channel exist" do fab!(:private_category) { Fabricate(:private_category, group: Fabricate(:group)) } fab!(:private_channel) { Fabricate(:chat_channel, chatable: private_category) } it "returns false" do expect(serializer.has_joinable_public_channels).to eq(false) end end context "when a joinable channel exists" do fab!(:channel) { Fabricate(:chat_channel) } it "returns true" do expect(serializer.has_joinable_public_channels).to eq(true) end end end describe "Deleting posts while deleting a user" do fab!(:user) it "queues a job to also delete chat messages" do deletion_opts = { delete_posts: true } expect { UserDestroyer.new(Discourse.system_user).destroy(user, deletion_opts) }.to change( Jobs::Chat::DeleteUserMessages.jobs, :size, ).by(1) end end describe "when using topic tags changed trigger automation" do describe "with the send message script" do fab!(:automation_1) do Fabricate( :automation, trigger: DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED, script: :send_chat_message, ) end fab!(:tag_1) { Fabricate(:tag) } fab!(:user_1) { Fabricate(:admin) } fab!(:topic_1) { Fabricate(:topic) } fab!(:channel_1) { Fabricate(:chat_channel) } before do SiteSetting.discourse_automation_enabled = true SiteSetting.tagging_enabled = true automation_1.upsert_field!( "watching_tags", "tags", { value: [tag_1.name] }, target: "trigger", ) automation_1.upsert_field!( "chat_channel_id", "text", { value: channel_1.id }, target: "script", ) automation_1.upsert_field!( "message", "message", { value: "[{{topic_title}}]({{topic_url}})" }, target: "script", ) end it "sends the message" do DiscourseTagging.tag_topic_by_names(topic_1, Guardian.new(user_1), [tag_1.name]) expect(channel_1.chat_messages.last.message).to eq( "[#{topic_1.title}](#{topic_1.relative_url})", ) end end end describe "when using post_edited_created trigger automation" do describe "with the send message script" do fab!(:automation_1) do Fabricate( :automation, trigger: DiscourseAutomation::Triggers::POST_CREATED_EDITED, script: :send_chat_message, ) end fab!(:user_1) { Fabricate(:admin) } fab!(:channel_1) { Fabricate(:chat_channel) } before do SiteSetting.discourse_automation_enabled = true automation_1.upsert_field!( "chat_channel_id", "text", { value: channel_1.id }, target: "script", ) automation_1.upsert_field!( "message", "message", { value: "[{{topic_title}}]({{topic_url}})\n{{post_quote}}" }, target: "script", ) end it "sends the message" do post = PostCreator.create(user_1, { title: "hello world topic", raw: "my name is fred" }) expect(channel_1.chat_messages.last.message).to eq( "[#{post.topic.title}](#{post.topic.relative_url})\n[quote=#{post.username}, post:#{post.post_number}, topic:#{post.topic_id}]\nmy name is fred\n[/quote]", ) end end end end