This commit introduces the ability to edit the channel
slug from the About tab for the chat channel when the user
is admin. Similar to the create channel modal functionality
introduced in 641e94f, if
the slug is left empty then we autogenerate a slug based
on the channel name, and if the user just changes the slug
manually we use that instead.
We do not do any link remapping or anything else of the
sort, when the category slug is changed that does not happen
either.
583 lines
17 KiB
Ruby
583 lines
17 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}",
|
||
params: {
|
||
channel: {
|
||
name_confirmation: channel_1.title(current_user),
|
||
},
|
||
}
|
||
|
||
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}",
|
||
params: {
|
||
channel: {
|
||
name_confirmation: channel_1.title(current_user),
|
||
},
|
||
}
|
||
|
||
expect(response.status).to eq(404)
|
||
end
|
||
end
|
||
|
||
context "when the confirmation doesn’t match the channel name" do
|
||
it "returns an error" do
|
||
delete "/chat/api/channels/#{channel_1.id}",
|
||
params: {
|
||
channel: {
|
||
name_confirmation: channel_1.title(current_user) + "foo",
|
||
},
|
||
}
|
||
|
||
expect(response.status).to eq(400)
|
||
end
|
||
end
|
||
|
||
context "with valid params" do
|
||
it "properly destroys the channel" do
|
||
delete "/chat/api/channels/#{channel_1.id}",
|
||
params: {
|
||
channel: {
|
||
name_confirmation: channel_1.title(current_user),
|
||
},
|
||
}
|
||
|
||
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}",
|
||
params: {
|
||
channel: {
|
||
name_confirmation: channel_1.title(current_user),
|
||
},
|
||
},
|
||
)
|
||
|
||
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}"
|
||
|
||
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 be_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 be_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
|