554 lines
16 KiB
Ruby
554 lines
16 KiB
Ruby
# frozen_string_literal: true
|
||
|
||
require "rails_helper"
|
||
|
||
RSpec.describe Chat::Api::ChatChannelsController do
|
||
before do
|
||
SiteSetting.chat_enabled = true
|
||
SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:everyone]
|
||
end
|
||
|
||
describe "#index" do
|
||
context "as anonymous user" do
|
||
it "returns an error" do
|
||
get "/chat/api/channels"
|
||
|
||
expect(response.status).to eq(403)
|
||
end
|
||
end
|
||
|
||
context "as disallowed user" do
|
||
fab!(:current_user) { Fabricate(:user) }
|
||
|
||
before do
|
||
SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:staff]
|
||
sign_in(current_user)
|
||
end
|
||
|
||
it "returns an error" do
|
||
get "/chat/api/channels"
|
||
|
||
expect(response.status).to eq(403)
|
||
end
|
||
end
|
||
|
||
context "as allowed user" do
|
||
fab!(:current_user) { Fabricate(:user) }
|
||
|
||
before { sign_in(current_user) }
|
||
|
||
context "with category channels" do
|
||
context "when channel is public" do
|
||
fab!(:channel_1) { Fabricate(:category_channel) }
|
||
|
||
it "returns the channel" do
|
||
get "/chat/api/channels"
|
||
|
||
expect(response.status).to eq(200)
|
||
expect(response.parsed_body["channels"].map { |channel| channel["id"] }).to eq(
|
||
[channel_1.id],
|
||
)
|
||
end
|
||
|
||
context "when chatable is destroyed" do
|
||
before { channel_1.chatable.destroy! }
|
||
|
||
it "returns nothing" do
|
||
get "/chat/api/channels"
|
||
|
||
expect(response.status).to eq(200)
|
||
expect(response.parsed_body["channels"]).to be_blank
|
||
end
|
||
end
|
||
end
|
||
|
||
context "when channel has limited access" do
|
||
fab!(:group_1) { Fabricate(:group) }
|
||
fab!(:channel_1) { Fabricate(:private_category_channel, group: group_1) }
|
||
|
||
context "when user has access" do
|
||
before { group_1.add(current_user) }
|
||
|
||
it "returns the channel" do
|
||
get "/chat/api/channels"
|
||
|
||
expect(response.status).to eq(200)
|
||
expect(response.parsed_body["channels"].map { |channel| channel["id"] }).to eq(
|
||
[channel_1.id],
|
||
)
|
||
end
|
||
end
|
||
|
||
context "when user has no access" do
|
||
it "returns nothing" do
|
||
get "/chat/api/channels"
|
||
|
||
expect(response.status).to eq(200)
|
||
expect(response.parsed_body["channels"]).to be_blank
|
||
end
|
||
|
||
context "when user is admin" do
|
||
before { sign_in(Fabricate(:admin)) }
|
||
|
||
it "returns the channels" do
|
||
get "/chat/api/channels"
|
||
|
||
expect(response.status).to eq(200)
|
||
expect(response.parsed_body["channels"].map { |channel| channel["id"] }).to eq(
|
||
[channel_1.id],
|
||
)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
context "with direct message channels" do
|
||
fab!(:dm_channel_1) { Fabricate(:direct_message_channel, users: [current_user]) }
|
||
|
||
it "doesnt return direct message channels" do
|
||
get "/chat/api/channels"
|
||
expect(response.parsed_body["channels"]).to be_blank
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
describe "#show" do
|
||
context "when anonymous" do
|
||
it "returns an error" do
|
||
get "/chat/api/channels/-999"
|
||
|
||
expect(response.status).to eq(403)
|
||
end
|
||
end
|
||
|
||
context "when user cannot access channel" do
|
||
fab!(:channel_1) { Fabricate(:private_category_channel) }
|
||
|
||
before { sign_in(Fabricate(:user)) }
|
||
|
||
it "returns an error" do
|
||
get "/chat/api/channels/#{channel_1.id}"
|
||
|
||
expect(response.status).to eq(403)
|
||
end
|
||
end
|
||
|
||
context "when user can access channel" do
|
||
fab!(:current_user) { Fabricate(:user) }
|
||
|
||
before { sign_in(current_user) }
|
||
|
||
context "when channel doesn’t exist" do
|
||
it "returns an error" do
|
||
get "/chat/api/channels/-999"
|
||
|
||
expect(response.status).to eq(404)
|
||
end
|
||
end
|
||
|
||
context "when channel exists" do
|
||
fab!(:channel_1) { Fabricate(:category_channel) }
|
||
|
||
it "can find channel by id" do
|
||
get "/chat/api/channels/#{channel_1.id}"
|
||
|
||
expect(response.status).to eq(200)
|
||
expect(response.parsed_body["channel"]["id"]).to eq(channel_1.id)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
describe "#destroy" do
|
||
fab!(:channel_1) { Fabricate(:category_channel) }
|
||
|
||
context "when user is not staff" do
|
||
fab!(:current_user) { Fabricate(:user) }
|
||
|
||
before { sign_in(current_user) }
|
||
|
||
it "returns an error" do
|
||
delete "/chat/api/channels/#{channel_1.id}"
|
||
|
||
expect(response.status).to eq(403)
|
||
end
|
||
end
|
||
|
||
context "when user is admin" do
|
||
fab!(:current_user) { Fabricate(:admin) }
|
||
|
||
before { sign_in(current_user) }
|
||
|
||
context "when the channel doesn’t exist" do
|
||
before { channel_1.destroy! }
|
||
|
||
it "returns an error" do
|
||
delete "/chat/api/channels/#{channel_1.id}"
|
||
|
||
expect(response.status).to eq(404)
|
||
end
|
||
end
|
||
|
||
context "with valid params" do
|
||
it "properly destroys the channel" do
|
||
delete "/chat/api/channels/#{channel_1.id}"
|
||
|
||
expect(response.status).to eq(200)
|
||
expect(channel_1.reload.trashed?).to eq(true)
|
||
expect(
|
||
job_enqueued?(job: :chat_channel_delete, args: { chat_channel_id: channel_1.id }),
|
||
).to eq(true)
|
||
expect(
|
||
UserHistory.exists?(
|
||
acting_user_id: current_user.id,
|
||
action: UserHistory.actions[:custom_staff],
|
||
custom_type: "chat_channel_delete",
|
||
),
|
||
).to eq(true)
|
||
end
|
||
|
||
it "generates a valid new slug to prevent collisions" do
|
||
SiteSetting.max_topic_title_length = 20
|
||
channel_1 = Fabricate(:chat_channel, name: "a" * SiteSetting.max_topic_title_length)
|
||
freeze_time(DateTime.parse("2022-07-08 09:30:00"))
|
||
old_slug = channel_1.slug
|
||
|
||
delete "/chat/api/channels/#{channel_1.id}"
|
||
|
||
expect(response.status).to eq(200)
|
||
expect(channel_1.reload.slug).to eq(
|
||
"20220708-0930-#{old_slug}-deleted".truncate(
|
||
SiteSetting.max_topic_title_length,
|
||
omission: "",
|
||
),
|
||
)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
describe "#create" do
|
||
fab!(:admin) { Fabricate(:admin) }
|
||
fab!(:category) { Fabricate(:category) }
|
||
|
||
let(:params) do
|
||
{
|
||
channel: {
|
||
type: category.class.name,
|
||
chatable_id: category.id,
|
||
name: "channel name",
|
||
description: "My new channel",
|
||
},
|
||
}
|
||
end
|
||
|
||
before { sign_in(admin) }
|
||
|
||
it "creates a channel associated to a category" do
|
||
post "/chat/api/channels", params: params
|
||
|
||
new_channel = ChatChannel.last
|
||
|
||
expect(new_channel.name).to eq(params[:channel][:name])
|
||
expect(new_channel.slug).to eq("channel-name")
|
||
expect(new_channel.description).to eq(params[:channel][:description])
|
||
expect(new_channel.chatable_type).to eq(category.class.name)
|
||
expect(new_channel.chatable_id).to eq(category.id)
|
||
end
|
||
|
||
it "creates a channel using the user-provided slug" do
|
||
new_params = params.dup
|
||
new_params[:channel][:slug] = "wow-so-cool"
|
||
post "/chat/api/channels", params: new_params
|
||
|
||
new_channel = ChatChannel.last
|
||
|
||
expect(new_channel.slug).to eq("wow-so-cool")
|
||
end
|
||
|
||
it "creates a channel sets auto_join_users to false by default" do
|
||
post "/chat/api/channels", params: params
|
||
|
||
new_channel = ChatChannel.last
|
||
|
||
expect(new_channel.auto_join_users).to eq(false)
|
||
end
|
||
|
||
it "creates a channel with auto_join_users set to true" do
|
||
params[:channel][:auto_join_users] = true
|
||
post "/chat/api/channels", params: params
|
||
|
||
new_channel = ChatChannel.last
|
||
|
||
expect(new_channel.auto_join_users).to eq(true)
|
||
end
|
||
|
||
describe "triggers the auto-join process" do
|
||
fab!(:chatters_group) { Fabricate(:group) }
|
||
fab!(:user) { Fabricate(:user, last_seen_at: 15.minute.ago) }
|
||
|
||
before do
|
||
Jobs.run_immediately!
|
||
Fabricate(:category_group, category: category, group: chatters_group)
|
||
chatters_group.add(user)
|
||
end
|
||
|
||
it "joins the user when auto_join_users is true" do
|
||
params[:channel][:auto_join_users] = true
|
||
post "/chat/api/channels", params: params
|
||
|
||
created_channel_id = response.parsed_body.dig("channel", "id")
|
||
membership_exists =
|
||
UserChatChannelMembership.find_by(
|
||
user: user,
|
||
chat_channel_id: created_channel_id,
|
||
following: true,
|
||
)
|
||
|
||
expect(membership_exists).to be_present
|
||
end
|
||
|
||
it "doesn't join the user when auto_join_users is false" do
|
||
params[:channel][:auto_join_users] = false
|
||
post "/chat/api/channels", params: params
|
||
|
||
created_channel_id = response.parsed_body.dig("channel", "id")
|
||
membership_exists =
|
||
UserChatChannelMembership.find_by(
|
||
user: user,
|
||
chat_channel_id: created_channel_id,
|
||
following: true,
|
||
)
|
||
|
||
expect(membership_exists).to be_nil
|
||
end
|
||
end
|
||
end
|
||
|
||
describe "#update" do
|
||
include_examples "channel access example", :put
|
||
|
||
context "when user can’t edit channel" do
|
||
fab!(:channel) { Fabricate(:category_channel) }
|
||
|
||
before { sign_in(Fabricate(:user)) }
|
||
|
||
it "returns a 403" do
|
||
put "/chat/api/channels/#{channel.id}",
|
||
params: {
|
||
channel: {
|
||
name: "joffrey",
|
||
description: "cat owner",
|
||
},
|
||
}
|
||
|
||
expect(response.status).to eq(403)
|
||
end
|
||
end
|
||
|
||
context "when user provided invalid params" do
|
||
fab!(:channel) { Fabricate(:category_channel, user_count: 10) }
|
||
|
||
before { sign_in(Fabricate(:admin)) }
|
||
|
||
it "doesn’t change invalid properties" do
|
||
put "/chat/api/channels/#{channel.id}", params: { user_count: 40 }
|
||
|
||
expect(channel.reload.user_count).to eq(10)
|
||
end
|
||
end
|
||
|
||
context "when user provided an empty name" do
|
||
fab!(:user) { Fabricate(:admin) }
|
||
fab!(:channel) do
|
||
Fabricate(:category_channel, name: "something", description: "something else")
|
||
end
|
||
|
||
before { sign_in(user) }
|
||
|
||
it "nullifies the field and doesn’t store an empty string" do
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { name: " " } }
|
||
|
||
expect(channel.reload.name).to eq(nil)
|
||
end
|
||
|
||
it "doesn’t nullify the description" do
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { name: " " } }
|
||
|
||
expect(channel.reload.description).to eq("something else")
|
||
end
|
||
end
|
||
|
||
context "when user provides an empty description" do
|
||
fab!(:user) { Fabricate(:admin) }
|
||
fab!(:channel) do
|
||
Fabricate(:category_channel, name: "something else", description: "something")
|
||
end
|
||
|
||
before { sign_in(user) }
|
||
|
||
it "nullifies the field and doesn’t store an empty string" do
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { description: " " } }
|
||
|
||
expect(channel.reload.description).to eq(nil)
|
||
end
|
||
|
||
it "doesn’t nullify the name" do
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { description: " " } }
|
||
|
||
expect(channel.reload.name).to eq("something else")
|
||
end
|
||
end
|
||
|
||
context "when user provides an empty slug" do
|
||
fab!(:user) { Fabricate(:admin) }
|
||
fab!(:channel) do
|
||
Fabricate(:category_channel, name: "something else", description: "something")
|
||
end
|
||
|
||
before { sign_in(user) }
|
||
|
||
it "does not nullify the slug" do
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { slug: " " } }
|
||
|
||
expect(channel.reload.slug).to eq("something-else")
|
||
end
|
||
end
|
||
|
||
context "when channel is a direct message channel" do
|
||
fab!(:user) { Fabricate(:admin) }
|
||
fab!(:channel) { Fabricate(:direct_message_channel) }
|
||
|
||
before { sign_in(user) }
|
||
|
||
it "raises a 403" do
|
||
put "/chat/api/channels/#{channel.id}"
|
||
|
||
expect(response.status).to eq(403)
|
||
end
|
||
end
|
||
|
||
context "when user provides valid params" do
|
||
fab!(:user) { Fabricate(:admin) }
|
||
fab!(:channel) { Fabricate(:category_channel) }
|
||
|
||
before { sign_in(user) }
|
||
|
||
it "sets properties" do
|
||
put "/chat/api/channels/#{channel.id}",
|
||
params: {
|
||
channel: {
|
||
name: "joffrey",
|
||
slug: "cat-king",
|
||
description: "cat owner",
|
||
},
|
||
}
|
||
|
||
expect(channel.reload.name).to eq("joffrey")
|
||
expect(channel.reload.slug).to eq("cat-king")
|
||
expect(channel.reload.description).to eq("cat owner")
|
||
end
|
||
|
||
it "publishes an update" do
|
||
messages =
|
||
MessageBus.track_publish("/chat/channel-edits") do
|
||
put "/chat/api/channels/#{channel.id}",
|
||
params: {
|
||
channel: {
|
||
name: "A new cat overlord",
|
||
},
|
||
}
|
||
end
|
||
|
||
message = messages[0]
|
||
channel.reload
|
||
expect(message.data[:chat_channel_id]).to eq(channel.id)
|
||
expect(message.data[:name]).to eq(channel.name)
|
||
expect(message.data[:slug]).to eq(channel.slug)
|
||
expect(message.data[:description]).to eq(channel.description)
|
||
end
|
||
|
||
it "returns a valid chat channel" do
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { name: "A new cat is born" } }
|
||
|
||
expect(response.parsed_body["channel"]).to match_response_schema("category_chat_channel")
|
||
end
|
||
|
||
describe "when updating allow_channel_wide_mentions" do
|
||
it "sets the new value" do
|
||
put "/chat/api/channels/#{channel.id}",
|
||
params: {
|
||
channel: {
|
||
allow_channel_wide_mentions: false,
|
||
},
|
||
}
|
||
|
||
expect(response.parsed_body["channel"]["allow_channel_wide_mentions"]).to eq(false)
|
||
end
|
||
end
|
||
|
||
describe "Updating a channel to add users automatically" do
|
||
it "sets the channel to auto-update users automatically" do
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { auto_join_users: true } }
|
||
|
||
expect(response.parsed_body["channel"]["auto_join_users"]).to eq(true)
|
||
end
|
||
|
||
it "tells staff members to slow down when toggling auto-update multiple times" do
|
||
RateLimiter.enable
|
||
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { auto_join_users: true } }
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { auto_join_users: false } }
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { auto_join_users: true } }
|
||
|
||
expect(response.status).to eq(429)
|
||
end
|
||
|
||
describe "triggers the auto-join process" do
|
||
fab!(:chatters_group) { Fabricate(:group) }
|
||
fab!(:another_user) { Fabricate(:user, last_seen_at: 15.minute.ago) }
|
||
|
||
before do
|
||
Jobs.run_immediately!
|
||
Fabricate(:category_group, category: channel.chatable, group: chatters_group)
|
||
chatters_group.add(another_user)
|
||
end
|
||
|
||
it "joins the user when auto_join_users is true" do
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { auto_join_users: true } }
|
||
|
||
created_channel_id = response.parsed_body["channel"]["id"]
|
||
membership_exists =
|
||
UserChatChannelMembership.find_by(
|
||
user: another_user,
|
||
chat_channel_id: created_channel_id,
|
||
following: true,
|
||
)
|
||
|
||
expect(membership_exists).to be_present
|
||
end
|
||
|
||
it "doesn't join the user when auto_join_users is false" do
|
||
put "/chat/api/channels/#{channel.id}", params: { channel: { auto_join_users: false } }
|
||
|
||
created_channel_id = response.parsed_body["channel"]["id"]
|
||
|
||
expect(created_channel_id).to be_present
|
||
|
||
membership_exists =
|
||
UserChatChannelMembership.find_by(
|
||
user: another_user,
|
||
chat_channel_id: created_channel_id,
|
||
following: true,
|
||
)
|
||
|
||
expect(membership_exists).to be_nil
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|