DEV: Apply syntax_tree formatting to `spec/*`

This commit is contained in:
David Taylor 2023-01-09 11:18:21 +00:00
parent 0cf6421716
commit cb932d6ee1
No known key found for this signature in database
GPG Key ID: 46904C18B1D3F434
907 changed files with 58693 additions and 45909 deletions

View File

@ -3,4 +3,3 @@
--ignore-files=app/*
--ignore-files=db/*
--ignore-files=lib/*
--ignore-files=spec/*

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
Fabricator(:allowed_pm_user) do
user
end
Fabricator(:allowed_pm_user) { user }

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
Fabricator(:api_key) do
end
Fabricator(:api_key) {}

View File

@ -2,6 +2,6 @@
Fabricator(:associated_group) do
name { sequence(:name) { |n| "group_#{n}" } }
provider_name 'google'
provider_name "google"
provider_id { SecureRandom.hex(20) }
end

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
Fabricator(:badge_type) do
name { sequence(:name) { |i| "Silver #{i}" } }
end
Fabricator(:badge_type) { name { sequence(:name) { |i| "Silver #{i}" } } }
Fabricator(:badge) do
name { sequence(:name) { |i| "Badge #{i}" } }

View File

@ -10,13 +10,14 @@ end
Fabricator(:bookmark_next_business_day_reminder, from: :bookmark) do
reminder_at do
date = if Time.zone.now.friday?
Time.zone.now + 3.days
elsif Time.zone.now.saturday?
Time.zone.now + 2.days
else
Time.zone.now + 1.day
end
date =
if Time.zone.now.friday?
Time.zone.now + 3.days
elsif Time.zone.now.saturday?
Time.zone.now + 2.days
else
Time.zone.now + 1.day
end
date.iso8601
end
reminder_set_at { Time.zone.now }

View File

@ -6,9 +6,7 @@ Fabricator(:category) do
user
end
Fabricator(:category_with_definition, from: :category) do
skip_category_definition false
end
Fabricator(:category_with_definition, from: :category) { skip_category_definition false }
Fabricator(:private_category, from: :category) do
transient :group
@ -20,7 +18,10 @@ Fabricator(:private_category, from: :category) do
after_build do |cat, transients|
cat.update!(read_restricted: true)
cat.category_groups.build(group_id: transients[:group].id, permission_type: transients[:permission_type] || CategoryGroup.permission_types[:full])
cat.category_groups.build(
group_id: transients[:group].id,
permission_type: transients[:permission_type] || CategoryGroup.permission_types[:full],
)
end
end
@ -33,7 +34,7 @@ Fabricator(:link_category, from: :category) do
end
Fabricator(:mailinglist_mirror_category, from: :category) do
email_in 'list@example.com'
email_in "list@example.com"
email_in_allow_strangers true
mailinglist_mirror true
end

View File

@ -2,5 +2,7 @@
Fabricator(:color_scheme) do
name { sequence(:name) { |i| "Palette #{i}" } }
color_scheme_colors(count: 2) { |attrs, i| Fabricate.build(:color_scheme_color, color_scheme: nil) }
color_scheme_colors(count: 2) do |attrs, i|
Fabricate.build(:color_scheme_color, color_scheme: nil)
end
end

View File

@ -5,7 +5,12 @@ Fabricator(:external_upload_stub) do
created_by { Fabricate(:user) }
original_filename "test.txt"
key { |attrs| FileStore::BaseStore.temporary_upload_path("test.txt", folder_prefix: attrs[:folder_prefix] || "") }
key do |attrs|
FileStore::BaseStore.temporary_upload_path(
"test.txt",
folder_prefix: attrs[:folder_prefix] || "",
)
end
upload_type "card_background"
filesize 1024
status 1
@ -14,16 +19,28 @@ end
Fabricator(:image_external_upload_stub, from: :external_upload_stub) do
original_filename "logo.png"
filesize 1024
key { |attrs| FileStore::BaseStore.temporary_upload_path("logo.png", folder_prefix: attrs[:folder_prefix] || "") }
key do |attrs|
FileStore::BaseStore.temporary_upload_path(
"logo.png",
folder_prefix: attrs[:folder_prefix] || "",
)
end
end
Fabricator(:attachment_external_upload_stub, from: :external_upload_stub) do
original_filename "file.pdf"
filesize 1024
key { |attrs| FileStore::BaseStore.temporary_upload_path("file.pdf", folder_prefix: attrs[:folder_prefix] || "") }
key do |attrs|
FileStore::BaseStore.temporary_upload_path(
"file.pdf",
folder_prefix: attrs[:folder_prefix] || "",
)
end
end
Fabricator(:multipart_external_upload_stub, from: :external_upload_stub) do
multipart true
external_upload_identifier { "#{SecureRandom.hex(6)}._#{SecureRandom.hex(6)}_#{SecureRandom.hex(6)}.d.ghQ" }
external_upload_identifier do
"#{SecureRandom.hex(6)}._#{SecureRandom.hex(6)}_#{SecureRandom.hex(6)}.d.ghQ"
end
end

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
Fabricator(:group) do
name { sequence(:name) { |n| "my_group_#{n}" } }
end
Fabricator(:group) { name { sequence(:name) { |n| "my_group_#{n}" } } }
Fabricator(:public_group, from: :group) do
public_admission true

View File

@ -2,5 +2,5 @@
Fabricator(:invite) do
invited_by(fabricator: :user)
email 'iceking@ADVENTURETIME.ooo'
email "iceking@ADVENTURETIME.ooo"
end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
Fabricator(:muted_user) do
user
end
Fabricator(:muted_user) { user }

View File

@ -27,7 +27,7 @@ Fabricator(:private_message_notification, from: :notification) do
original_post_type: post.post_type,
original_username: post.user.username,
revision_number: nil,
display_username: post.user.username
display_username: post.user.username,
}.to_json
end
end
@ -44,7 +44,7 @@ Fabricator(:bookmark_reminder_notification, from: :notification) do
original_username: post.user.username,
revision_number: nil,
display_username: post.user.username,
bookmark_name: "Check out Mr Freeze's opinion here"
bookmark_name: "Check out Mr Freeze's opinion here",
}.to_json
end
end
@ -58,7 +58,7 @@ Fabricator(:replied_notification, from: :notification) do
original_post_id: post.id,
original_username: post.user.username,
revision_number: nil,
display_username: post.user.username
display_username: post.user.username,
}.to_json
end
end
@ -73,7 +73,7 @@ Fabricator(:posted_notification, from: :notification) do
original_post_type: post.post_type,
original_username: post.user.username,
revision_number: nil,
display_username: post.user.username
display_username: post.user.username,
}.to_json
end
end
@ -87,7 +87,7 @@ Fabricator(:mentioned_notification, from: :notification) do
original_post_type: attrs[:post].post_type,
original_username: attrs[:post].user.username,
revision_number: nil,
display_username: attrs[:post].user.username
display_username: attrs[:post].user.username,
}.to_json
end
end
@ -101,7 +101,7 @@ Fabricator(:watching_first_post_notification, from: :notification) do
original_post_type: attrs[:post].post_type,
original_username: attrs[:post].user.username,
revision_number: nil,
display_username: attrs[:post].user.username
display_username: attrs[:post].user.username,
}.to_json
end
end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
Fabricator(:permalink) do
url { sequence(:url) { |i| "my/#{i}/url" } }
end
Fabricator(:permalink) { url { sequence(:url) { |i| "my/#{i}/url" } } }

View File

@ -7,19 +7,17 @@ Fabricator(:post) do
post_type Post.types[:regular]
# Fabrication bypasses PostCreator, for performance reasons, where the counts are updated so we have to handle this manually here.
after_create do |post, _transients|
UserStatCountUpdater.increment!(post)
end
after_create { |post, _transients| UserStatCountUpdater.increment!(post) }
end
Fabricator(:post_with_long_raw_content, from: :post) do
raw 'This is a sample post with semi-long raw content. The raw content is also more than
raw "This is a sample post with semi-long raw content. The raw content is also more than
two hundred characters to satisfy any test conditions that require content longer
than the typical test post raw content. It really is some long content, folks.'
than the typical test post raw content. It really is some long content, folks."
end
Fabricator(:post_with_youtube, from: :post) do
raw 'http://www.youtube.com/watch?v=9bZkp7q19f0'
raw "http://www.youtube.com/watch?v=9bZkp7q19f0"
cooked '<p><a href="http://www.youtube.com/watch?v=9bZkp7q19f0" class="onebox" target="_blank">http://www.youtube.com/watch?v=9bZkp7q19f0</a></p>'
end
@ -39,7 +37,7 @@ Fabricator(:basic_reply, from: :post) do
user(fabricator: :coding_horror)
reply_to_post_number 1
topic
raw 'this reply has no quotes'
raw "this reply has no quotes"
end
Fabricator(:reply, from: :post) do
@ -51,14 +49,12 @@ Fabricator(:reply, from: :post) do
'
end
Fabricator(:post_with_plenty_of_images, from: :post) do
cooked <<~HTML
Fabricator(:post_with_plenty_of_images, from: :post) { cooked <<~HTML }
<aside class="quote"><img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"></aside>
<div class="onebox-result"><img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"></div>
<div class="onebox"><img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"></div>
<p>With an emoji! <img src="//cdn.discourse.org/meta/images/emoji/twitter/smile.png?v=#{Emoji::EMOJI_VERSION}" title=":smile:" class="emoji" alt="smile" loading="lazy" width="20" height="20"></p>
HTML
end
Fabricator(:post_with_uploaded_image, from: :post) do
raw { "<img src=\"#{Fabricate(:image_upload)}\" width=\"1500\" height=\"2000\">" }
@ -101,8 +97,7 @@ Fabricator(:post_with_uploads, from: :post) do
"
end
Fabricator(:post_with_uploads_and_links, from: :post) do
raw <<~MD
Fabricator(:post_with_uploads_and_links, from: :post) { raw <<~MD }
<a href="/#{Discourse.store.upload_path}/original/2X/2345678901234567.jpg">Link</a>
<img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg">
<a href="http://www.google.com">Google</a>
@ -110,7 +105,6 @@ Fabricator(:post_with_uploads_and_links, from: :post) do
<a class="attachment" href="/#{Discourse.store.upload_path}/original/1X/af2c2618032c679333bebf745e75f9088748d737.txt">text.txt</a> (20 Bytes)
:smile:
MD
end
Fabricator(:post_with_external_links, from: :post) do
user
@ -130,14 +124,15 @@ Fabricator(:private_message_post, from: :post) do
transient :recipient
user
topic do |attrs|
Fabricate(:private_message_topic,
Fabricate(
:private_message_topic,
user: attrs[:user],
created_at: attrs[:created_at],
subtype: TopicSubtype.user_to_user,
topic_allowed_users: [
Fabricate.build(:topic_allowed_user, user: attrs[:user]),
Fabricate.build(:topic_allowed_user, user: attrs[:recipient] || Fabricate(:user))
]
Fabricate.build(:topic_allowed_user, user: attrs[:recipient] || Fabricate(:user)),
],
)
end
raw "Ssshh! This is our secret conversation!"
@ -147,16 +142,15 @@ Fabricator(:group_private_message_post, from: :post) do
transient :recipients
user
topic do |attrs|
Fabricate(:private_message_topic,
Fabricate(
:private_message_topic,
user: attrs[:user],
created_at: attrs[:created_at],
subtype: TopicSubtype.user_to_user,
topic_allowed_users: [
Fabricate.build(:topic_allowed_user, user: attrs[:user]),
],
topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: attrs[:user])],
topic_allowed_groups: [
Fabricate.build(:topic_allowed_group, group: attrs[:recipients] || Fabricate(:group))
]
Fabricate.build(:topic_allowed_group, group: attrs[:recipients] || Fabricate(:group)),
],
)
end
raw "Ssshh! This is our group secret conversation!"
@ -165,13 +159,12 @@ end
Fabricator(:private_message_post_one_user, from: :post) do
user
topic do |attrs|
Fabricate(:private_message_topic,
Fabricate(
:private_message_topic,
user: attrs[:user],
created_at: attrs[:created_at],
subtype: TopicSubtype.user_to_user,
topic_allowed_users: [
Fabricate.build(:topic_allowed_user, user: attrs[:user]),
]
topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: attrs[:user])],
)
end
raw "Ssshh! This is our secret conversation!"
@ -188,10 +181,6 @@ Fabricator(:post_via_email, from: :post) do
end
end
Fabricator(:whisper, from: :post) do
post_type Post.types[:whisper]
end
Fabricator(:whisper, from: :post) { post_type Post.types[:whisper] }
Fabricator(:small_action, from: :post) do
post_type Post.types[:small_action]
end
Fabricator(:small_action, from: :post) { post_type Post.types[:small_action] }

View File

@ -4,7 +4,5 @@ Fabricator(:post_revision) do
post
user
number 2
modifications do
{ "cooked" => ["<p>BEFORE</p>", "<p>AFTER</p>"], "raw" => ["BEFORE", "AFTER"] }
end
modifications { { "cooked" => %w[<p>BEFORE</p> <p>AFTER</p>], "raw" => %w[BEFORE AFTER] } }
end

View File

@ -2,70 +2,75 @@
Fabricator(:reviewable) do
reviewable_by_moderator true
type 'ReviewableUser'
type "ReviewableUser"
created_by { Fabricate(:user) }
target_id { Fabricate(:user).id }
target_type "User"
target_created_by { Fabricate(:user) }
category
score 1.23
payload {
{ list: [1, 2, 3], name: 'bandersnatch' }
}
payload { { list: [1, 2, 3], name: "bandersnatch" } }
end
Fabricator(:reviewable_queued_post_topic, class_name: :reviewable_queued_post) do
reviewable_by_moderator true
type 'ReviewableQueuedPost'
type "ReviewableQueuedPost"
created_by { Fabricate(:user) }
category
payload {
payload do
{
raw: "hello world post contents.",
title: "queued post title",
tags: ['cool', 'neat'],
tags: %w[cool neat],
extra: "some extra data",
archetype: 'regular'
archetype: "regular",
}
}
end
end
Fabricator(:reviewable_queued_post) do
reviewable_by_moderator true
type 'ReviewableQueuedPost'
type "ReviewableQueuedPost"
created_by { Fabricate(:user) }
topic
payload {
payload do
{
raw: "hello world post contents.",
reply_to_post_number: 1,
via_email: true,
raw_email: 'store_me',
raw_email: "store_me",
auto_track: true,
custom_fields: { hello: 'world' },
cooking_options: { cat: 'hat' },
custom_fields: {
hello: "world",
},
cooking_options: {
cat: "hat",
},
cook_method: Post.cook_methods[:raw_html],
image_sizes: { "http://foo.bar/image.png" => { "width" => 0, "height" => 222 } }
image_sizes: {
"http://foo.bar/image.png" => {
"width" => 0,
"height" => 222,
},
},
}
}
end
end
Fabricator(:reviewable_flagged_post) do
reviewable_by_moderator true
type 'ReviewableFlaggedPost'
type "ReviewableFlaggedPost"
created_by { Fabricate(:user) }
topic
target_type 'Post'
target_type "Post"
target { Fabricate(:post) }
reviewable_scores { |p| [
Fabricate.build(:reviewable_score, reviewable_id: p[:id]),
]}
reviewable_scores { |p| [Fabricate.build(:reviewable_score, reviewable_id: p[:id])] }
end
Fabricator(:reviewable_user) do
reviewable_by_moderator true
type 'ReviewableUser'
type "ReviewableUser"
created_by { Fabricate(:user) }
target_type 'User'
target_type "User"
target { Fabricate(:user) }
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
Fabricator(:screened_url) do
url { sequence(:url) { |n| "spammers#{n}.org/buy/stuff" } }
domain { sequence(:domain) { |n| "spammers#{n}.org" } }
url { sequence(:url) { |n| "spammers#{n}.org/buy/stuff" } }
domain { sequence(:domain) { |n| "spammers#{n}.org" } }
action_type ScreenedEmail.actions[:do_nothing]
end

View File

@ -1,13 +1,9 @@
# frozen_string_literal: true
Fabricator(:sidebar_section_link) do
user
end
Fabricator(:sidebar_section_link) { user }
Fabricator(:category_sidebar_section_link, from: :sidebar_section_link) do
linkable(fabricator: :category)
end
Fabricator(:tag_sidebar_section_link, from: :sidebar_section_link) do
linkable(fabricator: :tag)
end
Fabricator(:tag_sidebar_section_link, from: :sidebar_section_link) { linkable(fabricator: :tag) }

View File

@ -5,5 +5,7 @@ Fabricator(:single_sign_on_record) do
external_id { sequence(:external_id) { |i| "ext_#{i}" } }
external_username { sequence(:username) { |i| "bruce#{i}" } }
external_email { sequence(:email) { |i| "bruce#{i}@wayne.com" } }
last_payload { sequence(:last_payload) { |i| "nonce=#{i}1870a940bbcbb46f06880ed338d58a07&name=" } }
last_payload do
sequence(:last_payload) { |i| "nonce=#{i}1870a940bbcbb46f06880ed338d58a07&name=" }
end
end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
Fabricator(:tag) do
name { sequence(:name) { |i| "tag#{i + 1}" } }
end
Fabricator(:tag) { name { sequence(:name) { |i| "tag#{i + 1}" } } }

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
Fabricator(:tag_group) do
name { sequence(:name) { |i| "tag_group_#{i}" } }
end
Fabricator(:tag_group) { name { sequence(:name) { |i| "tag_group_#{i}" } } }

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
Fabricator(:topic_allowed_user) do
user
end
Fabricator(:topic_allowed_user) { user }

View File

@ -8,25 +8,21 @@ Fabricator(:topic) do
end
end
Fabricator(:deleted_topic, from: :topic) do
deleted_at { 1.minute.ago }
end
Fabricator(:deleted_topic, from: :topic) { deleted_at { 1.minute.ago } }
Fabricator(:closed_topic, from: :topic) do
closed true
end
Fabricator(:closed_topic, from: :topic) { closed true }
Fabricator(:banner_topic, from: :topic) do
archetype Archetype.banner
end
Fabricator(:banner_topic, from: :topic) { archetype Archetype.banner }
Fabricator(:private_message_topic, from: :topic) do
transient :recipient
category_id { nil }
title { sequence(:title) { |i| "This is a private message #{i}" } }
archetype "private_message"
topic_allowed_users { |t| [
Fabricate.build(:topic_allowed_user, user: t[:user]),
Fabricate.build(:topic_allowed_user, user: t[:recipient] || Fabricate(:user))
]}
topic_allowed_users do |t|
[
Fabricate.build(:topic_allowed_user, user: t[:user]),
Fabricate.build(:topic_allowed_user, user: t[:recipient] || Fabricate(:user)),
]
end
end

View File

@ -12,9 +12,7 @@ Fabricator(:upload) do
url do |attrs|
sequence(:url) do |n|
Discourse.store.get_path_for(
"original", n + 1, attrs[:sha1], ".#{attrs[:extension]}"
)
Discourse.store.get_path_for("original", n + 1, attrs[:sha1], ".#{attrs[:extension]}")
end
end
@ -35,15 +33,16 @@ Fabricator(:image_upload, from: :upload) do
transient color: "white"
after_create do |upload, transients|
file = Tempfile.new(['fabricated', '.png'])
file = Tempfile.new(%w[fabricated .png])
`convert -size #{upload.width}x#{upload.height} xc:#{transients[:color]} "#{file.path}"`
upload.url = Discourse.store.store_upload(file, upload)
upload.sha1 = Upload.generate_digest(file.path)
WebMock
.stub_request(:get, "http://#{Discourse.current_hostname}#{upload.url}")
.to_return(status: 200, body: File.new(file.path))
WebMock.stub_request(:get, "http://#{Discourse.current_hostname}#{upload.url}").to_return(
status: 200,
body: File.new(file.path),
)
end
end
@ -72,13 +71,9 @@ end
Fabricator(:upload_s3, from: :upload) do
url do |attrs|
sequence(:url) do |n|
path = +Discourse.store.get_path_for(
"original", n + 1, attrs[:sha1], ".#{attrs[:extension]}"
)
path = +Discourse.store.get_path_for("original", n + 1, attrs[:sha1], ".#{attrs[:extension]}")
if Rails.configuration.multisite
path.prepend(File.join(Discourse.store.upload_path, "/"))
end
path.prepend(File.join(Discourse.store.upload_path, "/")) if Rails.configuration.multisite
File.join(Discourse.store.absolute_base_url, path)
end
@ -87,15 +82,13 @@ end
Fabricator(:s3_image_upload, from: :upload_s3) do
after_create do |upload|
file = Tempfile.new(['fabricated', '.png'])
file = Tempfile.new(%w[fabricated .png])
`convert -size #{upload.width}x#{upload.height} xc:white "#{file.path}"`
upload.url = Discourse.store.store_upload(file, upload)
upload.sha1 = Upload.generate_digest(file.path)
WebMock
.stub_request(:get, upload.url)
.to_return(status: 200, body: File.new(file.path))
WebMock.stub_request(:get, upload.url).to_return(status: 200, body: File.new(file.path))
end
end

View File

@ -3,15 +3,15 @@
Fabricator(:user_api_key) do
user
client_id { SecureRandom.hex }
application_name 'some app'
application_name "some app"
end
Fabricator(:user_api_key_scope)
Fabricator(:readonly_user_api_key, from: :user_api_key) do
scopes { [Fabricate.build(:user_api_key_scope, name: 'read')] }
scopes { [Fabricate.build(:user_api_key_scope, name: "read")] }
end
Fabricator(:bookmarks_calendar_user_api_key, from: :user_api_key) do
scopes { [Fabricate.build(:user_api_key_scope, name: 'bookmarks_calendar')] }
scopes { [Fabricate.build(:user_api_key_scope, name: "bookmarks_calendar")] }
end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
Fabricator(:user_avatar) do
user
end
Fabricator(:user_avatar) { user }

View File

@ -1,13 +1,12 @@
# frozen_string_literal: true
Fabricator(:user_stat) do
end
Fabricator(:user_stat) {}
Fabricator(:user, class_name: :user) do
name 'Bruce Wayne'
name "Bruce Wayne"
username { sequence(:username) { |i| "bruce#{i}" } }
email { sequence(:email) { |i| "bruce#{i}@wayne.com" } }
password 'myawesomepassword'
password "myawesomepassword"
trust_level TrustLevel[1]
ip_address { sequence(:ip_address) { |i| "99.232.23.#{i % 254}" } }
active true
@ -18,31 +17,31 @@ Fabricator(:user_with_secondary_email, from: :user) do
end
Fabricator(:coding_horror, from: :user) do
name 'Coding Horror'
username 'CodingHorror'
email 'jeff@somewhere.com'
password 'mymoreawesomepassword'
name "Coding Horror"
username "CodingHorror"
email "jeff@somewhere.com"
password "mymoreawesomepassword"
end
Fabricator(:evil_trout, from: :user) do
name 'Evil Trout'
username 'eviltrout'
email 'eviltrout@somewhere.com'
password 'imafish123'
name "Evil Trout"
username "eviltrout"
email "eviltrout@somewhere.com"
password "imafish123"
end
Fabricator(:walter_white, from: :user) do
name 'Walter White'
username 'heisenberg'
email 'wwhite@bluemeth.com'
password 'letscook123'
name "Walter White"
username "heisenberg"
email "wwhite@bluemeth.com"
password "letscook123"
end
Fabricator(:inactive_user, from: :user) do
name 'Inactive User'
username 'inactive_user'
email 'inactive@idontexist.com'
password 'qwerqwer123'
name "Inactive User"
username "inactive_user"
email "inactive@idontexist.com"
password "qwerqwer123"
active false
end
@ -59,7 +58,7 @@ Fabricator(:moderator, from: :user) do
end
Fabricator(:admin, from: :user) do
name 'Anne Admin'
name "Anne Admin"
username { sequence(:username) { |i| "anne#{i}" } }
email { sequence(:email) { |i| "anne#{i}@discourse.org" } }
admin true
@ -71,17 +70,17 @@ Fabricator(:admin, from: :user) do
end
Fabricator(:newuser, from: :user) do
name 'Newbie Newperson'
username 'newbie'
email 'newbie@new.com'
name "Newbie Newperson"
username "newbie"
email "newbie@new.com"
trust_level TrustLevel[0]
end
Fabricator(:active_user, from: :user) do
name 'Luke Skywalker'
name "Luke Skywalker"
username { sequence(:username) { |i| "luke#{i}" } }
email { sequence(:email) { |i| "luke#{i}@skywalker.com" } }
password 'myawesomepassword'
password "myawesomepassword"
trust_level TrustLevel[1]
after_create do |user|
@ -91,29 +90,25 @@ Fabricator(:active_user, from: :user) do
end
Fabricator(:leader, from: :user) do
name 'Veteran McVeteranish'
name "Veteran McVeteranish"
username { sequence(:username) { |i| "leader#{i}" } }
email { sequence(:email) { |i| "leader#{i}@leaderfun.com" } }
trust_level TrustLevel[3]
end
Fabricator(:trust_level_0, from: :user) do
trust_level TrustLevel[0]
end
Fabricator(:trust_level_0, from: :user) { trust_level TrustLevel[0] }
Fabricator(:trust_level_1, from: :user) do
trust_level TrustLevel[1]
end
Fabricator(:trust_level_1, from: :user) { trust_level TrustLevel[1] }
Fabricator(:trust_level_4, from: :user) do
name 'Leader McElderson'
name "Leader McElderson"
username { sequence(:username) { |i| "tl4#{i}" } }
email { sequence(:email) { |i| "tl4#{i}@elderfun.com" } }
trust_level TrustLevel[4]
end
Fabricator(:anonymous, from: :user) do
name ''
name ""
username { sequence(:username) { |i| "anonymous#{i}" } }
email { sequence(:email) { |i| "anonymous#{i}@anonymous.com" } }
trust_level TrustLevel[1]
@ -127,13 +122,9 @@ Fabricator(:anonymous, from: :user) do
end
end
Fabricator(:staged, from: :user) do
staged true
end
Fabricator(:staged, from: :user) { staged true }
Fabricator(:unicode_user, from: :user) do
username { sequence(:username) { |i| "Löwe#{i}" } }
end
Fabricator(:unicode_user, from: :user) { username { sequence(:username) { |i| "Löwe#{i}" } } }
Fabricator(:bot, from: :user) do
id do

View File

@ -3,7 +3,7 @@
Fabricator(:user_field) do
name { sequence(:name) { |i| "field_#{i}" } }
description "user field description"
field_type 'text'
field_type "text"
editable true
required true
end

View File

@ -1,4 +1,3 @@
# frozen_string_literal: true
Fabricator(:user_history) do
end
Fabricator(:user_history) {}

View File

@ -1,4 +1,3 @@
# frozen_string_literal: true
Fabricator(:user_option) do
end
Fabricator(:user_option) {}

View File

@ -5,6 +5,4 @@ Fabricator(:user_profile) do
user
end
Fabricator(:user_profile_long, from: :user_profile) do
bio_raw ("trout" * 1000)
end
Fabricator(:user_profile_long, from: :user_profile) { bio_raw ("trout" * 1000) }

View File

@ -2,7 +2,7 @@
Fabricator(:user_second_factor_totp, from: :user_second_factor) do
user
data 'rcyryaqage3jexfj'
data "rcyryaqage3jexfj"
enabled true
method UserSecondFactor.methods[:totp]
end

View File

@ -5,8 +5,12 @@ Fabricator(:user_security_key) do
# Note: these values are valid and decode to a credential ID and COSE public key
# HOWEVER they are largely useless unless you have the device that created
# them. It is nice to have an approximation though.
credential_id { 'mJAJ4CznTO0SuLkJbYwpgK75ao4KMNIPlU5KWM92nq39kRbXzI9mSv6GxTcsMYoiPgaouNw7b7zBiS4vsQaO6A==' }
public_key { 'pQECAyYgASFYIMNgw4GCpwBUlR2SznJ1yY7B9yFvsuxhfo+C9kcA4IitIlggRdofrCezymy2B/YarX+gfB6gZKg648/cHIMjf6wWmmU=' }
credential_id do
"mJAJ4CznTO0SuLkJbYwpgK75ao4KMNIPlU5KWM92nq39kRbXzI9mSv6GxTcsMYoiPgaouNw7b7zBiS4vsQaO6A=="
end
public_key do
"pQECAyYgASFYIMNgw4GCpwBUlR2SznJ1yY7B9yFvsuxhfo+C9kcA4IitIlggRdofrCezymy2B/YarX+gfB6gZKg648/cHIMjf6wWmmU="
end
enabled true
factor_type { UserSecurityKey.factor_types[:second_factor] }
name { sequence(:name) { |i| "Security Key #{i + 1}" } }

View File

@ -1,62 +1,48 @@
# frozen_string_literal: true
Fabricator(:web_hook) do
payload_url 'https://meta.discourse.org/webhook_listener'
content_type WebHook.content_types['application/json']
payload_url "https://meta.discourse.org/webhook_listener"
content_type WebHook.content_types["application/json"]
wildcard_web_hook false
secret 'my_lovely_secret_for_web_hook'
secret "my_lovely_secret_for_web_hook"
verify_certificate true
active true
transient post_hook: WebHookEventType.find_by(name: 'post')
transient post_hook: WebHookEventType.find_by(name: "post")
after_build do |web_hook, transients|
web_hook.web_hook_event_types << transients[:post_hook]
end
after_build { |web_hook, transients| web_hook.web_hook_event_types << transients[:post_hook] }
end
Fabricator(:inactive_web_hook, from: :web_hook) do
active false
end
Fabricator(:inactive_web_hook, from: :web_hook) { active false }
Fabricator(:wildcard_web_hook, from: :web_hook) do
wildcard_web_hook true
end
Fabricator(:wildcard_web_hook, from: :web_hook) { wildcard_web_hook true }
Fabricator(:topic_web_hook, from: :web_hook) do
transient topic_hook: WebHookEventType.find_by(name: 'topic')
transient topic_hook: WebHookEventType.find_by(name: "topic")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:topic_hook]]
end
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:topic_hook]] }
end
Fabricator(:post_web_hook, from: :web_hook) do
transient topic_hook: WebHookEventType.find_by(name: 'post')
transient topic_hook: WebHookEventType.find_by(name: "post")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:post_hook]]
end
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:post_hook]] }
end
Fabricator(:user_web_hook, from: :web_hook) do
transient user_hook: WebHookEventType.find_by(name: 'user')
transient user_hook: WebHookEventType.find_by(name: "user")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:user_hook]]
end
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:user_hook]] }
end
Fabricator(:group_web_hook, from: :web_hook) do
transient group_hook: WebHookEventType.find_by(name: 'group')
transient group_hook: WebHookEventType.find_by(name: "group")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:group_hook]]
end
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:group_hook]] }
end
Fabricator(:category_web_hook, from: :web_hook) do
transient category_hook: WebHookEventType.find_by(name: 'category')
transient category_hook: WebHookEventType.find_by(name: "category")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:category_hook]]
@ -64,15 +50,13 @@ Fabricator(:category_web_hook, from: :web_hook) do
end
Fabricator(:tag_web_hook, from: :web_hook) do
transient tag_hook: WebHookEventType.find_by(name: 'tag')
transient tag_hook: WebHookEventType.find_by(name: "tag")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:tag_hook]]
end
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:tag_hook]] }
end
Fabricator(:reviewable_web_hook, from: :web_hook) do
transient reviewable_hook: WebHookEventType.find_by(name: 'reviewable')
transient reviewable_hook: WebHookEventType.find_by(name: "reviewable")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:reviewable_hook]]
@ -80,7 +64,7 @@ Fabricator(:reviewable_web_hook, from: :web_hook) do
end
Fabricator(:notification_web_hook, from: :web_hook) do
transient notification_hook: WebHookEventType.find_by(name: 'notification')
transient notification_hook: WebHookEventType.find_by(name: "notification")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:notification_hook]]
@ -88,7 +72,7 @@ Fabricator(:notification_web_hook, from: :web_hook) do
end
Fabricator(:user_badge_web_hook, from: :web_hook) do
transient user_badge_hook: WebHookEventType.find_by(name: 'user_badge')
transient user_badge_hook: WebHookEventType.find_by(name: "user_badge")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:user_badge_hook]]
@ -96,7 +80,7 @@ Fabricator(:user_badge_web_hook, from: :web_hook) do
end
Fabricator(:group_user_web_hook, from: :web_hook) do
transient group_user_hook: WebHookEventType.find_by(name: 'group_user')
transient group_user_hook: WebHookEventType.find_by(name: "group_user")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:group_user_hook]]
@ -104,15 +88,13 @@ Fabricator(:group_user_web_hook, from: :web_hook) do
end
Fabricator(:like_web_hook, from: :web_hook) do
transient like_hook: WebHookEventType.find_by(name: 'like')
transient like_hook: WebHookEventType.find_by(name: "like")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:like_hook]]
end
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:like_hook]] }
end
Fabricator(:user_promoted_web_hook, from: :web_hook) do
transient user_promoted_hook: WebHookEventType.find_by(name: 'user_promoted')
transient user_promoted_hook: WebHookEventType.find_by(name: "user_promoted")
after_build do |web_hook, transients|
web_hook.web_hook_event_types = [transients[:user_promoted_hook]]

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class DropEmailLogs < ActiveRecord::Migration[5.2]
DROPPED_TABLES ||= %i{email_logs}
DROPPED_TABLES ||= %i[email_logs]
def change
drop_table :email_logs

View File

@ -1,9 +1,7 @@
# frozen_string_literal: true
class DropPostColumns < ActiveRecord::Migration[5.2]
DROPPED_COLUMNS ||= {
posts: %i{via_email raw_email}
}
DROPPED_COLUMNS ||= { posts: %i[via_email raw_email] }
def up
remove_column :posts, :via_email

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class DropEmailLogsTable < ActiveRecord::Migration[5.2]
DROPPED_TABLES ||= %i{email_logs}
DROPPED_TABLES ||= %i[email_logs]
def up
drop_table :email_logs

View File

@ -6,8 +6,8 @@
# authors: xrav3nz
extend_content_security_policy(
script_src: ['https://from-plugin.com', '/local/path'],
object_src: ['https://test-stripping.com'],
frame_ancestors: ['https://frame-ancestors-plugin.ext'],
manifest_src: ['https://manifest-src.com']
script_src: %w[https://from-plugin.com /local/path],
object_src: ["https://test-stripping.com"],
frame_ancestors: ["https://frame-ancestors-plugin.ext"],
manifest_src: ["https://manifest-src.com"],
)

View File

@ -5,8 +5,7 @@
# version: 0.1
# authors: Frank Zappa
auth_provider title: 'with Ubuntu',
authenticator: Auth::FacebookAuthenticator.new
auth_provider title: "with Ubuntu", authenticator: Auth::FacebookAuthenticator.new
register_javascript <<JS
console.log("Hello world")

View File

@ -13,7 +13,8 @@ RSpec.describe ApplicationHelper do
it "sends crawler content to old mobiles" do
controller.stubs(:use_crawler_layout?).returns(false)
helper.request.user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25"
helper.request.user_agent =
"Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25"
expect(helper.include_crawler_content?).to eq(true)
end
@ -21,7 +22,8 @@ RSpec.describe ApplicationHelper do
it "does not send crawler content to new mobiles" do
controller.stubs(:use_crawler_layout?).returns(false)
helper.request.user_agent = "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Mobile Safari/537.36 (compatible"
helper.request.user_agent =
"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Mobile Safari/537.36 (compatible"
expect(helper.include_crawler_content?).to eq(false)
end
@ -29,94 +31,100 @@ RSpec.describe ApplicationHelper do
it "provides brotli links to brotli cdn" do
set_cdn_url "https://awesome.com"
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'br'
link = helper.preload_script('discourse')
helper.request.env["HTTP_ACCEPT_ENCODING"] = "br"
link = helper.preload_script("discourse")
expect(link).to eq(script_tag("https://awesome.com/brotli_asset/discourse.js"))
end
context "with s3 CDN" do
before do
global_setting :s3_bucket, 'test_bucket'
global_setting :s3_region, 'ap-australia'
global_setting :s3_access_key_id, '123'
global_setting :s3_secret_access_key, '123'
global_setting :s3_cdn_url, 'https://s3cdn.com'
global_setting :s3_bucket, "test_bucket"
global_setting :s3_region, "ap-australia"
global_setting :s3_access_key_id, "123"
global_setting :s3_secret_access_key, "123"
global_setting :s3_cdn_url, "https://s3cdn.com"
end
it "deals correctly with subfolder" do
set_subfolder "/community"
expect(helper.preload_script("discourse")).to include("https://s3cdn.com/assets/discourse.js")
expect(helper.preload_script("discourse")).to include(
"https://s3cdn.com/assets/discourse.js",
)
end
it "replaces cdn URLs with s3 cdn subfolder paths" do
global_setting :s3_cdn_url, 'https://s3cdn.com/s3_subpath'
global_setting :s3_cdn_url, "https://s3cdn.com/s3_subpath"
set_cdn_url "https://awesome.com"
set_subfolder "/community"
expect(helper.preload_script("discourse")).to include("https://s3cdn.com/s3_subpath/assets/discourse.js")
expect(helper.preload_script("discourse")).to include(
"https://s3cdn.com/s3_subpath/assets/discourse.js",
)
end
it "returns magic brotli mangling for brotli requests" do
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'br'
link = helper.preload_script('discourse')
helper.request.env["HTTP_ACCEPT_ENCODING"] = "br"
link = helper.preload_script("discourse")
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.br.js"))
end
it "gives s3 cdn if asset host is not set" do
link = helper.preload_script('discourse')
link = helper.preload_script("discourse")
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.js"))
end
it "can fall back to gzip compression" do
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'gzip'
link = helper.preload_script('discourse')
helper.request.env["HTTP_ACCEPT_ENCODING"] = "gzip"
link = helper.preload_script("discourse")
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.gz.js"))
end
it "gives s3 cdn even if asset host is set" do
set_cdn_url "https://awesome.com"
link = helper.preload_script('discourse')
link = helper.preload_script("discourse")
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.js"))
end
it "gives s3 cdn but without brotli/gzip extensions for theme tests assets" do
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'gzip, br'
link = helper.preload_script('discourse/tests/theme_qunit_ember_jquery')
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse/tests/theme_qunit_ember_jquery.js"))
helper.request.env["HTTP_ACCEPT_ENCODING"] = "gzip, br"
link = helper.preload_script("discourse/tests/theme_qunit_ember_jquery")
expect(link).to eq(
script_tag("https://s3cdn.com/assets/discourse/tests/theme_qunit_ember_jquery.js"),
)
end
it "uses separate asset CDN if configured" do
global_setting :s3_asset_cdn_url, "https://s3-asset-cdn.example.com"
expect(helper.preload_script("discourse")).to include("https://s3-asset-cdn.example.com/assets/discourse.js")
expect(helper.preload_script("discourse")).to include(
"https://s3-asset-cdn.example.com/assets/discourse.js",
)
end
end
end
describe "add_resource_preload_list" do
it "adds resources to the preload list when it's available" do
@links_to_preload = []
add_resource_preload_list('/assets/discourse.js', 'script')
add_resource_preload_list('/assets/discourse.css', 'style')
add_resource_preload_list("/assets/discourse.js", "script")
add_resource_preload_list("/assets/discourse.css", "style")
expect(@links_to_preload.size).to eq(2)
end
it "doesn't add resources to the preload list when it's not available" do
@links_to_preload = nil
add_resource_preload_list('/assets/discourse.js', 'script')
add_resource_preload_list('/assets/discourse.css', 'style')
add_resource_preload_list("/assets/discourse.js", "script")
add_resource_preload_list("/assets/discourse.css", "style")
expect(@links_to_preload).to eq(nil)
end
it "adds resources to the preload list when preload_script is called" do
@links_to_preload = []
helper.preload_script('discourse')
helper.preload_script("discourse")
expect(@links_to_preload.size).to eq(1)
end
@ -131,7 +139,7 @@ RSpec.describe ApplicationHelper do
it "adds resources as the correct type" do
@links_to_preload = []
helper.discourse_stylesheet_link_tag(:desktop)
helper.preload_script('discourse')
helper.preload_script("discourse")
expect(@links_to_preload[0]).to match(/as="style"/)
expect(@links_to_preload[1]).to match(/as="script"/)
@ -152,14 +160,14 @@ RSpec.describe ApplicationHelper do
context "when on homepage" do
it "will return sitelinks search tag" do
helper.stubs(:current_page?).returns(false)
helper.stubs(:current_page?).with('/').returns(true)
helper.stubs(:current_page?).with("/").returns(true)
expect(helper.render_sitelinks_search_tag).to include('"@type":"SearchAction"')
end
end
context "when not on homepage" do
it "will not return sitelinks search tag" do
helper.stubs(:current_page?).returns(true)
helper.stubs(:current_page?).with('/').returns(false)
helper.stubs(:current_page?).with("/").returns(false)
helper.stubs(:current_page?).with(Discourse.base_path).returns(false)
expect(helper.render_sitelinks_search_tag).to be_nil
end
@ -168,7 +176,7 @@ RSpec.describe ApplicationHelper do
context "for subfolder install" do
context "when on homepage" do
it "will return sitelinks search tag" do
Discourse.stubs(:base_path).returns('/subfolder-base-path/')
Discourse.stubs(:base_path).returns("/subfolder-base-path/")
helper.stubs(:current_page?).returns(false)
helper.stubs(:current_page?).with(Discourse.base_path).returns(true)
expect(helper.render_sitelinks_search_tag).to include('"@type":"SearchAction"')
@ -176,9 +184,9 @@ RSpec.describe ApplicationHelper do
end
context "when not on homepage" do
it "will not return sitelinks search tag" do
Discourse.stubs(:base_path).returns('/subfolder-base-path/')
Discourse.stubs(:base_path).returns("/subfolder-base-path/")
helper.stubs(:current_page?).returns(true)
helper.stubs(:current_page?).with('/').returns(false)
helper.stubs(:current_page?).with("/").returns(false)
helper.stubs(:current_page?).with(Discourse.base_path).returns(false)
expect(helper.render_sitelinks_search_tag).to be_nil
end
@ -189,18 +197,17 @@ RSpec.describe ApplicationHelper do
describe "application_logo_url" do
context "when a dark color scheme is active" do
before do
dark_theme = Theme.create(
name: "Dark",
user_id: -1,
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id
)
dark_theme =
Theme.create(
name: "Dark",
user_id: -1,
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
)
helper.request.env[:resolved_theme_id] = dark_theme.id
end
context "when on desktop" do
before do
session[:mobile_view] = '0'
end
before { session[:mobile_view] = "0" }
context "when logo_dark is not set" do
it "will return site_logo_url instead" do
@ -209,9 +216,7 @@ RSpec.describe ApplicationHelper do
end
context "when logo_dark is set" do
before do
SiteSetting.logo_dark = Fabricate(:upload, url: '/images/logo-dark.png')
end
before { SiteSetting.logo_dark = Fabricate(:upload, url: "/images/logo-dark.png") }
it "will return site_logo_dark_url" do
expect(helper.application_logo_url).to eq(SiteSetting.site_logo_dark_url)
@ -220,9 +225,7 @@ RSpec.describe ApplicationHelper do
end
context "when on mobile" do
before do
session[:mobile_view] = '1'
end
before { session[:mobile_view] = "1" }
context "when mobile_logo_dark is not set" do
it "will return site_mobile_logo_url instead" do
@ -232,7 +235,7 @@ RSpec.describe ApplicationHelper do
context "when mobile_logo_dark is set" do
before do
SiteSetting.mobile_logo_dark = Fabricate(:upload, url: '/images/mobile-logo-dark.png')
SiteSetting.mobile_logo_dark = Fabricate(:upload, url: "/images/mobile-logo-dark.png")
end
it "will return site_mobile_logo_dark_url" do
@ -254,11 +257,12 @@ RSpec.describe ApplicationHelper do
context "when dark theme is present" do
before do
_dark_theme = Theme.create(
name: "Dark",
user_id: -1,
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id
)
_dark_theme =
Theme.create(
name: "Dark",
user_id: -1,
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
)
end
context "when dark logo is not present" do
@ -268,9 +272,7 @@ RSpec.describe ApplicationHelper do
end
context "when dark logo is present" do
before do
SiteSetting.logo_dark = Fabricate(:upload, url: '/images/logo-dark.png')
end
before { SiteSetting.logo_dark = Fabricate(:upload, url: "/images/logo-dark.png") }
it "should return correct url" do
expect(helper.application_logo_dark_url).to eq(SiteSetting.site_logo_dark_url)
@ -280,13 +282,14 @@ RSpec.describe ApplicationHelper do
context "when dark theme is present and selected" do
before do
dark_theme = Theme.create(
name: "Dark",
user_id: -1,
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id
)
dark_theme =
Theme.create(
name: "Dark",
user_id: -1,
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
)
helper.request.env[:resolved_theme_id] = dark_theme.id
SiteSetting.logo_dark = Fabricate(:upload, url: '/images/logo-dark.png')
SiteSetting.logo_dark = Fabricate(:upload, url: "/images/logo-dark.png")
end
it "should return nothing" do
@ -298,119 +301,168 @@ RSpec.describe ApplicationHelper do
describe "#mobile_view?" do
context "when enable_mobile_theme is true" do
before do
SiteSetting.enable_mobile_theme = true
end
before { SiteSetting.enable_mobile_theme = true }
it "is true if mobile_view is '1' in the session" do
session[:mobile_view] = '1'
session[:mobile_view] = "1"
expect(helper.mobile_view?).to eq(true)
end
it "is false if mobile_view is '0' in the session" do
session[:mobile_view] = '0'
session[:mobile_view] = "0"
expect(helper.mobile_view?).to eq(false)
end
context "when mobile_view session is cleared" do
before do
params[:mobile_view] = 'auto'
end
before { params[:mobile_view] = "auto" }
it "is false if user agent is not mobile" do
session[:mobile_view] = '1'
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36')
session[:mobile_view] = "1"
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36",
)
expect(helper.mobile_view?).to be_falsey
end
it "is true for iPhone" do
session[:mobile_view] = '0'
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1')
session[:mobile_view] = "0"
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1",
)
expect(helper.mobile_view?).to eq(true)
end
end
context "when mobile_view is not set" do
it "is false if user agent is not mobile" do
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36')
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36",
)
expect(helper.mobile_view?).to be_falsey
end
it "is true for iPhone" do
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1')
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1",
)
expect(helper.mobile_view?).to eq(true)
end
it "is true for Android Samsung Galaxy" do
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925F Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36')
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925F Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36",
)
expect(helper.mobile_view?).to eq(true)
end
it "is true for Android Google Nexus 5X" do
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Linux; Android 6.0; Nexus 5X Build/MDB08I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.43 Mobile Safari/537.36')
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5X Build/MDB08I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.43 Mobile Safari/537.36",
)
expect(helper.mobile_view?).to eq(true)
end
it "is false for iPad" do
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B14 3 Safari/601.1")
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B14 3 Safari/601.1",
)
expect(helper.mobile_view?).to eq(false)
end
it "is false for Nexus 10 tablet" do
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 5.1.1; Nexus 10 Build/LMY49G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Safari/537.36")
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 10 Build/LMY49G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Safari/537.36",
)
expect(helper.mobile_view?).to be_falsey
end
it "is false for Nexus 7 tablet" do
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MMB29Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Safari/537.36")
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MMB29Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Safari/537.36",
)
expect(helper.mobile_view?).to be_falsey
end
end
end
context "when enable_mobile_theme is false" do
before do
SiteSetting.enable_mobile_theme = false
end
before { SiteSetting.enable_mobile_theme = false }
it "is false if mobile_view is '1' in the session" do
session[:mobile_view] = '1'
session[:mobile_view] = "1"
expect(helper.mobile_view?).to eq(false)
end
it "is false if mobile_view is '0' in the session" do
session[:mobile_view] = '0'
session[:mobile_view] = "0"
expect(helper.mobile_view?).to eq(false)
end
context "when mobile_view is not set" do
it "is false if user agent is not mobile" do
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36')
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36",
)
expect(helper.mobile_view?).to eq(false)
end
it "is false for iPhone" do
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; U; ru; CPU iPhone OS 4_2_1 like Mac OS X; ru) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5')
controller
.request
.stubs(:user_agent)
.returns(
"Mozilla/5.0 (iPhone; U; ru; CPU iPhone OS 4_2_1 like Mac OS X; ru) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5",
)
expect(helper.mobile_view?).to eq(false)
end
end
end
end
describe '#html_classes' do
describe "#html_classes" do
fab!(:user) { Fabricate(:user) }
it "includes 'rtl' when the I18n.locale is rtl" do
I18n.stubs(:locale).returns(:he)
expect(helper.html_classes.split(" ")).to include('rtl')
expect(helper.html_classes.split(" ")).to include("rtl")
end
it 'returns an empty string when the I18n.locale is not rtl' do
it "returns an empty string when the I18n.locale is not rtl" do
I18n.stubs(:locale).returns(:zh_TW)
expect(helper.html_classes.split(" ")).not_to include('rtl')
expect(helper.html_classes.split(" ")).not_to include("rtl")
end
describe 'text size' do
describe "text size" do
context "with a user option" do
before do
user.user_option.text_size = "larger"
@ -418,63 +470,65 @@ RSpec.describe ApplicationHelper do
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
end
it 'ignores invalid text sizes' do
it "ignores invalid text sizes" do
helper.request.cookies["text_size"] = "invalid"
expect(helper.html_classes.split(" ")).to include('text-size-larger')
expect(helper.html_classes.split(" ")).to include("text-size-larger")
end
it 'ignores missing text size' do
it "ignores missing text size" do
helper.request.cookies["text_size"] = nil
expect(helper.html_classes.split(" ")).to include('text-size-larger')
expect(helper.html_classes.split(" ")).to include("text-size-larger")
end
it 'ignores cookies with lower sequence' do
it "ignores cookies with lower sequence" do
user.user_option.update!(text_size_seq: 2)
helper.request.cookies["text_size"] = "normal|1"
expect(helper.html_classes.split(" ")).to include('text-size-larger')
expect(helper.html_classes.split(" ")).to include("text-size-larger")
end
it 'prioritises the cookie specified text size' do
it "prioritises the cookie specified text size" do
user.user_option.update!(text_size_seq: 2)
helper.request.cookies["text_size"] = "largest|4"
expect(helper.html_classes.split(" ")).to include('text-size-largest')
expect(helper.html_classes.split(" ")).to include("text-size-largest")
end
it 'includes the user specified text size' do
it "includes the user specified text size" do
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
expect(helper.html_classes.split(" ")).to include('text-size-larger')
expect(helper.html_classes.split(" ")).to include("text-size-larger")
end
end
it 'falls back to the default text size for anon' do
expect(helper.html_classes.split(" ")).to include('text-size-normal')
it "falls back to the default text size for anon" do
expect(helper.html_classes.split(" ")).to include("text-size-normal")
SiteSetting.default_text_size = "largest"
expect(helper.html_classes.split(" ")).to include('text-size-largest')
expect(helper.html_classes.split(" ")).to include("text-size-largest")
end
end
it "includes 'anon' for anonymous users and excludes when logged in" do
expect(helper.html_classes.split(" ")).to include('anon')
expect(helper.html_classes.split(" ")).to include("anon")
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
expect(helper.html_classes.split(" ")).not_to include('anon')
expect(helper.html_classes.split(" ")).not_to include("anon")
end
end
describe 'gsub_emoji_to_unicode' do
describe "gsub_emoji_to_unicode" do
it "converts all emoji to unicode" do
expect(helper.gsub_emoji_to_unicode('Boat Talk: my :sailboat: boat: why is it so slow? :snail:')).to eq("Boat Talk: my ⛵ boat: why is it so slow? 🐌")
expect(
helper.gsub_emoji_to_unicode("Boat Talk: my :sailboat: boat: why is it so slow? :snail:"),
).to eq("Boat Talk: my ⛵ boat: why is it so slow? 🐌")
end
end
describe 'preloaded_json' do
it 'returns empty JSON if preloaded is empty' do
describe "preloaded_json" do
it "returns empty JSON if preloaded is empty" do
@preloaded = nil
expect(helper.preloaded_json).to eq('{}')
expect(helper.preloaded_json).to eq("{}")
end
it 'escapes and strips invalid unicode and strips in json body' do
it "escapes and strips invalid unicode and strips in json body" do
@preloaded = { test: %{["< \x80"]} }
expect(helper.preloaded_json).to eq(%{{"test":"[\\"\\u003c \uFFFD\\"]"}})
end
@ -482,9 +536,7 @@ RSpec.describe ApplicationHelper do
describe "client_side_setup_data" do
context "when Rails.env.development? is true" do
before do
Rails.env.stubs(:development?).returns(true)
end
before { Rails.env.stubs(:development?).returns(true) }
it "returns the correct service worker url" do
expect(helper.client_side_setup_data[:service_worker_url]).to eq("service-worker.js")
@ -499,9 +551,7 @@ RSpec.describe ApplicationHelper do
end
context "if the DEBUG_PRELOADED_APP_DATA env var is provided" do
before do
ENV['DEBUG_PRELOADED_APP_DATA'] = 'true'
end
before { ENV["DEBUG_PRELOADED_APP_DATA"] = "true" }
it "returns that key as true" do
expect(helper.client_side_setup_data[:debug_preloaded_app_data]).to eq(true)
@ -510,54 +560,42 @@ RSpec.describe ApplicationHelper do
end
end
describe 'crawlable_meta_data' do
it 'Supports ASCII URLs with odd chars' do
result = helper.crawlable_meta_data(
url: (+"http://localhost/ión").force_encoding("ASCII-8BIT").freeze
)
describe "crawlable_meta_data" do
it "Supports ASCII URLs with odd chars" do
result =
helper.crawlable_meta_data(
url: (+"http://localhost/ión").force_encoding("ASCII-8BIT").freeze,
)
expect(result).to include("ión")
end
context "with opengraph image" do
it 'returns the correct image' do
SiteSetting.opengraph_image = Fabricate(:upload,
url: '/images/og-image.png'
)
it "returns the correct image" do
SiteSetting.opengraph_image = Fabricate(:upload, url: "/images/og-image.png")
SiteSetting.twitter_summary_large_image = Fabricate(:upload,
url: '/images/twitter.png'
)
SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.png")
SiteSetting.large_icon = Fabricate(:upload,
url: '/images/large_icon.png'
)
SiteSetting.large_icon = Fabricate(:upload, url: "/images/large_icon.png")
SiteSetting.apple_touch_icon = Fabricate(:upload,
url: '/images/default-apple-touch-icon.png'
)
SiteSetting.apple_touch_icon =
Fabricate(:upload, url: "/images/default-apple-touch-icon.png")
SiteSetting.logo = Fabricate(:upload, url: '/images/d-logo-sketch.png')
SiteSetting.logo = Fabricate(:upload, url: "/images/d-logo-sketch.png")
expect(
helper.crawlable_meta_data(image: "some-image.png")
).to include("some-image.png")
expect(helper.crawlable_meta_data(image: "some-image.png")).to include("some-image.png")
expect(helper.crawlable_meta_data).to include(
SiteSetting.site_opengraph_image_url
)
expect(helper.crawlable_meta_data).to include(SiteSetting.site_opengraph_image_url)
SiteSetting.opengraph_image = nil
expect(helper.crawlable_meta_data).to include(
SiteSetting.site_twitter_summary_large_image_url
SiteSetting.site_twitter_summary_large_image_url,
)
SiteSetting.twitter_summary_large_image = nil
expect(helper.crawlable_meta_data).to include(
SiteSetting.site_large_icon_url
)
expect(helper.crawlable_meta_data).to include(SiteSetting.site_large_icon_url)
SiteSetting.large_icon = nil
SiteSetting.logo_small = nil
@ -566,67 +604,62 @@ RSpec.describe ApplicationHelper do
SiteSetting.logo = nil
expect(helper.crawlable_meta_data).to include(Upload.find(SiteIconManager::SKETCH_LOGO_ID).url)
expect(helper.crawlable_meta_data).to include(
Upload.find(SiteIconManager::SKETCH_LOGO_ID).url,
)
end
it "does not allow SVG images for twitter:image, falls back to site logo or nothing if site logo is SVG too" do
SiteSetting.logo = Fabricate(:upload, url: '/images/d-logo-sketch.png')
SiteSetting.opengraph_image = Fabricate(:upload,
url: '/images/og-image.png'
)
SiteSetting.logo = Fabricate(:upload, url: "/images/d-logo-sketch.png")
SiteSetting.opengraph_image = Fabricate(:upload, url: "/images/og-image.png")
expect(helper.crawlable_meta_data).to include(<<~HTML)
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_opengraph_image_url}\" />
HTML
SiteSetting.opengraph_image = Fabricate(:upload,
url: '/images/og-image.svg'
)
SiteSetting.opengraph_image = Fabricate(:upload, url: "/images/og-image.svg")
expect(helper.crawlable_meta_data).to include(<<~HTML)
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_logo_url}\" />
HTML
SiteSetting.twitter_summary_large_image = Fabricate(:upload,
url: '/images/twitter.png'
)
SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.png")
expect(helper.crawlable_meta_data).to include(<<~HTML)
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_twitter_summary_large_image_url}\" />
HTML
SiteSetting.twitter_summary_large_image = Fabricate(:upload,
url: '/images/twitter.svg'
)
SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.svg")
expect(helper.crawlable_meta_data).to include(<<~HTML)
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_logo_url}\" />
HTML
SiteSetting.logo = Fabricate(:upload, url: '/images/d-logo-sketch.svg')
SiteSetting.logo = Fabricate(:upload, url: "/images/d-logo-sketch.svg")
expect(helper.crawlable_meta_data).not_to include("twitter:image")
end
end
end
describe 'discourse_color_scheme_stylesheets' do
describe "discourse_color_scheme_stylesheets" do
fab!(:user) { Fabricate(:user) }
it 'returns a stylesheet link tag by default' do
it "returns a stylesheet link tag by default" do
cs_stylesheets = helper.discourse_color_scheme_stylesheets
expect(cs_stylesheets).to include("stylesheets/color_definitions")
end
it 'returns two color scheme link tags when dark mode is enabled' do
SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pluck_first(:id)
it "returns two color scheme link tags when dark mode is enabled" do
SiteSetting.default_dark_mode_color_scheme_id =
ColorScheme.where(name: "Dark").pluck_first(:id)
cs_stylesheets = helper.discourse_color_scheme_stylesheets
expect(cs_stylesheets).to include("(prefers-color-scheme: dark)")
expect(cs_stylesheets.scan("stylesheets/color_definitions").size).to eq(2)
end
it 'handles a missing dark color scheme gracefully' do
it "handles a missing dark color scheme gracefully" do
scheme = ColorScheme.create!(name: "pyramid")
SiteSetting.default_dark_mode_color_scheme_id = scheme.id
scheme.destroy!
@ -638,7 +671,7 @@ RSpec.describe ApplicationHelper do
context "with custom light scheme" do
before do
@new_cs = Fabricate(:color_scheme, name: 'Flamboyant')
@new_cs = Fabricate(:color_scheme, name: "Flamboyant")
user.user_option.color_scheme_id = @new_cs.id
user.user_option.save!
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
@ -673,9 +706,10 @@ RSpec.describe ApplicationHelper do
user.user_option.dark_scheme_id = -1
user.user_option.save!
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
@new_cs = Fabricate(:color_scheme, name: 'Custom Color Scheme')
@new_cs = Fabricate(:color_scheme, name: "Custom Color Scheme")
SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pluck_first(:id)
SiteSetting.default_dark_mode_color_scheme_id =
ColorScheme.where(name: "Dark").pluck_first(:id)
end
it "returns no dark scheme stylesheet when user has disabled that option" do
@ -711,23 +745,24 @@ RSpec.describe ApplicationHelper do
end
describe "dark_color_scheme?" do
it 'returns false for the base color scheme' do
it "returns false for the base color scheme" do
expect(helper.dark_color_scheme?).to eq(false)
end
it 'works correctly for a dark scheme' do
dark_theme = Theme.create(
name: "Dark",
user_id: -1,
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id
)
it "works correctly for a dark scheme" do
dark_theme =
Theme.create(
name: "Dark",
user_id: -1,
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
)
helper.request.env[:resolved_theme_id] = dark_theme.id
expect(helper.dark_color_scheme?).to eq(true)
end
end
describe 'html_lang' do
describe "html_lang" do
fab!(:user) { Fabricate(:user) }
before do
@ -735,12 +770,12 @@ RSpec.describe ApplicationHelper do
SiteSetting.default_locale = :fr
end
it 'returns default locale if no request' do
it "returns default locale if no request" do
helper.request = nil
expect(helper.html_lang).to eq(SiteSetting.default_locale)
end
it 'returns current user locale if request' do
it "returns current user locale if request" do
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
expect(helper.html_lang).to eq(I18n.locale.to_s)
end

View File

@ -2,12 +2,8 @@
module RedisSnapshotHelper
def use_redis_snapshotting
before(:each) do
RedisSnapshot.begin_faux_transaction
end
before(:each) { RedisSnapshot.begin_faux_transaction }
after(:each) do
RedisSnapshot.end_faux_transaction
end
after(:each) { RedisSnapshot.end_faux_transaction }
end
end

View File

@ -1,13 +1,14 @@
# frozen_string_literal: true
RSpec.describe TopicsHelper do
describe "#categories_breadcrumb" do
let(:user) { Fabricate(:user) }
let(:category) { Fabricate(:category_with_definition) }
let(:subcategory) { Fabricate(:category_with_definition, parent_category_id: category.id) }
let(:subsubcategory) { Fabricate(:category_with_definition, parent_category_id: subcategory.id) }
let(:subsubcategory) do
Fabricate(:category_with_definition, parent_category_id: subcategory.id)
end
it "works with sub-sub-categories" do
SiteSetting.max_category_nesting = 3

View File

@ -3,18 +3,17 @@
RSpec.describe UserNotificationsHelper do
let(:upload_path) { Discourse.store.upload_path }
describe '#email_excerpt' do
let(:paragraphs) { [
"<p>This is the first paragraph, but you should read more.</p>",
"<p>And here is its friend, the second paragraph.</p>"
] }
let(:cooked) do
paragraphs.join("\n")
describe "#email_excerpt" do
let(:paragraphs) do
[
"<p>This is the first paragraph, but you should read more.</p>",
"<p>And here is its friend, the second paragraph.</p>",
]
end
let(:post_quote) do
<<~HTML
let(:cooked) { paragraphs.join("\n") }
let(:post_quote) { <<~HTML }
<aside class="quote no-group" data-post="859" data-topic="30">
<div class="title">
<div class="quote-controls"></div>
@ -24,19 +23,16 @@ RSpec.describe UserNotificationsHelper do
</blockquote>
</aside>
HTML
end
let(:image_paragraph) do
'<p><img src="//localhost:3000/uploads/b9.png" width="300" height="300"></p>'
end
let(:lightbox_image) do
<<~HTML
let(:lightbox_image) { <<~HTML }
<p><div class="lightbox-wrapper"><a class="lightbox" href="//localhost:3000/uploads/default/original/1X/123456.jpeg" data-download-href="//localhost:3000/uploads/default/123456" title="giant-meteor-2020"><img src="//localhost:3000/uploads/default/original/1X/123456.jpeg" alt="giant-meteor-2020" data-base62-sha1="3jcR88161od6Uthq1ixWKJh2ejp" width="517" height="152" data-small-upload="//localhost:3000/uploads/default/optimized/1X/123456_2_10x10.png"><div class="meta">
<svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">giant-meteor-2020</span><span class="informations">851×251 44 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg>
</div></a></div></p>
HTML
end
let(:expected_lightbox_image) do
'<div class="lightbox-wrapper"><a class="lightbox" href="//localhost:3000/uploads/default/original/1X/123456.jpeg" data-download-href="//localhost:3000/uploads/default/123456" title="giant-meteor-2020"><img src="//localhost:3000/uploads/default/original/1X/123456.jpeg" alt="giant-meteor-2020" data-base62-sha1="3jcR88161od6Uthq1ixWKJh2ejp" width="517" height="152" data-small-upload="//localhost:3000/uploads/default/optimized/1X/123456_2_10x10.png"></a></div>'
@ -53,14 +49,16 @@ RSpec.describe UserNotificationsHelper do
end
it "doesn't count emoji images" do
with_emoji = "<p>Hi <img src=\"/images/emoji/twitter/smile.png?v=#{Emoji::EMOJI_VERSION}\" title=\":smile:\" class=\"emoji\" alt=\":smile:\" loading=\"lazy\" width=\"20\" height=\"20\"></p>"
with_emoji =
"<p>Hi <img src=\"/images/emoji/twitter/smile.png?v=#{Emoji::EMOJI_VERSION}\" title=\":smile:\" class=\"emoji\" alt=\":smile:\" loading=\"lazy\" width=\"20\" height=\"20\"></p>"
arg = ([with_emoji] + paragraphs).join("\n")
SiteSetting.digest_min_excerpt_length = 50
expect(helper.email_excerpt(arg)).to eq([with_emoji, paragraphs[0]].join)
end
it "only counts link text" do
with_link = "<p>Hi <a href=\"https://really-long.essays.com/essay/number/9000/this-one-is-about-friends-and-got-a-C-minus-in-grade-9\">friends</a>!</p>"
with_link =
"<p>Hi <a href=\"https://really-long.essays.com/essay/number/9000/this-one-is-about-friends-and-got-a-C-minus-in-grade-9\">friends</a>!</p>"
arg = ([with_link] + paragraphs).join("\n")
SiteSetting.digest_min_excerpt_length = 50
expect(helper.email_excerpt(arg)).to eq([with_link, paragraphs[0]].join)
@ -81,11 +79,12 @@ RSpec.describe UserNotificationsHelper do
<p>AFTER</p>
HTML
expect(helper.email_excerpt(cooked)).to eq "<p>BEFORE</p><blockquote>\n <p>This is a user quote</p>\n</blockquote><p>AFTER</p>"
expect(
helper.email_excerpt(cooked),
).to eq "<p>BEFORE</p><blockquote>\n <p>This is a user quote</p>\n</blockquote><p>AFTER</p>"
end
it "defaults to content after post quote (image w/ no text)" do
cooked = <<~HTML
#{post_quote}
#{image_paragraph}
@ -94,7 +93,8 @@ RSpec.describe UserNotificationsHelper do
end
it "defaults to content after post quote (onebox)" do
aside_onebox = '<aside class="onebox wikipedia"><article class="onebox-body"><p>Onebox excerpt here</p></article><div class="onebox-metadata"></div></aside>'
aside_onebox =
'<aside class="onebox wikipedia"><article class="onebox-body"><p>Onebox excerpt here</p></article><div class="onebox-metadata"></div></aside>'
cooked = <<~HTML
#{post_quote}
#{aside_onebox}
@ -120,44 +120,40 @@ RSpec.describe UserNotificationsHelper do
end
end
describe '#logo_url' do
describe 'local store' do
describe "#logo_url" do
describe "local store" do
let(:upload) { Fabricate(:upload, sha1: "somesha1") }
before do
SiteSetting.logo = upload
end
before { SiteSetting.logo = upload }
it 'should return the right URL' do
it "should return the right URL" do
expect(helper.logo_url).to eq(
"http://test.localhost/#{upload_path}/original/1X/somesha1.png"
"http://test.localhost/#{upload_path}/original/1X/somesha1.png",
)
end
describe 'when cdn path is configured' do
describe "when cdn path is configured" do
before do
GlobalSetting.expects(:cdn_url)
.returns('https://some.localcdn.com')
.at_least_once
GlobalSetting.expects(:cdn_url).returns("https://some.localcdn.com").at_least_once
end
it 'should return the right URL' do
it "should return the right URL" do
expect(helper.logo_url).to eq(
"https://some.localcdn.com/#{upload_path}/original/1X/somesha1.png"
"https://some.localcdn.com/#{upload_path}/original/1X/somesha1.png",
)
end
end
describe 'when logo is an SVG' do
describe "when logo is an SVG" do
let(:upload) { Fabricate(:upload, extension: "svg") }
it 'should return nil' do
it "should return nil" do
expect(helper.logo_url).to eq(nil)
end
end
end
describe 's3 store' do
describe "s3 store" do
let(:upload) { Fabricate(:upload_s3, sha1: "somesha1") }
before do
@ -165,32 +161,27 @@ RSpec.describe UserNotificationsHelper do
SiteSetting.logo = upload
end
it 'should return the right URL' do
it "should return the right URL" do
expect(helper.logo_url).to eq(
"http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png"
"http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png",
)
end
describe 'when global cdn path is configured' do
it 'should return the right url' do
GlobalSetting.stubs(:cdn_url).returns('https://some.cdn.com/cluster')
describe "when global cdn path is configured" do
it "should return the right url" do
GlobalSetting.stubs(:cdn_url).returns("https://some.cdn.com/cluster")
expect(helper.logo_url).to eq(
"http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png"
"http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png",
)
end
end
describe 'when cdn path is configured' do
before do
SiteSetting.s3_cdn_url = 'https://some.cdn.com'
describe "when cdn path is configured" do
before { SiteSetting.s3_cdn_url = "https://some.cdn.com" }
end
it 'should return the right url' do
expect(helper.logo_url).to eq(
"https://some.cdn.com/original/1X/somesha1.png"
)
it "should return the right url" do
expect(helper.logo_url).to eq("https://some.cdn.com/original/1X/somesha1.png")
end
end
end

View File

@ -3,26 +3,23 @@
require "import_export"
RSpec.describe ImportExport::CategoryExporter do
fab!(:category) { Fabricate(:category) }
fab!(:group) { Fabricate(:group) }
fab!(:user) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) }
fab!(:user3) { Fabricate(:user) }
before do
STDOUT.stubs(:write)
end
before { STDOUT.stubs(:write) }
describe '.perform' do
it 'export the category when it is found' do
describe ".perform" do
it "export the category when it is found" do
data = ImportExport::CategoryExporter.new([category.id]).perform.export_data
expect(data[:categories].count).to eq(1)
expect(data[:groups].count).to eq(0)
end
it 'export the category with permission groups' do
it "export the category with permission groups" do
_category_group = Fabricate(:category_group, category: category, group: group)
data = ImportExport::CategoryExporter.new([category.id]).perform.export_data
@ -30,7 +27,7 @@ RSpec.describe ImportExport::CategoryExporter do
expect(data[:groups].count).to eq(1)
end
it 'export multiple categories' do
it "export multiple categories" do
category2 = Fabricate(:category)
_category_group = Fabricate(:category_group, category: category, group: group)
data = ImportExport::CategoryExporter.new([category.id, category2.id]).perform.export_data
@ -39,7 +36,7 @@ RSpec.describe ImportExport::CategoryExporter do
expect(data[:groups].count).to eq(1)
end
it 'export the category with topics and users' do
it "export the category with topics and users" do
topic1 = Fabricate(:topic, category: category, user_id: -1)
Fabricate(:post, topic: topic1, user: User.find(-1), post_number: 1)
topic2 = Fabricate(:topic, category: category, user: user)
@ -54,5 +51,4 @@ RSpec.describe ImportExport::CategoryExporter do
expect(data[:users].map { |u| u[:id] }).to match_array([user.id, user2.id, user3.id])
end
end
end

View File

@ -3,12 +3,9 @@
require "import_export/category_structure_exporter"
RSpec.describe ImportExport::CategoryStructureExporter do
before { STDOUT.stubs(:write) }
before do
STDOUT.stubs(:write)
end
it 'export all the categories' do
it "export all the categories" do
category = Fabricate(:category)
data = ImportExport::CategoryStructureExporter.new.perform.export_data
@ -17,7 +14,7 @@ RSpec.describe ImportExport::CategoryStructureExporter do
expect(data[:users].blank?).to eq(true)
end
it 'export all the categories with permission groups' do
it "export all the categories with permission groups" do
category = Fabricate(:category)
group = Fabricate(:group)
category_group = Fabricate(:category_group, category: category, group: group)
@ -28,7 +25,7 @@ RSpec.describe ImportExport::CategoryStructureExporter do
expect(data[:users].blank?).to eq(true)
end
it 'export all the categories with permission groups and users' do
it "export all the categories with permission groups and users" do
category = Fabricate(:category)
group = Fabricate(:group)
user = Fabricate(:user)
@ -40,5 +37,4 @@ RSpec.describe ImportExport::CategoryStructureExporter do
expect(data[:groups].count).to eq(1)
expect(data[:users].count).to eq(1)
end
end

View File

@ -3,12 +3,9 @@
require "import_export/group_exporter"
RSpec.describe ImportExport::GroupExporter do
before { STDOUT.stubs(:write) }
before do
STDOUT.stubs(:write)
end
it 'exports all the groups' do
it "exports all the groups" do
group = Fabricate(:group)
user = Fabricate(:user)
group_user = Fabricate(:group_user, group: group, user: user)
@ -18,7 +15,7 @@ RSpec.describe ImportExport::GroupExporter do
expect(data[:users].blank?).to eq(true)
end
it 'exports all the groups with users' do
it "exports all the groups with users" do
group = Fabricate(:group)
user = Fabricate(:user)
group_user = Fabricate(:group_user, group: group, user: user)
@ -27,5 +24,4 @@ RSpec.describe ImportExport::GroupExporter do
expect(data[:groups].map { |g| g[:id] }).to include(group.id)
expect(data[:users].map { |u| u[:id] }).to include(user.id)
end
end

View File

@ -3,9 +3,7 @@
require "import_export"
RSpec.describe ImportExport::Importer do
before do
STDOUT.stubs(:write)
end
before { STDOUT.stubs(:write) }
let(:import_data) do
import_file = Rack::Test::UploadedFile.new(file_from_fixtures("import-export.json", "json"))
@ -16,93 +14,79 @@ RSpec.describe ImportExport::Importer do
ImportExport::Importer.new(data).perform
end
describe '.perform' do
it 'topics and users' do
describe ".perform" do
it "topics and users" do
data = import_data.dup
data[:categories] = nil
data[:groups] = nil
expect {
import(data)
}.to not_change { Category.count }
.and not_change { Group.count }
.and change { Topic.count }.by(2)
.and change { User.count }.by(2)
expect { import(data) }.to not_change { Category.count }.and not_change {
Group.count
}.and change { Topic.count }.by(2).and change { User.count }.by(2)
end
context 'with categories and groups' do
it 'works' do
context "with categories and groups" do
it "works" do
data = import_data.dup
data[:topics] = nil
data[:users] = nil
expect {
import(data)
}.to change { Category.count }.by(6)
.and change { Group.count }.by(2)
.and change { Topic.count }.by(6)
.and not_change { User.count }
expect { import(data) }.to change { Category.count }.by(6).and change { Group.count }.by(
2,
).and change { Topic.count }.by(6).and not_change { User.count }
end
it 'works with sub-sub-categories' do
it "works with sub-sub-categories" do
data = import_data.dup
# 11 -> 10 -> 15
data[:categories].find { |c| c[:id] == 10 }[:parent_category_id] = 11
data[:categories].find { |c| c[:id] == 15 }[:parent_category_id] = 10
expect { import(data) }
.to change { Category.count }.by(6)
.and change { SiteSetting.max_category_nesting }.from(2).to(3)
expect { import(data) }.to change { Category.count }.by(6).and change {
SiteSetting.max_category_nesting
}.from(2).to(3)
end
it 'fixes permissions' do
it "fixes permissions" do
data = import_data.dup
data[:categories].find { |c| c[:id] == 10 }[:permissions_params] = { custom_group: 1 }
data[:categories].find { |c| c[:id] == 15 }[:permissions_params] = { staff: 1 }
permissions = data[:categories].find { |c| c[:id] == 10 }[:permissions_params]
expect { import(data) }
.to change { Category.count }.by(6)
.and change { permissions[:staff] }.from(nil).to(1)
expect { import(data) }.to change { Category.count }.by(6).and change {
permissions[:staff]
}.from(nil).to(1)
end
end
it 'categories, groups and users' do
it "categories, groups and users" do
data = import_data.dup
data[:topics] = nil
expect {
import(data)
}.to change { Category.count }.by(6)
.and change { Group.count }.by(2)
.and change { Topic.count }.by(6)
.and change { User.count }.by(2)
expect { import(data) }.to change { Category.count }.by(6).and change { Group.count }.by(
2,
).and change { Topic.count }.by(6).and change { User.count }.by(2)
end
it 'groups' do
it "groups" do
data = import_data.dup
data[:categories] = nil
data[:topics] = nil
data[:users] = nil
expect {
import(data)
}.to not_change { Category.count }
.and change { Group.count }.by(2)
.and not_change { Topic.count }
.and not_change { User.count }
expect { import(data) }.to not_change { Category.count }.and change { Group.count }.by(
2,
).and not_change { Topic.count }.and not_change { User.count }
end
it 'all' do
expect {
import(import_data)
}.to change { Category.count }.by(6)
.and change { Group.count }.by(2)
.and change { Topic.count }.by(8)
.and change { User.count }.by(2)
.and change { TranslationOverride.count }.by(1)
it "all" do
expect { import(import_data) }.to change { Category.count }.by(6).and change {
Group.count
}.by(2).and change { Topic.count }.by(8).and change { User.count }.by(2).and change {
TranslationOverride.count
}.by(1)
end
end
end

View File

@ -3,17 +3,14 @@
require "import_export"
RSpec.describe ImportExport::TopicExporter do
before do
STDOUT.stubs(:write)
end
before { STDOUT.stubs(:write) }
fab!(:user) { Fabricate(:user) }
fab!(:topic) { Fabricate(:topic, user: user) }
fab!(:post) { Fabricate(:post, topic: topic, user: user) }
describe '.perform' do
it 'export a single topic' do
describe ".perform" do
it "export a single topic" do
data = ImportExport::TopicExporter.new([topic.id]).perform.export_data
expect(data[:categories].blank?).to eq(true)
@ -22,7 +19,7 @@ RSpec.describe ImportExport::TopicExporter do
expect(data[:users].count).to eq(1)
end
it 'export multiple topics' do
it "export multiple topics" do
topic2 = Fabricate(:topic, user: user)
_post2 = Fabricate(:post, user: user, topic: topic2)
data = ImportExport::TopicExporter.new([topic.id, topic2.id]).perform.export_data
@ -33,5 +30,4 @@ RSpec.describe ImportExport::TopicExporter do
expect(data[:users].map { |u| u[:id] }).to match_array([user.id])
end
end
end

View File

@ -1,10 +1,10 @@
# frozen_string_literal: true
RSpec.describe 'Setting changes' do
describe '#must_approve_users' do
RSpec.describe "Setting changes" do
describe "#must_approve_users" do
before { SiteSetting.must_approve_users = false }
it 'does not approve a user with associated reviewables' do
it "does not approve a user with associated reviewables" do
user_pending_approval = Fabricate(:reviewable_user).target
SiteSetting.must_approve_users = true
@ -12,7 +12,7 @@ RSpec.describe 'Setting changes' do
expect(user_pending_approval.reload.approved?).to eq(false)
end
it 'approves a user with no associated reviewables' do
it "approves a user with no associated reviewables" do
non_approved_user = Fabricate(:user, approved: false)
SiteSetting.must_approve_users = true
@ -21,10 +21,10 @@ RSpec.describe 'Setting changes' do
end
end
describe '#reviewable_low_priority_threshold' do
describe "#reviewable_low_priority_threshold" do
let(:new_threshold) { 5 }
it 'sets the low priority value' do
it "sets the low priority value" do
medium_threshold = 10
Reviewable.set_priorities(medium: medium_threshold)

View File

@ -1,25 +1,21 @@
# frozen_string_literal: true
RSpec.describe 'api keys' do
RSpec.describe "api keys" do
let(:user) { Fabricate(:user) }
let(:api_key) { ApiKey.create!(user_id: user.id, created_by_id: Discourse.system_user) }
it 'works in headers' do
get '/session/current.json', headers: {
HTTP_API_KEY: api_key.key
}
it "works in headers" do
get "/session/current.json", headers: { HTTP_API_KEY: api_key.key }
expect(response.status).to eq(200)
expect(response.parsed_body["current_user"]["username"]).to eq(user.username)
end
it 'does not work in parameters' do
get '/session/current.json', params: {
api_key: api_key.key
}
it "does not work in parameters" do
get "/session/current.json", params: { api_key: api_key.key }
expect(response.status).to eq(404)
end
it 'allows parameters on ics routes' do
it "allows parameters on ics routes" do
get "/u/#{user.username}/bookmarks.ics?api_key=#{api_key.key}&api_username=#{user.username.downcase}"
expect(response.status).to eq(200)
@ -28,14 +24,14 @@ RSpec.describe 'api keys' do
expect(response.status).to eq(403)
end
it 'allows parameters for handle mail' do
it "allows parameters for handle mail" do
admin_api_key = ApiKey.create!(user: Fabricate(:admin), created_by_id: Discourse.system_user)
post "/admin/email/handle_mail.json?api_key=#{admin_api_key.key}", params: { email: "blah" }
expect(response.status).to eq(200)
end
it 'allows parameters for rss feeds' do
it "allows parameters for rss feeds" do
SiteSetting.login_required = true
get "/latest.rss?api_key=#{api_key.key}&api_username=#{user.username.downcase}"
@ -52,32 +48,28 @@ RSpec.describe 'api keys' do
plugin.add_api_parameter_route methods: [:get], actions: ["session#current"]
end
it 'allows parameter access to the registered route' do
get '/session/current.json', params: {
api_key: api_key.key
}
it "allows parameter access to the registered route" do
get "/session/current.json", params: { api_key: api_key.key }
expect(response.status).to eq(200)
end
end
end
RSpec.describe 'user api keys' do
RSpec.describe "user api keys" do
let(:user) { Fabricate(:user) }
let(:user_api_key) { Fabricate(:readonly_user_api_key, user: user) }
it 'updates last used time on use' do
it "updates last used time on use" do
freeze_time
user_api_key.update_columns(last_used_at: 7.days.ago)
get '/session/current.json', headers: {
HTTP_USER_API_KEY: user_api_key.key,
}
get "/session/current.json", headers: { HTTP_USER_API_KEY: user_api_key.key }
expect(user_api_key.reload.last_used_at).to eq_time(Time.zone.now)
end
it 'allows parameters on ics routes' do
it "allows parameters on ics routes" do
get "/u/#{user.username}/bookmarks.ics?user_api_key=#{user_api_key.key}"
expect(response.status).to eq(200)
@ -86,7 +78,7 @@ RSpec.describe 'user api keys' do
expect(response.status).to eq(403)
end
it 'allows parameters for rss feeds' do
it "allows parameters for rss feeds" do
SiteSetting.login_required = true
get "/latest.rss?user_api_key=#{user_api_key.key}"
@ -102,27 +94,19 @@ RSpec.describe 'user api keys' do
calendar_key = Fabricate(:bookmarks_calendar_user_api_key, user: admin)
get "/u/#{user.username}/bookmarks.json", headers: {
HTTP_USER_API_KEY: calendar_key.key,
}
get "/u/#{user.username}/bookmarks.json", headers: { HTTP_USER_API_KEY: calendar_key.key }
expect(response.status).to eq(403) # Does not allow json
get "/u/#{user.username}/bookmarks.ics", headers: {
HTTP_USER_API_KEY: calendar_key.key,
}
get "/u/#{user.username}/bookmarks.ics", headers: { HTTP_USER_API_KEY: calendar_key.key }
expect(response.status).to eq(200) # Allows ICS
# Now restrict the key
calendar_key.scopes.first.update(allowed_parameters: { username: admin.username })
get "/u/#{user.username}/bookmarks.ics", headers: {
HTTP_USER_API_KEY: calendar_key.key,
}
get "/u/#{user.username}/bookmarks.ics", headers: { HTTP_USER_API_KEY: calendar_key.key }
expect(response.status).to eq(403) # Cannot access another users calendar
get "/u/#{admin.username}/bookmarks.ics", headers: {
HTTP_USER_API_KEY: calendar_key.key,
}
get "/u/#{admin.username}/bookmarks.ics", headers: { HTTP_USER_API_KEY: calendar_key.key }
expect(response.status).to eq(200) # Can access own calendar
end
@ -138,12 +122,9 @@ RSpec.describe 'user api keys' do
user_api_key.save!
end
it 'allows parameter access to the registered route' do
get '/session/current.json', headers: {
HTTP_USER_API_KEY: user_api_key.key
}
it "allows parameter access to the registered route" do
get "/session/current.json", headers: { HTTP_USER_API_KEY: user_api_key.key }
expect(response.status).to eq(200)
end
end
end

View File

@ -12,9 +12,7 @@ RSpec.describe "auto reject reviewable users" do
Jobs::AutoQueueHandler.new.execute({})
expect(old_user.reload.rejected?).to eq(true)
expect(UserHistory.last.context).to eq(
I18n.t("user.destroy_reasons.reviewable_reject_auto")
)
expect(UserHistory.last.context).to eq(I18n.t("user.destroy_reasons.reviewable_reject_auto"))
end
end
end

View File

@ -3,11 +3,20 @@
RSpec.describe "hotlinked media blocking" do
let(:hotlinked_url) { "http://example.com/images/2/2e/Longcat1.png" }
let(:onebox_url) { "http://example.com/onebox" }
let(:png) { Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") }
let(:png) do
Base64.decode64(
"R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==",
)
end
before do
SiteSetting.download_remote_images_to_local = false
stub_request(:get, hotlinked_url).to_return(body: png, headers: { "Content-Type" => "image/png" })
stub_request(:get, hotlinked_url).to_return(
body: png,
headers: {
"Content-Type" => "image/png",
},
)
stub_image_size
end
@ -19,38 +28,65 @@ RSpec.describe "hotlinked media blocking" do
context "with hotlinked media blocked, before post-processing" do
before do
SiteSetting.block_hotlinked_media = true
Oneboxer.stubs(:cached_onebox).returns("<aside class='onebox'><img src='#{hotlinked_url}'></aside>")
Oneboxer.stubs(:cached_onebox).returns(
"<aside class='onebox'><img src='#{hotlinked_url}'></aside>",
)
end
it "blocks hotlinked images" do
post = Fabricate(:post, raw: "<img src='#{hotlinked_url}'>")
expect(post.cooked).not_to have_tag("img[src]")
expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url })
expect(post.cooked).to have_tag(
"img",
with: {
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url,
},
)
end
it "blocks hotlinked videos with src" do
post = Fabricate(:post, raw: "![alt text|video](#{hotlinked_url})")
expect(post.cooked).not_to have_tag("video source[src]")
expect(post.cooked).to have_tag("video source", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url })
expect(post.cooked).to have_tag(
"video source",
with: {
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url,
},
)
end
it "blocks hotlinked videos with srcset" do
srcset = "#{hotlinked_url} 1x,https://example.com 2x"
post = Fabricate(:post, raw: "<video><source srcset='#{srcset}'></video>")
expect(post.cooked).not_to have_tag("video source[srcset]")
expect(post.cooked).to have_tag("video source", with: { PrettyText::BLOCKED_HOTLINKED_SRCSET_ATTR => srcset })
expect(post.cooked).to have_tag(
"video source",
with: {
PrettyText::BLOCKED_HOTLINKED_SRCSET_ATTR => srcset,
},
)
end
it "blocks hotlinked audio" do
post = Fabricate(:post, raw: "![alt text|audio](#{hotlinked_url})")
expect(post.cooked).not_to have_tag("audio source[src]")
expect(post.cooked).to have_tag("audio source", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url })
expect(post.cooked).to have_tag(
"audio source",
with: {
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url,
},
)
end
it "blocks hotlinked onebox content when cached (post_analyzer)" do
post = Fabricate(:post, raw: "#{onebox_url}")
expect(post.cooked).not_to have_tag("img[src]")
expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url })
expect(post.cooked).to have_tag(
"img",
with: {
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url,
},
)
end
it "allows relative URLs" do
@ -81,8 +117,18 @@ RSpec.describe "hotlinked media blocking" do
post.reload
expect(post.cooked).to have_tag("img", with: { "src" => "https://example.com" })
expect(post.cooked).to have_tag("img", with: { "src" => "https://example.com/myimage.png" })
expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://example.com.malicious.com/myimage.png" })
expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://malicious.invalid/https://example.com" })
expect(post.cooked).to have_tag(
"img",
with: {
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://example.com.malicious.com/myimage.png",
},
)
expect(post.cooked).to have_tag(
"img",
with: {
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://malicious.invalid/https://example.com",
},
)
end
it "allows multiple exceptions" do
@ -126,7 +172,7 @@ RSpec.describe "hotlinked media blocking" do
expect(post.cooked).not_to have_tag("audio")
expect(post.cooked).to have_tag(
"a.blocked-hotlinked-placeholder[href^='http://example.com'][rel='noopener nofollow ugc']",
count: 4
count: 4,
)
end
end

View File

@ -6,13 +6,13 @@ RSpec.describe "category tag restrictions" do
DiscourseTagging.filter_allowed_tags(Guardian.new(user), opts)
end
fab!(:tag1) { Fabricate(:tag, name: 'tag1') }
fab!(:tag2) { Fabricate(:tag, name: 'tag2') }
fab!(:tag3) { Fabricate(:tag, name: 'tag3') }
fab!(:tag4) { Fabricate(:tag, name: 'tag4') }
let(:tag_with_colon) { Fabricate(:tag, name: 'with:colon') }
fab!(:tag1) { Fabricate(:tag, name: "tag1") }
fab!(:tag2) { Fabricate(:tag, name: "tag2") }
fab!(:tag3) { Fabricate(:tag, name: "tag3") }
fab!(:tag4) { Fabricate(:tag, name: "tag4") }
let(:tag_with_colon) { Fabricate(:tag, name: "with:colon") }
fab!(:user) { Fabricate(:user) }
fab!(:user) { Fabricate(:user) }
fab!(:admin) { Fabricate(:admin) }
before do
@ -23,29 +23,29 @@ RSpec.describe "category tag restrictions" do
context "with tags restricted to one category" do
fab!(:category_with_tags) { Fabricate(:category) }
fab!(:other_category) { Fabricate(:category) }
fab!(:other_category) { Fabricate(:category) }
before do
category_with_tags.tags = [tag1, tag2]
end
before { category_with_tags.tags = [tag1, tag2] }
it "tags belonging to that category can only be used there" do
msg = I18n.t(
"tags.forbidden.category_does_not_allow_tags",
count: 1,
tags: tag3.name,
category: category_with_tags.name
)
msg =
I18n.t(
"tags.forbidden.category_does_not_allow_tags",
count: 1,
tags: tag3.name,
category: category_with_tags.name,
)
expect {
create_post(category: category_with_tags, tags: [tag1.name, tag2.name, tag3.name])
}.to raise_error(StandardError, msg)
msg = I18n.t(
"tags.forbidden.restricted_tags_cannot_be_used_in_category",
count: 2,
tags: [tag1, tag2].map(&:name).sort.join(", "),
category: other_category.name
)
msg =
I18n.t(
"tags.forbidden.restricted_tags_cannot_be_used_in_category",
count: 2,
tags: [tag1, tag2].map(&:name).sort.join(", "),
category: other_category.name,
)
expect {
create_post(category: other_category, tags: [tag1.name, tag2.name, tag3.name])
}.to raise_error(StandardError, msg)
@ -53,27 +53,60 @@ RSpec.describe "category tag restrictions" do
it "search can show only permitted tags" do
expect(filter_allowed_tags.count).to eq(Tag.count)
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1, tag2])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category_with_tags),
[tag1, tag2],
)
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name], term: 'tag'), [tag2])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]), [tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name], term: 'tag'), [tag4])
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: category_with_tags,
selected_tags: [tag1.name],
),
[tag2],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: category_with_tags,
selected_tags: [tag1.name],
term: "tag",
),
[tag2],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag3, tag4],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]),
[tag4],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: other_category,
selected_tags: [tag3.name],
term: "tag",
),
[tag4],
)
end
it "search can handle colons in tag names" do
tag_with_colon
expect_same_tag_names(filter_allowed_tags(for_input: true, term: 'with:c'), [tag_with_colon])
expect_same_tag_names(filter_allowed_tags(for_input: true, term: "with:c"), [tag_with_colon])
end
it "can't create new tags in a restricted category" do
msg = I18n.t(
"tags.forbidden.category_does_not_allow_tags",
count: 1,
tags: "newtag",
category: category_with_tags.name
)
msg =
I18n.t(
"tags.forbidden.category_does_not_allow_tags",
count: 1,
tags: "newtag",
category: category_with_tags.name,
)
expect {
create_post(category: category_with_tags, tags: [tag1.name, "newtag"])
}.to raise_error(StandardError, msg)
@ -89,61 +122,161 @@ RSpec.describe "category tag restrictions" do
end
it "can create tags when changing category settings" do
expect { other_category.update(allowed_tags: ['newtag']) }.to change { Tag.count }.by(1)
expect { other_category.update(allowed_tags: [tag1.name, 'tag-stuff', tag2.name, 'another-tag']) }.to change { Tag.count }.by(2)
expect { other_category.update(allowed_tags: ["newtag"]) }.to change { Tag.count }.by(1)
expect {
other_category.update(allowed_tags: [tag1.name, "tag-stuff", tag2.name, "another-tag"])
}.to change { Tag.count }.by(2)
end
context 'with required tags from tag group' do
context "with required tags from tag group" do
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
before { category_with_tags.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) }
before do
category_with_tags.update!(
category_required_tag_groups: [
CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1),
],
)
end
it "search only returns the allowed tags" do
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag2.name]), [tag1])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category_with_tags),
[tag1],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: category_with_tags,
selected_tags: [tag1.name],
),
[tag2],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: category_with_tags,
selected_tags: [tag2.name],
),
[tag1],
)
end
end
context 'when category allows other tags to be used' do
before do
category_with_tags.update!(allow_global_tags: true)
end
context "when category allows other tags to be used" do
before { category_with_tags.update!(allow_global_tags: true) }
it "search can show the permitted tags" do
expect(filter_allowed_tags.count).to eq(Tag.count)
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1, tag2, tag3, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category_with_tags),
[tag1, tag2, tag3, tag4],
)
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2, tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name], term: 'tag'), [tag2, tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]), [tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name], term: 'tag'), [tag4])
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: category_with_tags,
selected_tags: [tag1.name],
),
[tag2, tag3, tag4],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: category_with_tags,
selected_tags: [tag1.name],
term: "tag",
),
[tag2, tag3, tag4],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag3, tag4],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: other_category,
selected_tags: [tag3.name],
),
[tag4],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: other_category,
selected_tags: [tag3.name],
term: "tag",
),
[tag4],
)
end
it "works if no tags are restricted to the category" do
other_category.update!(allow_global_tags: true)
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]), [tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name], term: 'tag'), [tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag3, tag4],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: other_category,
selected_tags: [tag3.name],
),
[tag4],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: other_category,
selected_tags: [tag3.name],
term: "tag",
),
[tag4],
)
end
context 'with required tags from tag group' do
context "with required tags from tag group" do
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
before { category_with_tags.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) }
before do
category_with_tags.update!(
category_required_tag_groups: [
CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1),
],
)
end
it "search only returns the allowed tags" do
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1, tag3])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2, tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag2.name]), [tag1, tag3])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category_with_tags),
[tag1, tag3],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: category_with_tags,
selected_tags: [tag1.name],
),
[tag2, tag3, tag4],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: category_with_tags,
selected_tags: [tag2.name],
),
[tag1, tag3],
)
end
end
end
end
context "with tag groups restricted to a category" do
fab!(:tag_group1) { Fabricate(:tag_group) }
fab!(:category) { Fabricate(:category) }
fab!(:other_category) { Fabricate(:category) }
fab!(:tag_group1) { Fabricate(:tag_group) }
fab!(:category) { Fabricate(:category) }
fab!(:other_category) { Fabricate(:category) }
before do
tag_group1.tags = [tag1, tag2]
@ -156,7 +289,10 @@ RSpec.describe "category tag restrictions" do
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
tag_group1.tags = [tag2, tag3, tag4]
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag2, tag3, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category),
[tag2, tag3, tag4],
)
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1])
end
@ -165,74 +301,122 @@ RSpec.describe "category tag restrictions" do
category.allowed_tags = [tag4.name]
category.reload
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category),
[tag1, tag2, tag4],
)
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3])
end
it "enforces restrictions when creating a topic" do
msg = I18n.t(
"tags.forbidden.category_does_not_allow_tags",
count: 1,
tags: "newtag",
category: category.name
msg =
I18n.t(
"tags.forbidden.category_does_not_allow_tags",
count: 1,
tags: "newtag",
category: category.name,
)
expect { create_post(category: category, tags: [tag1.name, "newtag"]) }.to raise_error(
StandardError,
msg,
)
expect {
create_post(category: category, tags: [tag1.name, "newtag"])
}.to raise_error(StandardError, msg)
end
it "handles colons" do
tag_with_colon
expect_same_tag_names(filter_allowed_tags(for_input: true, term: 'with:c'), [tag_with_colon])
expect_same_tag_names(filter_allowed_tags(for_input: true, term: "with:c"), [tag_with_colon])
end
context 'with required tags from tag group' do
context "with required tags from tag group" do
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
before { category.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) }
before do
category.update!(
category_required_tag_groups: [
CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1),
],
)
end
it "search only returns the allowed tags" do
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), [tag2])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), [tag1])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]),
[tag2],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]),
[tag1],
)
end
end
context 'when category allows other tags to be used' do
before do
category.update!(allow_global_tags: true)
end
context "when category allows other tags to be used" do
before { category.update!(allow_global_tags: true) }
it 'filters tags correctly' do
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag3, tag4])
it "filters tags correctly" do
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category),
[tag1, tag2, tag3, tag4],
)
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag3, tag4],
)
tag_group1.tags = [tag2, tag3, tag4]
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag3, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category),
[tag1, tag2, tag3, tag4],
)
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag1],
)
end
it "works if no tags are restricted to the category" do
other_category.update!(allow_global_tags: true)
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag3, tag4],
)
tag_group1.tags = [tag2, tag3, tag4]
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag1],
)
end
context 'with required tags from tag group' do
context "with required tags from tag group" do
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
before { category.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) }
before do
category.update!(
category_required_tag_groups: [
CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1),
],
)
end
it "search only returns the allowed tags" do
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag3])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), [tag2, tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), [tag1, tag3])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category),
[tag1, tag3],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]),
[tag2, tag3, tag4],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]),
[tag1, tag3],
)
end
end
context 'when another category has restricted tags using groups' do
context "when another category has restricted tags using groups" do
fab!(:category2) { Fabricate(:category) }
fab!(:tag_group2) { Fabricate(:tag_group) }
@ -242,32 +426,59 @@ RSpec.describe "category tag restrictions" do
category2.reload
end
it 'filters tags correctly' do
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category2), [tag2, tag3])
it "filters tags correctly" do
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category2),
[tag2, tag3],
)
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag4],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category),
[tag1, tag2, tag4],
)
end
it "doesn't care about tags in a group that isn't used in a category" do
unused_tag_group = Fabricate(:tag_group)
unused_tag_group.tags = [tag4]
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category2), [tag2, tag3])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category2),
[tag2, tag3],
)
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag4],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category),
[tag1, tag2, tag4],
)
end
end
context 'when another category has restricted tags' do
context "when another category has restricted tags" do
fab!(:category2) { Fabricate(:category) }
it "doesn't filter tags that are also restricted in another category" do
category2.tags = [tag2, tag3]
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category2), [tag2, tag3])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category2),
[tag2, tag3],
)
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag4],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category),
[tag1, tag2, tag4],
)
end
end
end
@ -278,80 +489,139 @@ RSpec.describe "category tag restrictions" do
tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id)
tag_group.tags = [tag3, tag4]
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag2])
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name, tag3.name]), [tag2, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, selected_tags: [tag1.name]),
[tag2, tag3, tag4],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, selected_tags: [tag1.name, tag3.name]),
[tag2, tag4],
)
end
it "for tagging a topic, filter_allowed_tags allows tags without parent tag" do
tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id)
tag_group.tags = [tag3, tag4]
expect_same_tag_names(filter_allowed_tags(for_topic: true), [tag1, tag2, tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_topic: true, selected_tags: [tag1.name]), [tag1, tag2, tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_topic: true, selected_tags: [tag1.name, tag3.name]), [tag1, tag2, tag3, tag4])
expect_same_tag_names(
filter_allowed_tags(for_topic: true, selected_tags: [tag1.name]),
[tag1, tag2, tag3, tag4],
)
expect_same_tag_names(
filter_allowed_tags(for_topic: true, selected_tags: [tag1.name, tag3.name]),
[tag1, tag2, tag3, tag4],
)
end
it "filter_allowed_tags returns tags common to more than one tag group with parent tag" do
common = Fabricate(:tag, name: 'common')
common = Fabricate(:tag, name: "common")
tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id)
tag_group.tags = [tag2, common]
tag_group = Fabricate(:tag_group, parent_tag_id: tag3.id)
tag_group.tags = [tag4]
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, common])
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, tag1])
expect_same_tag_names(
filter_allowed_tags(for_input: true, selected_tags: [tag1.name]),
[tag2, tag3, common],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, selected_tags: [tag3.name]),
[tag4, tag1],
)
tag_group.tags = [tag4, common]
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, common])
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, tag1, common])
expect_same_tag_names(
filter_allowed_tags(for_input: true, selected_tags: [tag1.name]),
[tag2, tag3, common],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, selected_tags: [tag3.name]),
[tag4, tag1, common],
)
parent_tag_group = Fabricate(:tag_group, tags: [tag1, tag3])
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, common])
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, tag1, common])
expect_same_tag_names(
filter_allowed_tags(for_input: true, selected_tags: [tag1.name]),
[tag2, tag3, common],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, selected_tags: [tag3.name]),
[tag4, tag1, common],
)
parent_tag_group.update!(one_per_topic: true)
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, common])
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, common])
expect_same_tag_names(
filter_allowed_tags(for_input: true, selected_tags: [tag1.name]),
[tag2, common],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, selected_tags: [tag3.name]),
[tag4, common],
)
end
context 'with required tags from tag group' do
context "with required tags from tag group" do
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag2]) }
fab!(:category) { Fabricate(:category, category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) }
fab!(:category) do
Fabricate(
:category,
category_required_tag_groups: [
CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1),
],
)
end
it "search only returns the allowed tags" do
tag_group_with_parent = Fabricate(:tag_group, parent_tag_id: tag1.id, tags: [tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), [tag1])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), [tag2, tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name, tag2.name]), [tag3, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category),
[tag1, tag2],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]),
[tag1],
)
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]),
[tag2, tag3, tag4],
)
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: category,
selected_tags: [tag1.name, tag2.name],
),
[tag3, tag4],
)
end
end
context "with category restrictions" do
fab!(:car_category) { Fabricate(:category) }
fab!(:other_category) { Fabricate(:category) }
fab!(:makes) { Fabricate(:tag_group, name: "Makes") }
fab!(:honda_group) { Fabricate(:tag_group, name: "Honda Models") }
fab!(:ford_group) { Fabricate(:tag_group, name: "Ford Models") }
fab!(:car_category) { Fabricate(:category) }
fab!(:other_category) { Fabricate(:category) }
fab!(:makes) { Fabricate(:tag_group, name: "Makes") }
fab!(:honda_group) { Fabricate(:tag_group, name: "Honda Models") }
fab!(:ford_group) { Fabricate(:tag_group, name: "Ford Models") }
before do
@tags = {}
['honda', 'ford', 'civic', 'accord', 'mustang', 'taurus'].each do |name|
%w[honda ford civic accord mustang taurus].each do |name|
@tags[name] = Fabricate(:tag, name: name)
end
makes.tags = [@tags['honda'], @tags['ford']]
makes.tags = [@tags["honda"], @tags["ford"]]
honda_group.parent_tag_id = @tags['honda'].id
honda_group.parent_tag_id = @tags["honda"].id
honda_group.save
honda_group.tags = [@tags['civic'], @tags['accord']]
honda_group.tags = [@tags["civic"], @tags["accord"]]
ford_group.parent_tag_id = @tags['ford'].id
ford_group.parent_tag_id = @tags["ford"].id
ford_group.save
ford_group.tags = [@tags['mustang'], @tags['taurus']]
ford_group.tags = [@tags["mustang"], @tags["taurus"]]
car_category.allowed_tag_groups = [makes.name, honda_group.name, ford_group.name]
end
@ -359,39 +629,75 @@ RSpec.describe "category tag restrictions" do
it "handles all those rules" do
# car tags can't be used outside of car category:
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag2, tag3, tag4])
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1, tag2, tag3, tag4])
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: other_category),
[tag1, tag2, tag3, tag4],
)
# in car category, a make tag must be given first:
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category))).to eq(['ford', 'honda'])
expect(
sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category)),
).to eq(%w[ford honda])
# model tags depend on which make is chosen:
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic', 'ford'])
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['honda', 'mustang', 'taurus'])
expect(
sorted_tag_names(
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["honda"]),
),
).to eq(%w[accord civic ford])
expect(
sorted_tag_names(
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]),
),
).to eq(%w[honda mustang taurus])
makes.update!(one_per_topic: true)
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic'])
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['mustang', 'taurus'])
expect(
sorted_tag_names(
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["honda"]),
),
).to eq(%w[accord civic])
expect(
sorted_tag_names(
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]),
),
).to eq(%w[mustang taurus])
honda_group.update!(one_per_topic: true)
ford_group.update!(one_per_topic: true)
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic'])
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['mustang', 'taurus'])
expect(
sorted_tag_names(
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["honda"]),
),
).to eq(%w[accord civic])
expect(
sorted_tag_names(
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]),
),
).to eq(%w[mustang taurus])
car_category.update!(allow_global_tags: true)
expect_same_tag_names(filter_allowed_tags(for_input: true, category: car_category),
['ford', 'honda', tag1, tag2, tag3, tag4]
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: car_category),
["ford", "honda", tag1, tag2, tag3, tag4],
)
expect_same_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']),
['mustang', 'taurus', tag1, tag2, tag3, tag4]
expect_same_tag_names(
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]),
["mustang", "taurus", tag1, tag2, tag3, tag4],
)
expect_same_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford', 'mustang']),
[tag1, tag2, tag3, tag4]
expect_same_tag_names(
filter_allowed_tags(
for_input: true,
category: car_category,
selected_tags: %w[ford mustang],
),
[tag1, tag2, tag3, tag4],
)
end
it "can apply the tags to a topic" do
post = create_post(category: car_category, tags: ['ford', 'mustang'])
expect(post.topic.tags.map(&:name).sort).to eq(['ford', 'mustang'])
post = create_post(category: car_category, tags: %w[ford mustang])
expect(post.topic.tags.map(&:name).sort).to eq(%w[ford mustang])
end
context "with limit one tag from each group" do
@ -402,24 +708,51 @@ RSpec.describe "category tag restrictions" do
end
it "can restrict one tag from each group" do
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category))).to eq(['ford', 'honda'])
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic'])
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['mustang', 'taurus'])
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford', 'mustang']))).to eq([])
expect(
sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category)),
).to eq(%w[ford honda])
expect(
sorted_tag_names(
filter_allowed_tags(
for_input: true,
category: car_category,
selected_tags: ["honda"],
),
),
).to eq(%w[accord civic])
expect(
sorted_tag_names(
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]),
),
).to eq(%w[mustang taurus])
expect(
sorted_tag_names(
filter_allowed_tags(
for_input: true,
category: car_category,
selected_tags: %w[ford mustang],
),
),
).to eq([])
end
it "can apply the tags to a topic" do
post = create_post(category: car_category, tags: ['ford', 'mustang'])
expect(post.topic.tags.map(&:name).sort).to eq(['ford', 'mustang'])
post = create_post(category: car_category, tags: %w[ford mustang])
expect(post.topic.tags.map(&:name).sort).to eq(%w[ford mustang])
end
it "can remove extra tags from the same group" do
# A weird case that input field wouldn't allow.
# Only one tag from car makers is allowed, but we're saying that two have been selected.
names = filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda', 'ford']).map(&:name)
expect(names.include?('honda') || names.include?('ford')).to eq(false)
expect(names).to include('civic')
expect(names).to include('mustang')
names =
filter_allowed_tags(
for_input: true,
category: car_category,
selected_tags: %w[honda ford],
).map(&:name)
expect(names.include?("honda") || names.include?("ford")).to eq(false)
expect(names).to include("civic")
expect(names).to include("mustang")
end
end
end
@ -441,9 +774,9 @@ RSpec.describe "tag topic counts per category" do
end
it "counts when a topic is created with tags" do
expect {
Fabricate(:topic, category: category, tags: [tag1, tag2])
}.to change { CategoryTagStat.count }.by(2)
expect { Fabricate(:topic, category: category, tags: [tag1, tag2]) }.to change {
CategoryTagStat.count
}.by(2)
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(1)
expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(1)
end
@ -461,7 +794,7 @@ RSpec.describe "tag topic counts per category" do
context "with topic with 2 tags" do
fab!(:topic) { Fabricate(:topic, category: category, tags: [tag1, tag2]) }
fab!(:post) { Fabricate(:post, user: topic.user, topic: topic) }
fab!(:post) { Fabricate(:post, user: topic.user, topic: topic) }
it "has correct counts after tag is removed from a topic" do
post
@ -473,7 +806,12 @@ RSpec.describe "tag topic counts per category" do
end
it "has correct counts after a topic's category changes" do
PostRevisor.new(post).revise!(topic.user, category_id: category2.id, raw: post.raw, tags: [tag1.name, tag2.name])
PostRevisor.new(post).revise!(
topic.user,
category_id: category2.id,
raw: post.raw,
tags: [tag1.name, tag2.name],
)
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0)
expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(0)
expect(CategoryTagStat.where(category: category2, tag: tag1).sum(:topic_count)).to eq(1)
@ -481,7 +819,12 @@ RSpec.describe "tag topic counts per category" do
end
it "has correct counts after topic's category AND tags changed" do
PostRevisor.new(post).revise!(topic.user, raw: post.raw, tags: [tag2.name, tag3.name], category_id: category2.id)
PostRevisor.new(post).revise!(
topic.user,
raw: post.raw,
tags: [tag2.name, tag3.name],
category_id: category2.id,
)
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0)
expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(0)
expect(CategoryTagStat.where(category: category, tag: tag3).sum(:topic_count)).to eq(0)
@ -496,8 +839,18 @@ RSpec.describe "tag topic counts per category" do
fab!(:post) { Fabricate(:post, user: topic.user, topic: topic) }
it "counts after topic becomes uncategorized" do
PostRevisor.new(post).revise!(topic.user, raw: post.raw, tags: [tag1.name], category_id: SiteSetting.uncategorized_category_id)
expect(CategoryTagStat.where(category: Category.find(SiteSetting.uncategorized_category_id), tag: tag1).sum(:topic_count)).to eq(1)
PostRevisor.new(post).revise!(
topic.user,
raw: post.raw,
tags: [tag1.name],
category_id: SiteSetting.uncategorized_category_id,
)
expect(
CategoryTagStat.where(
category: Category.find(SiteSetting.uncategorized_category_id),
tag: tag1,
).sum(:topic_count),
).to eq(1)
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0)
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
RSpec.describe 'content security policy integration' do
RSpec.describe "content security policy integration" do
it "adds the csp headers correctly" do
SiteSetting.content_security_policy = false
get "/"
@ -15,8 +14,10 @@ RSpec.describe 'content security policy integration' do
context "with different hostnames" do
before do
SiteSetting.content_security_policy = true
RailsMultisite::ConnectionManagement.stubs(:current_db_hostnames).returns(['primary.example.com', 'secondary.example.com'])
RailsMultisite::ConnectionManagement.stubs(:current_hostname).returns('primary.example.com')
RailsMultisite::ConnectionManagement.stubs(:current_db_hostnames).returns(
%w[primary.example.com secondary.example.com],
)
RailsMultisite::ConnectionManagement.stubs(:current_hostname).returns("primary.example.com")
end
it "works with the primary domain" do
@ -52,5 +53,4 @@ RSpec.describe 'content security policy integration' do
expect(response.headers["Content-Security-Policy"]).to include("https://test.localhost")
end
end
end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
describe 'Discord OAuth2' do
describe "Discord OAuth2" do
let(:access_token) { "discord_access_token_448" }
let(:client_id) { "abcdef11223344" }
let(:client_secret) { "adddcccdddd99922" }
@ -9,15 +9,14 @@ describe 'Discord OAuth2' do
fab!(:user1) { Fabricate(:user) }
def setup_discord_email_stub(email, verified:)
stub_request(:get, "https://discord.com/api/users/@me")
.with(
headers: {
"Authorization" => "Bearer #{access_token}"
}
)
.to_return(
status: 200,
body: JSON.dump(
stub_request(:get, "https://discord.com/api/users/@me").with(
headers: {
"Authorization" => "Bearer #{access_token}",
},
).to_return(
status: 200,
body:
JSON.dump(
id: "80351110224678912",
username: "Nelly",
discriminator: "1337",
@ -26,14 +25,14 @@ describe 'Discord OAuth2' do
email: email,
flags: 64,
banner: "06c16474723fe537c283b8efa61a30c8",
accent_color: 16711680,
accent_color: 16_711_680,
premium_type: 1,
public_flags: 64
public_flags: 64,
),
headers: {
"Content-Type" => "application/json"
}
)
headers: {
"Content-Type" => "application/json",
},
)
end
before do
@ -41,50 +40,49 @@ describe 'Discord OAuth2' do
SiteSetting.discord_client_id = client_id
SiteSetting.discord_secret = client_secret
stub_request(:post, "https://discord.com/api/oauth2/token")
.with(
body: hash_including(
stub_request(:post, "https://discord.com/api/oauth2/token").with(
body:
hash_including(
"client_id" => client_id,
"client_secret" => client_secret,
"code" => temp_code,
"grant_type" => "authorization_code",
"redirect_uri" => "http://test.localhost/auth/discord/callback"
)
)
.to_return(
status: 200,
body: Rack::Utils.build_query(
"redirect_uri" => "http://test.localhost/auth/discord/callback",
),
).to_return(
status: 200,
body:
Rack::Utils.build_query(
access_token: access_token,
scope: "identify emails guilds",
token_type: "Bearer",
expires_in: 604800,
expires_in: 604_800,
refresh_token: "D43f5y0ahjqew82jZ4NViEr2YafMKhue",
),
headers: {
"Content-Type" => "application/x-www-form-urlencoded"
}
)
headers: {
"Content-Type" => "application/x-www-form-urlencoded",
},
)
stub_request(:get, "https://discord.com/api/users/@me/guilds")
.with(
headers: {
"Authorization" => "Bearer #{access_token}"
}
)
.to_return(
status: 200,
body: JSON.dump(
stub_request(:get, "https://discord.com/api/users/@me/guilds").with(
headers: {
"Authorization" => "Bearer #{access_token}",
},
).to_return(
status: 200,
body:
JSON.dump(
id: "80351110224678912",
name: "1337 Krew",
icon: "8342729096ea3675442027381ff50dfe",
owner: true,
permissions: "36953089",
features: ["COMMUNITY", "NEWS"]
features: %w[COMMUNITY NEWS],
),
headers: {
"Content-Type" => "application/json"
}
)
headers: {
"Content-Type" => "application/json",
},
)
end
it "doesn't sign in anyone if the email from discord is not verified" do
@ -94,10 +92,7 @@ describe 'Discord OAuth2' do
setup_discord_email_stub(user1.email, verified: false)
post "/auth/discord/callback", params: {
state: session["omniauth.state"],
code: temp_code
}
post "/auth/discord/callback", params: { state: session["omniauth.state"], code: temp_code }
expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/")
@ -111,10 +106,7 @@ describe 'Discord OAuth2' do
setup_discord_email_stub(user1.email, verified: true)
post "/auth/discord/callback", params: {
state: session["omniauth.state"],
code: temp_code
}
post "/auth/discord/callback", params: { state: session["omniauth.state"], code: temp_code }
expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/")

View File

@ -6,14 +6,14 @@ RSpec.describe EmailStyle do
SiteSetting.email_custom_template = "<hello>%{email_content}</hello><%= (111 * 333) %>"
html = Email::Renderer.new(UserNotifications.signup(Fabricate(:user))).html
expect(html).not_to include("36963")
expect(html).to include('<hello>')
expect(html).to include("<hello>")
end
end
context "with a custom template" do
before do
SiteSetting.email_custom_template = "<body><h1>FOR YOU</h1><div>%{email_content}</div></body>"
SiteSetting.email_custom_css = 'h1 { color: red; } div.body { color: #FAB; }'
SiteSetting.email_custom_css = "h1 { color: red; } div.body { color: #FAB; }"
SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css
end
@ -22,36 +22,36 @@ RSpec.describe EmailStyle do
SiteSetting.remove_override!(:email_custom_css)
end
context 'with invite' do
context "with invite" do
fab!(:invite) { Fabricate(:invite) }
let(:invite_mail) { InviteMailer.send_invite(invite) }
subject(:mail_html) { Email::Renderer.new(invite_mail).html }
it 'applies customizations' do
it "applies customizations" do
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}")
end
it 'applies customizations if compiled is missing' do
it "applies customizations if compiled is missing" do
SiteSetting.remove_override!(:email_custom_css_compiled)
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}")
end
it 'can apply RTL attrs' do
SiteSetting.default_locale = 'he'
it "can apply RTL attrs" do
SiteSetting.default_locale = "he"
body_attrs = mail_html.match(/<body ([^>])+/)
expect(body_attrs[0]&.downcase).to match(/text-align:\s*right/)
expect(body_attrs[0]&.downcase).to include('dir="rtl"')
end
end
context 'when user_replied' do
context "when user_replied" do
let(:response_by_user) { Fabricate(:user, name: "John Doe") }
let(:category) { Fabricate(:category, name: 'India') }
let(:category) { Fabricate(:category, name: "India") }
let(:topic) { Fabricate(:topic, category: category, title: "Super cool topic") }
let(:post) { Fabricate(:post, topic: topic, raw: 'This is My super duper cool topic') }
let(:post) { Fabricate(:post, topic: topic, raw: "This is My super duper cool topic") }
let(:response) { Fabricate(:basic_reply, topic: post.topic, user: response_by_user) }
let(:user) { Fabricate(:user) }
let(:notification) { Fabricate(:replied_notification, user: user, post: response) }
@ -61,7 +61,7 @@ RSpec.describe EmailStyle do
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
notification_data_hash: notification.data_hash,
)
end
@ -72,42 +72,45 @@ RSpec.describe EmailStyle do
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
matches = mail_html.match(/<div style="([^"]+)" dm=\"body\">#{post.raw}/)
expect(matches[1]).to include('color: #FAB;') # custom
expect(matches[1]).to include('padding-top:5px;') # div.body
expect(matches[1]).to include("color: #FAB;") # custom
expect(matches[1]).to include("padding-top:5px;") # div.body
end
# TODO: translation override
end
context 'with signup' do
context "with signup" do
let(:signup_mail) { UserNotifications.signup(Fabricate(:user)) }
subject(:mail_html) { Email::Renderer.new(signup_mail).html }
it "customizations are applied to html part of emails" do
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
expect(mail_html).to include('activate-account')
expect(mail_html).to include("activate-account")
end
context 'with translation override' do
context "with translation override" do
before do
TranslationOverride.upsert!(
SiteSetting.default_locale,
'user_notifications.signup.text_body_template',
"CLICK THAT LINK: %{base_url}/u/activate-account/%{email_token}"
"user_notifications.signup.text_body_template",
"CLICK THAT LINK: %{base_url}/u/activate-account/%{email_token}",
)
end
after do
TranslationOverride.revert!(SiteSetting.default_locale, ['user_notifications.signup.text_body_template'])
TranslationOverride.revert!(
SiteSetting.default_locale,
["user_notifications.signup.text_body_template"],
)
end
it "applies customizations when translation override exists" do
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
expect(mail_html.scan('CLICK THAT LINK').count).to eq(1)
expect(mail_html.scan("CLICK THAT LINK").count).to eq(1)
end
end
context 'with some bad css' do
context "with some bad css" do
before do
SiteSetting.email_custom_css = '@import "nope.css"; h1 {{{ size: really big; '
SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css
@ -115,13 +118,15 @@ RSpec.describe EmailStyle do
it "can render the html" do
expect(mail_html.scan(/<h1\s*(?:style=""){0,1}>FOR YOU<\/h1>/).count).to eq(1)
expect(mail_html).to include('activate-account')
expect(mail_html).to include("activate-account")
end
end
end
context 'with digest' do
fab!(:popular_topic) { Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago) }
context "with digest" do
fab!(:popular_topic) do
Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago)
end
let(:summary_email) { UserNotifications.digest(Fabricate(:user)) }
subject(:mail_html) { Email::Renderer.new(summary_email).html }
@ -133,7 +138,7 @@ RSpec.describe EmailStyle do
it "doesn't apply customizations if apply_custom_styles_to_digest is disabled" do
SiteSetting.apply_custom_styles_to_digest = false
expect(mail_html).to_not include('<h1 style="color: red;">FOR YOU</h1>')
expect(mail_html).to_not include('FOR YOU')
expect(mail_html).to_not include("FOR YOU")
expect(mail_html).to include(popular_topic.title)
end
end

View File

@ -1,11 +1,13 @@
# frozen_string_literal: true
describe 'Facebook OAuth2' do
describe "Facebook OAuth2" do
let(:access_token) { "facebook_access_token_448" }
let(:app_id) { "432489234823984" }
let(:app_secret) { "adddcccdddd99922" }
let(:temp_code) { "facebook_temp_code_544254" }
let(:appsecret_proof) { OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, app_secret, access_token) }
let(:appsecret_proof) do
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, app_secret, access_token)
end
fab!(:user1) { Fabricate(:user) }
@ -18,19 +20,16 @@ describe 'Facebook OAuth2' do
}
body[:email] = email if email
stub_request(:get, "https://graph.facebook.com/v5.0/me?appsecret_proof=#{appsecret_proof}&fields=name,first_name,last_name,email")
.with(
headers: {
"Authorization" => "OAuth #{access_token}"
}
)
.to_return(
status: 200,
body: JSON.dump(body),
headers: {
"Content-Type" => "application/json"
}
)
stub_request(
:get,
"https://graph.facebook.com/v5.0/me?appsecret_proof=#{appsecret_proof}&fields=name,first_name,last_name,email",
).with(headers: { "Authorization" => "OAuth #{access_token}" }).to_return(
status: 200,
body: JSON.dump(body),
headers: {
"Content-Type" => "application/json",
},
)
end
before do
@ -38,27 +37,23 @@ describe 'Facebook OAuth2' do
SiteSetting.facebook_app_id = app_id
SiteSetting.facebook_app_secret = app_secret
stub_request(:post, "https://graph.facebook.com/v5.0/oauth/access_token")
.with(
body: hash_including(
stub_request(:post, "https://graph.facebook.com/v5.0/oauth/access_token").with(
body:
hash_including(
"client_id" => app_id,
"client_secret" => app_secret,
"code" => temp_code,
"grant_type" => "authorization_code",
"redirect_uri" => "http://test.localhost/auth/facebook/callback"
)
)
.to_return(
status: 200,
body: Rack::Utils.build_query(
access_token: access_token,
scope: "email",
token_type: "Bearer",
"redirect_uri" => "http://test.localhost/auth/facebook/callback",
),
headers: {
"Content-Type" => "application/x-www-form-urlencoded"
}
)
).to_return(
status: 200,
body:
Rack::Utils.build_query(access_token: access_token, scope: "email", token_type: "Bearer"),
headers: {
"Content-Type" => "application/x-www-form-urlencoded",
},
)
end
it "signs in the user if the API response from facebook includes an email (implies it's verified) and the email matches an existing user's" do
@ -68,10 +63,7 @@ describe 'Facebook OAuth2' do
setup_facebook_email_stub(email: user1.email)
post "/auth/facebook/callback", params: {
state: session["omniauth.state"],
code: temp_code
}
post "/auth/facebook/callback", params: { state: session["omniauth.state"], code: temp_code }
expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/")
@ -85,10 +77,7 @@ describe 'Facebook OAuth2' do
setup_facebook_email_stub(email: nil)
post "/auth/facebook/callback", params: {
state: session["omniauth.state"],
code: temp_code
}
post "/auth/facebook/callback", params: { state: session["omniauth.state"], code: temp_code }
expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/")

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
RSpec.describe PostAction do
it "triggers the 'flag_reviewed' event when there was at least one flag" do
admin = Fabricate(:admin)
@ -14,5 +13,4 @@ RSpec.describe PostAction do
events = DiscourseEvent.track_events { PostDestroyer.new(admin, flagged_post).destroy }
expect(events.map { |e| e[:event_name] }).to include(:flag_reviewed)
end
end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
describe 'GitHub Oauth2' do
describe "GitHub Oauth2" do
let(:access_token) { "github_access_token_448" }
let(:client_id) { "abcdef11223344" }
let(:client_secret) { "adddcccdddd99922" }
@ -10,19 +10,17 @@ describe 'GitHub Oauth2' do
fab!(:user2) { Fabricate(:user) }
def setup_github_emails_stub(emails)
stub_request(:get, "https://api.github.com/user/emails")
.with(
headers: {
"Authorization" => "Bearer #{access_token}"
}
)
.to_return(
status: 200,
body: JSON.dump(emails),
headers: {
"Content-Type" => "application/json"
}
)
stub_request(:get, "https://api.github.com/user/emails").with(
headers: {
"Authorization" => "Bearer #{access_token}",
},
).to_return(
status: 200,
body: JSON.dump(emails),
headers: {
"Content-Type" => "application/json",
},
)
end
before do
@ -30,35 +28,34 @@ describe 'GitHub Oauth2' do
SiteSetting.github_client_id = client_id
SiteSetting.github_client_secret = client_secret
stub_request(:post, "https://github.com/login/oauth/access_token")
.with(
body: hash_including(
stub_request(:post, "https://github.com/login/oauth/access_token").with(
body:
hash_including(
"client_id" => client_id,
"client_secret" => client_secret,
"code" => temp_code,
)
)
.to_return(
status: 200,
body: Rack::Utils.build_query(
),
).to_return(
status: 200,
body:
Rack::Utils.build_query(
access_token: access_token,
scope: "user:email",
token_type: "bearer"
token_type: "bearer",
),
headers: {
"Content-Type" => "application/x-www-form-urlencoded"
}
)
headers: {
"Content-Type" => "application/x-www-form-urlencoded",
},
)
stub_request(:get, "https://api.github.com/user")
.with(
headers: {
"Authorization" => "Bearer #{access_token}"
}
)
.to_return(
status: 200,
body: JSON.dump(
stub_request(:get, "https://api.github.com/user").with(
headers: {
"Authorization" => "Bearer #{access_token}",
},
).to_return(
status: 200,
body:
JSON.dump(
login: "octocat",
id: 1,
node_id: "MDQ6VXNlcjE=",
@ -94,20 +91,20 @@ describe 'GitHub Oauth2' do
private_gists: 81,
total_private_repos: 100,
owned_private_repos: 100,
disk_usage: 10000,
disk_usage: 10_000,
collaborators: 8,
two_factor_authentication: true,
plan: {
name: "Medium",
space: 400,
private_repos: 20,
collaborators: 0
}
collaborators: 0,
},
),
headers: {
"Content-Type" => "application/json"
}
)
headers: {
"Content-Type" => "application/json",
},
)
end
it "doesn't sign in anyone if none of the emails from github are verified" do
@ -117,25 +114,12 @@ describe 'GitHub Oauth2' do
setup_github_emails_stub(
[
{
email: user1.email,
primary: true,
verified: false,
visibility: "private"
},
{
email: user2.email,
primary: false,
verified: false,
visibility: "private"
}
]
{ email: user1.email, primary: true, verified: false, visibility: "private" },
{ email: user2.email, primary: false, verified: false, visibility: "private" },
],
)
post "/auth/github/callback", params: {
state: session["omniauth.state"],
code: temp_code
}
post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/")
expect(session[:current_user_id]).to be_blank
@ -148,25 +132,12 @@ describe 'GitHub Oauth2' do
setup_github_emails_stub(
[
{
email: user1.email,
primary: true,
verified: false,
visibility: "private"
},
{
email: user2.email,
primary: false,
verified: true,
visibility: "private"
}
]
{ email: user1.email, primary: true, verified: false, visibility: "private" },
{ email: user2.email, primary: false, verified: true, visibility: "private" },
],
)
post "/auth/github/callback", params: {
state: session["omniauth.state"],
code: temp_code
}
post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/")
expect(session[:current_user_id]).to eq(user2.id)
@ -183,21 +154,13 @@ describe 'GitHub Oauth2' do
email: "somerandomemail@discourse.org",
primary: true,
verified: true,
visibility: "private"
visibility: "private",
},
{
email: user2.email,
primary: false,
verified: false,
visibility: "private"
}
]
{ email: user2.email, primary: false, verified: false, visibility: "private" },
],
)
post "/auth/github/callback", params: {
state: session["omniauth.state"],
code: temp_code
}
post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/")
expect(session[:current_user_id]).to be_blank
@ -210,25 +173,12 @@ describe 'GitHub Oauth2' do
setup_github_emails_stub(
[
{
email: user1.email,
primary: true,
verified: true,
visibility: "private"
},
{
email: user2.email,
primary: false,
verified: true,
visibility: "private"
}
]
{ email: user1.email, primary: true, verified: true, visibility: "private" },
{ email: user2.email, primary: false, verified: true, visibility: "private" },
],
)
post "/auth/github/callback", params: {
state: session["omniauth.state"],
code: temp_code
}
post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/")
expect(session[:current_user_id]).to eq(user1.id)

View File

@ -6,18 +6,20 @@ RSpec.describe Group do
:group,
visibility_level: Group.visibility_levels[:public],
mentionable_level: Group::ALIAS_LEVELS[:nobody],
users: [ Fabricate(:user) ]
users: [Fabricate(:user)],
)
end
let(:post) { Fabricate(:post, raw: "mention @#{group.name}") }
before do
Jobs.run_immediately!
end
before { Jobs.run_immediately! }
it 'users can mention public groups, but does not create a notification' do
expect { post }.not_to change { Notification.where(notification_type: Notification.types[:group_mentioned]).count }
expect(post.cooked).to include("<a class=\"mention-group\" href=\"/groups/#{group.name}\">@#{group.name}</a>")
it "users can mention public groups, but does not create a notification" do
expect { post }.not_to change {
Notification.where(notification_type: Notification.types[:group_mentioned]).count
}
expect(post.cooked).to include(
"<a class=\"mention-group\" href=\"/groups/#{group.name}\">@#{group.name}</a>",
)
end
end

View File

@ -1,27 +1,31 @@
# frozen_string_literal: true
RSpec.describe 'invalid requests', type: :request do
RSpec.describe "invalid requests", type: :request do
before do
@orig_logger = Rails.logger
Rails.logger = @fake_logger = FakeLogger.new
end
after do
Rails.logger = @orig_logger
end
after { Rails.logger = @orig_logger }
it "handles NotFound with invalid json body" do
post "/latest.json", params: "{some: malformed: json", headers: { "content-type" => "application/json" }
post "/latest.json",
params: "{some: malformed: json",
headers: {
"content-type" => "application/json",
}
expect(response.status).to eq(404)
expect(@fake_logger.warnings.length).to eq(0)
expect(@fake_logger.errors.length).to eq(0)
end
it "handles EOFError when multipart request is malformed" do
post "/latest.json", params: "somecontent", headers: {
"content-type" => "multipart/form-data; boundary=abcde",
"content-length" => "1"
}
post "/latest.json",
params: "somecontent",
headers: {
"content-type" => "multipart/form-data; boundary=abcde",
"content-length" => "1",
}
expect(response.status).to eq(400)
expect(@fake_logger.warnings.length).to eq(0)
expect(@fake_logger.errors.length).to eq(0)
@ -33,5 +37,4 @@ RSpec.describe 'invalid requests', type: :request do
expect(@fake_logger.warnings.length).to eq(0)
expect(@fake_logger.errors.length).to eq(0)
end
end

View File

@ -1,45 +1,46 @@
# encoding: UTF-8
# frozen_string_literal: true
RSpec.describe 'invite only' do
describe '#create invite only' do
it 'can create user via API' do
RSpec.describe "invite only" do
describe "#create invite only" do
it "can create user via API" do
SiteSetting.invite_only = true
Jobs.run_immediately!
admin = Fabricate(:admin)
api_key = Fabricate(:api_key, user: admin)
post '/users.json', params: {
name: 'bob',
username: 'bob',
password: 'strongpassword',
email: 'bob@bob.com',
}, headers: {
HTTP_API_KEY: api_key.key,
HTTP_API_USERNAME: admin.username
}
post "/users.json",
params: {
name: "bob",
username: "bob",
password: "strongpassword",
email: "bob@bob.com",
},
headers: {
HTTP_API_KEY: api_key.key,
HTTP_API_USERNAME: admin.username,
}
user_id = response.parsed_body["user_id"]
expect(user_id).to be > 0
# activate and approve
put "/admin/users/#{user_id}/activate.json", headers: {
HTTP_API_KEY: api_key.key,
HTTP_API_USERNAME: admin.username
}
put "/admin/users/#{user_id}/activate.json",
headers: {
HTTP_API_KEY: api_key.key,
HTTP_API_USERNAME: admin.username,
}
put "/admin/users/#{user_id}/approve.json", headers: {
HTTP_API_KEY: api_key.key,
HTTP_API_USERNAME: admin.username
}
put "/admin/users/#{user_id}/approve.json",
headers: {
HTTP_API_KEY: api_key.key,
HTTP_API_USERNAME: admin.username,
}
u = User.find(user_id)
expect(u.active).to eq(true)
expect(u.approved).to eq(true)
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
RSpec.describe 'message bus integration' do
RSpec.describe "message bus integration" do
it "allows anonymous requests to the messagebus" do
post "/message-bus/poll"
expect(response.status).to eq(200)
@ -27,5 +26,4 @@ RSpec.describe 'message bus integration' do
expect(response.status).to eq(200)
end
end
end

View File

@ -1,19 +1,25 @@
# frozen_string_literal: true
RSpec.describe 'multisite', type: [:multisite, :request] do
RSpec.describe "multisite", type: %i[multisite request] do
it "works" do
get "http://test.localhost/session/csrf.json"
expect(response.status).to eq(200)
cookie = CGI.escape(response.cookies["_forum_session"])
id1 = session["session_id"]
get "http://test.localhost/session/csrf.json", headers: { "Cookie" => "_forum_session=#{cookie};" }
get "http://test.localhost/session/csrf.json",
headers: {
"Cookie" => "_forum_session=#{cookie};",
}
expect(response.status).to eq(200)
id2 = session["session_id"]
expect(id1).to eq(id2)
get "http://test2.localhost/session/csrf.json", headers: { "Cookie" => "_forum_session=#{cookie};" }
get "http://test2.localhost/session/csrf.json",
headers: {
"Cookie" => "_forum_session=#{cookie};",
}
expect(response.status).to eq(200)
id3 = session["session_id"]

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
RSpec.describe 'multisite', type: [:multisite, :request] do
RSpec.describe "multisite", type: %i[multisite request] do
it "should always allow /srv/status through" do
get "http://unknown.com/srv/status"
expect(response.status).to eq(200)
@ -13,10 +13,12 @@ RSpec.describe 'multisite', type: [:multisite, :request] do
end
it "should hit correct site otherwise" do
site_1_url = Fabricate(:topic, title: "Site 1 Topic Title", user: Discourse.system_user).relative_url
site_1_url =
Fabricate(:topic, title: "Site 1 Topic Title", user: Discourse.system_user).relative_url
test_multisite_connection('second') do
site_2_url = Fabricate(:topic, title: "Site 2 Topic Title", user: Discourse.system_user).relative_url
test_multisite_connection("second") do
site_2_url =
Fabricate(:topic, title: "Site 2 Topic Title", user: Discourse.system_user).relative_url
get "http://test.localhost/#{site_1_url}.json"
expect(request.env["RAILS_MULTISITE_HOST"]).to eq("test.localhost")

View File

@ -1,8 +1,7 @@
# encoding: UTF-8
# frozen_string_literal: true
RSpec.describe 'rate limiter integration' do
RSpec.describe "rate limiter integration" do
before do
RateLimiter.enable
RateLimiter.clear_all!
@ -13,12 +12,13 @@ RSpec.describe 'rate limiter integration' do
global_setting :reject_message_bus_queue_seconds, 0.1
post "/message-bus/#{SecureRandom.hex}/poll", headers: {
"HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.2}"
}
post "/message-bus/#{SecureRandom.hex}/poll",
headers: {
"HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.2}",
}
expect(response.status).to eq(429)
expect(response.headers['Retry-After'].to_i).to be > 29
expect(response.headers["Retry-After"].to_i).to be > 29
end
it "will not rate limit when all is good" do
@ -26,9 +26,10 @@ RSpec.describe 'rate limiter integration' do
global_setting :reject_message_bus_queue_seconds, 0.1
post "/message-bus/#{SecureRandom.hex}/poll", headers: {
"HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.05}"
}
post "/message-bus/#{SecureRandom.hex}/poll",
headers: {
"HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.05}",
}
expect(response.status).to eq(200)
end
@ -37,15 +38,15 @@ RSpec.describe 'rate limiter integration' do
name = Auth::DefaultCurrentUserProvider::TOKEN_COOKIE
# we try 11 times because the rate limit is 10
11.times {
11.times do
cookies[name] = SecureRandom.hex
get '/categories.json'
get "/categories.json"
expect(response.cookies.has_key?(name)).to eq(true)
expect(response.cookies[name]).to be_nil
}
end
end
it 'can cleanly limit requests and sets a Retry-After header' do
it "can cleanly limit requests and sets a Retry-After header" do
freeze_time
RateLimiter.clear_all!
@ -55,17 +56,19 @@ RSpec.describe 'rate limiter integration' do
global_setting :max_admin_api_reqs_per_minute, 1
get '/admin/api/keys.json', headers: {
HTTP_API_KEY: api_key.key,
HTTP_API_USERNAME: admin.username
}
get "/admin/api/keys.json",
headers: {
HTTP_API_KEY: api_key.key,
HTTP_API_USERNAME: admin.username,
}
expect(response.status).to eq(200)
get '/admin/api/keys.json', headers: {
HTTP_API_KEY: api_key.key,
HTTP_API_USERNAME: admin.username
}
get "/admin/api/keys.json",
headers: {
HTTP_API_KEY: api_key.key,
HTTP_API_USERNAME: admin.username,
}
expect(response.status).to eq(429)

View File

@ -1,16 +1,16 @@
# frozen_string_literal: true
RSpec.describe 'request tracker' do
RSpec.describe "request tracker" do
let(:api_key) do
Fabricate(
:api_key,
user: Fabricate.build(:user),
api_key_scopes: [ApiKeyScope.new(resource: 'users', action: 'show')]
api_key_scopes: [ApiKeyScope.new(resource: "users", action: "show")],
)
end
let(:user_api_key) do
Fabricate(:user_api_key, scopes: [Fabricate.build(:user_api_key_scope, name: 'session_info')])
Fabricate(:user_api_key, scopes: [Fabricate.build(:user_api_key_scope, name: "session_info")])
end
before do
@ -25,8 +25,8 @@ RSpec.describe 'request tracker' do
CachedCounting.disable
end
context 'when using an api key' do
it 'is counted as an API request' do
context "when using an api key" do
it "is counted as an API request" do
get "/u/#{api_key.user.username}.json", headers: { HTTP_API_KEY: api_key.key }
expect(response.status).to eq(200)
@ -37,9 +37,9 @@ RSpec.describe 'request tracker' do
end
end
context 'when using an user api key' do
it 'is counted as a user API request' do
get '/session/current.json', headers: { HTTP_USER_API_KEY: user_api_key.key }
context "when using an user api key" do
it "is counted as a user API request" do
get "/session/current.json", headers: { HTTP_USER_API_KEY: user_api_key.key }
expect(response.status).to eq(200)
CachedCounting.flush

View File

@ -2,45 +2,41 @@
# frozen_string_literal: true
RSpec.describe "spammers on same IP" do
let(:ip_address) { '182.189.119.174' }
let!(:spammer1) { Fabricate(:user, ip_address: ip_address) }
let!(:spammer2) { Fabricate(:user, ip_address: ip_address) }
let(:spammer3) { Fabricate(:user, ip_address: ip_address) }
let(:ip_address) { "182.189.119.174" }
let!(:spammer1) { Fabricate(:user, ip_address: ip_address) }
let!(:spammer2) { Fabricate(:user, ip_address: ip_address) }
let(:spammer3) { Fabricate(:user, ip_address: ip_address) }
context 'when flag_sockpuppets is disabled' do
let!(:first_post) { create_post(user: spammer1) }
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
context "when flag_sockpuppets is disabled" do
let!(:first_post) { create_post(user: spammer1) }
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
it 'should not increase spam count' do
expect(first_post.reload.spam_count).to eq(0)
it "should not increase spam count" do
expect(first_post.reload.spam_count).to eq(0)
expect(second_post.reload.spam_count).to eq(0)
end
end
context 'when flag_sockpuppets is enabled' do
before do
SiteSetting.flag_sockpuppets = true
end
context "when flag_sockpuppets is enabled" do
before { SiteSetting.flag_sockpuppets = true }
after do
SiteSetting.flag_sockpuppets = false
end
after { SiteSetting.flag_sockpuppets = false }
context 'when first spammer starts a topic' do
context "when first spammer starts a topic" do
let!(:first_post) { create_post(user: spammer1) }
context 'when second spammer replies' do
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
context "when second spammer replies" do
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
it 'should increase spam count' do
it "should increase spam count" do
expect(first_post.reload.spam_count).to eq(1)
expect(second_post.reload.spam_count).to eq(1)
end
context 'with third spam post' do
context "with third spam post" do
let!(:third_post) { create_post(user: spammer3, topic: first_post.topic) }
it 'should increase spam count' do
it "should increase spam count" do
expect(first_post.reload.spam_count).to eq(1)
expect(second_post.reload.spam_count).to eq(1)
expect(third_post.reload.spam_count).to eq(1)
@ -49,16 +45,18 @@ RSpec.describe "spammers on same IP" do
end
end
context 'when first user is not new' do
let!(:old_user) { Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago, trust_level: TrustLevel[1]) }
context "when first user is not new" do
let!(:old_user) do
Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago, trust_level: TrustLevel[1])
end
context 'when first user starts a topic' do
context "when first user starts a topic" do
let!(:first_post) { create_post(user: old_user) }
context 'with a reply by a new user at the same IP address' do
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
context "with a reply by a new user at the same IP address" do
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
it 'should increase the spam count correctly' do
it "should increase the spam count correctly" do
expect(first_post.reload.spam_count).to eq(0)
expect(second_post.reload.spam_count).to eq(1)
end

View File

@ -2,11 +2,11 @@
# frozen_string_literal: true
RSpec.describe "spam rules for users" do
describe 'auto-silence users based on flagging' do
fab!(:admin) { Fabricate(:admin) } # needed to send a system message
describe "auto-silence users based on flagging" do
fab!(:admin) { Fabricate(:admin) } # needed to send a system message
fab!(:moderator) { Fabricate(:moderator) }
fab!(:user1) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) }
fab!(:user1) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) }
before do
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:disabled]
@ -15,23 +15,21 @@ RSpec.describe "spam rules for users" do
SiteSetting.num_users_to_silence_new_user = 2
end
context 'when spammer is a new user' do
fab!(:spammer) { Fabricate(:user, trust_level: TrustLevel[0]) }
context "when spammer is a new user" do
fab!(:spammer) { Fabricate(:user, trust_level: TrustLevel[0]) }
context 'when spammer post is not flagged enough times' do
let!(:spam_post) { create_post(user: spammer) }
context "when spammer post is not flagged enough times" do
let!(:spam_post) { create_post(user: spammer) }
let!(:spam_post2) { create_post(user: spammer) }
before do
PostActionCreator.create(user1, spam_post, :spam)
end
before { PostActionCreator.create(user1, spam_post, :spam) }
it 'should not hide the post' do
it "should not hide the post" do
expect(spam_post.reload).to_not be_hidden
end
context 'when spam posts are flagged enough times, but not by enough users' do
it 'should not hide the post' do
context "when spam posts are flagged enough times, but not by enough users" do
it "should not hide the post" do
PostActionCreator.create(user1, spam_post2, :spam)
expect(spam_post.reload).to_not be_hidden
@ -40,16 +38,30 @@ RSpec.describe "spam rules for users" do
end
end
context 'when one spam post is flagged enough times by enough users' do
context "when one spam post is flagged enough times by enough users" do
fab!(:another_topic) { Fabricate(:topic) }
let!(:private_messages_count) { spammer.private_topics_count }
let!(:mod_pm_count) { moderator.private_topics_count }
let!(:reviewable) { PostActionCreator.spam(user2, spam_post).reviewable }
it 'should hide the posts' do
it "should hide the posts" do
expect(Guardian.new(spammer).can_create_topic?(nil)).to be(false)
expect { PostCreator.create(spammer, title: 'limited time offer for you', raw: 'better buy this stuff ok', archetype_id: 1) }.to raise_error(Discourse::InvalidAccess)
expect(PostCreator.create(spammer, topic_id: another_topic.id, raw: 'my reply is spam in your topic', archetype_id: 1)).to eq(nil)
expect {
PostCreator.create(
spammer,
title: "limited time offer for you",
raw: "better buy this stuff ok",
archetype_id: 1,
)
}.to raise_error(Discourse::InvalidAccess)
expect(
PostCreator.create(
spammer,
topic_id: another_topic.id,
raw: "my reply is spam in your topic",
archetype_id: 1,
),
).to eq(nil)
expect(spammer.reload).to be_silenced
expect(spam_post.reload).to be_hidden
expect(spam_post2.reload).to be_hidden
@ -57,22 +69,24 @@ RSpec.describe "spam rules for users" do
end
context "when a post is deleted" do
it 'should silence the spammer' do
spam_post.trash!(moderator); spammer.reload
it "should silence the spammer" do
spam_post.trash!(moderator)
spammer.reload
expect(spammer.reload).to be_silenced
end
end
context "when spammer becomes trust level 1" do
it 'should silence the spammer' do
spammer.change_trust_level!(TrustLevel[1]); spammer.reload
it "should silence the spammer" do
spammer.change_trust_level!(TrustLevel[1])
spammer.reload
expect(spammer.reload).to be_silenced
end
end
end
context 'with hide_post_sensitivity' do
it 'should silence the spammer' do
context "with hide_post_sensitivity" do
it "should silence the spammer" do
Reviewable.set_priorities(high: 2.0)
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:low]
PostActionCreator.create(user2, spam_post, :spam)
@ -84,19 +98,26 @@ RSpec.describe "spam rules for users" do
end
context "when spammer has trust level basic" do
let(:spammer) { Fabricate(:user, trust_level: TrustLevel[1]) }
let(:spammer) { Fabricate(:user, trust_level: TrustLevel[1]) }
context 'when one spam post is flagged enough times by enough users' do
let!(:spam_post) { Fabricate(:post, user: spammer) }
context "when one spam post is flagged enough times by enough users" do
let!(:spam_post) { Fabricate(:post, user: spammer) }
let!(:private_messages_count) { spammer.private_topics_count }
it 'should not allow spammer to create new posts' do
it "should not allow spammer to create new posts" do
PostActionCreator.create(user1, spam_post, :spam)
PostActionCreator.create(user2, spam_post, :spam)
expect(spam_post.reload).to_not be_hidden
expect(Guardian.new(spammer).can_create_topic?(nil)).to be(true)
expect { PostCreator.create(spammer, title: 'limited time offer for you', raw: 'better buy this stuff ok', archetype_id: 1) }.to_not raise_error
expect {
PostCreator.create(
spammer,
title: "limited time offer for you",
raw: "better buy this stuff ok",
archetype_id: 1,
)
}.to_not raise_error
expect(spammer.reload.private_topics_count).to eq(private_messages_count)
end
end
@ -104,11 +125,11 @@ RSpec.describe "spam rules for users" do
[[:user, trust_level: TrustLevel[2]], [:admin], [:moderator]].each do |spammer_args|
context "spammer is trusted #{spammer_args[0]}" do
let!(:spammer) { Fabricate(*spammer_args) }
let!(:spam_post) { Fabricate(:post, user: spammer) }
let!(:spammer) { Fabricate(*spammer_args) }
let!(:spam_post) { Fabricate(:post, user: spammer) }
let!(:private_messages_count) { spammer.private_topics_count }
it 'should not hide the post' do
it "should not hide the post" do
PostActionCreator.create(user1, spam_post, :spam)
PostActionCreator.create(user2, spam_post, :spam)

View File

@ -4,36 +4,34 @@
RSpec.describe Topic do
let(:job_klass) { Jobs::CloseTopic }
context 'when creating a topic without auto-close' do
context "when creating a topic without auto-close" do
let(:topic) { Fabricate(:topic, category: category) }
context 'when uncategorized' do
context "when uncategorized" do
let(:category) { nil }
it 'should not schedule the topic to auto-close' do
it "should not schedule the topic to auto-close" do
expect(topic.public_topic_timer).to eq(nil)
expect(job_klass.jobs).to eq([])
end
end
context 'with category without default auto-close' do
context "with category without default auto-close" do
let(:category) { Fabricate(:category, auto_close_hours: nil) }
it 'should not schedule the topic to auto-close' do
it "should not schedule the topic to auto-close" do
expect(topic.public_topic_timer).to eq(nil)
expect(job_klass.jobs).to eq([])
end
end
context 'when jobs may be queued' do
before do
freeze_time
end
context "when jobs may be queued" do
before { freeze_time }
context 'when category has a default auto-close' do
context "when category has a default auto-close" do
let(:category) { Fabricate(:category, auto_close_hours: 2.0) }
it 'should schedule the topic to auto-close' do
it "should schedule the topic to auto-close" do
topic
topic_status_update = TopicTimer.last
@ -42,11 +40,11 @@ RSpec.describe Topic do
expect(topic.public_topic_timer.execute_at).to be_within_one_second_of(2.hours.from_now)
end
context 'when topic was created by staff user' do
context "when topic was created by staff user" do
let(:admin) { Fabricate(:admin) }
let(:staff_topic) { Fabricate(:topic, user: admin, category: category) }
it 'should schedule the topic to auto-close' do
it "should schedule the topic to auto-close" do
staff_topic
topic_status_update = TopicTimer.last
@ -56,23 +54,24 @@ RSpec.describe Topic do
expect(topic_status_update.user).to eq(Discourse.system_user)
end
context 'when topic is closed manually' do
it 'should remove the schedule to auto-close the topic' do
context "when topic is closed manually" do
it "should remove the schedule to auto-close the topic" do
topic_timer_id = staff_topic.public_topic_timer.id
staff_topic.update_status('closed', true, admin)
staff_topic.update_status("closed", true, admin)
expect(TopicTimer.with_deleted.find(topic_timer_id).deleted_at)
.to be_within_one_second_of(Time.zone.now)
expect(
TopicTimer.with_deleted.find(topic_timer_id).deleted_at,
).to be_within_one_second_of(Time.zone.now)
end
end
end
context 'when topic was created by a non-staff user' do
context "when topic was created by a non-staff user" do
let(:regular_user) { Fabricate(:user) }
let(:regular_user_topic) { Fabricate(:topic, user: regular_user, category: category) }
it 'should schedule the topic to auto-close' do
it "should schedule the topic to auto-close" do
regular_user_topic
topic_status_update = TopicTimer.last

View File

@ -9,11 +9,11 @@ RSpec.describe "Topic Thumbnails" do
fab!(:topic) { Fabricate(:topic, image_upload_id: image.id) }
fab!(:user) { Fabricate(:user) }
describe 'latest' do
describe "latest" do
def get_topic
Discourse.redis.del(topic.thumbnail_job_redis_key(Topic.thumbnail_sizes))
Discourse.redis.del(topic.thumbnail_job_redis_key([]))
get '/latest.json'
get "/latest.json"
expect(response.status).to eq(200)
response.parsed_body["topic_list"]["topics"][0]
end
@ -27,11 +27,7 @@ RSpec.describe "Topic Thumbnails" do
context "with a theme" do
before do
theme = Fabricate(:theme)
theme.theme_modifier_set.topic_thumbnail_sizes = [
[10, 10],
[20, 20],
[30, 30]
]
theme.theme_modifier_set.topic_thumbnail_sizes = [[10, 10], [20, 20], [30, 30]]
theme.theme_modifier_set.save!
theme.set_default!
end
@ -39,15 +35,15 @@ RSpec.describe "Topic Thumbnails" do
it "includes the theme specified resolutions" do
topic_json = nil
expect do
topic_json = get_topic
end.to change { Jobs::GenerateTopicThumbnails.jobs.size }.by(2)
expect do topic_json = get_topic end.to change {
Jobs::GenerateTopicThumbnails.jobs.size
}.by(2)
expect(
Jobs::GenerateTopicThumbnails.jobs.map { |j| j["args"][0]["extra_sizes"] }
).to eq([
nil, # Job for core/plugin sizes
[[10, 10], [20, 20], [30, 30]]] # Job for theme sizes
expect(Jobs::GenerateTopicThumbnails.jobs.map { |j| j["args"][0]["extra_sizes"] }).to eq(
[
nil, # Job for core/plugin sizes
[[10, 10], [20, 20], [30, 30]],
], # Job for theme sizes
)
thumbnails = topic_json["thumbnails"]
@ -67,9 +63,9 @@ RSpec.describe "Topic Thumbnails" do
Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access)
# Request again
expect do
topic_json = get_topic
end.not_to change { Jobs::GenerateTopicThumbnails.jobs.size }
expect do topic_json = get_topic end.not_to change {
Jobs::GenerateTopicThumbnails.jobs.size
}
thumbnails = topic_json["thumbnails"]
@ -82,7 +78,6 @@ RSpec.describe "Topic Thumbnails" do
expect(thumbnails[1]["width"]).to eq(9)
expect(thumbnails[1]["height"]).to eq(9)
expect(thumbnails[1]["url"]).to include("/optimized/")
end
end
@ -92,25 +87,23 @@ RSpec.describe "Topic Thumbnails" do
plugin.register_topic_thumbnail_size [512, 512]
end
after do
DiscoursePluginRegistry.reset!
end
after { DiscoursePluginRegistry.reset! }
it "includes the theme specified resolutions" do
topic_json = nil
expect do
topic_json = get_topic
end.to change { Jobs::GenerateTopicThumbnails.jobs.size }.by(1)
expect do topic_json = get_topic end.to change {
Jobs::GenerateTopicThumbnails.jobs.size
}.by(1)
# Run the job
args = Jobs::GenerateTopicThumbnails.jobs.last["args"].first
Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access)
# Request again
expect do
topic_json = get_topic
end.not_to change { Jobs::GenerateTopicThumbnails.jobs.size }
expect do topic_json = get_topic end.not_to change {
Jobs::GenerateTopicThumbnails.jobs.size
}
thumbnails = topic_json["thumbnails"]

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
describe 'Twitter OAuth 1.0a' do
describe "Twitter OAuth 1.0a" do
let(:access_token) { "twitter_access_token_448" }
let(:consumer_key) { "abcdef11223344" }
let(:consumer_secret) { "adddcccdddd99922" }
@ -14,14 +14,15 @@ describe 'Twitter OAuth 1.0a' do
created_at: "Sat May 09 17:58:22 +0000 2009",
default_profile: false,
default_profile_image: false,
description: "I taught your phone that thing you like. The Mobile Partner Engineer @Twitter. ",
description:
"I taught your phone that thing you like. The Mobile Partner Engineer @Twitter. ",
favourites_count: 588,
follow_request_sent: nil,
followers_count: 10625,
followers_count: 10_625,
following: nil,
friends_count: 1181,
geo_enabled: true,
id: 38895958,
id: 38_895_958,
id_str: "38895958",
is_translator: false,
lang: "en",
@ -30,11 +31,14 @@ describe 'Twitter OAuth 1.0a' do
name: "Sean Cook",
notifications: nil,
profile_background_color: "1A1B1F",
profile_background_image_url: "http://a0.twimg.com/profile_background_images/495742332/purty_wood.png",
profile_background_image_url_https: "https://si0.twimg.com/profile_background_images/495742332/purty_wood.png",
profile_background_image_url:
"http://a0.twimg.com/profile_background_images/495742332/purty_wood.png",
profile_background_image_url_https:
"https://si0.twimg.com/profile_background_images/495742332/purty_wood.png",
profile_background_tile: true,
profile_image_url: "http://a0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG",
profile_image_url_https: "https://si0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG",
profile_image_url_https:
"https://si0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG",
profile_link_color: "2FC2EF",
profile_sidebar_border_color: "181A1E",
profile_sidebar_fill_color: "252429",
@ -46,19 +50,17 @@ describe 'Twitter OAuth 1.0a' do
statuses_count: 2609,
time_zone: "Pacific Time (US & Canada)",
url: nil,
utc_offset: -28800,
utc_offset: -28_800,
verified: true,
email: email
email: email,
}
stub_request(:get, "https://api.twitter.com/1.1/account/verify_credentials.json")
.with(
query: {
include_email: true,
include_entities: false,
skip_status: true
}
)
.to_return(status: 200, body: JSON.dump(body))
stub_request(:get, "https://api.twitter.com/1.1/account/verify_credentials.json").with(
query: {
include_email: true,
include_entities: false,
skip_status: true,
},
).to_return(status: 200, body: JSON.dump(body))
end
before do
@ -66,28 +68,28 @@ describe 'Twitter OAuth 1.0a' do
SiteSetting.twitter_consumer_key = consumer_key
SiteSetting.twitter_consumer_secret = consumer_secret
stub_request(:post, "https://api.twitter.com/oauth/request_token")
.to_return(
status: 200,
body: Rack::Utils.build_query(
stub_request(:post, "https://api.twitter.com/oauth/request_token").to_return(
status: 200,
body:
Rack::Utils.build_query(
oauth_token: access_token,
oauth_token_secret: oauth_token_secret,
oauth_callback_confirmed: true
oauth_callback_confirmed: true,
),
headers: {
"Content-Type" => "application/x-www-form-urlencoded"
}
)
stub_request(:post, "https://api.twitter.com/oauth/access_token")
.to_return(
status: 200,
body: Rack::Utils.build_query(
headers: {
"Content-Type" => "application/x-www-form-urlencoded",
},
)
stub_request(:post, "https://api.twitter.com/oauth/access_token").to_return(
status: 200,
body:
Rack::Utils.build_query(
oauth_token: access_token,
oauth_token_secret: oauth_token_secret,
user_id: "43423432422",
screen_name: "twitterapi"
)
)
screen_name: "twitterapi",
),
)
end
it "signs in the user if the API response from twitter includes an email (implies it's verified) and the email matches an existing user's" do

View File

@ -8,80 +8,121 @@ RSpec.describe WatchedWord do
fab!(:topic) { Fabricate(:topic) }
fab!(:first_post) { Fabricate(:post, topic: topic) }
let(:require_approval_word) { Fabricate(:watched_word, action: WatchedWord.actions[:require_approval]) }
let(:require_approval_word) do
Fabricate(:watched_word, action: WatchedWord.actions[:require_approval])
end
let(:flag_word) { Fabricate(:watched_word, action: WatchedWord.actions[:flag]) }
let(:block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) }
let(:another_block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) }
before_all do
WordWatcher.clear_cache!
end
before_all { WordWatcher.clear_cache! }
after do
WordWatcher.clear_cache!
end
after { WordWatcher.clear_cache! }
context "with block" do
def should_block_post(manager)
expect {
result = manager.perform
expect(result).to_not be_success
expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_word', word: block_word.word))
expect(result.errors[:base]&.first).to eq(
I18n.t("contains_blocked_word", word: block_word.word),
)
}.to_not change { Post.count }
end
it "escapes the blocked word in error message" do
block_word = Fabricate(:watched_word, action: WatchedWord.actions[:block], word: "<a>")
manager = NewPostManager.new(tl2_user, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id)
manager =
NewPostManager.new(
tl2_user,
raw: "Want some #{block_word.word} for cheap?",
topic_id: topic.id,
)
result = manager.perform
expect(result).to_not be_success
expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_word', word: "&lt;a&gt;"))
expect(result.errors[:base]&.first).to eq(I18n.t("contains_blocked_word", word: "&lt;a&gt;"))
end
it "should prevent the post from being created" do
manager = NewPostManager.new(tl2_user, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id)
manager =
NewPostManager.new(
tl2_user,
raw: "Want some #{block_word.word} for cheap?",
topic_id: topic.id,
)
should_block_post(manager)
end
it "look at title too" do
manager = NewPostManager.new(tl2_user, title: "We sell #{block_word.word} online", raw: "Want some poutine for cheap?", topic_id: topic.id)
manager =
NewPostManager.new(
tl2_user,
title: "We sell #{block_word.word} online",
raw: "Want some poutine for cheap?",
topic_id: topic.id,
)
should_block_post(manager)
end
it "should block the post from admin" do
manager = NewPostManager.new(admin, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id)
manager =
NewPostManager.new(
admin,
raw: "Want some #{block_word.word} for cheap?",
topic_id: topic.id,
)
should_block_post(manager)
end
it "should block the post from moderator" do
manager = NewPostManager.new(moderator, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id)
manager =
NewPostManager.new(
moderator,
raw: "Want some #{block_word.word} for cheap?",
topic_id: topic.id,
)
should_block_post(manager)
end
it "should block the post if it contains multiple blocked words" do
manager = NewPostManager.new(moderator, raw: "Want some #{block_word.word} #{another_block_word.word} for cheap?", topic_id: topic.id)
manager =
NewPostManager.new(
moderator,
raw: "Want some #{block_word.word} #{another_block_word.word} for cheap?",
topic_id: topic.id,
)
expect {
result = manager.perform
expect(result).to_not be_success
expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_words', words: [block_word.word, another_block_word.word].sort.join(', ')))
expect(result.errors[:base]&.first).to eq(
I18n.t(
"contains_blocked_words",
words: [block_word.word, another_block_word.word].sort.join(", "),
),
)
}.to_not change { Post.count }
end
it "should block in a private message too" do
manager = NewPostManager.new(
tl2_user,
raw: "Want some #{block_word.word} for cheap?",
title: 'this is a new title',
archetype: Archetype.private_message,
target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username
)
manager =
NewPostManager.new(
tl2_user,
raw: "Want some #{block_word.word} for cheap?",
title: "this is a new title",
archetype: Archetype.private_message,
target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username,
)
should_block_post(manager)
end
it "blocks on revisions" do
post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user)
expect {
PostRevisor.new(post).revise!(post.user, { raw: "Want some #{block_word.word} for cheap?" }, revised_at: post.updated_at + 10.seconds)
PostRevisor.new(post).revise!(
post.user,
{ raw: "Want some #{block_word.word} for cheap?" },
revised_at: post.updated_at + 10.seconds,
)
expect(post.errors).to be_present
post.reload
}.to_not change { post.raw }
@ -90,27 +131,48 @@ RSpec.describe WatchedWord do
context "with require_approval" do
it "should queue the post for approval" do
manager = NewPostManager.new(tl2_user, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id)
manager =
NewPostManager.new(
tl2_user,
raw: "My dog's name is #{require_approval_word.word}.",
topic_id: topic.id,
)
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:watched_word)
end
it "looks at title too" do
manager = NewPostManager.new(tl2_user, title: "You won't believe these #{require_approval_word.word} dog names!", raw: "My dog's name is Porkins.", topic_id: topic.id)
manager =
NewPostManager.new(
tl2_user,
title: "You won't believe these #{require_approval_word.word} dog names!",
raw: "My dog's name is Porkins.",
topic_id: topic.id,
)
result = manager.perform
expect(result.action).to eq(:enqueued)
end
it "should not queue posts from admin" do
manager = NewPostManager.new(admin, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id)
manager =
NewPostManager.new(
admin,
raw: "My dog's name is #{require_approval_word.word}.",
topic_id: topic.id,
)
result = manager.perform
expect(result).to be_success
expect(result.action).to eq(:create_post)
end
it "should not queue posts from moderator" do
manager = NewPostManager.new(moderator, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id)
manager =
NewPostManager.new(
moderator,
raw: "My dog's name is #{require_approval_word.word}.",
topic_id: topic.id,
)
result = manager.perform
expect(result).to be_success
expect(result.action).to eq(:create_post)
@ -118,13 +180,14 @@ RSpec.describe WatchedWord do
it "doesn't need approval in a private message" do
Group.refresh_automatic_groups!
manager = NewPostManager.new(
tl2_user,
raw: "Want some #{require_approval_word.word} for cheap?",
title: 'this is a new title',
archetype: Archetype.private_message,
target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username
)
manager =
NewPostManager.new(
tl2_user,
raw: "Want some #{require_approval_word.word} for cheap?",
title: "this is a new title",
archetype: Archetype.private_message,
target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username,
)
result = manager.perform
expect(result).to be_success
expect(result.action).to eq(:create_post)
@ -134,74 +197,122 @@ RSpec.describe WatchedWord do
context "with flag" do
def should_flag_post(author, raw, topic)
post = Fabricate(:post, raw: raw, topic: topic, user: author)
expect {
Jobs::ProcessPost.new.execute(post_id: post.id)
}.to change { PostAction.count }.by(1)
expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true)
expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to change { PostAction.count }.by(
1,
)
expect(
PostAction.where(
post_id: post.id,
post_action_type_id: PostActionType.types[:inappropriate],
).exists?,
).to eq(true)
end
def should_not_flag_post(author, raw, topic)
post = Fabricate(:post, raw: raw, topic: topic, user: author)
expect {
Jobs::ProcessPost.new.execute(post_id: post.id)
}.to_not change { PostAction.count }
expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to_not change { PostAction.count }
end
it "should flag the post as inappropriate" do
topic = Fabricate(:topic, user: tl2_user)
post = Fabricate(:post, raw: "I said.... #{flag_word.word}", topic: topic, user: tl2_user)
Jobs::ProcessPost.new.execute(post_id: post.id)
expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true)
expect(
PostAction.where(
post_id: post.id,
post_action_type_id: PostActionType.types[:inappropriate],
).exists?,
).to eq(true)
reviewable = ReviewableFlaggedPost.where(target: post)
expect(reviewable).to be_present
expect(ReviewableScore.where(reviewable: reviewable, reason: 'watched_word')).to be_present
expect(ReviewableScore.where(reviewable: reviewable, reason: "watched_word")).to be_present
end
it "should look at the title too" do
should_flag_post(tl2_user, "I thought the movie was not bad actually.", Fabricate(:topic, user: tl2_user, title: "Read my #{flag_word.word} review!"))
should_flag_post(
tl2_user,
"I thought the movie was not bad actually.",
Fabricate(:topic, user: tl2_user, title: "Read my #{flag_word.word} review!"),
)
end
it "shouldn't flag posts by admin" do
should_not_flag_post(admin, "I thought the #{flag_word.word} was bad.", Fabricate(:topic, user: admin))
should_not_flag_post(
admin,
"I thought the #{flag_word.word} was bad.",
Fabricate(:topic, user: admin),
)
end
it "shouldn't flag posts by moderator" do
should_not_flag_post(moderator, "I thought the #{flag_word.word} was bad.", Fabricate(:topic, user: moderator))
should_not_flag_post(
moderator,
"I thought the #{flag_word.word} was bad.",
Fabricate(:topic, user: moderator),
)
end
it "is compatible with flag_sockpuppets" do
SiteSetting.flag_sockpuppets = true
ip_address = '182.189.119.174'
ip_address = "182.189.119.174"
user1 = Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago)
user2 = Fabricate(:user, ip_address: ip_address)
first = create_post(user: user1, created_at: 2.days.ago)
sockpuppet_post = create_post(user: user2, topic: first.topic, raw: "I thought the #{flag_word.word} was bad.")
sockpuppet_post =
create_post(
user: user2,
topic: first.topic,
raw: "I thought the #{flag_word.word} was bad.",
)
expect(PostAction.where(post_id: sockpuppet_post.id).count).to eq(1)
end
it "flags in private message too" do
post = Fabricate(:private_message_post, raw: "Want some #{flag_word.word} for cheap?", user: tl2_user)
expect {
Jobs::ProcessPost.new.execute(post_id: post.id)
}.to change { PostAction.count }.by(1)
expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true)
post =
Fabricate(
:private_message_post,
raw: "Want some #{flag_word.word} for cheap?",
user: tl2_user,
)
expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to change { PostAction.count }.by(
1,
)
expect(
PostAction.where(
post_id: post.id,
post_action_type_id: PostActionType.types[:inappropriate],
).exists?,
).to eq(true)
end
it "flags on revisions" do
Jobs.run_immediately!
post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user)
expect {
PostRevisor.new(post).revise!(post.user, { raw: "Want some #{flag_word.word} for cheap?" }, revised_at: post.updated_at + 10.seconds)
PostRevisor.new(post).revise!(
post.user,
{ raw: "Want some #{flag_word.word} for cheap?" },
revised_at: post.updated_at + 10.seconds,
)
}.to change { PostAction.count }.by(1)
expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true)
expect(
PostAction.where(
post_id: post.id,
post_action_type_id: PostActionType.types[:inappropriate],
).exists?,
).to eq(true)
end
it "should not flag on rebake" do
post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user, raw: "I have coupon codes. Message me.")
post =
Fabricate(
:post,
topic: Fabricate(:topic, user: tl2_user),
user: tl2_user,
raw: "I have coupon codes. Message me.",
)
Fabricate(:watched_word, action: WatchedWord.actions[:flag], word: "coupon")
expect {
post.rebake!
}.to_not change { PostAction.count }
expect { post.rebake! }.to_not change { PostAction.count }
end
end
end

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true
def list_files(base_dir, pattern = '*')
def list_files(base_dir, pattern = "*")
Dir[File.join("#{base_dir}", pattern)]
end
def list_js_files(base_dir)
list_files(base_dir, '**/*.es6')
list_files(base_dir, "**/*.es6")
end
def grep_files(files, regex)
@ -17,10 +17,10 @@ def grep_file(file, regex)
lines.count > 0 ? file : nil
end
RSpec.describe 'Coding style' do
describe 'Javascript' do
RSpec.describe "Coding style" do
describe "Javascript" do
it 'prevents this.get("foo") pattern' do
js_files = list_js_files('app/assets/javascripts')
js_files = list_js_files("app/assets/javascripts")
offenses = grep_files(js_files, /this\.get\("\w+"\)/)
expect(offenses).to be_empty, <<~TEXT
@ -33,7 +33,7 @@ RSpec.describe 'Coding style' do
end
end
describe 'Post Migrations' do
describe "Post Migrations" do
def check_offenses(files, method_name, constant_name)
method_name_regex = /#{Regexp.escape(method_name)}/
constant_name_regex = /#{Regexp.escape(constant_name)}/
@ -57,8 +57,8 @@ RSpec.describe 'Coding style' do
contains_method_name ? contains_constant_name : true
end
it 'ensures dropped tables and columns are stored in constants' do
migration_files = list_files('db/post_migrate', '**/*.rb')
it "ensures dropped tables and columns are stored in constants" do
migration_files = list_files("db/post_migrate", "**/*.rb")
check_offenses(migration_files, "ColumnDropper.execute_drop", "DROPPED_COLUMNS")
check_offenses(migration_files, "TableDropper.execute_drop", "DROPPED_TABLES")

View File

@ -1,76 +1,72 @@
# frozen_string_literal: true
RSpec.describe "CommonMark" do
it 'passes spec' do
it "passes spec" do
SiteSetting.traditional_markdown_linebreaks = true
SiteSetting.enable_markdown_typographer = false
SiteSetting.highlighted_languages = 'ruby|aa'
SiteSetting.highlighted_languages = "ruby|aa"
html, state, md = nil
failed = 0
File.readlines(Rails.root + 'spec/fixtures/md/spec.txt').each do |line|
if line == "```````````````````````````````` example\n"
state = :example
next
end
if line == "````````````````````````````````\n"
md.gsub!('→', "\t")
html ||= String.new
html.gsub!('→', "\t")
html.strip!
# normalize brs
html.gsub!('<br />', '<br>')
html.gsub!('<hr />', '<hr>')
html.gsub!(/<img([^>]+) \/>/, "<img\\1>")
SiteSetting.enable_markdown_linkify = false
cooked = PrettyText.markdown(md, sanitize: false)
cooked.strip!
cooked.gsub!(" class=\"lang-auto\"", '')
cooked.gsub!(/<span class="hashtag">(.*)<\/span>/, "\\1")
cooked.gsub!(/<a name="(.*)" class="anchor" href="#\1*"><\/a>/, "")
# we support data-attributes which is not in the spec
cooked.gsub!("<pre data-code-startline=\"3\">", '<pre>')
# we don't care about this
cooked.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
html.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
html.gsub!("language-ruby", "lang-ruby")
html.gsub!("language-aa", "lang-aa")
# strip out unsupported languages
html.gsub!(/ class="language-[;f].*"/, "")
unless cooked == html
failed += 1
puts "FAILED SPEC"
puts "Expected: "
puts html
puts "Got: "
puts cooked
puts "Markdown: "
puts md
puts
File
.readlines(Rails.root + "spec/fixtures/md/spec.txt")
.each do |line|
if line == "```````````````````````````````` example\n"
state = :example
next
end
html, state, md = nil
next
end
if state == :example && line == ".\n"
state = :html
next
end
if line == "````````````````````````````````\n"
md.gsub!("", "\t")
html ||= String.new
html.gsub!("", "\t")
html.strip!
if state == :example
md = (md || String.new) << line
end
# normalize brs
html.gsub!("<br />", "<br>")
html.gsub!("<hr />", "<hr>")
html.gsub!(%r{<img([^>]+) />}, "<img\\1>")
if state == :html
html = (html || String.new) << line
end
SiteSetting.enable_markdown_linkify = false
cooked = PrettyText.markdown(md, sanitize: false)
cooked.strip!
cooked.gsub!(" class=\"lang-auto\"", "")
cooked.gsub!(%r{<span class="hashtag">(.*)</span>}, "\\1")
cooked.gsub!(%r{<a name="(.*)" class="anchor" href="#\1*"></a>}, "")
# we support data-attributes which is not in the spec
cooked.gsub!("<pre data-code-startline=\"3\">", "<pre>")
# we don't care about this
cooked.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
html.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
html.gsub!("language-ruby", "lang-ruby")
html.gsub!("language-aa", "lang-aa")
# strip out unsupported languages
html.gsub!(%r{ class="language-[;f].*"}, "")
end
unless cooked == html
failed += 1
puts "FAILED SPEC"
puts "Expected: "
puts html
puts "Got: "
puts cooked
puts "Markdown: "
puts md
puts
end
html, state, md = nil
next
end
if state == :example && line == ".\n"
state = :html
next
end
md = (md || String.new) << line if state == :example
html = (html || String.new) << line if state == :html
end
expect(failed).to eq(0)
end

View File

@ -37,10 +37,12 @@ RSpec.describe "i18n integrity checks" do
end
it "has an i18n key for each Badge description" do
Badge.where(system: true).each do |b|
expect(b.long_description).to be_present
expect(b.description).to be_present
end
Badge
.where(system: true)
.each do |b|
expect(b.long_description).to be_present
expect(b.description).to be_present
end
end
Dir["#{Rails.root}/config/locales/{client,server}.*.yml"].each do |path|
@ -116,20 +118,14 @@ RSpec.describe "fallbacks" do
it "finds the fallback translation" do
I18n.backend.store_translations(:en, test: "en test")
I18n.with_locale("pl_PL") do
expect(I18n.t("test")).to eq("en test")
end
I18n.with_locale("pl_PL") { expect(I18n.t("test")).to eq("en test") }
end
context "when in a multi-threaded environment" do
it "finds the fallback translation" do
I18n.backend.store_translations(:en, test: "en test")
thread = Thread.new do
I18n.with_locale("pl_PL") do
expect(I18n.t("test")).to eq("en test")
end
end
thread = Thread.new { I18n.with_locale("pl_PL") { expect(I18n.t("test")).to eq("en test") } }
begin
thread.join

View File

@ -1,13 +1,12 @@
# frozen_string_literal: true
RSpec.describe "constants match ruby" do
let(:ctx) { MiniRacer::Context.new }
def parse(file)
# mini racer doesn't handle JS modules so we'll do this hack
source = File.read("#{Rails.root}/app/assets/javascripts/#{file}")
source.gsub!(/^export */, '')
source.gsub!(/^export */, "")
ctx.eval(source)
end
@ -16,12 +15,9 @@ RSpec.describe "constants match ruby" do
parse("pretty-text/addon/emoji/version.js")
priorities = ctx.eval("SEARCH_PRIORITIES")
Searchable::PRIORITIES.each do |key, value|
expect(priorities[key.to_s]).to eq(value)
end
Searchable::PRIORITIES.each { |key, value| expect(priorities[key.to_s]).to eq(value) }
expect(ctx.eval("SEARCH_PHRASE_REGEXP")).to eq(Search::PHRASE_MATCH_REGEXP_PATTERN)
expect(ctx.eval("IMAGE_VERSION")).to eq(Emoji::EMOJI_VERSION)
end
end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
RSpec.describe 'Oj' do
RSpec.describe "Oj" do
it "is enabled" do
classes = Set.new
tracer = TracePoint.new(:c_call) { |tp| classes << tp.defined_class }
@ -11,7 +11,7 @@ RSpec.describe 'Oj' do
it "escapes HTML entities the same as ActiveSupport" do
expect("<b>hello</b>".to_json).to eq("\"\\u003cb\\u003ehello\\u003c/b\\u003e\"")
expect('"hello world"'.to_json). to eq('"\"hello world\""')
expect('"hello world"'.to_json).to eq('"\"hello world\""')
expect("\u2028\u2029><&".to_json).to eq('"\u2028\u2029\u003e\u003c\u0026"')
end
end

View File

@ -3,11 +3,12 @@
RSpec.describe ::Jobs::Onceoff do
it "can run all once off jobs without errors" do
# Load all once offs
Dir[Rails.root + 'app/jobs/onceoff/*.rb'].each do |f|
require_relative '../../app/jobs/onceoff/' + File.basename(f)
Dir[Rails.root + "app/jobs/onceoff/*.rb"].each do |f|
require_relative "../../app/jobs/onceoff/" + File.basename(f)
end
ObjectSpace.each_object(Class)
ObjectSpace
.each_object(Class)
.select { |klass| klass.superclass == ::Jobs::Onceoff }
.each { |job| job.new.execute_onceoff(nil) }
end

View File

@ -1,10 +1,10 @@
# frozen_string_literal: true
RSpec.describe "site setting integrity checks" do
let(:site_setting_file) { File.join(Rails.root, 'config', 'site_settings.yml') }
let(:site_setting_file) { File.join(Rails.root, "config", "site_settings.yml") }
let(:yaml) { YAML.load_file(site_setting_file) }
%w(hidden client).each do |property|
%w[hidden client].each do |property|
it "set #{property} value as true or not set" do
yaml.each_value do |category|
category.each_value do |setting|
@ -24,16 +24,16 @@ RSpec.describe "site setting integrity checks" do
yaml.each_value do |category|
category.each do |setting_name, setting|
next unless setting.is_a?(Hash)
if setting['locale_default']
setting['locale_default'].each_pair do |k, v|
if setting["locale_default"]
setting["locale_default"].each_pair do |k, v|
expect(LocaleSiteSetting.valid_value?(k.to_s)).to be_truthy,
"'#{k}' is not a valid locale_default key for '#{setting_name}' site setting"
"'#{k}' is not a valid locale_default key for '#{setting_name}' site setting"
case setting['default']
case setting["default"]
when TrueClass, FalseClass
expect(v.class == TrueClass || v.class == FalseClass).to be_truthy
else
expect(v).to be_a_kind_of(setting['default'].class)
expect(v).to be_a_kind_of(setting["default"].class)
end
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.describe Jobs::AboutStats do
it 'caches the stats' do
it "caches the stats" do
begin
stats = About.fetch_stats.to_json
cache_key = About.stats_cache_key

View File

@ -6,33 +6,33 @@ RSpec.describe Jobs::ActivationReminderEmails do
# should be between 2 and 3 days
let(:created_at) { 50.hours.ago }
it 'should email inactive users' do
it "should email inactive users" do
user = Fabricate(:user, active: false, created_at: created_at)
expect { described_class.new.execute({}) }
.to change { ActionMailer::Base.deliveries.size }.by(1)
.and change { user.email_tokens.count }.by(1)
expect { described_class.new.execute({}) }.to change { ActionMailer::Base.deliveries.size }.by(
1,
).and change { user.email_tokens.count }.by(1)
expect(user.custom_fields['activation_reminder']).to eq("t")
expect(user.custom_fields["activation_reminder"]).to eq("t")
expect { described_class.new.execute({}) }.not_to change { ActionMailer::Base.deliveries.size }
user.activate
expect(user.reload.custom_fields['activation_reminder']).to eq(nil)
expect(user.reload.custom_fields["activation_reminder"]).to eq(nil)
end
it 'should not email active users' do
it "should not email active users" do
user = Fabricate(:user, active: true, created_at: created_at)
expect { described_class.new.execute({}) }
.to not_change { ActionMailer::Base.deliveries.size }
.and not_change { user.email_tokens.count }
expect { described_class.new.execute({}) }.to not_change {
ActionMailer::Base.deliveries.size
}.and not_change { user.email_tokens.count }
end
it 'should not email staged users' do
it "should not email staged users" do
user = Fabricate(:user, active: false, staged: true, created_at: created_at)
expect { described_class.new.execute({}) }
.to not_change { ActionMailer::Base.deliveries.size }
.and not_change { user.email_tokens.count }
expect { described_class.new.execute({}) }.to not_change {
ActionMailer::Base.deliveries.size
}.and not_change { user.email_tokens.count }
end
end

View File

@ -4,12 +4,10 @@ RSpec.describe Jobs::AutoExpireUserApiKeys do
fab!(:key1) { Fabricate(:readonly_user_api_key) }
fab!(:key2) { Fabricate(:readonly_user_api_key) }
context 'when user api key is unused in last 1 days' do
before do
SiteSetting.expire_user_api_keys_days = 1
end
context "when user api key is unused in last 1 days" do
before { SiteSetting.expire_user_api_keys_days = 1 }
it 'should revoke the key' do
it "should revoke the key" do
freeze_time
key1.update!(last_used_at: 2.days.ago)

View File

@ -9,15 +9,15 @@ RSpec.describe Jobs::AutoQueueHandler do
Fabricate(:user),
Fabricate(:post),
PostActionType.types[:spam],
message: 'this is the initial message'
message: "this is the initial message",
).perform
end
fab!(:post_action) { spam_result.post_action }
fab!(:old) {
fab!(:old) do
spam_result.reviewable.update_column(:created_at, 61.days.ago)
spam_result.reviewable
}
end
fab!(:not_old) { Fabricate(:reviewable_flagged_post, created_at: 59.days.ago) }

View File

@ -1,9 +1,10 @@
# frozen_string_literal: true
RSpec.describe Jobs::AutomaticGroupMembership do
it "raises an error when the group id is missing" do
expect { Jobs::AutomaticGroupMembership.new.execute({}) }.to raise_error(Discourse::InvalidParameters)
expect { Jobs::AutomaticGroupMembership.new.execute({}) }.to raise_error(
Discourse::InvalidParameters,
)
end
it "updates the membership" do
@ -14,19 +15,28 @@ RSpec.describe Jobs::AutomaticGroupMembership do
user4 = Fabricate(:user, email: "yes@wat.com")
EmailToken.confirm(Fabricate(:email_token, user: user4).token)
user5 = Fabricate(:user, email: "sso@wat.com")
user5.create_single_sign_on_record(external_id: 123, external_email: "hacker@wat.com", last_payload: "")
user5.create_single_sign_on_record(
external_id: 123,
external_email: "hacker@wat.com",
last_payload: "",
)
user6 = Fabricate(:user, email: "sso2@wat.com")
user6.create_single_sign_on_record(external_id: 456, external_email: "sso2@wat.com", last_payload: "")
user6.create_single_sign_on_record(
external_id: 456,
external_email: "sso2@wat.com",
last_payload: "",
)
group = Fabricate(:group, automatic_membership_email_domains: "wat.com")
automatic = nil
called = false
blk = Proc.new do |_u, _g, options|
automatic = options[:automatic]
called = true
end
blk =
Proc.new do |_u, _g, options|
automatic = options[:automatic]
called = true
end
begin
DiscourseEvent.on(:user_added_to_group, &blk)
@ -47,5 +57,4 @@ RSpec.describe Jobs::AutomaticGroupMembership do
expect(group.users.include?(user6)).to eq(true)
expect(group.user_count).to eq(2)
end
end

View File

@ -8,13 +8,7 @@ RSpec.describe Jobs::BookmarkReminderNotifications do
let(:bookmark1) { Fabricate(:bookmark, user: user) }
let(:bookmark2) { Fabricate(:bookmark, user: user) }
let(:bookmark3) { Fabricate(:bookmark, user: user) }
let!(:bookmarks) do
[
bookmark1,
bookmark2,
bookmark3
]
end
let!(:bookmarks) { [bookmark1, bookmark2, bookmark3] }
before do
# this is done to avoid model validations on Bookmark
@ -66,10 +60,12 @@ RSpec.describe Jobs::BookmarkReminderNotifications do
end
end
it 'will not send notification when topic is not available' do
it "will not send notification when topic is not available" do
bookmark1.bookmarkable.topic.destroy
bookmark2.bookmarkable.topic.destroy
bookmark3.bookmarkable.topic.destroy
expect { subject.execute }.not_to change { Notification.where(notification_type: Notification.types[:bookmark_reminder]).count }
expect { subject.execute }.not_to change {
Notification.where(notification_type: Notification.types[:bookmark_reminder]).count
}
end
end

View File

@ -1,13 +1,16 @@
# frozen_string_literal: true
RSpec.describe Jobs::BulkGrantTrustLevel do
it "raises an error when trust_level is missing" do
expect { Jobs::BulkGrantTrustLevel.new.execute(user_ids: [1, 2]) }.to raise_error(Discourse::InvalidParameters)
expect { Jobs::BulkGrantTrustLevel.new.execute(user_ids: [1, 2]) }.to raise_error(
Discourse::InvalidParameters,
)
end
it "raises an error when user_ids are missing" do
expect { Jobs::BulkGrantTrustLevel.new.execute(trust_level: 0) }.to raise_error(Discourse::InvalidParameters)
expect { Jobs::BulkGrantTrustLevel.new.execute(trust_level: 0) }.to raise_error(
Discourse::InvalidParameters,
)
end
it "updates the trust_level" do

View File

@ -1,34 +1,43 @@
# frozen_string_literal: true
RSpec.describe Jobs::BulkInvite do
describe '#execute' do
describe "#execute" do
fab!(:user) { Fabricate(:user) }
fab!(:admin) { Fabricate(:admin) }
fab!(:group1) { Fabricate(:group, name: 'group1') }
fab!(:group2) { Fabricate(:group, name: 'group2') }
fab!(:group1) { Fabricate(:group, name: "group1") }
fab!(:group2) { Fabricate(:group, name: "group2") }
fab!(:topic) { Fabricate(:topic) }
let(:staged_user) { Fabricate(:user, staged: true, active: false) }
let(:email) { 'test@discourse.org' }
let(:invites) { [{ email: user.email }, { email: staged_user.email }, { email: 'test2@discourse.org' }, { email: 'test@discourse.org', groups: 'GROUP1;group2', topic_id: topic.id }, { email: 'invalid' }] }
it 'raises an error when the invites array is missing' do
expect { Jobs::BulkInvite.new.execute(current_user_id: user.id) }
.to raise_error(Discourse::InvalidParameters, /invites/)
let(:email) { "test@discourse.org" }
let(:invites) do
[
{ email: user.email },
{ email: staged_user.email },
{ email: "test2@discourse.org" },
{ email: "test@discourse.org", groups: "GROUP1;group2", topic_id: topic.id },
{ email: "invalid" },
]
end
it 'raises an error when current_user_id is not valid' do
expect { Jobs::BulkInvite.new.execute(invites: invites) }
.to raise_error(Discourse::InvalidParameters, /current_user_id/)
end
it 'creates the right invites' do
described_class.new.execute(
current_user_id: admin.id,
invites: invites
it "raises an error when the invites array is missing" do
expect { Jobs::BulkInvite.new.execute(current_user_id: user.id) }.to raise_error(
Discourse::InvalidParameters,
/invites/,
)
end
it "raises an error when current_user_id is not valid" do
expect { Jobs::BulkInvite.new.execute(invites: invites) }.to raise_error(
Discourse::InvalidParameters,
/current_user_id/,
)
end
it "creates the right invites" do
described_class.new.execute(current_user_id: admin.id, invites: invites)
expect(Invite.exists?(email: staged_user.email)).to eq(true)
expect(Invite.exists?(email: 'test2@discourse.org')).to eq(true)
expect(Invite.exists?(email: "test2@discourse.org")).to eq(true)
invite = Invite.last
expect(invite.email).to eq(email)
@ -42,13 +51,10 @@ RSpec.describe Jobs::BulkInvite do
expect(post.raw).to include("1 error")
end
it 'does not create invited groups for automatic groups' do
it "does not create invited groups for automatic groups" do
group2.update!(automatic: true)
described_class.new.execute(
current_user_id: admin.id,
invites: invites
)
described_class.new.execute(current_user_id: admin.id, invites: invites)
invite = Invite.last
expect(invite.email).to eq(email)
@ -58,37 +64,31 @@ RSpec.describe Jobs::BulkInvite do
expect(post.raw).to include("1 warning")
end
it 'does not create invited groups record if the user can not manage the group' do
it "does not create invited groups record if the user can not manage the group" do
group1.add_owner(user)
described_class.new.execute(
current_user_id: user.id,
invites: invites
)
described_class.new.execute(current_user_id: user.id, invites: invites)
invite = Invite.last
expect(invite.email).to eq(email)
expect(invite.invited_groups.pluck(:group_id)).to contain_exactly(group1.id)
end
it 'adds existing users to valid groups' do
existing_user = Fabricate(:user, email: 'test@discourse.org')
it "adds existing users to valid groups" do
existing_user = Fabricate(:user, email: "test@discourse.org")
group2.update!(automatic: true)
expect do
described_class.new.execute(
current_user_id: admin.id,
invites: invites
)
described_class.new.execute(current_user_id: admin.id, invites: invites)
end.to change { Invite.count }.by(2)
expect(Invite.exists?(email: staged_user.email)).to eq(true)
expect(Invite.exists?(email: 'test2@discourse.org')).to eq(true)
expect(Invite.exists?(email: "test2@discourse.org")).to eq(true)
expect(existing_user.reload.groups).to eq([group1])
end
it 'can create staged users and prepopulate user fields' do
it "can create staged users and prepopulate user fields" do
user_field = Fabricate(:user_field, name: "Location")
user_field_color = Fabricate(:user_field, field_type: "dropdown", name: "Color")
user_field_color.user_field_options.create!(value: "Red")
@ -98,40 +98,33 @@ RSpec.describe Jobs::BulkInvite do
described_class.new.execute(
current_user_id: admin.id,
invites: [
{ email: 'test@discourse.org' }, # new user without user fields
{ email: user.email, location: 'value 1', color: 'blue' }, # existing user with user fields
{ email: staged_user.email, location: 'value 2', color: 'redd' }, # existing staged user with user fields
{ email: 'test2@discourse.org', location: 'value 3' } # new staged user with user fields
]
{ email: "test@discourse.org" }, # new user without user fields
{ email: user.email, location: "value 1", color: "blue" }, # existing user with user fields
{ email: staged_user.email, location: "value 2", color: "redd" }, # existing staged user with user fields
{ email: "test2@discourse.org", location: "value 3" }, # new staged user with user fields
],
)
expect(Invite.count).to eq(3)
expect(User.where(staged: true).find_by_email('test@discourse.org')).to eq(nil)
expect(user.user_fields[user_field.id.to_s]).to eq('value 1')
expect(user.user_fields[user_field_color.id.to_s]).to eq('Blue')
expect(staged_user.user_fields[user_field.id.to_s]).to eq('value 2')
expect(User.where(staged: true).find_by_email("test@discourse.org")).to eq(nil)
expect(user.user_fields[user_field.id.to_s]).to eq("value 1")
expect(user.user_fields[user_field_color.id.to_s]).to eq("Blue")
expect(staged_user.user_fields[user_field.id.to_s]).to eq("value 2")
expect(staged_user.user_fields[user_field_color.id.to_s]).to eq(nil)
new_staged_user = User.where(staged: true).find_by_email('test2@discourse.org')
expect(new_staged_user.user_fields[user_field.id.to_s]).to eq('value 3')
new_staged_user = User.where(staged: true).find_by_email("test2@discourse.org")
expect(new_staged_user.user_fields[user_field.id.to_s]).to eq("value 3")
end
context 'when there are more than 200 invites' do
context "when there are more than 200 invites" do
let(:bulk_invites) { [] }
before do
202.times do |i|
bulk_invites << { "email": "test_#{i}@discourse.org" }
end
end
before { 202.times { |i| bulk_invites << { email: "test_#{i}@discourse.org" } } }
it 'rate limits email sending' do
described_class.new.execute(
current_user_id: admin.id,
invites: bulk_invites
)
it "rate limits email sending" do
described_class.new.execute(current_user_id: admin.id, invites: bulk_invites)
invite = Invite.last
expect(invite.email).to eq('test_201@discourse.org')
expect(invite.email).to eq("test_201@discourse.org")
expect(invite.emailed_status).to eq(Invite.emailed_status_types[:bulk_pending])
expect(Jobs::ProcessBulkInviteEmails.jobs.size).to eq(1)
end

View File

@ -31,5 +31,4 @@ RSpec.describe Jobs::BumpTopic do
expect(topic.reload.public_topic_timer).to eq(nil)
end
end

View File

@ -4,53 +4,40 @@ RSpec.describe Jobs::CheckNewFeatures do
def build_feature_hash(id:, created_at:, discourse_version: "2.9.0.beta10")
{
id: id,
user_id: 89432,
user_id: 89_432,
emoji: "👤",
title: "New fancy feature!",
description: "",
link: "https://meta.discourse.org/t/-/238821",
created_at: created_at.iso8601,
updated_at: (created_at + 1.minutes).iso8601,
discourse_version: discourse_version
discourse_version: discourse_version,
}
end
def stub_meta_new_features_endpoint(*features)
stub_request(:get, "https://meta.discourse.org/new-features.json")
.to_return(
status: 200,
body: JSON.dump(features),
headers: {
"Content-Type" => "application/json"
}
)
stub_request(:get, "https://meta.discourse.org/new-features.json").to_return(
status: 200,
body: JSON.dump(features),
headers: {
"Content-Type" => "application/json",
},
)
end
fab!(:admin1) { Fabricate(:admin) }
fab!(:admin2) { Fabricate(:admin) }
let(:feature1) do
build_feature_hash(
id: 35,
created_at: 3.days.ago,
discourse_version: "2.8.1.beta12"
)
build_feature_hash(id: 35, created_at: 3.days.ago, discourse_version: "2.8.1.beta12")
end
let(:feature2) do
build_feature_hash(
id: 34,
created_at: 2.days.ago,
discourse_version: "2.8.1.beta13"
)
build_feature_hash(id: 34, created_at: 2.days.ago, discourse_version: "2.8.1.beta13")
end
let(:pending_feature) do
build_feature_hash(
id: 37,
created_at: 1.day.ago,
discourse_version: "2.8.1.beta14"
)
build_feature_hash(id: 37, created_at: 1.day.ago, discourse_version: "2.8.1.beta14")
end
before do
@ -59,9 +46,7 @@ RSpec.describe Jobs::CheckNewFeatures do
stub_meta_new_features_endpoint(feature1, feature2, pending_feature)
end
after do
DiscourseUpdates.clean_state
end
after { DiscourseUpdates.clean_state }
it "backfills last viewed feature for admins who don't have last viewed feature" do
DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta12")
@ -70,8 +55,12 @@ RSpec.describe Jobs::CheckNewFeatures do
described_class.new.execute({})
expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id).iso8601).to eq(feature1[:created_at])
expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id).iso8601).to eq(Time.zone.now.iso8601)
expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id).iso8601).to eq(
feature1[:created_at],
)
expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id).iso8601).to eq(
Time.zone.now.iso8601,
)
end
it "notifies admins about new features that are available in the site's version" do
@ -79,14 +68,18 @@ RSpec.describe Jobs::CheckNewFeatures do
described_class.new.execute({})
expect(admin1.notifications.where(
notification_type: Notification.types[:new_features],
read: false
).count).to eq(1)
expect(admin2.notifications.where(
notification_type: Notification.types[:new_features],
read: false
).count).to eq(1)
expect(
admin1
.notifications
.where(notification_type: Notification.types[:new_features], read: false)
.count,
).to eq(1)
expect(
admin2
.notifications
.where(notification_type: Notification.types[:new_features], read: false)
.count,
).to eq(1)
end
it "consolidates new features notifications" do
@ -94,10 +87,11 @@ RSpec.describe Jobs::CheckNewFeatures do
described_class.new.execute({})
notification = admin1.notifications.where(
notification_type: Notification.types[:new_features],
read: false
).first
notification =
admin1
.notifications
.where(notification_type: Notification.types[:new_features], read: false)
.first
expect(notification).to be_present
DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta14")
@ -106,10 +100,11 @@ RSpec.describe Jobs::CheckNewFeatures do
# old notification is destroyed
expect(Notification.find_by(id: notification.id)).to eq(nil)
notification = admin1.notifications.where(
notification_type: Notification.types[:new_features],
read: false
).first
notification =
admin1
.notifications
.where(notification_type: Notification.types[:new_features], read: false)
.first
# new notification is created
expect(notification).to be_present
end
@ -121,6 +116,8 @@ RSpec.describe Jobs::CheckNewFeatures do
described_class.new.execute({})
expect(admin1.notifications.count).to eq(0)
expect(admin2.notifications.where(notification_type: Notification.types[:new_features]).count).to eq(1)
expect(
admin2.notifications.where(notification_type: Notification.types[:new_features]).count,
).to eq(1)
end
end

View File

@ -5,13 +5,13 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do
fab!(:topic) { Fabricate(:topic, created_at: 5.hours.ago) }
fab!(:dismissed_topic_user) { Fabricate(:dismissed_topic_user, user: user, topic: topic) }
describe '#delete_overdue_dismissals!' do
it 'does not delete when new_topic_duration_minutes is set to always' do
describe "#delete_overdue_dismissals!" do
it "does not delete when new_topic_duration_minutes is set to always" do
user.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::ALWAYS)
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
end
it 'deletes when new_topic_duration_minutes is set to since last visit' do
it "deletes when new_topic_duration_minutes is set to since last visit" do
user.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::LAST_VISIT)
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
@ -19,7 +19,7 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do
expect { described_class.new.execute({}) }.to change { DismissedTopicUser.count }.by(-1)
end
it 'deletes when new_topic_duration_minutes is set to created in the last day' do
it "deletes when new_topic_duration_minutes is set to created in the last day" do
user.user_option.update(new_topic_duration_minutes: 1440)
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
@ -28,7 +28,7 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do
end
end
describe '#delete_over_the_limit_dismissals!' do
describe "#delete_over_the_limit_dismissals!" do
fab!(:user2) { Fabricate(:user, created_at: 1.days.ago, previous_visit_at: 1.days.ago) }
fab!(:topic2) { Fabricate(:topic, created_at: 6.hours.ago) }
fab!(:topic3) { Fabricate(:topic, created_at: 2.hours.ago) }
@ -41,7 +41,7 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do
user2.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::ALWAYS)
end
it 'deletes over the limit dismissals' do
it "deletes over the limit dismissals" do
described_class.new.execute({})
expect(dismissed_topic_user.reload).to be_present
expect(dismissed_topic_user2.reload).to be_present

View File

@ -6,12 +6,27 @@ RSpec.describe Jobs::CleanUpAssociatedAccounts do
it "deletes the correct records" do
freeze_time
last_week = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "1", updated_at: 7.days.ago)
today = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "12", updated_at: 12.hours.ago)
connected = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "123", user: Fabricate(:user), updated_at: 12.hours.ago)
last_week =
UserAssociatedAccount.create!(
provider_name: "twitter",
provider_uid: "1",
updated_at: 7.days.ago,
)
today =
UserAssociatedAccount.create!(
provider_name: "twitter",
provider_uid: "12",
updated_at: 12.hours.ago,
)
connected =
UserAssociatedAccount.create!(
provider_name: "twitter",
provider_uid: "123",
user: Fabricate(:user),
updated_at: 12.hours.ago,
)
expect { subject }.to change { UserAssociatedAccount.count }.by(-1)
expect(UserAssociatedAccount.all).to contain_exactly(today, connected)
end
end

View File

@ -5,9 +5,7 @@ RSpec.describe Jobs::CleanUpEmailLogs do
fab!(:email_log2) { Fabricate(:email_log, created_at: 2.weeks.ago) }
fab!(:email_log3) { Fabricate(:email_log, created_at: 2.days.ago) }
let!(:skipped_email_log) do
Fabricate(:skipped_email_log, created_at: 2.years.ago)
end
let!(:skipped_email_log) { Fabricate(:skipped_email_log, created_at: 2.years.ago) }
fab!(:skipped_email_log2) { Fabricate(:skipped_email_log) }
@ -23,10 +21,6 @@ RSpec.describe Jobs::CleanUpEmailLogs do
expect(EmailLog.all).to contain_exactly(email_log, email_log2, email_log3)
expect(SkippedEmailLog.all).to contain_exactly(
skipped_email_log,
skipped_email_log2
)
expect(SkippedEmailLog.all).to contain_exactly(skipped_email_log, skipped_email_log2)
end
end

View File

@ -4,22 +4,16 @@ RSpec.describe Jobs::CleanUpInactiveUsers do
it "should clean up new users that have been inactive" do
SiteSetting.clean_up_inactive_users_after_days = 0
user = Fabricate(:user,
last_seen_at: 5.days.ago,
trust_level: TrustLevel.levels[:newuser]
)
user = Fabricate(:user, last_seen_at: 5.days.ago, trust_level: TrustLevel.levels[:newuser])
Fabricate(:active_user)
Fabricate(:post, user: Fabricate(:user,
trust_level: TrustLevel.levels[:newuser],
last_seen_at: 5.days.ago
)).user
Fabricate(
:post,
user: Fabricate(:user, trust_level: TrustLevel.levels[:newuser], last_seen_at: 5.days.ago),
).user
Fabricate(:user,
trust_level: TrustLevel.levels[:newuser],
last_seen_at: 2.days.ago
)
Fabricate(:user, trust_level: TrustLevel.levels[:newuser], last_seen_at: 2.days.ago)
Fabricate(:user, trust_level: TrustLevel.levels[:basic])
@ -27,8 +21,7 @@ RSpec.describe Jobs::CleanUpInactiveUsers do
SiteSetting.clean_up_inactive_users_after_days = 4
expect { described_class.new.execute({}) }
.to change { User.count }.by(-1)
expect { described_class.new.execute({}) }.to change { User.count }.by(-1)
expect(User.exists?(id: user.id)).to eq(false)
end
@ -43,7 +36,8 @@ RSpec.describe Jobs::CleanUpInactiveUsers do
it "doesn't delete inactive mods" do
SiteSetting.clean_up_inactive_users_after_days = 4
moderator = Fabricate(:moderator, last_seen_at: 5.days.ago, trust_level: TrustLevel.levels[:newuser])
moderator =
Fabricate(:moderator, last_seen_at: 5.days.ago, trust_level: TrustLevel.levels[:newuser])
expect { described_class.new.execute({}) }.to_not change { User.count }
expect(User.exists?(moderator.id)).to eq(true)

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.describe Jobs::CleanUpPostReplyKeys do
it 'removes old post_reply_keys' do
it "removes old post_reply_keys" do
freeze_time
reply_key1 = Fabricate(:post_reply_key, created_at: 1.day.ago)
@ -10,16 +10,12 @@ RSpec.describe Jobs::CleanUpPostReplyKeys do
SiteSetting.disallow_reply_by_email_after_days = 0
expect { Jobs::CleanUpPostReplyKeys.new.execute({}) }
.not_to change { PostReplyKey.count }
expect { Jobs::CleanUpPostReplyKeys.new.execute({}) }.not_to change { PostReplyKey.count }
SiteSetting.disallow_reply_by_email_after_days = 2
expect { Jobs::CleanUpPostReplyKeys.new.execute({}) }
.to change { PostReplyKey.count }.by(-1)
expect { Jobs::CleanUpPostReplyKeys.new.execute({}) }.to change { PostReplyKey.count }.by(-1)
expect(PostReplyKey.all).to contain_exactly(
reply_key1, reply_key2
)
expect(PostReplyKey.all).to contain_exactly(reply_key1, reply_key2)
end
end

View File

@ -37,7 +37,7 @@ RSpec.describe Jobs::CleanUpUnusedStagedUsers do
end
end
context 'when staged user is not old enough' do
context "when staged user is not old enough" do
before { staged_user.update!(created_at: 5.months.ago) }
include_examples "does not delete"
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
RSpec.describe Jobs::CleanUpUploads do
def fabricate_upload(attributes = {})
Fabricate(:upload, { created_at: 2.hours.ago }.merge(attributes))
end
@ -22,7 +21,6 @@ RSpec.describe Jobs::CleanUpUploads do
end
it "only runs upload cleanup every grace period / 2 time" do
SiteSetting.clean_orphan_uploads_grace_period_hours = 48
expired = fabricate_upload(created_at: 49.hours.ago)
Jobs::CleanUpUploads.new.execute(nil)
@ -38,44 +36,31 @@ RSpec.describe Jobs::CleanUpUploads do
Jobs::CleanUpUploads.new.execute(nil)
expect(Upload.exists?(id: upload.id)).to eq(false)
end
it "deletes orphan uploads" do
expect do
Jobs::CleanUpUploads.new.execute(nil)
end.to change { Upload.count }.by(-1)
expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1)
expect(Upload.exists?(id: expired_upload.id)).to eq(false)
end
describe 'unused callbacks' do
before do
Upload.add_unused_callback do |uploads|
uploads.where.not(id: expired_upload.id)
end
end
describe "unused callbacks" do
before { Upload.add_unused_callback { |uploads| uploads.where.not(id: expired_upload.id) } }
after do
Upload.reset_unused_callbacks
end
after { Upload.reset_unused_callbacks }
it 'does not delete uploads skipped by an unused callback' do
expect do
Jobs::CleanUpUploads.new.execute(nil)
end.not_to change { Upload.count }
it "does not delete uploads skipped by an unused callback" do
expect do Jobs::CleanUpUploads.new.execute(nil) end.not_to change { Upload.count }
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
end
it 'deletes other uploads not skipped by an unused callback' do
it "deletes other uploads not skipped by an unused callback" do
expired_upload2 = fabricate_upload
upload = fabricate_upload
UploadReference.create(target: Fabricate(:post), upload: upload)
expect do
Jobs::CleanUpUploads.new.execute(nil)
end.to change { Upload.count }.by(-1)
expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1)
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
expect(Upload.exists?(id: expired_upload2.id)).to eq(false)
@ -83,33 +68,23 @@ RSpec.describe Jobs::CleanUpUploads do
end
end
describe 'in use callbacks' do
before do
Upload.add_in_use_callback do |upload|
expired_upload.id == upload.id
end
end
describe "in use callbacks" do
before { Upload.add_in_use_callback { |upload| expired_upload.id == upload.id } }
after do
Upload.reset_in_use_callbacks
end
after { Upload.reset_in_use_callbacks }
it 'does not delete uploads that are in use by callback' do
expect do
Jobs::CleanUpUploads.new.execute(nil)
end.not_to change { Upload.count }
it "does not delete uploads that are in use by callback" do
expect do Jobs::CleanUpUploads.new.execute(nil) end.not_to change { Upload.count }
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
end
it 'deletes other uploads that are not in use by callback' do
it "deletes other uploads that are not in use by callback" do
expired_upload2 = fabricate_upload
upload = fabricate_upload
UploadReference.create(target: Fabricate(:post), upload: upload)
expect do
Jobs::CleanUpUploads.new.execute(nil)
end.to change { Upload.count }.by(-1)
expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1)
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
expect(Upload.exists?(id: expired_upload2.id)).to eq(false)
@ -117,27 +92,20 @@ RSpec.describe Jobs::CleanUpUploads do
end
end
describe 'when clean_up_uploads is disabled' do
before do
SiteSetting.clean_up_uploads = false
end
describe "when clean_up_uploads is disabled" do
before { SiteSetting.clean_up_uploads = false }
it 'should still delete invalid upload records' do
upload2 = fabricate_upload(
url: "",
retain_hours: nil
)
it "should still delete invalid upload records" do
upload2 = fabricate_upload(url: "", retain_hours: nil)
expect do
Jobs::CleanUpUploads.new.execute(nil)
end.to change { Upload.count }.by(-1)
expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1)
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
expect(Upload.exists?(id: upload2.id)).to eq(false)
end
end
it 'does not clean up upload site settings' do
it "does not clean up upload site settings" do
begin
original_provider = SiteSetting.provider
SiteSetting.provider = SiteSettings::DbProvider.new(SiteSetting)
@ -161,8 +129,7 @@ RSpec.describe Jobs::CleanUpUploads do
SiteSetting.large_icon = large_icon_upload
SiteSetting.opengraph_image = opengraph_image_upload
SiteSetting.twitter_summary_large_image =
twitter_summary_large_image_upload
SiteSetting.twitter_summary_large_image = twitter_summary_large_image_upload
SiteSetting.favicon = favicon_upload
SiteSetting.apple_touch_icon = apple_touch_icon_upload
@ -170,20 +137,13 @@ RSpec.describe Jobs::CleanUpUploads do
Jobs::CleanUpUploads.new.execute(nil)
[
logo_upload,
logo_small_upload,
digest_logo_upload,
mobile_logo_upload,
large_icon_upload,
opengraph_image_upload,
twitter_summary_large_image_upload,
favicon_upload,
apple_touch_icon_upload,
system_upload
logo_upload, logo_small_upload, digest_logo_upload, mobile_logo_upload, large_icon_upload,
opengraph_image_upload, twitter_summary_large_image_upload, favicon_upload,
apple_touch_icon_upload, system_upload,
].each { |record| expect(Upload.exists?(id: record.id)).to eq(true) }
fabricate_upload
SiteSetting.opengraph_image = ''
SiteSetting.opengraph_image = ""
Jobs::CleanUpUploads.new.execute(nil)
ensure
@ -307,15 +267,19 @@ RSpec.describe Jobs::CleanUpUploads do
upload2 = fabricate_upload
upload3 = fabricate_upload
Fabricate(:reviewable_queued_post_topic, payload: {
raw: "#{upload.short_url}\n#{upload2.short_url}"
})
Fabricate(:reviewable_queued_post_topic,
Fabricate(
:reviewable_queued_post_topic,
payload: {
raw: "#{upload3.short_url}"
raw: "#{upload.short_url}\n#{upload2.short_url}",
},
status: Reviewable.statuses[:rejected]
)
Fabricate(
:reviewable_queued_post_topic,
payload: {
raw: "#{upload3.short_url}",
},
status: Reviewable.statuses[:rejected],
)
Jobs::CleanUpUploads.new.execute(nil)
@ -350,7 +314,7 @@ RSpec.describe Jobs::CleanUpUploads do
it "does not delete custom emojis" do
upload = fabricate_upload
CustomEmoji.create!(name: 'test', upload: upload)
CustomEmoji.create!(name: "test", upload: upload)
Jobs::CleanUpUploads.new.execute(nil)
@ -371,7 +335,12 @@ RSpec.describe Jobs::CleanUpUploads do
it "does not delete theme setting uploads" do
theme = Fabricate(:theme)
theme_upload = fabricate_upload
ThemeSetting.create!(theme: theme, data_type: ThemeSetting.types[:upload], value: theme_upload.id.to_s, name: "my_setting_name")
ThemeSetting.create!(
theme: theme,
data_type: ThemeSetting.types[:upload],
value: theme_upload.id.to_s,
name: "my_setting_name",
)
Jobs::CleanUpUploads.new.execute(nil)
@ -390,10 +359,30 @@ RSpec.describe Jobs::CleanUpUploads do
end
it "deletes external upload stubs that have expired" do
external_stub1 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:created], created_at: 10.minutes.ago)
external_stub2 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:created], created_at: (ExternalUploadStub::CREATED_EXPIRY_HOURS.hours + 10.minutes).ago)
external_stub3 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:uploaded], created_at: 10.minutes.ago)
external_stub4 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:uploaded], created_at: (ExternalUploadStub::UPLOADED_EXPIRY_HOURS.hours + 10.minutes).ago)
external_stub1 =
Fabricate(
:external_upload_stub,
status: ExternalUploadStub.statuses[:created],
created_at: 10.minutes.ago,
)
external_stub2 =
Fabricate(
:external_upload_stub,
status: ExternalUploadStub.statuses[:created],
created_at: (ExternalUploadStub::CREATED_EXPIRY_HOURS.hours + 10.minutes).ago,
)
external_stub3 =
Fabricate(
:external_upload_stub,
status: ExternalUploadStub.statuses[:uploaded],
created_at: 10.minutes.ago,
)
external_stub4 =
Fabricate(
:external_upload_stub,
status: ExternalUploadStub.statuses[:uploaded],
created_at: (ExternalUploadStub::UPLOADED_EXPIRY_HOURS.hours + 10.minutes).ago,
)
Jobs::CleanUpUploads.new.execute(nil)
expect(ExternalUploadStub.pluck(:id)).to contain_exactly(external_stub1.id, external_stub3.id)
end

View File

@ -3,27 +3,29 @@
RSpec.describe Jobs::CleanUpUserExportTopics do
fab!(:user) { Fabricate(:user) }
it 'should delete ancient user export system messages' do
post_en = SystemMessage.create_from_system_user(
user,
:csv_export_succeeded,
download_link: "http://example.com/download",
file_name: "xyz_en.gz",
file_size: "55",
export_title: "user_archive"
)
it "should delete ancient user export system messages" do
post_en =
SystemMessage.create_from_system_user(
user,
:csv_export_succeeded,
download_link: "http://example.com/download",
file_name: "xyz_en.gz",
file_size: "55",
export_title: "user_archive",
)
topic_en = post_en.topic
topic_en.update!(created_at: 5.days.ago)
I18n.locale = :fr
post_fr = SystemMessage.create_from_system_user(
user,
:csv_export_succeeded,
download_link: "http://example.com/download",
file_name: "xyz_fr.gz",
file_size: "56",
export_title: "user_archive"
)
post_fr =
SystemMessage.create_from_system_user(
user,
:csv_export_succeeded,
download_link: "http://example.com/download",
file_name: "xyz_fr.gz",
file_size: "56",
export_title: "user_archive",
)
topic_fr = post_fr.topic
topic_fr.update!(created_at: 5.days.ago)

View File

@ -3,22 +3,15 @@
RSpec.describe Jobs::CloseTopic do
fab!(:admin) { Fabricate(:admin) }
fab!(:topic) do
Fabricate(:topic_timer, user: admin).topic
end
fab!(:topic) { Fabricate(:topic_timer, user: admin).topic }
it 'should be able to close a topic' do
it "should be able to close a topic" do
freeze_time(61.minutes.from_now) do
described_class.new.execute(
topic_timer_id: topic.public_topic_timer.id,
state: true
)
described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true)
expect(topic.reload.closed).to eq(true)
expect(Post.last.raw).to eq(I18n.t(
'topic_statuses.autoclosed_enabled_minutes', count: 61
))
expect(Post.last.raw).to eq(I18n.t("topic_statuses.autoclosed_enabled_minutes", count: 61))
end
end
@ -30,61 +23,47 @@ RSpec.describe Jobs::CloseTopic do
end
end
describe 'when trying to close a topic that has already been closed' do
it 'should delete the topic timer' do
describe "when trying to close a topic that has already been closed" do
it "should delete the topic timer" do
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
topic.update!(closed: true)
expect do
described_class.new.execute(
topic_timer_id: topic.public_topic_timer.id,
state: true
)
described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true)
end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false)
end
end
describe 'when trying to close a topic that has been deleted' do
it 'should delete the topic timer' do
describe "when trying to close a topic that has been deleted" do
it "should delete the topic timer" do
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
topic.trash!
expect do
described_class.new.execute(
topic_timer_id: topic.public_topic_timer.id,
state: true
)
described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true)
end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false)
end
end
describe 'when user is no longer authorized to close topics' do
describe "when user is no longer authorized to close topics" do
fab!(:user) { Fabricate(:user) }
fab!(:topic) do
Fabricate(:topic_timer, user: user).topic
end
fab!(:topic) { Fabricate(:topic_timer, user: user).topic }
it 'should destroy the topic timer' do
it "should destroy the topic timer" do
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
expect do
described_class.new.execute(
topic_timer_id: topic.public_topic_timer.id,
state: true
)
described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true)
end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false)
expect(topic.reload.closed).to eq(false)
end
it "should reconfigure topic timer if category's topics are set to autoclose" do
category = Fabricate(:category,
auto_close_based_on_last_post: true,
auto_close_hours: 5
)
category = Fabricate(:category, auto_close_based_on_last_post: true, auto_close_hours: 5)
topic = Fabricate(:topic, category: category)
topic.public_topic_timer.update!(user: user)
@ -92,12 +71,10 @@ RSpec.describe Jobs::CloseTopic do
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
expect do
described_class.new.execute(
topic_timer_id: topic.public_topic_timer.id,
state: true
)
end.to change { topic.reload.public_topic_timer.user }.from(user).to(Discourse.system_user)
.and change { topic.public_topic_timer.id }
described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true)
end.to change { topic.reload.public_topic_timer.user }.from(user).to(
Discourse.system_user,
).and change { topic.public_topic_timer.id }
expect(topic.reload.closed).to eq(false)
end

Some files were not shown because too many files have changed in this diff Show More