2019-04-30 08:27:42 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2022-07-28 10:27:38 +08:00
|
|
|
RSpec.describe Group do
|
2017-02-03 16:51:32 +08:00
|
|
|
let(:admin) { Fabricate(:admin) }
|
|
|
|
let(:user) { Fabricate(:user) }
|
2017-06-02 16:38:14 +08:00
|
|
|
let(:group) { Fabricate(:group) }
|
2013-05-06 12:49:56 +08:00
|
|
|
|
2023-06-05 23:38:50 +08:00
|
|
|
it_behaves_like "it has custom fields"
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
describe "Validations" do
|
2023-05-08 12:19:10 +08:00
|
|
|
it { is_expected.to allow_value("#{"a" * 996}.com").for(:automatic_membership_email_domains) }
|
2023-04-26 23:24:12 +08:00
|
|
|
it do
|
2023-05-08 12:19:10 +08:00
|
|
|
is_expected.not_to allow_value("#{"a" * 997}.com").for(:automatic_membership_email_domains)
|
2023-04-26 23:24:12 +08:00
|
|
|
end
|
|
|
|
it { is_expected.to validate_length_of(:bio_raw).is_at_most(3000) }
|
2023-05-16 22:07:56 +08:00
|
|
|
it { is_expected.to validate_length_of(:membership_request_template).is_at_most(5000) }
|
2023-04-26 23:24:12 +08:00
|
|
|
it { is_expected.to validate_length_of(:full_name).is_at_most(100) }
|
|
|
|
|
2018-04-06 17:11:00 +08:00
|
|
|
describe "#grant_trust_level" do
|
|
|
|
describe "when trust level is not valid" do
|
|
|
|
it "should not be valid" do
|
|
|
|
group.grant_trust_level = 123_456
|
|
|
|
|
|
|
|
expect(group.valid?).to eq(false)
|
|
|
|
|
|
|
|
expect(group.errors.full_messages.join(",")).to eq(
|
|
|
|
I18n.t("groups.errors.grant_trust_level_not_valid", trust_level: 123_456),
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-05-07 14:02:11 +08:00
|
|
|
describe "#name" do
|
2018-04-02 18:17:06 +08:00
|
|
|
context "when a user with a similar name exists" do
|
|
|
|
it "should not be valid" do
|
|
|
|
new_group = Fabricate.build(:group, name: admin.username.upcase)
|
|
|
|
|
|
|
|
expect(new_group).to_not be_valid
|
|
|
|
|
|
|
|
expect(new_group.errors.full_messages.first).to include(
|
|
|
|
I18n.t("activerecord.errors.messages.taken"),
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when a group with a similar name exists" do
|
|
|
|
it "should not be valid" do
|
|
|
|
new_group = Fabricate.build(:group, name: group.name.upcase)
|
|
|
|
|
|
|
|
expect(new_group).to_not be_valid
|
|
|
|
|
|
|
|
expect(new_group.errors.full_messages.first).to include(
|
|
|
|
I18n.t("activerecord.errors.messages.taken"),
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-01 03:39:11 +08:00
|
|
|
describe "#posts_for" do
|
|
|
|
it "returns the post in the group" do
|
|
|
|
p = Fabricate(:post)
|
|
|
|
group.add(p.user)
|
|
|
|
|
|
|
|
posts = group.posts_for(Guardian.new)
|
|
|
|
expect(posts).to include(p)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "doesn't include unlisted posts" do
|
|
|
|
p = Fabricate(:post)
|
|
|
|
p.topic.update_column(:visible, false)
|
|
|
|
group.add(p.user)
|
|
|
|
|
|
|
|
posts = group.posts_for(Guardian.new)
|
|
|
|
expect(posts).not_to include(p)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-01-08 18:53:52 +08:00
|
|
|
describe "#builtin" do
|
2022-07-28 00:14:14 +08:00
|
|
|
context "when verifying enum sequence" do
|
2016-01-08 18:53:52 +08:00
|
|
|
before { @builtin = Group.builtin }
|
|
|
|
|
|
|
|
it "'moderators' should be at 1st position" do
|
|
|
|
expect(@builtin[:moderators]).to eq(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "'trust_level_2' should be at 4th position" do
|
|
|
|
expect(@builtin[:trust_level_2]).to eq(4)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-06-17 08:46:30 +08:00
|
|
|
# UGLY but perf is horrible with this callback
|
|
|
|
before { User.set_callback(:create, :after, :ensure_in_trust_level_group) }
|
|
|
|
after { User.skip_callback(:create, :after, :ensure_in_trust_level_group) }
|
|
|
|
|
2013-05-09 15:37:34 +08:00
|
|
|
describe "validation" do
|
|
|
|
let(:group) { build(:group) }
|
|
|
|
|
|
|
|
it "is invalid for blank" do
|
|
|
|
group.name = ""
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(group.valid?).to eq false
|
2013-05-09 15:37:34 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "is valid for a longer name" do
|
|
|
|
group.name = "this_is_a_name"
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(group.valid?).to eq true
|
2013-05-09 15:37:34 +08:00
|
|
|
end
|
2013-06-17 11:43:06 +08:00
|
|
|
|
|
|
|
it "is invalid for non names" do
|
|
|
|
group.name = "this is_a_name"
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(group.valid?).to eq false
|
2013-06-17 11:43:06 +08:00
|
|
|
end
|
2014-12-16 20:07:15 +08:00
|
|
|
|
2017-07-27 14:39:47 +08:00
|
|
|
it "strips trailing and leading spaces" do
|
|
|
|
group.name = " dragon "
|
|
|
|
|
|
|
|
expect(group.save).to eq(true)
|
|
|
|
expect(group.reload.name).to eq("dragon")
|
|
|
|
end
|
|
|
|
|
2014-12-16 20:07:15 +08:00
|
|
|
it "is invalid for case-insensitive existing names" do
|
|
|
|
build(:group, name: "this_is_a_name").save
|
|
|
|
group.name = "This_Is_A_Name"
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(group.valid?).to eq false
|
2014-12-16 20:07:15 +08:00
|
|
|
end
|
2015-11-27 13:35:16 +08:00
|
|
|
|
|
|
|
it "is invalid for poorly formatted domains" do
|
|
|
|
group.automatic_membership_email_domains = "wikipedia.org|*@example.com"
|
|
|
|
expect(group.valid?).to eq false
|
|
|
|
end
|
|
|
|
|
|
|
|
it "is valid for proper domains" do
|
|
|
|
group.automatic_membership_email_domains = "discourse.org|wikipedia.org"
|
|
|
|
expect(group.valid?).to eq true
|
|
|
|
end
|
2015-12-07 19:39:28 +08:00
|
|
|
|
2016-04-09 00:11:58 +08:00
|
|
|
it "is valid for newer TLDs" do
|
|
|
|
group.automatic_membership_email_domains = "discourse.institute"
|
|
|
|
expect(group.valid?).to eq true
|
|
|
|
end
|
|
|
|
|
2015-12-07 19:39:28 +08:00
|
|
|
it "is invalid for bad incoming email" do
|
|
|
|
group.incoming_email = "foo.bar.org"
|
|
|
|
expect(group.valid?).to eq(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "is valid for proper incoming email" do
|
|
|
|
group.incoming_email = "foo@bar.org"
|
|
|
|
expect(group.valid?).to eq(true)
|
|
|
|
end
|
2017-06-02 22:15:57 +08:00
|
|
|
|
|
|
|
context "when a group has no owners" do
|
2017-07-27 14:39:47 +08:00
|
|
|
describe "group has not been persisted" do
|
|
|
|
it "should not allow membership requests" do
|
|
|
|
group = Fabricate.build(:group, allow_membership_requests: true)
|
|
|
|
|
|
|
|
expect(group.valid?).to eq(false)
|
|
|
|
|
|
|
|
expect(group.errors.full_messages).to include(
|
|
|
|
I18n.t("groups.errors.cant_allow_membership_requests"),
|
|
|
|
)
|
|
|
|
|
|
|
|
group.group_users.build(user_id: user.id, owner: true)
|
|
|
|
|
|
|
|
expect(group.valid?).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-06-02 22:15:57 +08:00
|
|
|
it "should not allow membership requests" do
|
|
|
|
group.allow_membership_requests = true
|
|
|
|
|
|
|
|
expect(group.valid?).to eq(false)
|
|
|
|
|
|
|
|
expect(group.errors.full_messages).to include(
|
|
|
|
I18n.t("groups.errors.cant_allow_membership_requests"),
|
|
|
|
)
|
|
|
|
|
|
|
|
group.allow_membership_requests = false
|
|
|
|
group.save!
|
|
|
|
|
|
|
|
group.add_owner(user)
|
|
|
|
group.allow_membership_requests = true
|
|
|
|
|
|
|
|
expect(group.valid?).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
2013-05-09 15:37:34 +08:00
|
|
|
end
|
|
|
|
|
2013-09-06 12:07:23 +08:00
|
|
|
def real_admins
|
2017-07-24 21:17:42 +08:00
|
|
|
Group[:admins].user_ids.reject { |id| id < 0 }
|
2013-09-06 12:07:23 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def real_moderators
|
2017-07-24 21:17:42 +08:00
|
|
|
Group[:moderators].user_ids.reject { |id| id < 0 }
|
2013-09-06 12:07:23 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def real_staff
|
2017-07-24 21:17:42 +08:00
|
|
|
Group[:staff].user_ids.reject { |id| id < 0 }
|
2013-09-06 12:07:23 +08:00
|
|
|
end
|
|
|
|
|
2018-09-17 15:46:43 +08:00
|
|
|
describe "#primary_group=" do
|
|
|
|
before { group.add(user) }
|
2015-04-10 10:17:28 +08:00
|
|
|
|
2021-10-29 01:26:44 +08:00
|
|
|
it "updates all members' #primary_group" do
|
2018-09-17 15:46:43 +08:00
|
|
|
expect { group.update(primary_group: true) }.to change { user.reload.primary_group }.from(
|
|
|
|
nil,
|
|
|
|
).to(group)
|
|
|
|
expect { group.update(primary_group: false) }.to change { user.reload.primary_group }.from(
|
|
|
|
group,
|
|
|
|
).to(nil)
|
|
|
|
end
|
2021-10-29 01:26:44 +08:00
|
|
|
|
|
|
|
it "updates all members' #flair_group" do
|
|
|
|
expect { group.update(primary_group: true) }.to change { user.reload.flair_group }.from(
|
|
|
|
nil,
|
|
|
|
).to(group)
|
|
|
|
expect { group.update(primary_group: false) }.to change { user.reload.flair_group }.from(
|
|
|
|
group,
|
|
|
|
).to(nil)
|
|
|
|
end
|
2015-04-10 10:17:28 +08:00
|
|
|
end
|
|
|
|
|
2018-09-17 15:46:43 +08:00
|
|
|
describe "#title=" do
|
|
|
|
it "updates the member's title only if it was blank or exact match" do
|
|
|
|
group.add(user)
|
2015-04-10 10:17:28 +08:00
|
|
|
|
2018-09-17 15:46:43 +08:00
|
|
|
expect { group.update(title: "Awesome") }.to change { user.reload.title }.from(nil).to(
|
|
|
|
"Awesome",
|
|
|
|
)
|
|
|
|
expect { group.update(title: "Super") }.to change { user.reload.title }.from("Awesome").to(
|
|
|
|
"Super",
|
|
|
|
)
|
2015-04-10 10:17:28 +08:00
|
|
|
|
2018-09-17 15:46:43 +08:00
|
|
|
user.update(title: "Differently Awesome")
|
|
|
|
expect { group.update(title: "Awesome") }.to_not change { user.reload.title }
|
|
|
|
end
|
2015-04-10 10:17:28 +08:00
|
|
|
|
2018-09-17 15:46:43 +08:00
|
|
|
it "doesn't update non-member's title" do
|
|
|
|
user.update(title: group.title)
|
|
|
|
expect { group.update(title: "Super") }.to_not change { user.reload.title }
|
|
|
|
end
|
2015-04-10 10:17:28 +08:00
|
|
|
end
|
|
|
|
|
2023-12-26 14:39:18 +08:00
|
|
|
describe ".auto_groups_between" do
|
|
|
|
it "returns the auto groups between lower and upper bounds" do
|
|
|
|
expect(
|
|
|
|
described_class.auto_groups_between(:trust_level_0, :trust_level_3),
|
|
|
|
).to contain_exactly(10, 11, 12, 13)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "excludes the undefined groups between staff and TL0" do
|
|
|
|
expect(described_class.auto_groups_between(:admins, :trust_level_0)).to contain_exactly(
|
|
|
|
1,
|
|
|
|
2,
|
|
|
|
3,
|
|
|
|
10,
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns an empty array when lower group is higher than upper group" do
|
|
|
|
expect(described_class.auto_groups_between(:trust_level_1, :trust_level_0)).to be_empty
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns an empty array when passing an unknown group" do
|
|
|
|
expect(described_class.auto_groups_between(:trust_level_0, :trust_level_1337)).to be_empty
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-01-18 12:20:23 +08:00
|
|
|
describe ".refresh_automatic_group!" do
|
2019-06-17 13:10:47 +08:00
|
|
|
it "does not include staged users in any automatic groups" do
|
|
|
|
staged = Fabricate(:staged, trust_level: 1)
|
|
|
|
|
|
|
|
Group.refresh_automatic_group!(:trust_level_0)
|
|
|
|
Group.refresh_automatic_group!(:trust_level_1)
|
|
|
|
|
|
|
|
expect(GroupUser.where(user_id: staged.id).count).to eq(0)
|
|
|
|
|
2020-03-17 23:48:24 +08:00
|
|
|
staged.unstage!
|
2019-06-17 13:10:47 +08:00
|
|
|
|
|
|
|
expect(GroupUser.where(user_id: staged.id).count).to eq(2)
|
|
|
|
end
|
|
|
|
|
2022-07-27 22:34:08 +08:00
|
|
|
describe "after updating automatic group members" do
|
2023-11-10 06:47:59 +08:00
|
|
|
fab!(:user)
|
2022-07-27 22:34:08 +08:00
|
|
|
|
|
|
|
it "triggers an event when a user is removed from an automatic group" do
|
|
|
|
tl3_users = Group.find(Group::AUTO_GROUPS[:trust_level_3])
|
|
|
|
tl3_users.add(user)
|
|
|
|
|
2023-03-01 12:35:21 +08:00
|
|
|
_events = DiscourseEvent.track_events { Group.refresh_automatic_group!(:trust_level_3) }
|
2022-07-27 22:34:08 +08:00
|
|
|
|
|
|
|
expect(GroupUser.exists?(group: tl3_users, user: user)).to eq(false)
|
|
|
|
publish_event_job_args = Jobs::PublishGroupMembershipUpdates.jobs.last["args"].first
|
|
|
|
expect(publish_event_job_args["user_ids"]).to include(user.id)
|
|
|
|
expect(publish_event_job_args["group_id"]).to eq(tl3_users.id)
|
|
|
|
expect(publish_event_job_args["type"]).to include("remove")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "triggers an event when a user is added to an automatic group" do
|
|
|
|
tl0_users = Group.find(Group::AUTO_GROUPS[:trust_level_0])
|
|
|
|
|
|
|
|
expect(GroupUser.exists?(group: tl0_users, user: user)).to eq(false)
|
|
|
|
|
2023-11-07 11:59:40 +08:00
|
|
|
events = DiscourseEvent.track_events { Group.refresh_automatic_group!(:trust_level_0) }
|
2022-07-27 22:34:08 +08:00
|
|
|
|
2023-11-07 11:59:40 +08:00
|
|
|
expect(events).to include(event_name: :group_updated, params: [tl0_users])
|
2022-07-27 22:34:08 +08:00
|
|
|
expect(GroupUser.exists?(group: tl0_users, user: user)).to eq(true)
|
|
|
|
publish_event_job_args = Jobs::PublishGroupMembershipUpdates.jobs.last["args"].first
|
|
|
|
expect(publish_event_job_args["user_ids"]).to include(user.id)
|
|
|
|
expect(publish_event_job_args["group_id"]).to eq(tl0_users.id)
|
|
|
|
expect(publish_event_job_args["type"]).to eq("add")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-03-06 14:44:08 +08:00
|
|
|
it "makes sure the everyone group is not visible except to staff" do
|
2017-01-18 12:20:23 +08:00
|
|
|
g = Group.refresh_automatic_group!(:everyone)
|
2019-03-06 14:44:08 +08:00
|
|
|
expect(g.visibility_level).to eq(Group.visibility_levels[:staff])
|
2017-01-18 12:20:23 +08:00
|
|
|
end
|
|
|
|
|
2019-07-09 03:09:50 +08:00
|
|
|
it "makes sure automatic groups are visible to logged on users" do
|
|
|
|
g = Group.refresh_automatic_group!(:moderators)
|
|
|
|
expect(g.visibility_level).to eq(Group.visibility_levels[:logged_on_users])
|
|
|
|
|
|
|
|
tl0 = Group.refresh_automatic_group!(:trust_level_0)
|
|
|
|
expect(tl0.visibility_level).to eq(Group.visibility_levels[:logged_on_users])
|
|
|
|
end
|
|
|
|
|
2017-12-19 10:13:58 +08:00
|
|
|
it "ensures that the moderators group is messageable by all" do
|
|
|
|
group = Group.find(Group::AUTO_GROUPS[:moderators])
|
|
|
|
group.update!(messageable_level: Group::ALIAS_LEVELS[:nobody])
|
|
|
|
Group.refresh_automatic_group!(:moderators)
|
|
|
|
|
|
|
|
expect(group.reload.messageable_level).to eq(Group::ALIAS_LEVELS[:everyone])
|
|
|
|
end
|
|
|
|
|
2017-07-14 13:15:33 +08:00
|
|
|
it "does not reset the localized name" do
|
|
|
|
begin
|
|
|
|
I18n.locale = SiteSetting.default_locale = "fi"
|
|
|
|
|
|
|
|
group = Group.find(Group::AUTO_GROUPS[:everyone])
|
|
|
|
group.update!(name: I18n.t("groups.default_names.everyone"))
|
|
|
|
|
|
|
|
Group.refresh_automatic_group!(:everyone)
|
|
|
|
|
|
|
|
expect(group.reload.name).to eq(I18n.t("groups.default_names.everyone"))
|
|
|
|
|
|
|
|
I18n.locale = SiteSetting.default_locale = "en"
|
|
|
|
|
|
|
|
Group.refresh_automatic_group!(:everyone)
|
|
|
|
|
|
|
|
expect(group.reload.name).to eq(I18n.t("groups.default_names.everyone"))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-01-18 12:20:23 +08:00
|
|
|
it "uses the localized name if name has not been taken" do
|
|
|
|
begin
|
|
|
|
I18n.locale = SiteSetting.default_locale = "de"
|
|
|
|
|
|
|
|
group = Group.refresh_automatic_group!(:staff)
|
|
|
|
|
|
|
|
expect(group.name).to_not eq("staff")
|
|
|
|
expect(group.name).to eq(I18n.t("groups.default_names.staff"))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "does not use the localized name if name has already been taken" do
|
|
|
|
begin
|
|
|
|
I18n.locale = SiteSetting.default_locale = "de"
|
|
|
|
|
2019-02-20 05:31:03 +08:00
|
|
|
Fabricate(:group, name: I18n.t("groups.default_names.staff").upcase)
|
2017-01-18 12:20:23 +08:00
|
|
|
group = Group.refresh_automatic_group!(:staff)
|
|
|
|
expect(group.name).to eq("staff")
|
2019-02-20 05:31:03 +08:00
|
|
|
|
2019-04-23 17:49:05 +08:00
|
|
|
Fabricate(:user, username: I18n.t("groups.default_names.moderators").upcase)
|
2019-02-20 05:31:03 +08:00
|
|
|
group = Group.refresh_automatic_group!(:moderators)
|
|
|
|
expect(group.name).to eq("moderators")
|
2017-01-18 12:20:23 +08:00
|
|
|
end
|
|
|
|
end
|
2019-02-20 05:31:03 +08:00
|
|
|
|
|
|
|
it "always uses the default locale" do
|
|
|
|
SiteSetting.default_locale = "de"
|
|
|
|
I18n.locale = "en"
|
|
|
|
|
|
|
|
group = Group.refresh_automatic_group!(:staff)
|
|
|
|
|
|
|
|
expect(group.name).to_not eq("staff")
|
|
|
|
expect(group.name).to eq(I18n.t("groups.default_names.staff", locale: "de"))
|
|
|
|
end
|
2016-10-24 10:03:22 +08:00
|
|
|
end
|
|
|
|
|
2015-04-10 10:17:28 +08:00
|
|
|
it "Correctly handles removal of primary group" do
|
2021-08-04 14:24:46 +08:00
|
|
|
group = Fabricate(:group, flair_icon: "icon")
|
2015-02-09 13:03:09 +08:00
|
|
|
user = Fabricate(:user)
|
|
|
|
group.add(user)
|
|
|
|
group.save
|
|
|
|
|
|
|
|
user.primary_group = group
|
|
|
|
user.save
|
|
|
|
|
|
|
|
group.reload
|
|
|
|
|
|
|
|
group.remove(user)
|
|
|
|
group.save
|
|
|
|
|
|
|
|
user.reload
|
|
|
|
expect(user.primary_group).to eq nil
|
2021-08-04 14:24:46 +08:00
|
|
|
expect(user.flair_group_id).to eq nil
|
2015-02-09 13:03:09 +08:00
|
|
|
end
|
|
|
|
|
2013-05-06 12:49:56 +08:00
|
|
|
it "Can update moderator/staff/admin groups correctly" do
|
|
|
|
admin = Fabricate(:admin)
|
|
|
|
moderator = Fabricate(:moderator)
|
|
|
|
|
|
|
|
Group.refresh_automatic_groups!(:admins, :staff, :moderators)
|
|
|
|
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(real_admins).to eq [admin.id]
|
|
|
|
expect(real_moderators).to eq [moderator.id]
|
|
|
|
expect(real_staff.sort).to eq [moderator.id, admin.id].sort
|
2013-05-06 12:49:56 +08:00
|
|
|
|
|
|
|
admin.admin = false
|
|
|
|
admin.save
|
|
|
|
|
|
|
|
Group.refresh_automatic_group!(:admins)
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(real_admins).to be_empty
|
2013-05-06 12:49:56 +08:00
|
|
|
|
|
|
|
moderator.revoke_moderation!
|
|
|
|
|
|
|
|
admin.grant_admin!
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(real_admins).to eq [admin.id]
|
|
|
|
expect(real_staff).to eq [admin.id]
|
2013-05-06 12:49:56 +08:00
|
|
|
|
|
|
|
admin.revoke_admin!
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(real_admins).to be_empty
|
|
|
|
expect(real_staff).to be_empty
|
2013-05-06 12:49:56 +08:00
|
|
|
|
|
|
|
admin.grant_moderation!
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(real_moderators).to eq [admin.id]
|
|
|
|
expect(real_staff).to eq [admin.id]
|
2013-05-06 12:49:56 +08:00
|
|
|
|
|
|
|
admin.revoke_moderation!
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(real_admins).to be_empty
|
|
|
|
expect(real_staff).to eq []
|
2018-08-15 14:59:56 +08:00
|
|
|
|
2021-07-01 09:27:11 +08:00
|
|
|
# we need some work to set min username to 6
|
2018-08-15 14:59:56 +08:00
|
|
|
|
|
|
|
User
|
|
|
|
.where("length(username) < 6")
|
|
|
|
.each do |u|
|
|
|
|
u.username = u.username + "ZZZZZZ"
|
|
|
|
u.save!
|
|
|
|
end
|
|
|
|
|
|
|
|
SiteSetting.min_username_length = 6
|
|
|
|
Group.refresh_automatic_groups!(:staff)
|
|
|
|
# should not explode here
|
2013-05-06 12:49:56 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "Correctly updates automatic trust level groups" do
|
|
|
|
user = Fabricate(:user)
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(Group[:trust_level_0].user_ids).to include user.id
|
2014-06-17 08:46:30 +08:00
|
|
|
|
2014-09-05 13:20:39 +08:00
|
|
|
user.change_trust_level!(TrustLevel[1])
|
2013-05-06 12:49:56 +08:00
|
|
|
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(Group[:trust_level_1].user_ids).to include user.id
|
2013-05-06 12:49:56 +08:00
|
|
|
|
2014-09-05 13:20:39 +08:00
|
|
|
user.change_trust_level!(TrustLevel[2])
|
2013-05-06 12:49:56 +08:00
|
|
|
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(Group[:trust_level_1].user_ids).to include user.id
|
|
|
|
expect(Group[:trust_level_2].user_ids).to include user.id
|
2013-05-08 08:32:29 +08:00
|
|
|
|
|
|
|
user2 = Fabricate(:coding_horror)
|
2014-09-05 13:20:39 +08:00
|
|
|
user2.change_trust_level!(TrustLevel[3])
|
2013-05-08 08:32:29 +08:00
|
|
|
|
2018-02-01 04:55:01 +08:00
|
|
|
expect(Group[:trust_level_2].user_ids).to include(user.id, user2.id)
|
2013-05-06 12:49:56 +08:00
|
|
|
end
|
|
|
|
|
2013-05-08 13:20:38 +08:00
|
|
|
it "Correctly updates all automatic groups upon request" do
|
2017-07-24 21:17:42 +08:00
|
|
|
admin = Fabricate(:admin)
|
2013-05-08 13:20:38 +08:00
|
|
|
user = Fabricate(:user)
|
2014-09-05 13:20:39 +08:00
|
|
|
user.change_trust_level!(TrustLevel[2])
|
2013-05-08 13:20:38 +08:00
|
|
|
|
2018-06-19 14:13:14 +08:00
|
|
|
DB.exec("UPDATE groups SET user_count = 0 WHERE id = #{Group::AUTO_GROUPS[:trust_level_2]}")
|
2013-05-08 13:20:38 +08:00
|
|
|
|
2022-12-20 02:12:03 +08:00
|
|
|
Group.delete_all
|
2013-05-08 13:20:38 +08:00
|
|
|
Group.refresh_automatic_groups!
|
|
|
|
|
|
|
|
groups = Group.includes(:users).to_a
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(groups.count).to eq Group::AUTO_GROUPS.count
|
2013-05-08 13:20:38 +08:00
|
|
|
|
2016-04-09 00:11:58 +08:00
|
|
|
g = groups.find { |grp| grp.id == Group::AUTO_GROUPS[:admins] }
|
2018-02-01 04:55:01 +08:00
|
|
|
expect(g.users.count).to eq g.user_count
|
|
|
|
expect(g.users.pluck(:id)).to contain_exactly(admin.id)
|
2013-05-08 13:20:38 +08:00
|
|
|
|
2016-04-09 00:11:58 +08:00
|
|
|
g = groups.find { |grp| grp.id == Group::AUTO_GROUPS[:staff] }
|
2018-02-01 04:55:01 +08:00
|
|
|
expect(g.users.count).to eq g.user_count
|
|
|
|
expect(g.users.pluck(:id)).to contain_exactly(admin.id)
|
2014-06-17 08:46:30 +08:00
|
|
|
|
2016-04-09 00:11:58 +08:00
|
|
|
g = groups.find { |grp| grp.id == Group::AUTO_GROUPS[:trust_level_1] }
|
2017-07-24 21:17:42 +08:00
|
|
|
expect(g.users.count).to eq g.user_count
|
2018-02-01 04:55:01 +08:00
|
|
|
expect(g.users.pluck(:id)).to contain_exactly(admin.id, user.id)
|
2014-06-17 08:46:30 +08:00
|
|
|
|
2016-04-09 00:11:58 +08:00
|
|
|
g = groups.find { |grp| grp.id == Group::AUTO_GROUPS[:trust_level_2] }
|
2017-07-24 21:17:42 +08:00
|
|
|
expect(g.users.count).to eq g.user_count
|
2018-02-01 04:55:01 +08:00
|
|
|
expect(g.users.pluck(:id)).to contain_exactly(user.id)
|
2013-05-08 13:20:38 +08:00
|
|
|
end
|
|
|
|
|
2013-05-09 09:33:56 +08:00
|
|
|
it "can set members via usernames helper" do
|
|
|
|
g = Fabricate(:group)
|
|
|
|
u1 = Fabricate(:user)
|
|
|
|
u2 = Fabricate(:user)
|
|
|
|
u3 = Fabricate(:user)
|
|
|
|
|
|
|
|
g.add(u1)
|
|
|
|
g.save!
|
|
|
|
|
|
|
|
usernames = "#{u2.username},#{u3.username}"
|
|
|
|
|
|
|
|
# no side effects please
|
|
|
|
g.usernames = usernames
|
|
|
|
g.reload
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(g.users.count).to eq 1
|
2013-05-09 09:33:56 +08:00
|
|
|
|
|
|
|
g.usernames = usernames
|
|
|
|
g.save!
|
|
|
|
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(g.usernames.split(",").sort).to eq usernames.split(",").sort
|
2013-05-09 09:33:56 +08:00
|
|
|
end
|
|
|
|
|
2018-03-27 14:23:35 +08:00
|
|
|
describe "new" do
|
2023-06-21 22:00:19 +08:00
|
|
|
subject(:group) { Fabricate.build(:group) }
|
2013-11-18 23:52:01 +08:00
|
|
|
|
2018-03-27 14:23:35 +08:00
|
|
|
it "triggers a extensibility event" do
|
2023-06-21 22:00:19 +08:00
|
|
|
event = DiscourseEvent.track_events { group.save! }.first
|
2018-03-27 14:23:35 +08:00
|
|
|
|
|
|
|
expect(event[:event_name]).to eq(:group_created)
|
2023-06-21 22:00:19 +08:00
|
|
|
expect(event[:params].first).to eq(group)
|
2018-03-27 14:23:35 +08:00
|
|
|
end
|
|
|
|
end
|
2013-05-09 09:33:56 +08:00
|
|
|
|
2018-03-27 14:23:35 +08:00
|
|
|
describe "destroy" do
|
2023-11-10 06:47:59 +08:00
|
|
|
fab!(:user)
|
2019-05-07 11:12:20 +08:00
|
|
|
fab!(:group) { Fabricate(:group, users: [user]) }
|
2013-05-09 09:33:56 +08:00
|
|
|
|
2018-03-27 14:23:35 +08:00
|
|
|
before { group.add(user) }
|
|
|
|
|
|
|
|
it "it deleted correctly" do
|
|
|
|
group.destroy!
|
|
|
|
expect(User.where(id: user.id).count).to eq 1
|
|
|
|
expect(GroupUser.where(group_id: group.id).count).to eq 0
|
|
|
|
end
|
|
|
|
|
|
|
|
it "triggers a extensibility event" do
|
|
|
|
event = DiscourseEvent.track_events { group.destroy! }.first
|
|
|
|
|
|
|
|
expect(event[:event_name]).to eq(:group_destroyed)
|
|
|
|
expect(event[:params].first).to eq(group)
|
|
|
|
end
|
2018-09-17 15:46:43 +08:00
|
|
|
|
|
|
|
it "strips the user's title and unsets the user's primary group when exact match" do
|
|
|
|
group.update(title: "Awesome")
|
|
|
|
user.update(primary_group: group)
|
|
|
|
|
|
|
|
group.destroy!
|
|
|
|
|
|
|
|
user.reload
|
|
|
|
expect(user.title).to eq(nil)
|
|
|
|
expect(user.primary_group).to eq(nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "does not strip title or unset primary group when not exact match" do
|
|
|
|
primary_group = Fabricate(:group, primary_group: true, title: "Different")
|
|
|
|
primary_group.add(user)
|
|
|
|
group.update(title: "Awesome")
|
|
|
|
|
|
|
|
group.destroy!
|
|
|
|
|
|
|
|
user.reload
|
|
|
|
expect(user.title).to eq("Different")
|
|
|
|
expect(user.primary_group).to eq(primary_group)
|
|
|
|
end
|
2020-05-25 04:12:55 +08:00
|
|
|
|
|
|
|
it "doesn't fail when the user gets destroyed" do
|
|
|
|
group.update(title: "Awesome")
|
|
|
|
group.add(user)
|
|
|
|
user.reload
|
|
|
|
|
|
|
|
UserDestroyer.new(Discourse.system_user).destroy(user)
|
|
|
|
end
|
2013-05-09 09:33:56 +08:00
|
|
|
end
|
|
|
|
|
2014-04-25 21:14:05 +08:00
|
|
|
it "has custom fields" do
|
|
|
|
group = Fabricate(:group)
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(group.custom_fields["a"]).to be_nil
|
2014-04-25 21:14:05 +08:00
|
|
|
|
|
|
|
group.custom_fields["hugh"] = "jackman"
|
|
|
|
group.custom_fields["jack"] = "black"
|
|
|
|
group.save
|
|
|
|
|
|
|
|
group = Group.find(group.id)
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(group.custom_fields).to eq("hugh" => "jackman", "jack" => "black")
|
2014-04-25 21:14:05 +08:00
|
|
|
end
|
|
|
|
|
2013-07-23 08:10:36 +08:00
|
|
|
it "allows you to lookup a new group by name" do
|
|
|
|
group = Fabricate(:group)
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(group.id).to eq Group[group.name].id
|
|
|
|
expect(group.id).to eq Group[group.name.to_sym].id
|
2013-07-23 08:10:36 +08:00
|
|
|
end
|
|
|
|
|
2023-05-12 01:39:33 +08:00
|
|
|
it "allows you to lookup a group by integer id" do
|
|
|
|
group = Fabricate(:group)
|
2023-05-24 18:45:22 +08:00
|
|
|
expect(Group.lookup_groups(group_ids: group.id)).to contain_exactly(group)
|
2023-05-12 01:39:33 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "allows you to lookup groups by comma separated string" do
|
|
|
|
group1 = Fabricate(:group)
|
|
|
|
group2 = Fabricate(:group)
|
2023-05-24 18:45:22 +08:00
|
|
|
expect(Group.lookup_groups(group_ids: "#{group1.id},#{group2.id}")).to contain_exactly(
|
|
|
|
group1,
|
|
|
|
group2,
|
|
|
|
)
|
2023-05-12 01:39:33 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "allows you to lookup groups by array" do
|
|
|
|
group1 = Fabricate(:group)
|
|
|
|
group2 = Fabricate(:group)
|
2023-05-24 18:45:22 +08:00
|
|
|
expect(Group.lookup_groups(group_ids: [group1.id, group2.id])).to contain_exactly(
|
|
|
|
group1,
|
|
|
|
group2,
|
|
|
|
)
|
2023-05-12 01:39:33 +08:00
|
|
|
end
|
|
|
|
|
2014-06-17 08:46:30 +08:00
|
|
|
it "can find desired groups correctly" do
|
2023-05-24 18:45:22 +08:00
|
|
|
expect(Group.desired_trust_level_groups(2)).to contain_exactly(10, 11, 12)
|
2014-06-17 08:46:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "correctly handles trust level changes" do
|
|
|
|
user = Fabricate(:user, trust_level: 2)
|
|
|
|
Group.user_trust_level_change!(user.id, 2)
|
|
|
|
|
2023-05-24 18:45:22 +08:00
|
|
|
expect(user.groups.map(&:name)).to match_array %w[trust_level_0 trust_level_1 trust_level_2]
|
2014-06-17 08:46:30 +08:00
|
|
|
|
|
|
|
Group.user_trust_level_change!(user.id, 0)
|
|
|
|
user.reload
|
2023-05-24 18:45:22 +08:00
|
|
|
expect(user.groups.map(&:name)).to contain_exactly("trust_level_0")
|
2014-06-17 08:46:30 +08:00
|
|
|
end
|
|
|
|
|
2021-06-01 16:34:41 +08:00
|
|
|
it "generates an event when applying group from trust level change" do
|
|
|
|
called = nil
|
|
|
|
block = Proc.new { |user, group| called = { user_id: user.id, group_id: group.id } }
|
|
|
|
|
|
|
|
begin
|
|
|
|
DiscourseEvent.on(:user_added_to_group, &block)
|
|
|
|
|
|
|
|
user = Fabricate(:user, trust_level: 2)
|
|
|
|
Group.user_trust_level_change!(user.id, 2)
|
|
|
|
|
|
|
|
expect(called).to eq(user_id: user.id, group_id: Group.find_by(name: "trust_level_2").id)
|
|
|
|
ensure
|
|
|
|
DiscourseEvent.off(:user_added_to_group, &block)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
describe "group management" do
|
2023-11-10 06:47:59 +08:00
|
|
|
fab!(:group)
|
2015-01-09 07:35:52 +08:00
|
|
|
|
|
|
|
it "by default has no managers" do
|
2015-11-09 21:55:16 +08:00
|
|
|
expect(group.group_users.where("group_users.owner")).to be_empty
|
2015-01-09 07:35:52 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "multiple managers can be appointed" do
|
|
|
|
2.times do |i|
|
|
|
|
u = Fabricate(:user)
|
2015-11-09 21:55:16 +08:00
|
|
|
group.add_owner(u)
|
2015-01-09 07:35:52 +08:00
|
|
|
end
|
2015-11-09 21:55:16 +08:00
|
|
|
expect(group.group_users.where("group_users.owner").count).to eq(2)
|
2015-01-09 07:35:52 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "manager has authority to edit membership" do
|
|
|
|
u = Fabricate(:user)
|
|
|
|
expect(Guardian.new(u).can_edit?(group)).to be_falsy
|
2015-11-09 21:55:16 +08:00
|
|
|
group.add_owner(u)
|
2015-01-09 07:35:52 +08:00
|
|
|
expect(Guardian.new(u).can_edit?(group)).to be_truthy
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-04-10 14:19:08 +08:00
|
|
|
describe "trust level management" do
|
|
|
|
it "correctly grants a trust level to members" do
|
|
|
|
group = Fabricate(:group, grant_trust_level: 2)
|
|
|
|
u0 = Fabricate(:user, trust_level: 0)
|
|
|
|
u3 = Fabricate(:user, trust_level: 3)
|
2015-09-02 04:52:05 +08:00
|
|
|
|
2018-04-10 14:19:08 +08:00
|
|
|
group.add(u0)
|
|
|
|
expect(u0.reload.trust_level).to eq(2)
|
2015-09-02 04:52:05 +08:00
|
|
|
|
2018-04-10 14:19:08 +08:00
|
|
|
group.add(u3)
|
|
|
|
expect(u3.reload.trust_level).to eq(3)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "when a user has qualified for trust level 1" do
|
|
|
|
fab!(:user) { Fabricate(:user, trust_level: 1, created_at: Time.zone.now - 10.years) }
|
2015-09-02 04:52:05 +08:00
|
|
|
|
2023-03-01 12:35:21 +08:00
|
|
|
fab!(:group) { Fabricate(:group, grant_trust_level: 3) }
|
|
|
|
fab!(:group2) { Fabricate(:group, grant_trust_level: 2) }
|
2017-11-24 04:55:44 +08:00
|
|
|
|
2018-04-10 14:19:08 +08:00
|
|
|
before { user.user_stat.update!(topics_entered: 999, posts_read_count: 999, time_read: 999) }
|
2017-11-24 04:55:44 +08:00
|
|
|
|
2018-04-10 14:19:08 +08:00
|
|
|
it "should not demote the user" do
|
|
|
|
group.add(user)
|
|
|
|
group2.add(user)
|
2017-11-24 04:55:44 +08:00
|
|
|
|
2023-03-01 12:35:21 +08:00
|
|
|
expect(user.reload.trust_level).to eq(3)
|
2017-11-24 04:55:44 +08:00
|
|
|
|
2018-04-10 14:19:08 +08:00
|
|
|
group.remove(user)
|
2017-11-24 04:55:44 +08:00
|
|
|
|
2023-03-01 12:35:21 +08:00
|
|
|
expect(user.reload.trust_level).to eq(2)
|
|
|
|
|
|
|
|
group2.remove(user)
|
|
|
|
|
|
|
|
expect(user.reload.trust_level).to eq(1)
|
2018-04-10 14:19:08 +08:00
|
|
|
end
|
|
|
|
end
|
2017-11-24 04:55:44 +08:00
|
|
|
|
2018-04-10 14:19:08 +08:00
|
|
|
it "adjusts the user trust level" do
|
|
|
|
g0 = Fabricate(:group, grant_trust_level: 2)
|
|
|
|
g1 = Fabricate(:group, grant_trust_level: 3)
|
|
|
|
g2 = Fabricate(:group)
|
|
|
|
|
|
|
|
user = Fabricate(:user, trust_level: 0)
|
|
|
|
|
|
|
|
# Add a group without one to consider `NULL` check
|
|
|
|
g2.add(user)
|
2018-08-25 06:41:03 +08:00
|
|
|
expect(user.group_granted_trust_level).to be_nil
|
2018-04-10 14:19:08 +08:00
|
|
|
expect(user.manual_locked_trust_level).to be_nil
|
|
|
|
|
|
|
|
g0.add(user)
|
|
|
|
expect(user.reload.trust_level).to eq(2)
|
2018-08-25 06:41:03 +08:00
|
|
|
expect(user.group_granted_trust_level).to eq(2)
|
2018-04-10 14:19:08 +08:00
|
|
|
expect(user.manual_locked_trust_level).to be_nil
|
|
|
|
|
|
|
|
g1.add(user)
|
|
|
|
expect(user.reload.trust_level).to eq(3)
|
2018-08-25 06:41:03 +08:00
|
|
|
expect(user.group_granted_trust_level).to eq(3)
|
2018-04-10 14:19:08 +08:00
|
|
|
expect(user.manual_locked_trust_level).to be_nil
|
|
|
|
|
|
|
|
g1.remove(user)
|
|
|
|
expect(user.reload.trust_level).to eq(2)
|
2018-08-25 06:41:03 +08:00
|
|
|
expect(user.group_granted_trust_level).to eq(2)
|
2018-04-10 14:19:08 +08:00
|
|
|
expect(user.manual_locked_trust_level).to be_nil
|
|
|
|
|
|
|
|
g0.remove(user)
|
|
|
|
user.reload
|
|
|
|
expect(user.manual_locked_trust_level).to be_nil
|
2018-08-25 06:41:03 +08:00
|
|
|
expect(user.group_granted_trust_level).to be_nil
|
2018-04-10 14:19:08 +08:00
|
|
|
expect(user.trust_level).to eq(0)
|
|
|
|
end
|
2017-11-24 01:58:46 +08:00
|
|
|
end
|
|
|
|
|
2016-12-05 16:18:24 +08:00
|
|
|
it "should cook the bio" do
|
|
|
|
group = Fabricate(:group)
|
2019-04-29 15:32:25 +08:00
|
|
|
group.update!(bio_raw: "This is a group for :unicorn: lovers")
|
2016-12-05 16:18:24 +08:00
|
|
|
|
|
|
|
expect(group.bio_cooked).to include("unicorn.png")
|
2019-06-12 23:37:44 +08:00
|
|
|
|
|
|
|
group.update!(bio_raw: "")
|
|
|
|
|
|
|
|
expect(group.bio_cooked).to eq(nil)
|
2016-12-05 16:18:24 +08:00
|
|
|
end
|
|
|
|
|
2017-02-03 16:51:32 +08:00
|
|
|
describe ".visible_groups" do
|
2017-07-04 03:26:46 +08:00
|
|
|
def can_view?(user, group)
|
|
|
|
Group.visible_groups(user).where(id: group.id).exists?
|
2017-02-03 16:51:32 +08:00
|
|
|
end
|
|
|
|
|
2017-07-04 03:26:46 +08:00
|
|
|
it "correctly restricts group visibility" do
|
|
|
|
group = Fabricate.build(:group, visibility_level: Group.visibility_levels[:owners])
|
2019-07-09 03:09:50 +08:00
|
|
|
logged_on_user = Fabricate(:user)
|
2017-07-04 03:26:46 +08:00
|
|
|
member = Fabricate(:user)
|
|
|
|
group.add(member)
|
|
|
|
group.save!
|
2017-02-03 16:51:32 +08:00
|
|
|
|
2017-07-04 03:26:46 +08:00
|
|
|
owner = Fabricate(:user)
|
|
|
|
group.add_owner(owner)
|
2017-02-03 16:51:32 +08:00
|
|
|
|
2017-07-04 03:26:46 +08:00
|
|
|
moderator = Fabricate(:user, moderator: true)
|
|
|
|
admin = Fabricate(:user, admin: true)
|
2017-02-03 16:51:32 +08:00
|
|
|
|
2017-07-04 03:26:46 +08:00
|
|
|
expect(can_view?(admin, group)).to eq(true)
|
|
|
|
expect(can_view?(owner, group)).to eq(true)
|
|
|
|
expect(can_view?(moderator, group)).to eq(false)
|
|
|
|
expect(can_view?(member, group)).to eq(false)
|
2019-07-09 03:09:50 +08:00
|
|
|
expect(can_view?(logged_on_user, group)).to eq(false)
|
2017-07-04 03:26:46 +08:00
|
|
|
expect(can_view?(nil, group)).to eq(false)
|
2017-02-03 16:51:32 +08:00
|
|
|
|
2023-06-14 23:25:45 +08:00
|
|
|
group.add_owner(moderator)
|
|
|
|
|
|
|
|
expect(can_view?(moderator, group)).to eq(true)
|
|
|
|
|
|
|
|
GroupUser.delete_by(group: group, user: moderator)
|
2017-07-04 03:26:46 +08:00
|
|
|
group.update_columns(visibility_level: Group.visibility_levels[:staff])
|
|
|
|
|
|
|
|
expect(can_view?(admin, group)).to eq(true)
|
|
|
|
expect(can_view?(owner, group)).to eq(true)
|
|
|
|
expect(can_view?(moderator, group)).to eq(true)
|
|
|
|
expect(can_view?(member, group)).to eq(false)
|
2019-07-09 03:09:50 +08:00
|
|
|
expect(can_view?(logged_on_user, group)).to eq(false)
|
2017-07-04 03:26:46 +08:00
|
|
|
expect(can_view?(nil, group)).to eq(false)
|
|
|
|
|
|
|
|
group.update_columns(visibility_level: Group.visibility_levels[:members])
|
|
|
|
|
|
|
|
expect(can_view?(admin, group)).to eq(true)
|
|
|
|
expect(can_view?(owner, group)).to eq(true)
|
2020-09-11 10:50:13 +08:00
|
|
|
expect(can_view?(moderator, group)).to eq(true)
|
2017-07-04 03:26:46 +08:00
|
|
|
expect(can_view?(member, group)).to eq(true)
|
2019-07-09 03:09:50 +08:00
|
|
|
expect(can_view?(logged_on_user, group)).to eq(false)
|
2017-07-04 03:26:46 +08:00
|
|
|
expect(can_view?(nil, group)).to eq(false)
|
|
|
|
|
|
|
|
group.update_columns(visibility_level: Group.visibility_levels[:public])
|
|
|
|
|
|
|
|
expect(can_view?(admin, group)).to eq(true)
|
|
|
|
expect(can_view?(owner, group)).to eq(true)
|
|
|
|
expect(can_view?(moderator, group)).to eq(true)
|
|
|
|
expect(can_view?(member, group)).to eq(true)
|
2019-07-09 03:09:50 +08:00
|
|
|
expect(can_view?(logged_on_user, group)).to eq(true)
|
2017-07-04 03:26:46 +08:00
|
|
|
expect(can_view?(nil, group)).to eq(true)
|
2019-07-09 03:09:50 +08:00
|
|
|
|
|
|
|
group.update_columns(visibility_level: Group.visibility_levels[:logged_on_users])
|
|
|
|
|
|
|
|
expect(can_view?(admin, group)).to eq(true)
|
|
|
|
expect(can_view?(owner, group)).to eq(true)
|
|
|
|
expect(can_view?(moderator, group)).to eq(true)
|
|
|
|
expect(can_view?(member, group)).to eq(true)
|
|
|
|
expect(can_view?(logged_on_user, group)).to eq(true)
|
|
|
|
expect(can_view?(nil, group)).to eq(false)
|
2017-02-03 16:51:32 +08:00
|
|
|
end
|
|
|
|
end
|
2017-06-02 16:38:14 +08:00
|
|
|
|
2019-08-14 21:30:04 +08:00
|
|
|
describe ".members_visible_groups" do
|
|
|
|
def can_view?(user, group)
|
|
|
|
Group.members_visible_groups(user).exists?(id: group.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "correctly restricts group members visibility" do
|
|
|
|
group = Fabricate.build(:group, members_visibility_level: Group.visibility_levels[:owners])
|
|
|
|
logged_on_user = Fabricate(:user)
|
|
|
|
member = Fabricate(:user)
|
|
|
|
group.add(member)
|
|
|
|
group.save!
|
|
|
|
|
|
|
|
owner = Fabricate(:user)
|
|
|
|
group.add_owner(owner)
|
|
|
|
|
|
|
|
moderator = Fabricate(:user, moderator: true)
|
|
|
|
admin = Fabricate(:user, admin: true)
|
|
|
|
|
|
|
|
expect(can_view?(admin, group)).to eq(true)
|
|
|
|
expect(can_view?(owner, group)).to eq(true)
|
|
|
|
expect(can_view?(moderator, group)).to eq(false)
|
|
|
|
expect(can_view?(member, group)).to eq(false)
|
|
|
|
expect(can_view?(logged_on_user, group)).to eq(false)
|
|
|
|
expect(can_view?(nil, group)).to eq(false)
|
|
|
|
|
2023-06-14 23:25:45 +08:00
|
|
|
group.add_owner(moderator)
|
|
|
|
|
|
|
|
expect(can_view?(moderator, group)).to eq(true)
|
|
|
|
|
|
|
|
GroupUser.delete_by(group: group, user: moderator)
|
2019-08-14 21:30:04 +08:00
|
|
|
group.update_columns(members_visibility_level: Group.visibility_levels[:staff])
|
|
|
|
|
|
|
|
expect(can_view?(admin, group)).to eq(true)
|
|
|
|
expect(can_view?(owner, group)).to eq(true)
|
|
|
|
expect(can_view?(moderator, group)).to eq(true)
|
|
|
|
expect(can_view?(member, group)).to eq(false)
|
|
|
|
expect(can_view?(logged_on_user, group)).to eq(false)
|
|
|
|
expect(can_view?(nil, group)).to eq(false)
|
|
|
|
|
|
|
|
group.update_columns(members_visibility_level: Group.visibility_levels[:members])
|
|
|
|
|
|
|
|
expect(can_view?(admin, group)).to eq(true)
|
|
|
|
expect(can_view?(owner, group)).to eq(true)
|
2020-09-11 10:50:13 +08:00
|
|
|
expect(can_view?(moderator, group)).to eq(true)
|
2019-08-14 21:30:04 +08:00
|
|
|
expect(can_view?(member, group)).to eq(true)
|
|
|
|
expect(can_view?(logged_on_user, group)).to eq(false)
|
|
|
|
expect(can_view?(nil, group)).to eq(false)
|
|
|
|
|
|
|
|
group.update_columns(members_visibility_level: Group.visibility_levels[:public])
|
|
|
|
|
|
|
|
expect(can_view?(admin, group)).to eq(true)
|
|
|
|
expect(can_view?(owner, group)).to eq(true)
|
|
|
|
expect(can_view?(moderator, group)).to eq(true)
|
|
|
|
expect(can_view?(member, group)).to eq(true)
|
|
|
|
expect(can_view?(logged_on_user, group)).to eq(true)
|
|
|
|
expect(can_view?(nil, group)).to eq(true)
|
|
|
|
|
|
|
|
group.update_columns(members_visibility_level: Group.visibility_levels[:logged_on_users])
|
|
|
|
|
|
|
|
expect(can_view?(admin, group)).to eq(true)
|
|
|
|
expect(can_view?(owner, group)).to eq(true)
|
|
|
|
expect(can_view?(moderator, group)).to eq(true)
|
|
|
|
expect(can_view?(member, group)).to eq(true)
|
|
|
|
expect(can_view?(logged_on_user, group)).to eq(true)
|
|
|
|
expect(can_view?(nil, group)).to eq(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-09-17 15:46:43 +08:00
|
|
|
describe "#remove" do
|
|
|
|
before { group.add(user) }
|
|
|
|
|
2018-09-21 10:06:08 +08:00
|
|
|
context "when stripping title" do
|
|
|
|
it "only strips user's title if exact match" do
|
|
|
|
group.update!(title: "Awesome")
|
|
|
|
expect { group.remove(user) }.to change { user.reload.title }.from("Awesome").to(nil)
|
2018-09-17 15:46:43 +08:00
|
|
|
|
2018-09-21 10:06:08 +08:00
|
|
|
group.add(user)
|
|
|
|
user.update_columns(title: "Different")
|
|
|
|
expect { group.remove(user) }.to_not change { user.reload.title }
|
|
|
|
end
|
|
|
|
|
|
|
|
it "grants another title when the user has other available titles" do
|
|
|
|
group.update!(title: "Awesome")
|
|
|
|
Fabricate(:group, title: "Super").add(user)
|
|
|
|
|
|
|
|
expect { group.remove(user) }.to change { user.reload.title }.from("Awesome").to("Super")
|
|
|
|
end
|
2018-09-17 15:46:43 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "unsets the user's primary group" do
|
|
|
|
user.update(primary_group: group)
|
|
|
|
expect { group.remove(user) }.to change { user.reload.primary_group }.from(group).to(nil)
|
|
|
|
end
|
2019-09-11 00:58:08 +08:00
|
|
|
|
|
|
|
it "triggers a user_removed_from_group event" do
|
|
|
|
events = DiscourseEvent.track_events { group.remove(user) }.map { |e| e[:event_name] }
|
|
|
|
expect(events).to include(:user_removed_from_group)
|
|
|
|
end
|
2023-03-30 21:06:36 +08:00
|
|
|
|
|
|
|
describe "with webhook" do
|
2023-11-10 06:47:59 +08:00
|
|
|
fab!(:group_user_web_hook)
|
2023-03-30 21:06:36 +08:00
|
|
|
|
|
|
|
it "Enqueues webhook events" do
|
|
|
|
group.remove(user)
|
|
|
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
|
|
|
|
|
|
|
expect(job_args["event_name"]).to eq("user_removed_from_group")
|
|
|
|
payload = JSON.parse(job_args["payload"])
|
|
|
|
expect(payload["group_id"]).to eq(group.id)
|
|
|
|
expect(payload["user_id"]).to eq(user.id)
|
|
|
|
end
|
|
|
|
end
|
2018-09-17 15:46:43 +08:00
|
|
|
end
|
|
|
|
|
2017-06-02 16:38:14 +08:00
|
|
|
describe "#add" do
|
2018-09-17 15:46:43 +08:00
|
|
|
it "grants the title only if the new member does not have title" do
|
|
|
|
group.update(title: "Awesome")
|
|
|
|
expect { group.add(user) }.to change { user.reload.title }.from(nil).to("Awesome")
|
|
|
|
|
|
|
|
group.remove(user)
|
|
|
|
user.update(title: "Already Awesome")
|
|
|
|
expect { group.add(user) }.not_to change { user.reload.title }
|
|
|
|
end
|
|
|
|
|
|
|
|
it "always sets user's primary group" do
|
2018-11-14 08:28:41 +08:00
|
|
|
group.update(primary_group: true, title: "AAAA")
|
2018-09-17 15:46:43 +08:00
|
|
|
expect { group.add(user) }.to change { user.reload.primary_group }.from(nil).to(group)
|
|
|
|
|
2018-11-14 08:28:41 +08:00
|
|
|
new_group = Fabricate(:group, primary_group: true, title: "BBBB")
|
|
|
|
|
|
|
|
expect {
|
|
|
|
new_group.add(user)
|
|
|
|
user.reload
|
|
|
|
}.to change { user.primary_group }.from(group).to(new_group).and change { user.title }.from(
|
|
|
|
"AAAA",
|
|
|
|
).to("BBBB")
|
2018-09-17 15:46:43 +08:00
|
|
|
end
|
|
|
|
|
2019-08-06 18:29:46 +08:00
|
|
|
it "can send a notification to the user" do
|
|
|
|
expect { group.add(user, notify: true) }.to change { Notification.count }.by(1)
|
|
|
|
|
|
|
|
notification = Notification.last
|
|
|
|
expect(notification.notification_type).to eq(Notification.types[:membership_request_accepted])
|
|
|
|
expect(notification.user_id).to eq(user.id)
|
|
|
|
end
|
|
|
|
|
2019-09-11 00:58:08 +08:00
|
|
|
it "triggers a user_added_to_group event" do
|
2020-11-12 03:46:13 +08:00
|
|
|
automatic = nil
|
|
|
|
called = false
|
2019-09-11 00:58:08 +08:00
|
|
|
|
2020-11-12 03:46:13 +08:00
|
|
|
block =
|
|
|
|
Proc.new do |_u, _g, options|
|
|
|
|
automatic = options[:automatic]
|
|
|
|
called = true
|
|
|
|
end
|
|
|
|
begin
|
|
|
|
DiscourseEvent.on(:user_added_to_group, &block)
|
2019-09-11 00:58:08 +08:00
|
|
|
|
|
|
|
group.add(user)
|
|
|
|
|
|
|
|
expect(automatic).to eql(false)
|
|
|
|
expect(called).to eq(true)
|
|
|
|
ensure
|
2020-11-12 03:46:13 +08:00
|
|
|
DiscourseEvent.off(:user_added_to_group, &block)
|
2019-09-11 00:58:08 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-06-02 16:38:14 +08:00
|
|
|
context "when adding a user into a public group" do
|
2023-11-10 06:47:59 +08:00
|
|
|
fab!(:category)
|
2017-06-02 16:38:14 +08:00
|
|
|
|
|
|
|
it "should publish the group's categories to the client" do
|
2017-07-28 10:37:10 +08:00
|
|
|
group.update!(public_admission: true, categories: [category])
|
2017-06-02 16:38:14 +08:00
|
|
|
|
2018-05-08 10:50:11 +08:00
|
|
|
message = MessageBus.track_publish("/categories") { group.add(user) }.first
|
2017-06-02 16:38:14 +08:00
|
|
|
|
|
|
|
expect(message.data[:categories].count).to eq(1)
|
|
|
|
expect(message.data[:categories].first[:id]).to eq(category.id)
|
|
|
|
expect(message.user_ids).to eq([user.id])
|
|
|
|
end
|
2017-08-16 10:38:30 +08:00
|
|
|
|
|
|
|
describe "when group belongs to more than #{Group::PUBLISH_CATEGORIES_LIMIT} categories" do
|
|
|
|
it "should publish a message to refresh the user's client" do
|
|
|
|
(Group::PUBLISH_CATEGORIES_LIMIT + 1).times { group.categories << Fabricate(:category) }
|
|
|
|
|
|
|
|
message = MessageBus.track_publish { group.add(user) }.first
|
|
|
|
|
|
|
|
expect(message.data).to eq("clobber")
|
|
|
|
expect(message.channel).to eq("/refresh_client")
|
|
|
|
expect(message.user_ids).to eq([user.id])
|
|
|
|
end
|
|
|
|
end
|
2017-06-02 16:38:14 +08:00
|
|
|
end
|
|
|
|
end
|
2017-06-29 15:36:51 +08:00
|
|
|
|
2017-11-02 10:20:14 +08:00
|
|
|
describe ".search_groups" do
|
2022-03-03 13:57:52 +08:00
|
|
|
def search_group_names(name)
|
|
|
|
Group.search_groups(name, sort: :auto).map(&:name)
|
|
|
|
end
|
2017-06-29 15:36:51 +08:00
|
|
|
|
|
|
|
it "should return the right groups" do
|
2022-12-20 02:12:03 +08:00
|
|
|
Group.delete_all
|
|
|
|
Group.refresh_automatic_groups!
|
|
|
|
|
2022-03-03 13:57:52 +08:00
|
|
|
group_name =
|
|
|
|
Fabricate(:group, name: "tEsT_more_things", full_name: "Abc something awesome").name
|
|
|
|
|
|
|
|
expect(search_group_names("te")).to eq([group_name])
|
|
|
|
expect(search_group_names("TE")).to eq([group_name])
|
|
|
|
expect(search_group_names("es")).to eq([group_name])
|
|
|
|
expect(search_group_names("ES")).to eq([group_name])
|
|
|
|
expect(search_group_names("ngs")).to eq([group_name])
|
|
|
|
expect(search_group_names("sOmEthi")).to eq([group_name])
|
|
|
|
expect(search_group_names("abc")).to eq([group_name])
|
|
|
|
expect(search_group_names("sOmEthi")).to eq([group_name])
|
|
|
|
expect(search_group_names("test2")).to eq([])
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should prioritize prefix matches on group's name or fullname" do
|
|
|
|
Fabricate(:group, name: "pears_11", full_name: "fred apple")
|
|
|
|
Fabricate(:group, name: "apples", full_name: "jane orange")
|
|
|
|
Fabricate(:group, name: "oranges2", full_name: "nothing")
|
|
|
|
Fabricate(:group, name: "oranges1", full_name: "ms fred")
|
|
|
|
|
|
|
|
expect(search_group_names("ap")).to eq(%w[apples pears_11])
|
|
|
|
expect(search_group_names("fr")).to eq(%w[pears_11 oranges1])
|
|
|
|
expect(search_group_names("oran")).to eq(%w[oranges1 oranges2 apples])
|
|
|
|
|
|
|
|
expect(search_group_names("pearsX11")).to eq([])
|
2017-06-29 15:36:51 +08:00
|
|
|
end
|
|
|
|
end
|
2017-07-27 14:39:47 +08:00
|
|
|
|
|
|
|
describe "#bulk_add" do
|
|
|
|
it "should be able to add multiple users" do
|
|
|
|
group.bulk_add([user.id, admin.id])
|
|
|
|
|
|
|
|
expect(group.group_users.map(&:user_id)).to contain_exactly(user.id, admin.id)
|
|
|
|
end
|
2018-02-23 16:39:49 +08:00
|
|
|
|
|
|
|
it "updates group user count" do
|
|
|
|
expect {
|
2018-02-23 17:55:15 +08:00
|
|
|
group.bulk_add([user.id, admin.id])
|
|
|
|
group.reload
|
2023-03-30 21:06:36 +08:00
|
|
|
}.to change { group.user_count }.from(0).to(2)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#bulk_remove" do
|
|
|
|
it "removes multiple users from the group and doesn't error with user_ids not present" do
|
|
|
|
group.bulk_add([user.id, admin.id])
|
|
|
|
|
|
|
|
group.bulk_remove([user.id, admin.id, admin.id + 1])
|
|
|
|
|
|
|
|
expect(group.group_users.count).to be_zero
|
|
|
|
end
|
|
|
|
|
|
|
|
it "updates group user count" do
|
|
|
|
group.bulk_add([user.id, admin.id])
|
|
|
|
expect(group.reload.user_count).to eq(2)
|
|
|
|
|
|
|
|
group.bulk_remove([user.id, admin.id])
|
|
|
|
expect(group.reload.user_count).to eq(0)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "with webhook" do
|
2023-11-10 06:47:59 +08:00
|
|
|
fab!(:group_user_web_hook)
|
2023-03-30 21:06:36 +08:00
|
|
|
|
|
|
|
it "Enqueues user_removed_from_group webhook events for each group_user" do
|
|
|
|
group.bulk_add([user.id, admin.id])
|
|
|
|
|
|
|
|
group.bulk_remove([user.id, admin.id])
|
|
|
|
Jobs::EmitWebHookEvent
|
|
|
|
.jobs
|
|
|
|
.last(2)
|
|
|
|
.each do |event|
|
|
|
|
job_args = event["args"].first
|
|
|
|
expect(job_args["event_name"]).to eq("user_removed_from_group")
|
|
|
|
payload = JSON.parse(job_args["payload"])
|
|
|
|
expect(payload["group_id"]).to eq(group.id)
|
|
|
|
expect([user.id, admin.id]).to include(payload["user_id"])
|
|
|
|
end
|
|
|
|
end
|
2018-02-23 16:39:49 +08:00
|
|
|
end
|
2017-07-27 14:39:47 +08:00
|
|
|
end
|
2017-09-29 00:34:19 +08:00
|
|
|
|
|
|
|
it "Correctly updates has_messages" do
|
|
|
|
group = Fabricate(:group, has_messages: true)
|
|
|
|
topic = Fabricate(:private_message_topic)
|
|
|
|
|
|
|
|
# when group message is not present
|
|
|
|
Group.refresh_has_messages!
|
|
|
|
group.reload
|
|
|
|
expect(group.has_messages?).to eq false
|
|
|
|
|
|
|
|
# when group message is present
|
|
|
|
group.update!(has_messages: true)
|
|
|
|
TopicAllowedGroup.create!(topic_id: topic.id, group_id: group.id)
|
|
|
|
Group.refresh_has_messages!
|
|
|
|
group.reload
|
|
|
|
expect(group.has_messages?).to eq true
|
|
|
|
end
|
2018-09-20 11:24:01 +08:00
|
|
|
|
|
|
|
describe "#automatic_group_membership" do
|
2020-04-23 00:37:39 +08:00
|
|
|
let(:group) { Fabricate(:group, automatic_membership_email_domains: "example.com") }
|
2018-09-20 11:24:01 +08:00
|
|
|
|
2020-04-23 00:37:39 +08:00
|
|
|
it "should be triggered on create and update" do
|
|
|
|
expect { group }.to change { Jobs::AutomaticGroupMembership.jobs.size }.by(1)
|
2018-09-20 11:24:01 +08:00
|
|
|
|
2020-04-23 00:37:39 +08:00
|
|
|
job = Jobs::AutomaticGroupMembership.jobs.last
|
2018-09-20 11:24:01 +08:00
|
|
|
|
2020-04-23 00:37:39 +08:00
|
|
|
expect(job["args"].first["group_id"]).to eq(group.id)
|
2018-09-20 11:24:01 +08:00
|
|
|
|
2020-04-23 00:37:39 +08:00
|
|
|
Jobs::AutomaticGroupMembership.jobs.clear
|
2018-09-20 11:24:01 +08:00
|
|
|
|
2020-04-23 00:37:39 +08:00
|
|
|
expect do group.update!(name: "asdiaksjdias") end.to change {
|
|
|
|
Jobs::AutomaticGroupMembership.jobs.size
|
|
|
|
}.by(1)
|
2018-09-20 11:24:01 +08:00
|
|
|
|
2020-04-23 00:37:39 +08:00
|
|
|
job = Jobs::AutomaticGroupMembership.jobs.last
|
2018-09-20 11:24:01 +08:00
|
|
|
|
2020-04-23 00:37:39 +08:00
|
|
|
expect(job["args"].first["group_id"]).to eq(group.id)
|
2018-09-20 11:24:01 +08:00
|
|
|
end
|
|
|
|
end
|
2018-11-27 05:49:57 +08:00
|
|
|
|
2020-10-28 05:01:58 +08:00
|
|
|
describe "IMAP" do
|
2020-08-04 12:19:57 +08:00
|
|
|
let(:group) { Fabricate(:group) }
|
|
|
|
|
|
|
|
def mock_imap
|
|
|
|
@mocked_imap_provider =
|
|
|
|
MockedImapProvider.new(
|
|
|
|
group.imap_server,
|
|
|
|
port: group.imap_port,
|
|
|
|
ssl: group.imap_ssl,
|
|
|
|
username: group.email_username,
|
|
|
|
password: group.email_password,
|
|
|
|
)
|
|
|
|
Imap::Providers::Detector.stubs(:init_with_detected_provider).returns(@mocked_imap_provider)
|
|
|
|
end
|
|
|
|
|
|
|
|
def configure_imap
|
|
|
|
group.update(
|
|
|
|
imap_server: "imap.gmail.com",
|
|
|
|
imap_port: 993,
|
|
|
|
imap_ssl: true,
|
FEATURE: Improve group email settings UI (#13083)
This overhauls the user interface for the group email settings management, aiming to make it a lot easier to test the settings entered and confirm they are correct before proceeding. We do this by forcing the user to test the settings before they can be saved to the database. It also includes some quality of life improvements around setting up IMAP and SMTP for our first supported provider, GMail. This PR does not remove the old group email config, that will come in a subsequent PR. This is related to https://meta.discourse.org/t/imap-support-for-group-inboxes/160588 so read that if you would like more backstory.
### UI
Both site settings of `enable_imap` and `enable_smtp` must be true to test this. You must enable SMTP first to enable IMAP.
You can prefill the SMTP settings with GMail configuration. To proceed with saving these settings you must test them, which is handled by the EmailSettingsValidator.
If there is an issue with the configuration or credentials a meaningful error message should be shown.
IMAP settings must also be validated when IMAP is enabled, before saving.
When saving IMAP, we fetch the mailboxes for that account and populate them. This mailbox must be selected and saved for IMAP to work (the feature acts as though it is disabled until the mailbox is selected and saved):
### Database & Backend
This adds several columns to the Groups table. The purpose of this change is to make it much more explicit that SMTP/IMAP is enabled for a group, rather than relying on settings not being null. Also included is an UPDATE query to backfill these columns. These columns are automatically filled when updating the group.
For GMail, we now filter the mailboxes returned. This is so users cannot use a mailbox like Sent or Trash for syncing, which would generally be disastrous.
There is a new group endpoint for testing email settings. This may be useful in the future for other places in our UI, at which point it can be extracted to a more generic endpoint or module to be included.
2021-05-28 07:28:18 +08:00
|
|
|
imap_enabled: true,
|
2020-08-04 12:19:57 +08:00
|
|
|
email_username: "test@gmail.com",
|
|
|
|
email_password: "testPassword1!",
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
def enable_imap
|
|
|
|
SiteSetting.enable_imap = true
|
|
|
|
@mocked_imap_provider.stubs(:connect!)
|
FEATURE: Improve group email settings UI (#13083)
This overhauls the user interface for the group email settings management, aiming to make it a lot easier to test the settings entered and confirm they are correct before proceeding. We do this by forcing the user to test the settings before they can be saved to the database. It also includes some quality of life improvements around setting up IMAP and SMTP for our first supported provider, GMail. This PR does not remove the old group email config, that will come in a subsequent PR. This is related to https://meta.discourse.org/t/imap-support-for-group-inboxes/160588 so read that if you would like more backstory.
### UI
Both site settings of `enable_imap` and `enable_smtp` must be true to test this. You must enable SMTP first to enable IMAP.
You can prefill the SMTP settings with GMail configuration. To proceed with saving these settings you must test them, which is handled by the EmailSettingsValidator.
If there is an issue with the configuration or credentials a meaningful error message should be shown.
IMAP settings must also be validated when IMAP is enabled, before saving.
When saving IMAP, we fetch the mailboxes for that account and populate them. This mailbox must be selected and saved for IMAP to work (the feature acts as though it is disabled until the mailbox is selected and saved):
### Database & Backend
This adds several columns to the Groups table. The purpose of this change is to make it much more explicit that SMTP/IMAP is enabled for a group, rather than relying on settings not being null. Also included is an UPDATE query to backfill these columns. These columns are automatically filled when updating the group.
For GMail, we now filter the mailboxes returned. This is so users cannot use a mailbox like Sent or Trash for syncing, which would generally be disastrous.
There is a new group endpoint for testing email settings. This may be useful in the future for other places in our UI, at which point it can be extracted to a more generic endpoint or module to be included.
2021-05-28 07:28:18 +08:00
|
|
|
@mocked_imap_provider.stubs(:list_mailboxes_with_attributes).returns(
|
|
|
|
[stub(attr: [], name: "Inbox")],
|
|
|
|
)
|
2020-08-04 12:19:57 +08:00
|
|
|
@mocked_imap_provider.stubs(:list_mailboxes).returns(["Inbox"])
|
|
|
|
@mocked_imap_provider.stubs(:disconnect!)
|
|
|
|
end
|
|
|
|
|
|
|
|
before { Discourse.redis.del("group_imap_mailboxes_#{group.id}") }
|
|
|
|
|
2020-10-28 05:01:58 +08:00
|
|
|
describe "#imap_mailboxes" do
|
|
|
|
it "returns an empty array if group imap is not configured" do
|
|
|
|
expect(group.imap_mailboxes).to eq([])
|
|
|
|
end
|
2020-08-04 12:19:57 +08:00
|
|
|
|
2020-10-28 05:01:58 +08:00
|
|
|
it "returns an empty array and does not contact IMAP server if group imap is configured but the setting is disabled" do
|
|
|
|
configure_imap
|
|
|
|
Imap::Providers::Detector.expects(:init_with_detected_provider).never
|
|
|
|
expect(group.imap_mailboxes).to eq([])
|
|
|
|
end
|
|
|
|
|
|
|
|
it "logs the imap error if one occurs" do
|
|
|
|
configure_imap
|
|
|
|
mock_imap
|
|
|
|
SiteSetting.enable_imap = true
|
|
|
|
@mocked_imap_provider.stubs(:connect!).raises(Net::IMAP::NoResponseError)
|
|
|
|
group.imap_mailboxes
|
|
|
|
expect(group.reload.imap_last_error).not_to eq(nil)
|
|
|
|
end
|
2020-08-04 12:19:57 +08:00
|
|
|
|
2020-10-28 05:01:58 +08:00
|
|
|
it "returns a list of mailboxes from the IMAP provider" do
|
|
|
|
configure_imap
|
|
|
|
mock_imap
|
|
|
|
enable_imap
|
|
|
|
expect(group.imap_mailboxes).to eq(["Inbox"])
|
|
|
|
end
|
|
|
|
|
|
|
|
it "caches the login and mailbox fetch" do
|
|
|
|
configure_imap
|
|
|
|
mock_imap
|
|
|
|
enable_imap
|
|
|
|
group.imap_mailboxes
|
|
|
|
Imap::Providers::Detector.expects(:init_with_detected_provider).never
|
|
|
|
group.imap_mailboxes
|
|
|
|
end
|
2020-08-04 12:19:57 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
describe "Unicode usernames and group names" do
|
2019-04-23 18:22:47 +08:00
|
|
|
before { SiteSetting.unicode_usernames = true }
|
|
|
|
|
|
|
|
it "should normalize the name" do
|
|
|
|
group = Fabricate(:group, name: "Bücherwurm") # NFD
|
|
|
|
expect(group.name).to eq("Bücherwurm") # NFC
|
|
|
|
end
|
|
|
|
end
|
2020-08-07 00:27:27 +08:00
|
|
|
|
|
|
|
describe "default notifications" do
|
|
|
|
let(:category1) { Fabricate(:category) }
|
|
|
|
let(:category2) { Fabricate(:category) }
|
|
|
|
let(:category3) { Fabricate(:category) }
|
2020-08-14 05:20:23 +08:00
|
|
|
let(:category4) { Fabricate(:category) }
|
2020-08-07 00:27:27 +08:00
|
|
|
let(:tag1) { Fabricate(:tag) }
|
|
|
|
let(:tag2) { Fabricate(:tag) }
|
|
|
|
let(:tag3) { Fabricate(:tag) }
|
2020-08-14 05:20:23 +08:00
|
|
|
let(:tag4) { Fabricate(:tag) }
|
2020-08-07 00:27:27 +08:00
|
|
|
let(:synonym1) { Fabricate(:tag, target_tag: tag1) }
|
|
|
|
let(:synonym2) { Fabricate(:tag, target_tag: tag2) }
|
|
|
|
|
|
|
|
it "can set category notifications" do
|
|
|
|
group.watching_category_ids = [category1.id, category2.id]
|
|
|
|
group.tracking_category_ids = [category3.id]
|
2020-08-14 05:20:23 +08:00
|
|
|
group.regular_category_ids = [category4.id]
|
2020-08-07 00:27:27 +08:00
|
|
|
group.save!
|
|
|
|
expect(
|
|
|
|
GroupCategoryNotificationDefault.lookup(group, :watching).pluck(:category_id),
|
|
|
|
).to contain_exactly(category1.id, category2.id)
|
|
|
|
expect(GroupCategoryNotificationDefault.lookup(group, :tracking).pluck(:category_id)).to eq(
|
|
|
|
[category3.id],
|
|
|
|
)
|
2020-08-14 05:20:23 +08:00
|
|
|
expect(GroupCategoryNotificationDefault.lookup(group, :regular).pluck(:category_id)).to eq(
|
|
|
|
[category4.id],
|
|
|
|
)
|
2020-08-07 00:27:27 +08:00
|
|
|
|
|
|
|
new_group = Fabricate.build(:group)
|
|
|
|
new_group.watching_category_ids = [category1.id, category2.id]
|
|
|
|
new_group.save!
|
|
|
|
expect(
|
|
|
|
GroupCategoryNotificationDefault.lookup(new_group, :watching).pluck(:category_id),
|
|
|
|
).to contain_exactly(category1.id, category2.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can remove categories" do
|
|
|
|
[category1, category2].each do |category|
|
|
|
|
GroupCategoryNotificationDefault.create!(
|
|
|
|
group: group,
|
|
|
|
category: category,
|
|
|
|
notification_level: GroupCategoryNotificationDefault.notification_levels[:watching],
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
group.watching_category_ids = [category2.id]
|
|
|
|
group.save!
|
|
|
|
expect(GroupCategoryNotificationDefault.lookup(group, :watching).pluck(:category_id)).to eq(
|
|
|
|
[category2.id],
|
|
|
|
)
|
|
|
|
|
|
|
|
group.watching_category_ids = []
|
|
|
|
group.save!
|
|
|
|
expect(
|
|
|
|
GroupCategoryNotificationDefault.lookup(group, :watching).pluck(:category_id),
|
|
|
|
).to be_empty
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can set tag notifications" do
|
2020-08-14 05:20:23 +08:00
|
|
|
group.regular_tags = [tag4.name]
|
2020-08-07 00:27:27 +08:00
|
|
|
group.watching_tags = [tag1.name, tag2.name]
|
|
|
|
group.tracking_tags = [tag3.name]
|
|
|
|
group.save!
|
2020-08-14 05:20:23 +08:00
|
|
|
expect(GroupTagNotificationDefault.lookup(group, :regular).pluck(:tag_id)).to eq([tag4.id])
|
2020-08-07 00:27:27 +08:00
|
|
|
expect(
|
|
|
|
GroupTagNotificationDefault.lookup(group, :watching).pluck(:tag_id),
|
|
|
|
).to contain_exactly(tag1.id, tag2.id)
|
|
|
|
expect(GroupTagNotificationDefault.lookup(group, :tracking).pluck(:tag_id)).to eq([tag3.id])
|
|
|
|
|
|
|
|
new_group = Fabricate.build(:group)
|
|
|
|
new_group.watching_first_post_tags = [tag1.name, tag3.name]
|
|
|
|
new_group.save!
|
|
|
|
expect(
|
|
|
|
GroupTagNotificationDefault.lookup(new_group, :watching_first_post).pluck(:tag_id),
|
|
|
|
).to contain_exactly(tag1.id, tag3.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can take tag synonyms" do
|
|
|
|
group.tracking_tags = [synonym1.name, synonym2.name, tag3.name]
|
|
|
|
group.save!
|
|
|
|
expect(
|
|
|
|
GroupTagNotificationDefault.lookup(group, :tracking).pluck(:tag_id),
|
|
|
|
).to contain_exactly(tag1.id, tag2.id, tag3.id)
|
|
|
|
|
|
|
|
group.tracking_tags = [synonym1.name, synonym2.name, tag1.name, tag2.name, tag3.name]
|
|
|
|
group.save!
|
|
|
|
expect(
|
|
|
|
GroupTagNotificationDefault.lookup(group, :tracking).pluck(:tag_id),
|
|
|
|
).to contain_exactly(tag1.id, tag2.id, tag3.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can remove tags" do
|
|
|
|
[tag1, tag2].each do |tag|
|
|
|
|
GroupTagNotificationDefault.create!(
|
|
|
|
group: group,
|
|
|
|
tag: tag,
|
|
|
|
notification_level: GroupTagNotificationDefault.notification_levels[:watching],
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
group.watching_tags = [tag2.name]
|
|
|
|
group.save!
|
|
|
|
expect(GroupTagNotificationDefault.lookup(group, :watching).pluck(:tag_id)).to eq([tag2.id])
|
|
|
|
|
|
|
|
group.watching_tags = []
|
|
|
|
group.save!
|
|
|
|
expect(GroupTagNotificationDefault.lookup(group, :watching)).to be_empty
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can apply default notifications for admins group" do
|
|
|
|
group = Group.find(Group::AUTO_GROUPS[:admins])
|
|
|
|
group.tracking_category_ids = [category1.id]
|
|
|
|
group.tracking_tags = [tag1.name]
|
|
|
|
group.save!
|
|
|
|
user.grant_admin!
|
|
|
|
expect(CategoryUser.lookup(user, :tracking).pluck(:category_id)).to eq([category1.id])
|
|
|
|
expect(TagUser.lookup(user, :tracking).pluck(:tag_id)).to eq([tag1.id])
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can apply default notifications for staff group" do
|
|
|
|
group = Group.find(Group::AUTO_GROUPS[:staff])
|
|
|
|
group.tracking_category_ids = [category1.id]
|
|
|
|
group.tracking_tags = [tag1.name]
|
|
|
|
group.save!
|
|
|
|
user.grant_admin!
|
|
|
|
expect(CategoryUser.lookup(user, :tracking).pluck(:category_id)).to eq([category1.id])
|
|
|
|
expect(TagUser.lookup(user, :tracking).pluck(:tag_id)).to eq([tag1.id])
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can apply default notifications from two automatic groups" do
|
|
|
|
staff = Group.find(Group::AUTO_GROUPS[:staff])
|
|
|
|
staff.tracking_category_ids = [category1.id]
|
|
|
|
staff.tracking_tags = [tag1.name]
|
|
|
|
staff.save!
|
|
|
|
admins = Group.find(Group::AUTO_GROUPS[:admins])
|
|
|
|
admins.tracking_category_ids = [category2.id]
|
|
|
|
admins.tracking_tags = [tag2.name]
|
|
|
|
admins.save!
|
|
|
|
user.grant_admin!
|
|
|
|
expect(CategoryUser.lookup(user, :tracking).pluck(:category_id)).to contain_exactly(
|
|
|
|
category1.id,
|
|
|
|
category2.id,
|
|
|
|
)
|
|
|
|
expect(TagUser.lookup(user, :tracking).pluck(:tag_id)).to contain_exactly(tag1.id, tag2.id)
|
|
|
|
end
|
|
|
|
end
|
FEATURE: Improve group email settings UI (#13083)
This overhauls the user interface for the group email settings management, aiming to make it a lot easier to test the settings entered and confirm they are correct before proceeding. We do this by forcing the user to test the settings before they can be saved to the database. It also includes some quality of life improvements around setting up IMAP and SMTP for our first supported provider, GMail. This PR does not remove the old group email config, that will come in a subsequent PR. This is related to https://meta.discourse.org/t/imap-support-for-group-inboxes/160588 so read that if you would like more backstory.
### UI
Both site settings of `enable_imap` and `enable_smtp` must be true to test this. You must enable SMTP first to enable IMAP.
You can prefill the SMTP settings with GMail configuration. To proceed with saving these settings you must test them, which is handled by the EmailSettingsValidator.
If there is an issue with the configuration or credentials a meaningful error message should be shown.
IMAP settings must also be validated when IMAP is enabled, before saving.
When saving IMAP, we fetch the mailboxes for that account and populate them. This mailbox must be selected and saved for IMAP to work (the feature acts as though it is disabled until the mailbox is selected and saved):
### Database & Backend
This adds several columns to the Groups table. The purpose of this change is to make it much more explicit that SMTP/IMAP is enabled for a group, rather than relying on settings not being null. Also included is an UPDATE query to backfill these columns. These columns are automatically filled when updating the group.
For GMail, we now filter the mailboxes returned. This is so users cannot use a mailbox like Sent or Trash for syncing, which would generally be disastrous.
There is a new group endpoint for testing email settings. This may be useful in the future for other places in our UI, at which point it can be extracted to a more generic endpoint or module to be included.
2021-05-28 07:28:18 +08:00
|
|
|
|
|
|
|
describe "email setting changes" do
|
|
|
|
it "enables smtp and records the change" do
|
|
|
|
group.update(
|
|
|
|
smtp_port: 587,
|
|
|
|
smtp_ssl: true,
|
|
|
|
smtp_server: "smtp.gmail.com",
|
|
|
|
email_username: "test@gmail.com",
|
|
|
|
email_password: "password",
|
|
|
|
)
|
|
|
|
|
|
|
|
group.record_email_setting_changes!(user)
|
|
|
|
group.reload
|
|
|
|
|
|
|
|
expect(group.smtp_enabled).to eq(true)
|
|
|
|
expect(group.smtp_updated_at).not_to eq(nil)
|
|
|
|
expect(group.smtp_updated_by).to eq(user)
|
|
|
|
end
|
|
|
|
|
2022-02-07 11:52:01 +08:00
|
|
|
it "records the change for singular setting changes" do
|
|
|
|
group.update(
|
|
|
|
smtp_port: 587,
|
|
|
|
smtp_ssl: true,
|
|
|
|
smtp_server: "smtp.gmail.com",
|
|
|
|
email_username: "test@gmail.com",
|
|
|
|
email_password: "password",
|
|
|
|
)
|
|
|
|
group.record_email_setting_changes!(user)
|
|
|
|
group.reload
|
|
|
|
|
|
|
|
old_updated_at = group.smtp_updated_at
|
|
|
|
group.update(email_from_alias: "somealias@gmail.com")
|
|
|
|
group.record_email_setting_changes!(user)
|
|
|
|
expect(group.reload.smtp_updated_at).not_to eq_time(old_updated_at)
|
|
|
|
end
|
|
|
|
|
FEATURE: Improve group email settings UI (#13083)
This overhauls the user interface for the group email settings management, aiming to make it a lot easier to test the settings entered and confirm they are correct before proceeding. We do this by forcing the user to test the settings before they can be saved to the database. It also includes some quality of life improvements around setting up IMAP and SMTP for our first supported provider, GMail. This PR does not remove the old group email config, that will come in a subsequent PR. This is related to https://meta.discourse.org/t/imap-support-for-group-inboxes/160588 so read that if you would like more backstory.
### UI
Both site settings of `enable_imap` and `enable_smtp` must be true to test this. You must enable SMTP first to enable IMAP.
You can prefill the SMTP settings with GMail configuration. To proceed with saving these settings you must test them, which is handled by the EmailSettingsValidator.
If there is an issue with the configuration or credentials a meaningful error message should be shown.
IMAP settings must also be validated when IMAP is enabled, before saving.
When saving IMAP, we fetch the mailboxes for that account and populate them. This mailbox must be selected and saved for IMAP to work (the feature acts as though it is disabled until the mailbox is selected and saved):
### Database & Backend
This adds several columns to the Groups table. The purpose of this change is to make it much more explicit that SMTP/IMAP is enabled for a group, rather than relying on settings not being null. Also included is an UPDATE query to backfill these columns. These columns are automatically filled when updating the group.
For GMail, we now filter the mailboxes returned. This is so users cannot use a mailbox like Sent or Trash for syncing, which would generally be disastrous.
There is a new group endpoint for testing email settings. This may be useful in the future for other places in our UI, at which point it can be extracted to a more generic endpoint or module to be included.
2021-05-28 07:28:18 +08:00
|
|
|
it "enables imap and records the change" do
|
|
|
|
group.update(
|
|
|
|
imap_port: 587,
|
|
|
|
imap_ssl: true,
|
|
|
|
imap_server: "imap.gmail.com",
|
|
|
|
email_username: "test@gmail.com",
|
|
|
|
email_password: "password",
|
|
|
|
)
|
|
|
|
|
|
|
|
group.record_email_setting_changes!(user)
|
|
|
|
group.reload
|
|
|
|
|
|
|
|
expect(group.imap_enabled).to eq(true)
|
|
|
|
expect(group.imap_updated_at).not_to eq(nil)
|
|
|
|
expect(group.imap_updated_by).to eq(user)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "disables smtp and records the change" do
|
|
|
|
group.update(
|
|
|
|
smtp_port: 587,
|
|
|
|
smtp_ssl: true,
|
|
|
|
smtp_server: "smtp.gmail.com",
|
|
|
|
email_username: "test@gmail.com",
|
|
|
|
email_password: "password",
|
|
|
|
smtp_updated_by: user,
|
|
|
|
)
|
|
|
|
|
|
|
|
group.record_email_setting_changes!(user)
|
|
|
|
group.reload
|
|
|
|
|
|
|
|
group.update(
|
|
|
|
smtp_port: nil,
|
|
|
|
smtp_ssl: false,
|
|
|
|
smtp_server: nil,
|
|
|
|
email_username: nil,
|
|
|
|
email_password: nil,
|
|
|
|
)
|
|
|
|
|
|
|
|
group.record_email_setting_changes!(user)
|
|
|
|
group.reload
|
|
|
|
|
|
|
|
expect(group.smtp_enabled).to eq(false)
|
|
|
|
expect(group.smtp_updated_at).not_to eq(nil)
|
|
|
|
expect(group.smtp_updated_by).to eq(user)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "disables imap and records the change" do
|
|
|
|
group.update(
|
|
|
|
imap_port: 587,
|
|
|
|
imap_ssl: true,
|
|
|
|
imap_server: "imap.gmail.com",
|
|
|
|
email_username: "test@gmail.com",
|
|
|
|
email_password: "password",
|
|
|
|
)
|
|
|
|
|
|
|
|
group.record_email_setting_changes!(user)
|
|
|
|
group.reload
|
|
|
|
|
|
|
|
group.update(
|
|
|
|
imap_port: nil,
|
|
|
|
imap_ssl: false,
|
|
|
|
imap_server: nil,
|
|
|
|
email_username: nil,
|
|
|
|
email_password: nil,
|
|
|
|
)
|
|
|
|
|
|
|
|
group.record_email_setting_changes!(user)
|
|
|
|
group.reload
|
|
|
|
|
|
|
|
expect(group.imap_enabled).to eq(false)
|
|
|
|
expect(group.imap_updated_at).not_to eq(nil)
|
|
|
|
expect(group.imap_updated_by).to eq(user)
|
|
|
|
end
|
|
|
|
end
|
2021-06-03 12:47:32 +08:00
|
|
|
|
|
|
|
describe "#find_by_email" do
|
|
|
|
it "finds the group by any of its incoming emails" do
|
|
|
|
group.update!(incoming_email: "abc@test.com|support@test.com")
|
|
|
|
expect(Group.find_by_email("abc@test.com")).to eq(group)
|
|
|
|
expect(Group.find_by_email("support@test.com")).to eq(group)
|
|
|
|
expect(Group.find_by_email("nope@test.com")).to eq(nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "finds the group by its email_username" do
|
|
|
|
group.update!(email_username: "abc@test.com", incoming_email: "support@test.com")
|
|
|
|
expect(Group.find_by_email("abc@test.com")).to eq(group)
|
|
|
|
expect(Group.find_by_email("support@test.com")).to eq(group)
|
|
|
|
expect(Group.find_by_email("nope@test.com")).to eq(nil)
|
|
|
|
end
|
2022-02-07 11:52:01 +08:00
|
|
|
|
|
|
|
it "finds the group by its email_from_alias" do
|
|
|
|
group.update!(email_username: "abc@test.com", email_from_alias: "somealias@test.com")
|
|
|
|
expect(Group.find_by_email("abc@test.com")).to eq(group)
|
|
|
|
expect(Group.find_by_email("somealias@test.com")).to eq(group)
|
|
|
|
expect(Group.find_by_email("nope@test.com")).to eq(nil)
|
|
|
|
end
|
2021-06-03 12:47:32 +08:00
|
|
|
end
|
2013-04-29 14:33:24 +08:00
|
|
|
end
|