DEV: Enable RSpec/InstanceVariable rule globally

This commit is contained in:
Loïc Guitaut
2025-02-14 14:54:31 +01:00
parent a0f681b256
commit 4019823211
17 changed files with 385 additions and 426 deletions

View File

@@ -35,3 +35,6 @@ Discourse/Plugins/NoMonkeyPatching:
Lint/Debugger:
Exclude:
- script/**/*
RSpec/InstanceVariable:
Enabled: true

View File

@@ -30,7 +30,7 @@ describe "ZapierWebhook" do
it "logs an error and do nothing" do
expect { automation.trigger! }.not_to change {
Jobs::DiscourseAutomation::CallZapierWebhook.jobs.length
Jobs::DiscourseAutomation::CallZapierWebhook.jobs.size
}
expect(Rails.logger.warnings.first).to match(/is not a valid Zapier/)

View File

@@ -1,26 +1,24 @@
# frozen_string_literal: true
describe Jobs::Chat::ChannelDelete do
RSpec.describe Jobs::Chat::ChannelDelete do
fab!(:chat_channel)
fab!(:user1) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) }
fab!(:user3) { Fabricate(:user) }
let(:users) { [user1, user2, user3] }
fab!(:users) { [user1, user2, user3] }
fab!(:messages) { Array.new(20) { Fabricate(:chat_message, chat_channel:, user: users.sample) } }
fab!(:incoming_chat_webhook) { Fabricate(:incoming_chat_webhook, chat_channel:) }
let(:message_ids) { messages.map(&:id) }
before do
messages = []
20.times do
messages << Fabricate(:chat_message, chat_channel: chat_channel, user: users.sample)
end
@message_ids = messages.map(&:id)
10.times { Chat::MessageReaction.create(chat_message: messages.sample, user: users.sample) }
10.times do
upload = Fabricate(:upload, user: users.sample)
message = messages.sample
UploadReference.create(target: message, upload: upload)
UploadReference.create(target: message, upload:)
end
Chat::UserMention.create(
@@ -29,11 +27,7 @@ describe Jobs::Chat::ChannelDelete do
notifications: [Fabricate(:notification)],
)
@incoming_chat_webhook_id = Fabricate(:incoming_chat_webhook, chat_channel: chat_channel)
Chat::WebhookEvent.create(
incoming_chat_webhook: @incoming_chat_webhook_id,
chat_message: messages.sample,
)
Chat::WebhookEvent.create(incoming_chat_webhook:, chat_message: messages.sample)
revision_message = messages.sample
Fabricate(
@@ -43,31 +37,30 @@ describe Jobs::Chat::ChannelDelete do
new_message: revision_message.message,
)
Chat::Draft.create(chat_channel: chat_channel, user: users.sample, data: "wow some draft")
Chat::Draft.create(chat_channel:, user: users.sample, data: "wow some draft")
Fabricate(:user_chat_channel_membership, chat_channel: chat_channel, user: user1)
Fabricate(:user_chat_channel_membership, chat_channel: chat_channel, user: user2)
Fabricate(:user_chat_channel_membership, chat_channel: chat_channel, user: user3)
Fabricate(:user_chat_channel_membership, chat_channel:, user: user1)
Fabricate(:user_chat_channel_membership, chat_channel:, user: user2)
Fabricate(:user_chat_channel_membership, chat_channel:, user: user3)
chat_channel.trash!
end
def counts
{
incoming_webhooks: Chat::IncomingWebhook.where(chat_channel_id: chat_channel.id).count,
webhook_events:
Chat::WebhookEvent.where(incoming_chat_webhook_id: @incoming_chat_webhook_id).count,
drafts: Chat::Draft.where(chat_channel: chat_channel).count,
incoming_webhooks: Chat::IncomingWebhook.where(chat_channel:).count,
webhook_events: Chat::WebhookEvent.where(incoming_chat_webhook:).count,
drafts: Chat::Draft.where(chat_channel:).count,
channel_memberships: Chat::UserChatChannelMembership.where(chat_channel: chat_channel).count,
revisions: Chat::MessageRevision.where(chat_message_id: @message_ids).count,
mentions: Chat::Mention.where(chat_message_id: @message_ids).count,
revisions: Chat::MessageRevision.where(chat_message_id: message_ids).count,
mentions: Chat::Mention.where(chat_message_id: message_ids).count,
upload_references:
UploadReference.where(
target_id: @message_ids,
target_id: message_ids,
target_type: Chat::Message.polymorphic_name,
).count,
messages: Chat::Message.where(id: @message_ids).count,
reactions: Chat::MessageReaction.where(chat_message_id: @message_ids).count,
messages: Chat::Message.where(id: message_ids).count,
reactions: Chat::MessageReaction.where(chat_message_id: message_ids).count,
}
end

View File

@@ -6,24 +6,22 @@ describe Jobs::Chat::NotifyMentioned do
fab!(:user_1) { Fabricate(:user, refresh_auto_groups: true) }
fab!(:user_2) { Fabricate(:user, refresh_auto_groups: true) }
fab!(:public_channel) { Fabricate(:category_channel) }
fab!(:chat_group) { Fabricate(:group, users: [user_1, user_2]) }
let!(:result) do
Chat::CreateDirectMessageChannel.call(
guardian: user_1.guardian,
params: {
target_usernames: [user_1.username, user_2.username],
},
) do |result|
on_success { result }
on_failure { service_failed!(result) }
end
end
let(:personal_chat_channel) { result.channel }
before do
user_1.reload
user_2.reload
@chat_group = Fabricate(:group, users: [user_1, user_2])
result =
Chat::CreateDirectMessageChannel.call(
guardian: user_1.guardian,
params: {
target_usernames: [user_1.username, user_2.username],
},
)
service_failed!(result) if result.failure?
@personal_chat_channel = result.channel
[user_1, user_2].each do |u|
Fabricate(:user_chat_channel_membership, chat_channel: public_channel, user: u)
end
@@ -147,10 +145,10 @@ describe Jobs::Chat::NotifyMentioned do
it "does nothing if user is not participating in a private channel" do
user_3 = Fabricate(:user)
@chat_group.add(user_3)
chat_group.add(user_3)
to_notify_map = { direct_mentions: [user_3.id] }
message = create_chat_message(channel: @personal_chat_channel)
message = create_chat_message(channel: personal_chat_channel)
PostAlerter.expects(:push_notification).never
@@ -227,7 +225,7 @@ describe Jobs::Chat::NotifyMentioned do
message = create_chat_message
Fabricate(:all_chat_mention, chat_message: message)
Fabricate(:here_chat_mention, chat_message: message)
Fabricate(:group_chat_mention, group: @chat_group, chat_message: message)
Fabricate(:group_chat_mention, group: chat_group, chat_message: message)
desktop_notification =
track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map)
@@ -248,7 +246,7 @@ describe Jobs::Chat::NotifyMentioned do
message = create_chat_message
Fabricate(:all_chat_mention, chat_message: message)
Fabricate(:here_chat_mention, chat_message: message)
Fabricate(:group_chat_mention, group: @chat_group, chat_message: message)
Fabricate(:group_chat_mention, group: chat_group, chat_message: message)
PostAlerter.expects(:push_notification).with(
user_2,
@@ -275,7 +273,7 @@ describe Jobs::Chat::NotifyMentioned do
message = create_chat_message
Fabricate(:all_chat_mention, chat_message: message)
Fabricate(:here_chat_mention, chat_message: message)
Fabricate(:group_chat_mention, group: @chat_group, chat_message: message)
Fabricate(:group_chat_mention, group: chat_group, chat_message: message)
created_notification =
track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map)
@@ -334,7 +332,7 @@ describe Jobs::Chat::NotifyMentioned do
context "with private channels" do
it "users a different translated title" do
message = create_chat_message(channel: @personal_chat_channel)
message = create_chat_message(channel: personal_chat_channel)
Fabricate(:all_chat_mention, chat_message: message)
desktop_notification =
@@ -389,7 +387,7 @@ describe Jobs::Chat::NotifyMentioned do
context "with private channels" do
it "uses a different translated title" do
message = create_chat_message(channel: @personal_chat_channel)
message = create_chat_message(channel: personal_chat_channel)
Fabricate(:here_chat_mention, chat_message: message)
desktop_notification =
@@ -462,7 +460,7 @@ describe Jobs::Chat::NotifyMentioned do
context "with private channels" do
it "users a different translated title" do
message = create_chat_message(channel: @personal_chat_channel)
message = create_chat_message(channel: personal_chat_channel)
desktop_notification =
track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map)
@@ -480,13 +478,13 @@ describe Jobs::Chat::NotifyMentioned do
end
describe "group mentions" do
let(:to_notify_ids_map) { { @chat_group.name.to_sym => [user_2.id] } }
let(:to_notify_ids_map) { { chat_group.name.to_sym => [user_2.id] } }
let(:payload_translated_title) do
I18n.t(
"discourse_push_notifications.popup.chat_mention.other_type",
username: user_1.username,
identifier: "@#{@chat_group.name}",
identifier: "@#{chat_group.name}",
channel: public_channel.title(user_2),
)
end
@@ -495,19 +493,19 @@ describe Jobs::Chat::NotifyMentioned do
it "includes here mention specific data to core notifications" do
message = create_chat_message
Fabricate(:group_chat_mention, group: @chat_group, chat_message: message)
Fabricate(:group_chat_mention, group: chat_group, chat_message: message)
created_notification =
track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map)
data_hash = created_notification.data_hash
expect(data_hash[:identifier]).to eq(@chat_group.name)
expect(data_hash[:identifier]).to eq(chat_group.name)
expect(data_hash[:is_group_mention]).to eq(true)
end
it "includes here mention specific data to desktop notifications" do
message = create_chat_message
Fabricate(:group_chat_mention, group: @chat_group, chat_message: message)
Fabricate(:group_chat_mention, group: chat_group, chat_message: message)
desktop_notification =
track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map)
@@ -517,8 +515,8 @@ describe Jobs::Chat::NotifyMentioned do
context "with private channels" do
it "uses a different translated title" do
message = create_chat_message(channel: @personal_chat_channel)
Fabricate(:group_chat_mention, group: @chat_group, chat_message: message)
message = create_chat_message(channel: personal_chat_channel)
Fabricate(:group_chat_mention, group: chat_group, chat_message: message)
desktop_notification =
track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map)
@@ -527,7 +525,7 @@ describe Jobs::Chat::NotifyMentioned do
I18n.t(
"discourse_push_notifications.popup.direct_message_chat_mention.other_type",
username: user_1.username,
identifier: "@#{@chat_group.name}",
identifier: "@#{chat_group.name}",
)
expect(desktop_notification.data[:translated_title]).to eq(expected_title)

View File

@@ -96,13 +96,12 @@ describe Chat::ChannelArchiveService do
thread.update!(replies_count: num - 1)
end
def start_archive
@channel_archive =
described_class.create_archive_process(
chat_channel: channel,
acting_user: user,
topic_params: topic_params,
)
let(:channel_archive) do
described_class.create_archive_process(
chat_channel: channel,
acting_user: user,
topic_params: topic_params,
)
end
context "when archiving to a new topic" do
@@ -111,7 +110,7 @@ describe Chat::ChannelArchiveService do
end
it "makes a topic, deletes all the messages, creates posts for batches of messages, and changes the channel to archived" do
create_messages(50) && start_archive
create_messages(50) && channel_archive
reaction_message = Chat::Message.last
Chat::MessageReaction.create!(
chat_message: reaction_message,
@@ -119,16 +118,16 @@ describe Chat::ChannelArchiveService do
emoji: "+1",
)
stub_const(Chat::ChannelArchiveService, "ARCHIVED_MESSAGES_PER_POST", 5) do
described_class.new(@channel_archive).execute
described_class.new(channel_archive).execute
end
@channel_archive.reload
expect(@channel_archive.destination_topic.title).to eq("This will be a new topic")
expect(@channel_archive.destination_topic.category).to eq(category)
expect(@channel_archive.destination_topic.user).to eq(Discourse.system_user)
expect(@channel_archive.destination_topic.tags.map(&:name)).to match_array(%w[news gossip])
channel_archive.reload
expect(channel_archive.destination_topic.title).to eq("This will be a new topic")
expect(channel_archive.destination_topic.category).to eq(category)
expect(channel_archive.destination_topic.user).to eq(Discourse.system_user)
expect(channel_archive.destination_topic.tags.map(&:name)).to match_array(%w[news gossip])
topic = @channel_archive.destination_topic
topic = channel_archive.destination_topic
expect(topic.posts.count).to eq(11)
topic
.posts
@@ -144,9 +143,9 @@ describe Chat::ChannelArchiveService do
end
expect(topic.archived).to eq(true)
expect(@channel_archive.archived_messages).to eq(50)
expect(@channel_archive.chat_channel.status).to eq("archived")
expect(@channel_archive.chat_channel.chat_messages.count).to eq(0)
expect(channel_archive.archived_messages).to eq(50)
expect(channel_archive.chat_channel.status).to eq("archived")
expect(channel_archive.chat_channel.chat_messages.count).to eq(0)
end
xit "creates the correct posts for a channel with messages and threads" do
@@ -159,14 +158,14 @@ describe Chat::ChannelArchiveService do
create_threaded_messages(27, title: "another long thread")
create_messages(10)
start_archive
channel_archive
stub_const(Chat::ChannelArchiveService, "ARCHIVED_MESSAGES_PER_POST", 5) do
described_class.new(@channel_archive).execute
described_class.new(channel_archive).execute
end
@channel_archive.reload
topic = @channel_archive.destination_topic
channel_archive.reload
topic = channel_archive.destination_topic
expect(topic.posts.count).to eq(14)
topic
@@ -201,32 +200,32 @@ describe Chat::ChannelArchiveService do
end
expect(topic.archived).to eq(true)
expect(@channel_archive.archived_messages).to eq(55)
expect(@channel_archive.chat_channel.status).to eq("archived")
expect(@channel_archive.chat_channel.chat_messages.count).to eq(0)
expect(channel_archive.archived_messages).to eq(55)
expect(channel_archive.chat_channel.status).to eq("archived")
expect(channel_archive.chat_channel.chat_messages.count).to eq(0)
end
it "does not stop the process if the post length is too high (validations disabled)" do
create_messages(50) && start_archive
create_messages(50) && channel_archive
SiteSetting.max_post_length = 1
described_class.new(@channel_archive).execute
expect(@channel_archive.reload.complete?).to eq(true)
described_class.new(channel_archive).execute
expect(channel_archive.reload.complete?).to eq(true)
end
it "successfully links uploads from messages to the post" do
create_messages(3) && start_archive
create_messages(3) && channel_archive
UploadReference.create!(target: Chat::Message.last, upload: Fabricate(:upload))
described_class.new(@channel_archive).execute
expect(@channel_archive.reload.complete?).to eq(true)
expect(@channel_archive.destination_topic.posts.last.upload_references.count).to eq(1)
described_class.new(channel_archive).execute
expect(channel_archive.reload.complete?).to eq(true)
expect(channel_archive.destination_topic.posts.last.upload_references.count).to eq(1)
end
it "successfully sends a private message to the archiving user" do
create_messages(3) && start_archive
described_class.new(@channel_archive).execute
expect(@channel_archive.reload.complete?).to eq(true)
create_messages(3) && channel_archive
described_class.new(channel_archive).execute
expect(channel_archive.reload.complete?).to eq(true)
pm_topic = Topic.private_messages.last
expect(pm_topic.topic_allowed_users.first.user).to eq(@channel_archive.archived_by)
expect(pm_topic.topic_allowed_users.first.user).to eq(channel_archive.archived_by)
expect(pm_topic.title).to eq(
I18n.t("system_messages.chat_channel_archive_complete.subject_template"),
)
@@ -235,12 +234,12 @@ describe Chat::ChannelArchiveService do
it "does not continue archiving if the destination topic fails to be created" do
SiteSetting.max_emojis_in_title = 1
create_messages(3) && start_archive
@channel_archive.update!(destination_topic_title: "Wow this is the new title :tada: :joy:")
described_class.new(@channel_archive).execute
expect(@channel_archive.reload.complete?).to eq(false)
expect(@channel_archive.reload.failed?).to eq(true)
expect(@channel_archive.archive_error).to eq("Title can't have more than 1 emoji")
create_messages(3) && channel_archive
channel_archive.update!(destination_topic_title: "Wow this is the new title :tada: :joy:")
described_class.new(channel_archive).execute
expect(channel_archive.reload.complete?).to eq(false)
expect(channel_archive.reload.failed?).to eq(true)
expect(channel_archive.archive_error).to eq("Title can't have more than 1 emoji")
pm_topic = Topic.private_messages.last
expect(pm_topic.title).to eq(
@@ -250,9 +249,9 @@ describe Chat::ChannelArchiveService do
end
it "uses the channel slug to autolink a hashtag for the channel in the PM" do
create_messages(3) && start_archive
described_class.new(@channel_archive).execute
expect(@channel_archive.reload.complete?).to eq(true)
create_messages(3) && channel_archive
described_class.new(channel_archive).execute
expect(channel_archive.reload.complete?).to eq(true)
pm_topic = Topic.private_messages.last
expect(pm_topic.first_post.cooked).to have_tag(
"a",
@@ -289,9 +288,9 @@ describe Chat::ChannelArchiveService do
expect(
Chat::UserChatChannelMembership.where(chat_channel: channel, following: true).count,
).to eq(3)
start_archive
described_class.new(@channel_archive).execute
expect(@channel_archive.reload.complete?).to eq(true)
channel_archive
described_class.new(channel_archive).execute
expect(channel_archive.reload.complete?).to eq(true)
expect(
Chat::UserChatChannelMembership.where(chat_channel: channel, following: true).count,
).to eq(0)
@@ -301,9 +300,9 @@ describe Chat::ChannelArchiveService do
Chat::UserChatChannelMembership.last.update!(
last_read_message_id: channel.chat_messages.first.id,
)
start_archive
described_class.new(@channel_archive).execute
expect(@channel_archive.reload.complete?).to eq(true)
channel_archive
described_class.new(channel_archive).execute
expect(channel_archive.reload.complete?).to eq(true)
expect(Chat::UserChatChannelMembership.last.last_read_message_id).to eq(
channel.chat_messages.last.id,
)
@@ -315,9 +314,9 @@ describe Chat::ChannelArchiveService do
before { SiteSetting.chat_archive_destination_topic_status = "archived" }
it "archives the topic" do
create_messages(3) && start_archive
described_class.new(@channel_archive).execute
topic = @channel_archive.destination_topic
create_messages(3) && channel_archive
described_class.new(channel_archive).execute
topic = channel_archive.destination_topic
topic.reload
expect(topic.archived).to eq(true)
end
@@ -327,9 +326,9 @@ describe Chat::ChannelArchiveService do
before { SiteSetting.chat_archive_destination_topic_status = "open" }
it "leaves the topic open" do
create_messages(3) && start_archive
described_class.new(@channel_archive).execute
topic = @channel_archive.destination_topic
create_messages(3) && channel_archive
described_class.new(channel_archive).execute
topic = channel_archive.destination_topic
topic.reload
expect(topic.archived).to eq(false)
expect(topic.open?).to eq(true)
@@ -340,9 +339,9 @@ describe Chat::ChannelArchiveService do
before { SiteSetting.chat_archive_destination_topic_status = "closed" }
it "closes the topic" do
create_messages(3) && start_archive
described_class.new(@channel_archive).execute
topic = @channel_archive.destination_topic
create_messages(3) && channel_archive
described_class.new(channel_archive).execute
topic = channel_archive.destination_topic
topic.reload
expect(topic.archived).to eq(false)
expect(topic.closed?).to eq(true)
@@ -351,13 +350,13 @@ describe Chat::ChannelArchiveService do
context "when archiving to an existing topic" do
it "does not change the status of the topic" do
create_messages(3) && start_archive
@channel_archive.update(
create_messages(3) && channel_archive
channel_archive.update(
destination_topic_title: nil,
destination_topic_id: Fabricate(:topic).id,
)
described_class.new(@channel_archive).execute
topic = @channel_archive.destination_topic
described_class.new(channel_archive).execute
topic = channel_archive.destination_topic
topic.reload
expect(topic.archived).to eq(false)
expect(topic.closed?).to eq(false)
@@ -373,7 +372,7 @@ describe Chat::ChannelArchiveService do
before { 3.times { Fabricate(:post, topic: topic) } }
it "deletes all the messages, creates posts for batches of messages, and changes the channel to archived" do
create_messages(50) && start_archive
create_messages(50) && channel_archive
reaction_message = Chat::Message.last
Chat::MessageReaction.create!(
chat_message: reaction_message,
@@ -381,15 +380,15 @@ describe Chat::ChannelArchiveService do
emoji: "+1",
)
stub_const(Chat::ChannelArchiveService, "ARCHIVED_MESSAGES_PER_POST", 5) do
described_class.new(@channel_archive).execute
described_class.new(channel_archive).execute
end
@channel_archive.reload
expect(@channel_archive.destination_topic.title).to eq(topic.title)
expect(@channel_archive.destination_topic.category).to eq(topic.category)
expect(@channel_archive.destination_topic.user).to eq(topic.user)
channel_archive.reload
expect(channel_archive.destination_topic.title).to eq(topic.title)
expect(channel_archive.destination_topic.category).to eq(topic.category)
expect(channel_archive.destination_topic.user).to eq(topic.user)
topic = @channel_archive.destination_topic
topic = channel_archive.destination_topic
# existing posts + 10 archive posts
expect(topic.posts.count).to eq(13)
@@ -407,13 +406,13 @@ describe Chat::ChannelArchiveService do
end
expect(topic.archived).to eq(false)
expect(@channel_archive.archived_messages).to eq(50)
expect(@channel_archive.chat_channel.status).to eq("archived")
expect(@channel_archive.chat_channel.chat_messages.count).to eq(0)
expect(channel_archive.archived_messages).to eq(50)
expect(channel_archive.chat_channel.status).to eq("archived")
expect(channel_archive.chat_channel.chat_messages.count).to eq(0)
end
it "handles errors gracefully, sends a private message to the archiving user, and is idempotent on retry" do
create_messages(35) && start_archive
create_messages(35) && channel_archive
Chat::ChannelArchiveService
.any_instance
@@ -421,26 +420,26 @@ describe Chat::ChannelArchiveService do
.raises(FakeArchiveError.new("this is a test error"))
stub_const(Chat::ChannelArchiveService, "ARCHIVED_MESSAGES_PER_POST", 5) do
expect { described_class.new(@channel_archive).execute }.to raise_error(FakeArchiveError)
expect { described_class.new(channel_archive).execute }.to raise_error(FakeArchiveError)
end
expect(@channel_archive.reload.archive_error).to eq("this is a test error")
expect(channel_archive.reload.archive_error).to eq("this is a test error")
pm_topic = Topic.private_messages.last
expect(pm_topic.topic_allowed_users.first.user).to eq(@channel_archive.archived_by)
expect(pm_topic.topic_allowed_users.first.user).to eq(channel_archive.archived_by)
expect(pm_topic.title).to eq(
I18n.t("system_messages.chat_channel_archive_failed.subject_template"),
)
Chat::ChannelArchiveService.any_instance.unstub(:create_post)
stub_const(Chat::ChannelArchiveService, "ARCHIVED_MESSAGES_PER_POST", 5) do
described_class.new(@channel_archive).execute
described_class.new(channel_archive).execute
end
@channel_archive.reload
expect(@channel_archive.archive_error).to eq(nil)
expect(@channel_archive.archived_messages).to eq(35)
expect(@channel_archive.complete?).to eq(true)
channel_archive.reload
expect(channel_archive.archive_error).to eq(nil)
expect(channel_archive.archived_messages).to eq(35)
expect(channel_archive.complete?).to eq(true)
# existing posts + 7 archive posts
expect(topic.posts.count).to eq(10)
end

View File

@@ -5,16 +5,12 @@ describe Chat::Notifier do
fab!(:channel) { Fabricate(:category_channel) }
fab!(:user_1) { Fabricate(:user, refresh_auto_groups: true) }
fab!(:user_2) { Fabricate(:user) }
fab!(:chat_group) do
Fabricate(:group, users: [user_1, user_2], mentionable_level: Group::ALIAS_LEVELS[:everyone])
end
before do
@chat_group =
Fabricate(
:group,
users: [user_1, user_2],
mentionable_level: Group::ALIAS_LEVELS[:everyone],
)
SiteSetting.chat_allowed_groups = @chat_group.id
SiteSetting.chat_allowed_groups = chat_group.id
[user_1, user_2].each do |u|
Fabricate(:user_chat_channel_membership, chat_channel: channel, user: u)
end
@@ -104,7 +100,7 @@ describe Chat::Notifier do
shared_examples "ensure only channel members are notified" do
it "will never include someone outside the channel" do
user3 = Fabricate(:user)
@chat_group.add(user3)
chat_group.add(user3)
another_channel = Fabricate(:category_channel)
Fabricate(:user_chat_channel_membership, chat_channel: another_channel, user: user3)
msg = build_cooked_msg(mention, user_1)
@@ -116,7 +112,7 @@ describe Chat::Notifier do
it "will never include someone not following the channel anymore" do
user3 = Fabricate(:user)
@chat_group.add(user3)
chat_group.add(user3)
Fabricate(
:user_chat_channel_membership,
following: false,
@@ -132,7 +128,7 @@ describe Chat::Notifier do
it "will never include someone who is suspended" do
user3 = Fabricate(:user, suspended_till: 2.years.from_now)
@chat_group.add(user3)
chat_group.add(user3)
Fabricate(
:user_chat_channel_membership,
following: true,
@@ -258,7 +254,7 @@ describe Chat::Notifier do
describe "direct_mentions" do
it "only include mentioned users who are already in the channel" do
user_3 = Fabricate(:user)
@chat_group.add(user_3)
chat_group.add(user_3)
another_channel = Fabricate(:category_channel)
Fabricate(:user_chat_channel_membership, chat_channel: another_channel, user: user_3)
msg = build_cooked_msg("Is @#{user_3.username} here? And @#{user_2.username}", user_1)
@@ -326,7 +322,7 @@ describe Chat::Notifier do
end
fab!(:other_channel) { Fabricate(:category_channel) }
before { @chat_group.add(user_3) }
before { chat_group.add(user_3) }
let(:mention) { "hello @#{group.name}!" }
let(:list_key) { group.name }
@@ -346,19 +342,19 @@ describe Chat::Notifier do
user: user_3,
following: true,
)
msg = build_cooked_msg("Hello @#{@chat_group.name} and @#{group.name}", user_1)
msg = build_cooked_msg("Hello @#{chat_group.name} and @#{group.name}", user_1)
to_notify = described_class.new(msg, msg.created_at).notify_new
expect(to_notify[@chat_group.name]).to contain_exactly(user_2.id, user_3.id)
expect(to_notify[chat_group.name]).to contain_exactly(user_2.id, user_3.id)
expect(to_notify[list_key]).to be_empty
second_msg = build_cooked_msg("Hello @#{group.name} and @#{@chat_group.name}", user_1)
second_msg = build_cooked_msg("Hello @#{group.name} and @#{chat_group.name}", user_1)
to_notify_2 = described_class.new(second_msg, second_msg.created_at).notify_new
expect(to_notify_2[list_key]).to contain_exactly(user_2.id, user_3.id)
expect(to_notify_2[@chat_group.name]).to be_empty
expect(to_notify_2[chat_group.name]).to be_empty
end
it "skips groups with too many members" do
@@ -478,7 +474,7 @@ describe Chat::Notifier do
result.channel
end
before { @chat_group.add(user_3) }
before { chat_group.add(user_3) }
it "notify posts of users who are not participating in a personal message" do
msg =
@@ -533,7 +529,7 @@ describe Chat::Notifier do
describe "users who can be invited to join the channel" do
fab!(:user_3) { Fabricate(:user) }
before { @chat_group.add(user_3) }
before { chat_group.add(user_3) }
it "can invite chat user without channel membership" do
msg = build_cooked_msg("Hello @#{user_3.username}", user_1)

View File

@@ -292,18 +292,19 @@ RSpec.shared_examples "a chat channel model" do
end
describe "#remove" do
let!(:membership) { private_category_channel.add(user1) }
before do
group.add(user1)
@membership = private_category_channel.add(user1)
private_category_channel.reload
private_category_channel.update!(user_count_stale: false)
end
it "updates the membership for the user and decreases the count" do
membership = private_category_channel.remove(user1)
private_category_channel.remove(user1)
private_category_channel.reload
expect(@membership.reload.following).to eq(false)
expect(membership.reload.following).to eq(false)
expect(private_category_channel.user_count_stale).to eq(true)
expect_job_enqueued(
job: Jobs::Chat::UpdateChannelUserCount,
@@ -318,7 +319,7 @@ RSpec.shared_examples "a chat channel model" do
end
it "does nothing if the user is not following the channel" do
@membership.update!(following: false)
membership.update!(following: false)
private_category_channel.remove(user1)
private_category_channel.reload

View File

@@ -760,23 +760,22 @@ RSpec.describe DiscourseNarrativeBot::TrackSelector do
end
context "when new and advanced user triggers overlap" do
before do
@overrides = []
@overrides << TranslationOverride.upsert!(
I18n.locale,
"discourse_narrative_bot.new_user_narrative.reset_trigger",
"tutorial",
)
@overrides << TranslationOverride.upsert!(
I18n.locale,
"discourse_narrative_bot.advanced_user_narrative.reset_trigger",
"tutorial advanced",
)
let!(:overrides) do
[
TranslationOverride.upsert!(
I18n.locale,
"discourse_narrative_bot.new_user_narrative.reset_trigger",
"tutorial",
),
TranslationOverride.upsert!(
I18n.locale,
"discourse_narrative_bot.advanced_user_narrative.reset_trigger",
"tutorial advanced",
),
]
end
after { @overrides.each(&:destroy!) }
after { overrides.each(&:destroy!) }
it "should start the right track" do
post.update!(

View File

@@ -1310,56 +1310,55 @@ RSpec.describe SessionController do
end
describe "local attribute override from SSO payload" do
fab!(:user)
let!(:sso) { get_sso("/hello/world") }
let(:reversed_username) { user.username.reverse }
let(:suggested_username) { UserNameSuggester.suggest(sso.username || sso.name || sso.email) }
let(:suggested_name) { User.suggest_name(sso.name || sso.username || sso.email) }
before do
SiteSetting.email_editable = false
SiteSetting.auth_overrides_email = true
SiteSetting.auth_overrides_username = true
SiteSetting.auth_overrides_name = true
@user = Fabricate(:user)
sso.external_id = "997"
sso.username = reversed_username
sso.email = "#{reversed_username}@garbage.org"
sso.name = user.name.reverse
@sso = get_sso("/hello/world")
@sso.external_id = "997"
@reversed_username = @user.username.reverse
@sso.username = @reversed_username
@sso.email = "#{@reversed_username}@garbage.org"
@reversed_name = @user.name.reverse
@sso.name = @reversed_name
@suggested_username = UserNameSuggester.suggest(@sso.username || @sso.name || @sso.email)
@suggested_name = User.suggest_name(@sso.name || @sso.username || @sso.email)
@user.create_single_sign_on_record(external_id: "997", last_payload: "")
user.create_single_sign_on_record(external_id: "997", last_payload: "")
end
it "stores the external attributes" do
get "/session/sso_login", params: Rack::Utils.parse_query(@sso.payload), headers: headers
@user.single_sign_on_record.reload
expect(@user.single_sign_on_record.external_username).to eq(@sso.username)
expect(@user.single_sign_on_record.external_email).to eq(@sso.email)
expect(@user.single_sign_on_record.external_name).to eq(@sso.name)
get "/session/sso_login", params: Rack::Utils.parse_query(sso.payload), headers: headers
user.single_sign_on_record.reload
expect(user.single_sign_on_record.external_username).to eq(sso.username)
expect(user.single_sign_on_record.external_email).to eq(sso.email)
expect(user.single_sign_on_record.external_name).to eq(sso.name)
end
it "overrides attributes" do
get "/session/sso_login", params: Rack::Utils.parse_query(@sso.payload), headers: headers
get "/session/sso_login", params: Rack::Utils.parse_query(sso.payload), headers: headers
logged_on_user = Discourse.current_user_provider.new(request.env).current_user
expect(logged_on_user.username).to eq(@suggested_username)
expect(logged_on_user.email).to eq("#{@reversed_username}@garbage.org")
expect(logged_on_user.name).to eq(@sso.name)
expect(logged_on_user.username).to eq(suggested_username)
expect(logged_on_user.email).to eq("#{reversed_username}@garbage.org")
expect(logged_on_user.name).to eq(sso.name)
end
it "does not change matching attributes for an existing account" do
@sso.username = @user.username
@sso.name = @user.name
@sso.email = @user.email
sso.username = user.username
sso.name = user.name
sso.email = user.email
get "/session/sso_login", params: Rack::Utils.parse_query(@sso.payload), headers: headers
get "/session/sso_login", params: Rack::Utils.parse_query(sso.payload), headers: headers
logged_on_user = Discourse.current_user_provider.new(request.env).current_user
expect(logged_on_user.username).to eq(@user.username)
expect(logged_on_user.name).to eq(@user.name)
expect(logged_on_user.email).to eq(@user.email)
expect(logged_on_user.username).to eq(user.username)
expect(logged_on_user.name).to eq(user.name)
expect(logged_on_user.email).to eq(user.email)
end
end
@@ -1375,9 +1374,11 @@ RSpec.describe SessionController do
end
describe "#sso_provider" do
fab!(:user) { Fabricate(:user, password: "myfrogs123ADMIN", active: true, admin: true) }
let(:headers) { { host: Discourse.current_hostname } }
let(:logo_fixture) { "http://#{Discourse.current_hostname}/uploads/logo.png" }
fab!(:user) { Fabricate(:user, password: "myfrogs123ADMIN", active: true, admin: true) }
let(:sso) { DiscourseConnectProvider.new }
before do
stub_request(:any, %r{#{Discourse.current_hostname}/uploads}).to_return(
@@ -1397,35 +1398,33 @@ RSpec.describe SessionController do
somewhere.over.rainbow|newSecretForOverRainbow
].join("\n")
@sso = DiscourseConnectProvider.new
@sso.nonce = "mynonce"
@sso.return_sso_url = "http://somewhere.over.rainbow/sso"
sso.nonce = "mynonce"
sso.return_sso_url = "http://somewhere.over.rainbow/sso"
@user = user
group = Fabricate(:group)
group.add(@user)
group.add(user)
@user.create_user_avatar!
UserAvatar.import_url_for_user(logo_fixture, @user)
UserProfile.import_url_for_user(logo_fixture, @user, is_card_background: false)
UserProfile.import_url_for_user(logo_fixture, @user, is_card_background: true)
user.create_user_avatar!
UserAvatar.import_url_for_user(logo_fixture, user)
UserProfile.import_url_for_user(logo_fixture, user, is_card_background: false)
UserProfile.import_url_for_user(logo_fixture, user, is_card_background: true)
@user.reload
@user.user_avatar.reload
@user.user_profile.reload
user.reload
user.user_avatar.reload
user.user_profile.reload
EmailToken.update_all(confirmed: true)
end
describe "can act as an SSO provider" do
it "successfully logs in and redirects user to return_sso_url when the user is not logged in" do
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
expect(response).to redirect_to("/login")
post "/session.json",
params: {
login: @user.username,
login: user.username,
password: "myfrogs123ADMIN",
},
xhr: true,
@@ -1438,13 +1437,13 @@ RSpec.describe SessionController do
payload = location.split("?")[1]
sso2 = DiscourseConnectProvider.parse(payload)
expect(sso2.email).to eq(@user.email)
expect(sso2.name).to eq(@user.name)
expect(sso2.username).to eq(@user.username)
expect(sso2.external_id).to eq(@user.id.to_s)
expect(sso2.email).to eq(user.email)
expect(sso2.name).to eq(user.name)
expect(sso2.username).to eq(user.username)
expect(sso2.external_id).to eq(user.id.to_s)
expect(sso2.admin).to eq(true)
expect(sso2.moderator).to eq(false)
expect(sso2.groups).to eq(@user.groups.pluck(:name).join(","))
expect(sso2.groups).to eq(user.groups.pluck(:name).join(","))
expect(sso2.avatar_url.blank?).to_not eq(true)
expect(sso2.profile_background_url.blank?).to_not eq(true)
@@ -1458,10 +1457,10 @@ RSpec.describe SessionController do
end
it "correctly logs in for secondary domain secrets" do
sign_in @user
sign_in user
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("newSecretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("newSecretForOverRainbow"))
expect(response.status).to eq(302)
redirect_uri = URI.parse(response.location)
expect(redirect_uri.host).to eq("somewhere.over.rainbow")
@@ -1471,7 +1470,7 @@ RSpec.describe SessionController do
expect(redirect_query["sig"][0]).to eq(expected_sig)
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("oldSecretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("oldSecretForOverRainbow"))
expect(response.status).to eq(302)
redirect_uri = URI.parse(response.location)
expect(redirect_uri.host).to eq("somewhere.over.rainbow")
@@ -1483,7 +1482,7 @@ RSpec.describe SessionController do
it "fails to log in if secret is wrong" do
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForRandomSite"))
params: Rack::Utils.parse_query(sso.payload("secretForRandomSite"))
expect(response.status).to eq(422)
end
@@ -1504,10 +1503,10 @@ RSpec.describe SessionController do
end
it "successfully redirects user to return_sso_url when the user is logged in" do
sign_in(@user)
sign_in(user)
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
location = response.header["Location"]
expect(location).to match(%r{^http://somewhere.over.rainbow/sso})
@@ -1515,13 +1514,13 @@ RSpec.describe SessionController do
payload = location.split("?")[1]
sso2 = DiscourseConnectProvider.parse(payload)
expect(sso2.email).to eq(@user.email)
expect(sso2.name).to eq(@user.name)
expect(sso2.username).to eq(@user.username)
expect(sso2.external_id).to eq(@user.id.to_s)
expect(sso2.email).to eq(user.email)
expect(sso2.name).to eq(user.name)
expect(sso2.username).to eq(user.username)
expect(sso2.external_id).to eq(user.id.to_s)
expect(sso2.admin).to eq(true)
expect(sso2.moderator).to eq(false)
expect(sso2.groups).to eq(@user.groups.pluck(:name).join(","))
expect(sso2.groups).to eq(user.groups.pluck(:name).join(","))
expect(sso2.avatar_url.blank?).to_not eq(true)
expect(sso2.profile_background_url.blank?).to_not eq(true)
@@ -1535,10 +1534,10 @@ RSpec.describe SessionController do
end
it "fails with a nice error message if `prompt` parameter has an invalid value" do
@sso.prompt = "xyzpdq"
sso.prompt = "xyzpdq"
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
expect(response.status).to eq(400)
expect(response.body).to eq(
@@ -1547,10 +1546,10 @@ RSpec.describe SessionController do
end
it "redirects browser to return_sso_url with auth failure when prompt=none is requested and the user is not logged in" do
@sso.prompt = "none"
sso.prompt = "none"
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
location = response.header["Location"]
expect(location).to match(%r{^http://somewhere.over.rainbow/sso})
@@ -1588,7 +1587,7 @@ RSpec.describe SessionController do
},
)
@user.create_user_avatar!
user.create_user_avatar!
upload =
Fabricate(
:upload,
@@ -1604,29 +1603,28 @@ RSpec.describe SessionController do
url: "//s3-upload-bucket.s3.amazonaws.com/something/else",
)
@user.update_columns(uploaded_avatar_id: upload.id)
user.update_columns(uploaded_avatar_id: upload.id)
upload1 = Fabricate(:upload_s3)
upload2 = Fabricate(:upload_s3)
@user.user_profile.update!(
user.user_profile.update!(
profile_background_upload: upload1,
card_background_upload: upload2,
)
@user.reload
@user.user_avatar.reload
@user.user_profile.reload
user.reload
user.user_avatar.reload
user.user_profile.reload
sign_in(@user)
sign_in(user)
stub_request(:get, "http://cdn.com/something/else").to_return(
body: lambda { |request| File.new(Rails.root + "spec/fixtures/images/logo.png") },
)
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
location = response.header["Location"]
# javascript code will handle redirection of user to return_sso_url
expect(location).to match(%r{^http://somewhere.over.rainbow/sso})
@@ -1646,11 +1644,11 @@ RSpec.describe SessionController do
end
it "successfully logs out and redirects user to return_sso_url when the user is logged in" do
sign_in(@user)
sign_in(user)
@sso.logout = true
sso.logout = true
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
location = response.header["Location"]
expect(location).to match(%r{^http://somewhere.over.rainbow/sso$})
@@ -1661,9 +1659,9 @@ RSpec.describe SessionController do
end
it "successfully logs out and redirects user to return_sso_url when the user is not logged in" do
@sso.logout = true
sso.logout = true
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
location = response.header["Location"]
expect(location).to match(%r{^http://somewhere.over.rainbow/sso$})
@@ -1676,12 +1674,12 @@ RSpec.describe SessionController do
describe "can act as a 2FA provider" do
fab!(:user_totp) { Fabricate(:user_second_factor_totp, user: user) }
before { @sso.require_2fa = true }
before { sso.require_2fa = true }
it "requires the user to confirm 2FA before they are redirected to the SSO return URL" do
sign_in(user)
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
uri = URI(response.location)
expect(uri.hostname).to eq(Discourse.current_hostname)
expect(uri.path).to eq("/session/2fa")
@@ -1698,7 +1696,7 @@ RSpec.describe SessionController do
# attempt no. 2 to bypass 2fa
get "/session/sso_provider",
params: { second_factor_nonce: nonce }.merge(
Rack::Utils.parse_query(@sso.payload("secretForOverRainbow")),
Rack::Utils.parse_query(sso.payload("secretForOverRainbow")),
)
expect(response.status).to eq(401)
expect(response.parsed_body["error"]).to eq(
@@ -1734,7 +1732,7 @@ RSpec.describe SessionController do
backup_codes = user.generate_backup_codes
sign_in(user)
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
uri = URI(response.location)
expect(uri.hostname).to eq(Discourse.current_hostname)
expect(uri.path).to eq("/session/2fa")
@@ -1764,7 +1762,7 @@ RSpec.describe SessionController do
it "redirects the user back to the SSO return url and indicates in the payload that they do not have 2fa methods" do
sign_in(user)
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
expect(response.status).to eq(302)
redirect_url = response.location
@@ -1780,14 +1778,14 @@ RSpec.describe SessionController do
context "when there is no logged in user" do
it "redirects the user to login first" do
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
expect(response.status).to eq(302)
expect(response.location).to eq("http://#{Discourse.current_hostname}/login")
end
it "doesn't make the user confirm 2fa twice if they've just logged in and confirmed 2fa while doing so" do
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
post "/session.json",
params: {
@@ -1813,7 +1811,7 @@ RSpec.describe SessionController do
user_totp.destroy!
user.reload
get "/session/sso_provider",
params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow"))
params: Rack::Utils.parse_query(sso.payload("secretForOverRainbow"))
post "/session.json",
params: {

View File

@@ -2958,39 +2958,40 @@ RSpec.describe TopicsController do
json["post_stream"]["posts"].map { |post| post["id"] }
end
let(:post_ids) { topic.posts.pluck(:id) }
before do
TopicView.stubs(:chunk_size).returns(2)
@post_ids = topic.posts.pluck(:id)
3.times { @post_ids << Fabricate(:post, user: post_author1, topic: topic).id }
3.times { post_ids << Fabricate(:post, user: post_author1, topic: topic).id }
end
it "grabs the correct set of posts" do
get "/t/#{topic.slug}/#{topic.id}.json"
expect(response.status).to eq(200)
expect(extract_post_stream).to eq(@post_ids[0..1])
expect(extract_post_stream).to eq(post_ids[0..1])
get "/t/#{topic.slug}/#{topic.id}.json", params: { page: 1 }
expect(response.status).to eq(200)
expect(extract_post_stream).to eq(@post_ids[0..1])
expect(extract_post_stream).to eq(post_ids[0..1])
get "/t/#{topic.slug}/#{topic.id}.json", params: { page: 2 }
expect(response.status).to eq(200)
expect(extract_post_stream).to eq(@post_ids[2..3])
expect(extract_post_stream).to eq(post_ids[2..3])
post_number = topic.posts.pluck(:post_number).sort[3]
get "/t/#{topic.slug}/#{topic.id}/#{post_number}.json"
expect(response.status).to eq(200)
expect(extract_post_stream).to eq(@post_ids[-2..-1])
expect(extract_post_stream).to eq(post_ids[-2..-1])
TopicView.stubs(:chunk_size).returns(3)
get "/t/#{topic.slug}/#{topic.id}.json", params: { page: 1 }
expect(response.status).to eq(200)
expect(extract_post_stream).to eq(@post_ids[0..2])
expect(extract_post_stream).to eq(post_ids[0..2])
get "/t/#{topic.slug}/#{topic.id}.json", params: { page: 2 }
expect(response.status).to eq(200)
expect(extract_post_stream).to eq(@post_ids[3..3])
expect(extract_post_stream).to eq(post_ids[3..3])
get "/t/#{topic.slug}/#{topic.id}.json", params: { page: 3 }
expect(response.status).to eq(404)
@@ -2999,7 +3000,7 @@ RSpec.describe TopicsController do
get "/t/#{topic.slug}/#{topic.id}.json", params: { page: 1 }
expect(response.status).to eq(200)
expect(extract_post_stream).to eq(@post_ids[0..3])
expect(extract_post_stream).to eq(post_ids[0..3])
get "/t/#{topic.slug}/#{topic.id}.json", params: { page: 2 }
expect(response.status).to eq(404)

View File

@@ -718,15 +718,16 @@ RSpec.describe UsersController do
params
end
let(:user) { Fabricate.build(:user, email: "foobar@example.com", password: "strongpassword") }
before do
UsersController.any_instance.stubs(:honeypot_value).returns(nil)
UsersController.any_instance.stubs(:challenge_value).returns(nil)
SiteSetting.allow_new_registrations = true
@user = Fabricate.build(:user, email: "foobar@example.com", password: "strongpassword")
end
let(:post_user_params) do
{ name: @user.name, username: @user.username, password: "strongpassword", email: @user.email }
{ name: user.name, username: user.username, password: "strongpassword", email: user.email }
end
def post_user(extra_params = {})
@@ -737,8 +738,8 @@ RSpec.describe UsersController do
it "should raise the right error" do
post "/u.json",
params: {
name: @user.name,
username: @user.username,
name: user.name,
username: user.username,
password: "testing12352343",
}
expect(response.status).to eq(400)
@@ -772,7 +773,7 @@ RSpec.describe UsersController do
SiteSetting.default_locale = "en"
I18n.stubs(:locale).returns(:fr)
post_user
expect(User.find_by(username: @user.username).locale).to eq("fr")
expect(User.find_by(username: user.username).locale).to eq("fr")
end
it "requires invite code when specified" do
@@ -796,7 +797,7 @@ RSpec.describe UsersController do
it "sets the timezone" do
post_user(timezone: "Australia/Brisbane")
expect(response.status).to eq(200)
expect(User.find_by(username: @user.username).user_option.timezone).to eq(
expect(User.find_by(username: user.username).user_option.timezone).to eq(
"Australia/Brisbane",
)
end
@@ -1412,7 +1413,7 @@ RSpec.describe UsersController do
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["success"]).to eq(true)
expect(User.find_by(username: @user.username).active).to eq(false)
expect(User.find_by(username: user.username).active).to eq(false)
end
shared_examples "honeypot fails" do
@@ -1436,10 +1437,10 @@ RSpec.describe UsersController do
before { UsersController.any_instance.stubs(:honeypot_value).returns("abc") }
let(:create_params) do
{
name: @user.name,
username: @user.username,
name: user.name,
username: user.username,
password: "strongpassword",
email: @user.email,
email: user.email,
password_confirmation: "wrong",
}
end
@@ -1450,10 +1451,10 @@ RSpec.describe UsersController do
before { UsersController.any_instance.stubs(:challenge_value).returns("abc") }
let(:create_params) do
{
name: @user.name,
username: @user.username,
name: user.name,
username: user.username,
password: "strongpassword",
email: @user.email,
email: user.email,
challenge: "abc",
}
end
@@ -1464,12 +1465,7 @@ RSpec.describe UsersController do
before { SiteSetting.invite_only = true }
let(:create_params) do
{
name: @user.name,
username: @user.username,
password: "strongpassword",
email: @user.email,
}
{ name: user.name, username: user.username, password: "strongpassword", email: user.email }
end
include_examples "honeypot fails"
@@ -1494,7 +1490,7 @@ RSpec.describe UsersController do
context "when password is blank" do
let(:create_params) do
{ name: @user.name, username: @user.username, password: "", email: @user.email }
{ name: user.name, username: user.username, password: "", email: user.email }
end
include_examples "failed signup"
end
@@ -1502,23 +1498,23 @@ RSpec.describe UsersController do
context "when password is too long" do
let(:create_params) do
{
name: @user.name,
username: @user.username,
name: user.name,
username: user.username,
password: "x" * (User.max_password_length + 1),
email: @user.email,
email: user.email,
}
end
include_examples "failed signup"
end
context "when password param is missing" do
let(:create_params) { { name: @user.name, username: @user.username, email: @user.email } }
let(:create_params) { { name: user.name, username: user.username, email: user.email } }
include_examples "failed signup"
end
context "with a reserved username" do
let(:create_params) do
{ name: @user.name, username: "Reserved", email: @user.email, password: "strongpassword" }
{ name: user.name, username: "Reserved", email: user.email, password: "strongpassword" }
end
before { SiteSetting.reserved_usernames = "a|reserved|b" }
include_examples "failed signup"
@@ -1527,9 +1523,9 @@ RSpec.describe UsersController do
context "with a username that matches a user route" do
let(:create_params) do
{
name: @user.name,
name: user.name,
username: "account-created",
email: @user.email,
email: user.email,
password: "strongpassword",
}
end
@@ -1537,7 +1533,7 @@ RSpec.describe UsersController do
end
context "with a missing username" do
let(:create_params) { { name: @user.name, email: @user.email, password: "x" * 20 } }
let(:create_params) { { name: user.name, email: user.email, password: "x" * 20 } }
it "should not create a new User" do
expect { post "/u.json", params: create_params }.to_not change { User.count }
@@ -1549,12 +1545,7 @@ RSpec.describe UsersController do
before { User.any_instance.stubs(:save).raises(ActiveRecord::StatementInvalid.new("Oh no")) }
let(:create_params) do
{
name: @user.name,
username: @user.username,
password: "strongpassword",
email: @user.email,
}
{ name: user.name, username: user.username, password: "strongpassword", email: user.email }
end
include_examples "failed signup"
@@ -1567,7 +1558,7 @@ RSpec.describe UsersController do
context "without a value for the fields" do
let(:create_params) do
{ name: @user.name, password: "watwatwat", username: @user.username, email: @user.email }
{ name: user.name, password: "watwatwat", username: user.username, email: user.email }
end
include_examples "failed signup"
end
@@ -1712,10 +1703,10 @@ RSpec.describe UsersController do
let(:create_params) do
{
name: @user.name,
name: user.name,
password: "suChS3cuRi7y",
username: @user.username,
email: @user.email,
username: user.username,
email: user.email,
user_fields: {
user_field.id.to_s => "value1",
another_field.id.to_s => "value2",
@@ -1726,7 +1717,7 @@ RSpec.describe UsersController do
it "should succeed without the optional field" do
post "/u.json", params: create_params
expect(response.status).to eq(200)
inserted = User.find_by_email(@user.email)
inserted = User.find_by_email(user.email)
expect(inserted).to be_present
expect(inserted.custom_fields).to be_present
expect(inserted.custom_fields["user_field_#{user_field.id}"]).to eq("value1")
@@ -1738,7 +1729,7 @@ RSpec.describe UsersController do
create_params[:user_fields][optional_field.id.to_s] = "value3"
post "/u.json", params: create_params.merge(create_params)
expect(response.status).to eq(200)
inserted = User.find_by_email(@user.email)
inserted = User.find_by_email(user.email)
expect(inserted).to be_present
expect(inserted.custom_fields).to be_present
expect(inserted.custom_fields["user_field_#{user_field.id}"]).to eq("value1")
@@ -1750,7 +1741,7 @@ RSpec.describe UsersController do
create_params[:user_fields][optional_field.id.to_s] = ("x" * 3000)
post "/u.json", params: create_params.merge(create_params)
expect(response.status).to eq(200)
inserted = User.find_by_email(@user.email)
inserted = User.find_by_email(user.email)
val = inserted.custom_fields["user_field_#{optional_field.id}"]
expect(val.length).to eq(UserField.max_length)
@@ -1763,18 +1754,13 @@ RSpec.describe UsersController do
context "without values for the fields" do
let(:create_params) do
{
name: @user.name,
password: "suChS3cuRi7y",
username: @user.username,
email: @user.email,
}
{ name: user.name, password: "suChS3cuRi7y", username: user.username, email: user.email }
end
it "should succeed" do
post "/u.json", params: create_params
expect(response.status).to eq(200)
inserted = User.find_by_email(@user.email)
inserted = User.find_by_email(user.email)
expect(inserted).to be_present
expect(inserted.custom_fields).not_to be_present
expect(inserted.custom_fields["user_field_#{user_field.id}"]).to be_blank

View File

@@ -15,6 +15,7 @@ RSpec.describe ExternalUploadManager do
let(:external_upload_stub_metadata) { {} }
let!(:external_upload_stub) { Fabricate(:image_external_upload_stub, created_by: user) }
let(:s3_bucket_name) { SiteSetting.s3_upload_bucket }
let(:fake_s3) { FakeS3.create }
before do
SiteSetting.authorized_extensions += "|pdf"
@@ -25,7 +26,12 @@ RSpec.describe ExternalUploadManager do
SiteSetting.s3_backup_bucket = "s3-backup-bucket"
SiteSetting.backup_location = BackupLocationSiteSetting::S3
prepare_fake_s3
fake_s3.bucket(s3_bucket_name).put_object(
key: external_upload_stub.key,
size: object_size,
last_modified: Time.zone.now,
metadata: external_upload_stub_metadata,
)
stub_download_object_filehelper
end
@@ -73,8 +79,8 @@ RSpec.describe ExternalUploadManager do
it "copies the stubbed upload on S3 to its new destination and deletes it" do
upload = manager.transform!
bucket = @fake_s3.bucket(SiteSetting.s3_upload_bucket)
expect(@fake_s3.operation_called?(:copy_object)).to eq(true)
bucket = fake_s3.bucket(SiteSetting.s3_upload_bucket)
expect(fake_s3.operation_called?(:copy_object)).to eq(true)
expect(bucket.find_object(Discourse.store.get_path_for_upload(upload))).to be_present
expect(bucket.find_object(external_upload_stub.key)).to be_nil
end
@@ -117,8 +123,8 @@ RSpec.describe ExternalUploadManager do
it "creates a new upload in s3 (not copy) and deletes the original stubbed upload" do
upload = manager.transform!
bucket = @fake_s3.bucket(SiteSetting.s3_upload_bucket)
expect(@fake_s3.operation_called?(:copy_object)).to eq(false)
bucket = fake_s3.bucket(SiteSetting.s3_upload_bucket)
expect(fake_s3.operation_called?(:copy_object)).to eq(false)
expect(bucket.find_object(Discourse.store.get_path_for_upload(upload))).to be_present
expect(bucket.find_object(external_upload_stub.key)).to be_nil
end
@@ -136,7 +142,7 @@ RSpec.describe ExternalUploadManager do
)
expect(ExternalUploadStub.exists?(id: external_upload_stub.id)).to eq(false)
bucket = @fake_s3.bucket(SiteSetting.s3_upload_bucket)
bucket = fake_s3.bucket(SiteSetting.s3_upload_bucket)
expect(bucket.find_object(external_upload_stub.key)).to be_nil
end
@@ -148,7 +154,7 @@ RSpec.describe ExternalUploadManager do
external_stub = ExternalUploadStub.find(external_upload_stub.id)
expect(external_stub.status).to eq(ExternalUploadStub.statuses[:failed])
bucket = @fake_s3.bucket(SiteSetting.s3_upload_bucket)
bucket = fake_s3.bucket(SiteSetting.s3_upload_bucket)
expect(bucket.find_object(external_upload_stub.key)).to be_present
end
end
@@ -168,7 +174,7 @@ RSpec.describe ExternalUploadManager do
),
).to eq("1")
bucket = @fake_s3.bucket(SiteSetting.s3_upload_bucket)
bucket = fake_s3.bucket(SiteSetting.s3_upload_bucket)
expect(bucket.find_object(external_upload_stub.key)).to be_nil
end
@@ -178,7 +184,7 @@ RSpec.describe ExternalUploadManager do
external_stub = ExternalUploadStub.find(external_upload_stub.id)
expect(external_stub.status).to eq(ExternalUploadStub.statuses[:failed])
bucket = @fake_s3.bucket(SiteSetting.s3_upload_bucket)
bucket = fake_s3.bucket(SiteSetting.s3_upload_bucket)
expect(bucket.find_object(external_upload_stub.key)).to be_present
end
end
@@ -218,7 +224,7 @@ RSpec.describe ExternalUploadManager do
it "copies the stubbed upload on S3 to its new destination and deletes it" do
upload = manager.transform!
bucket = @fake_s3.bucket(SiteSetting.s3_upload_bucket)
bucket = fake_s3.bucket(SiteSetting.s3_upload_bucket)
expect(bucket.find_object(Discourse.store.get_path_for_upload(upload))).to be_present
expect(bucket.find_object(external_upload_stub.key)).to be_nil
end
@@ -259,7 +265,7 @@ RSpec.describe ExternalUploadManager do
end
it "copies the stubbed upload on S3 to its new destination and deletes it" do
bucket = @fake_s3.bucket(SiteSetting.s3_backup_bucket)
bucket = fake_s3.bucket(SiteSetting.s3_backup_bucket)
expect(bucket.find_object(external_upload_stub.key)).to be_present
manager.transform!
@@ -283,15 +289,4 @@ RSpec.describe ExternalUploadManager do
body: object_file.read,
)
end
def prepare_fake_s3
@fake_s3 = FakeS3.create
@fake_s3.bucket(s3_bucket_name).put_object(
key: external_upload_stub.key,
size: object_size,
last_modified: Time.zone.now,
metadata: external_upload_stub_metadata,
)
end
end

View File

@@ -319,10 +319,9 @@ RSpec.describe UserDestroyer do
context "when user has posts with links" do
context "with external links" do
before do
@post = Fabricate(:post_with_external_links, user: user)
TopicLink.extract_from(@post)
end
let(:post) { Fabricate(:post_with_external_links, user: user) }
before { TopicLink.extract_from(post) }
it "doesn't add ScreenedUrl records by default" do
ScreenedUrl.expects(:watch).never
@@ -336,9 +335,10 @@ RSpec.describe UserDestroyer do
end
context "with internal links" do
let(:post) { Fabricate(:post_with_external_links, user: user) }
before do
@post = Fabricate(:post_with_external_links, user: user)
TopicLink.extract_from(@post)
TopicLink.extract_from(post)
TopicLink.where(user: user).update_all(internal: true)
end
@@ -349,10 +349,9 @@ RSpec.describe UserDestroyer do
end
context "with oneboxed links" do
before do
@post = Fabricate(:post_with_youtube, user: user)
TopicLink.extract_from(@post)
end
let(:post) { Fabricate(:post_with_youtube, user: user) }
before { TopicLink.extract_from(post) }
it "doesn't add ScreenedUrl records" do
ScreenedUrl.expects(:watch).never
@@ -415,17 +414,16 @@ RSpec.describe UserDestroyer do
end
context "when user liked things" do
before do
@topic = Fabricate(:topic, user: Fabricate(:user))
@post = Fabricate(:post, user: @topic.user, topic: @topic)
PostActionCreator.like(user, @post)
end
let!(:topic) { Fabricate(:topic, user: Fabricate(:user)) }
let!(:post) { Fabricate(:post, user: topic.user, topic: topic) }
before { PostActionCreator.like(user, post) }
it "should destroy the like" do
expect { UserDestroyer.new(admin).destroy(user, delete_posts: true) }.to change {
PostAction.count
}.by(-1)
expect(@post.reload.like_count).to eq(0)
expect(post.reload.like_count).to eq(0)
end
end

View File

@@ -69,16 +69,16 @@ RSpec.describe UserSilencer do
end
context "with a plugin hook" do
before do
@override_silence_message = ->(opts) do
let(:override_silence_message) do
->(opts) do
opts[:silence_message_params][:message_title] = "override title"
opts[:silence_message_params][:message_raw] = "override raw"
end
DiscourseEvent.on(:user_silenced, &@override_silence_message)
end
after { DiscourseEvent.off(:user_silenced, &@override_silence_message) }
before { DiscourseEvent.on(:user_silenced, &override_silence_message) }
after { DiscourseEvent.off(:user_silenced, &override_silence_message) }
it "allows the message to be overridden" do
UserSilencer.silence(user, admin)

View File

@@ -12,12 +12,13 @@ RSpec.describe UsernameChanger do
it "should change the username" do
new_username = "#{user.username}1234"
result = nil
events =
DiscourseEvent
.track_events { @result = UsernameChanger.change(user, new_username) }
.track_events { result = UsernameChanger.change(user, new_username) }
.last(2)
expect(@result).to eq(true)
expect(result).to eq(true)
event = events.first
expect(event[:event_name]).to eq(:username_changed)
@@ -36,10 +37,10 @@ RSpec.describe UsernameChanger do
it "do nothing if the new username is the same" do
new_username = user.username
events =
DiscourseEvent.track_events { @result = UsernameChanger.change(user, new_username) }
result = nil
events = DiscourseEvent.track_events { result = UsernameChanger.change(user, new_username) }
expect(@result).to eq(false)
expect(result).to eq(false)
expect(events.count).to be_zero
end
end
@@ -50,8 +51,7 @@ RSpec.describe UsernameChanger do
let(:username_lower_before_change) { user.username_lower }
it "should not change the username" do
@result = UsernameChanger.change(user, wrong_username)
expect(@result).to eq(false)
expect(UsernameChanger.change(user, wrong_username)).to eq(false)
user.reload
expect(user.username).to eq(username_before_change)
@@ -82,18 +82,17 @@ RSpec.describe UsernameChanger do
end
describe "allow custom minimum username length from site settings" do
before do
@custom_min = 2
SiteSetting.min_username_length = @custom_min
end
let(:custom_min) { 2 }
before { SiteSetting.min_username_length = custom_min }
it "should allow a shorter username than default" do
result = UsernameChanger.change(user, "a" * @custom_min)
result = UsernameChanger.change(user, "a" * custom_min)
expect(result).not_to eq(false)
end
it "should not allow a shorter username than limit" do
result = UsernameChanger.change(user, "a" * (@custom_min - 1))
result = UsernameChanger.change(user, "a" * (custom_min - 1))
expect(result).to eq(false)
end

View File

@@ -2,30 +2,27 @@
RSpec.describe UsernameCheckerService do
describe "#check_username" do
before do
@service = UsernameCheckerService.new
@nil_email = nil
@email = "vincentvega@example.com"
end
let(:service) { UsernameCheckerService.new }
let(:email) { nil }
context "when username is invalid" do
it "rejects too short usernames" do
result = @service.check_username("a", @nil_email)
result = service.check_username("a", email)
expect(result).to have_key(:errors)
end
it "rejects too long usernames" do
result = @service.check_username("a123456789b123456789c123456789", @nil_email)
result = service.check_username("a123456789b123456789c123456789", email)
expect(result).to have_key(:errors)
end
it "rejects usernames with invalid characters" do
result = @service.check_username("vincent-", @nil_email)
result = service.check_username("vincent-", email)
expect(result).to have_key(:errors)
end
it "rejects usernames that do not start with an alphanumeric character" do
result = @service.check_username(".vincent", @nil_email)
result = service.check_username(".vincent", email)
expect(result).to have_key(:errors)
end
@@ -33,13 +30,13 @@ RSpec.describe UsernameCheckerService do
before { SiteSetting.reserved_usernames = "test|donkey" }
it "rejects usernames that are reserved" do
result = @service.check_username("test", @nil_email)
result = service.check_username("test", email)
expect(result[:available]).to eq(false)
end
it "allows reserved username checker to be skipped" do
@service = UsernameCheckerService.new(allow_reserved_username: true)
result = @service.check_username("test", @nil_email)
service = UsernameCheckerService.new(allow_reserved_username: true)
result = service.check_username("test", email)
expect(result[:available]).to eq(true)
end
end
@@ -48,14 +45,14 @@ RSpec.describe UsernameCheckerService do
it "username not available locally" do
User.stubs(:username_available?).returns(false)
UserNameSuggester.stubs(:suggest).returns("einar-j")
result = @service.check_username("vincent", @nil_email)
result = service.check_username("vincent", email)
expect(result[:available]).to eq(false)
expect(result[:suggestion]).to eq("einar-j")
end
it "username available locally" do
User.stubs(:username_available?).returns(true)
result = @service.check_username("vincent", @nil_email)
result = service.check_username("vincent", email)
expect(result[:available]).to eq(true)
end
end

View File

@@ -71,16 +71,15 @@ shared_examples "forgot password scenarios" do
end
context "when user only has security key configured" do
before do
@authenticator =
page.driver.browser.add_virtual_authenticator(
Selenium::WebDriver::VirtualAuthenticatorOptions.new,
)
create_user_security_key(user)
let!(:authenticator) do
page.driver.browser.add_virtual_authenticator(
Selenium::WebDriver::VirtualAuthenticatorOptions.new,
)
end
after { @authenticator.remove! }
before { create_user_security_key(user) }
after { authenticator.remove! }
it "should allow a user to reset password with a security key" do
visit_reset_password_link
@@ -116,16 +115,15 @@ shared_examples "forgot password scenarios" do
context "when user has security key and backup codes configured" do
fab!(:user_second_factor_backup) { Fabricate(:user_second_factor_backup, user:) }
before do
@authenticator =
page.driver.browser.add_virtual_authenticator(
Selenium::WebDriver::VirtualAuthenticatorOptions.new,
)
create_user_security_key(user)
let!(:authenticator) do
page.driver.browser.add_virtual_authenticator(
Selenium::WebDriver::VirtualAuthenticatorOptions.new,
)
end
after { @authenticator.remove! }
before { create_user_security_key(user) }
after { authenticator.remove! }
it "should allow a user to reset password with backup code instead of security key" do
visit_reset_password_link
@@ -148,16 +146,15 @@ shared_examples "forgot password scenarios" do
fab!(:user_second_factor_totp) { Fabricate(:user_second_factor_totp, user:) }
fab!(:user_second_factor_backup) { Fabricate(:user_second_factor_backup, user:) }
before do
@authenticator =
page.driver.browser.add_virtual_authenticator(
Selenium::WebDriver::VirtualAuthenticatorOptions.new,
)
create_user_security_key(user)
let!(:authenticator) do
page.driver.browser.add_virtual_authenticator(
Selenium::WebDriver::VirtualAuthenticatorOptions.new,
)
end
after { @authenticator.remove! }
before { create_user_security_key(user) }
after { authenticator.remove! }
it "should allow a user to toggle from security key to TOTP and between TOTP and backup codes" do
visit_reset_password_link
@@ -179,16 +176,15 @@ shared_examples "forgot password scenarios" do
context "when user has TOTP and security key configured but no backup codes" do
fab!(:user_second_factor_totp) { Fabricate(:user_second_factor_totp, user:) }
before do
@authenticator =
page.driver.browser.add_virtual_authenticator(
Selenium::WebDriver::VirtualAuthenticatorOptions.new,
)
create_user_security_key(user)
let!(:authenticator) do
page.driver.browser.add_virtual_authenticator(
Selenium::WebDriver::VirtualAuthenticatorOptions.new,
)
end
after { @authenticator.remove! }
before { create_user_security_key(user) }
after { authenticator.remove! }
it "should allow a user to reset password with TOTP instead of security key" do
visit_reset_password_link