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=app/*
--ignore-files=db/* --ignore-files=db/*
--ignore-files=lib/* --ignore-files=lib/*
--ignore-files=spec/*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,9 +6,7 @@ Fabricator(:category) do
user user
end end
Fabricator(:category_with_definition, from: :category) do Fabricator(:category_with_definition, from: :category) { skip_category_definition false }
skip_category_definition false
end
Fabricator(:private_category, from: :category) do Fabricator(:private_category, from: :category) do
transient :group transient :group
@ -20,7 +18,10 @@ Fabricator(:private_category, from: :category) do
after_build do |cat, transients| after_build do |cat, transients|
cat.update!(read_restricted: true) 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
end end
@ -33,7 +34,7 @@ Fabricator(:link_category, from: :category) do
end end
Fabricator(:mailinglist_mirror_category, from: :category) do Fabricator(:mailinglist_mirror_category, from: :category) do
email_in 'list@example.com' email_in "list@example.com"
email_in_allow_strangers true email_in_allow_strangers true
mailinglist_mirror true mailinglist_mirror true
end end

View File

@ -2,5 +2,7 @@
Fabricator(:color_scheme) do Fabricator(:color_scheme) do
name { sequence(:name) { |i| "Palette #{i}" } } 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 end

View File

@ -5,7 +5,12 @@ Fabricator(:external_upload_stub) do
created_by { Fabricate(:user) } created_by { Fabricate(:user) }
original_filename "test.txt" 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" upload_type "card_background"
filesize 1024 filesize 1024
status 1 status 1
@ -14,16 +19,28 @@ end
Fabricator(:image_external_upload_stub, from: :external_upload_stub) do Fabricator(:image_external_upload_stub, from: :external_upload_stub) do
original_filename "logo.png" original_filename "logo.png"
filesize 1024 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 end
Fabricator(:attachment_external_upload_stub, from: :external_upload_stub) do Fabricator(:attachment_external_upload_stub, from: :external_upload_stub) do
original_filename "file.pdf" original_filename "file.pdf"
filesize 1024 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 end
Fabricator(:multipart_external_upload_stub, from: :external_upload_stub) do Fabricator(:multipart_external_upload_stub, from: :external_upload_stub) do
multipart true 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 end

View File

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

View File

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

View File

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

View File

@ -27,7 +27,7 @@ Fabricator(:private_message_notification, from: :notification) do
original_post_type: post.post_type, original_post_type: post.post_type,
original_username: post.user.username, original_username: post.user.username,
revision_number: nil, revision_number: nil,
display_username: post.user.username display_username: post.user.username,
}.to_json }.to_json
end end
end end
@ -44,7 +44,7 @@ Fabricator(:bookmark_reminder_notification, from: :notification) do
original_username: post.user.username, original_username: post.user.username,
revision_number: nil, revision_number: nil,
display_username: post.user.username, 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 }.to_json
end end
end end
@ -58,7 +58,7 @@ Fabricator(:replied_notification, from: :notification) do
original_post_id: post.id, original_post_id: post.id,
original_username: post.user.username, original_username: post.user.username,
revision_number: nil, revision_number: nil,
display_username: post.user.username display_username: post.user.username,
}.to_json }.to_json
end end
end end
@ -73,7 +73,7 @@ Fabricator(:posted_notification, from: :notification) do
original_post_type: post.post_type, original_post_type: post.post_type,
original_username: post.user.username, original_username: post.user.username,
revision_number: nil, revision_number: nil,
display_username: post.user.username display_username: post.user.username,
}.to_json }.to_json
end end
end end
@ -87,7 +87,7 @@ Fabricator(:mentioned_notification, from: :notification) do
original_post_type: attrs[:post].post_type, original_post_type: attrs[:post].post_type,
original_username: attrs[:post].user.username, original_username: attrs[:post].user.username,
revision_number: nil, revision_number: nil,
display_username: attrs[:post].user.username display_username: attrs[:post].user.username,
}.to_json }.to_json
end end
end end
@ -101,7 +101,7 @@ Fabricator(:watching_first_post_notification, from: :notification) do
original_post_type: attrs[:post].post_type, original_post_type: attrs[:post].post_type,
original_username: attrs[:post].user.username, original_username: attrs[:post].user.username,
revision_number: nil, revision_number: nil,
display_username: attrs[:post].user.username display_username: attrs[:post].user.username,
}.to_json }.to_json
end end
end end

View File

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

View File

@ -7,19 +7,17 @@ Fabricator(:post) do
post_type Post.types[:regular] post_type Post.types[:regular]
# Fabrication bypasses PostCreator, for performance reasons, where the counts are updated so we have to handle this manually here. # Fabrication bypasses PostCreator, for performance reasons, where the counts are updated so we have to handle this manually here.
after_create do |post, _transients| after_create { |post, _transients| UserStatCountUpdater.increment!(post) }
UserStatCountUpdater.increment!(post)
end
end end
Fabricator(:post_with_long_raw_content, from: :post) do 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 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 end
Fabricator(:post_with_youtube, from: :post) do 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>' 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 end
@ -39,7 +37,7 @@ Fabricator(:basic_reply, from: :post) do
user(fabricator: :coding_horror) user(fabricator: :coding_horror)
reply_to_post_number 1 reply_to_post_number 1
topic topic
raw 'this reply has no quotes' raw "this reply has no quotes"
end end
Fabricator(:reply, from: :post) do Fabricator(:reply, from: :post) do
@ -51,14 +49,12 @@ Fabricator(:reply, from: :post) do
' '
end end
Fabricator(:post_with_plenty_of_images, from: :post) do Fabricator(:post_with_plenty_of_images, from: :post) { cooked <<~HTML }
cooked <<~HTML
<aside class="quote"><img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"></aside> <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-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> <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> <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 HTML
end
Fabricator(:post_with_uploaded_image, from: :post) do Fabricator(:post_with_uploaded_image, from: :post) do
raw { "<img src=\"#{Fabricate(:image_upload)}\" width=\"1500\" height=\"2000\">" } raw { "<img src=\"#{Fabricate(:image_upload)}\" width=\"1500\" height=\"2000\">" }
@ -101,8 +97,7 @@ Fabricator(:post_with_uploads, from: :post) do
" "
end end
Fabricator(:post_with_uploads_and_links, from: :post) do Fabricator(:post_with_uploads_and_links, from: :post) { raw <<~MD }
raw <<~MD
<a href="/#{Discourse.store.upload_path}/original/2X/2345678901234567.jpg">Link</a> <a href="/#{Discourse.store.upload_path}/original/2X/2345678901234567.jpg">Link</a>
<img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"> <img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg">
<a href="http://www.google.com">Google</a> <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) <a class="attachment" href="/#{Discourse.store.upload_path}/original/1X/af2c2618032c679333bebf745e75f9088748d737.txt">text.txt</a> (20 Bytes)
:smile: :smile:
MD MD
end
Fabricator(:post_with_external_links, from: :post) do Fabricator(:post_with_external_links, from: :post) do
user user
@ -130,14 +124,15 @@ Fabricator(:private_message_post, from: :post) do
transient :recipient transient :recipient
user user
topic do |attrs| topic do |attrs|
Fabricate(:private_message_topic, Fabricate(
:private_message_topic,
user: attrs[:user], user: attrs[:user],
created_at: attrs[:created_at], created_at: attrs[:created_at],
subtype: TopicSubtype.user_to_user, subtype: TopicSubtype.user_to_user,
topic_allowed_users: [ topic_allowed_users: [
Fabricate.build(:topic_allowed_user, user: attrs[:user]), 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 end
raw "Ssshh! This is our secret conversation!" raw "Ssshh! This is our secret conversation!"
@ -147,16 +142,15 @@ Fabricator(:group_private_message_post, from: :post) do
transient :recipients transient :recipients
user user
topic do |attrs| topic do |attrs|
Fabricate(:private_message_topic, Fabricate(
:private_message_topic,
user: attrs[:user], user: attrs[:user],
created_at: attrs[:created_at], created_at: attrs[:created_at],
subtype: TopicSubtype.user_to_user, subtype: TopicSubtype.user_to_user,
topic_allowed_users: [ topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: attrs[:user])],
Fabricate.build(:topic_allowed_user, user: attrs[:user]),
],
topic_allowed_groups: [ topic_allowed_groups: [
Fabricate.build(:topic_allowed_group, group: attrs[:recipients] || Fabricate(:group)) Fabricate.build(:topic_allowed_group, group: attrs[:recipients] || Fabricate(:group)),
] ],
) )
end end
raw "Ssshh! This is our group secret conversation!" raw "Ssshh! This is our group secret conversation!"
@ -165,13 +159,12 @@ end
Fabricator(:private_message_post_one_user, from: :post) do Fabricator(:private_message_post_one_user, from: :post) do
user user
topic do |attrs| topic do |attrs|
Fabricate(:private_message_topic, Fabricate(
:private_message_topic,
user: attrs[:user], user: attrs[:user],
created_at: attrs[:created_at], created_at: attrs[:created_at],
subtype: TopicSubtype.user_to_user, subtype: TopicSubtype.user_to_user,
topic_allowed_users: [ topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: attrs[:user])],
Fabricate.build(:topic_allowed_user, user: attrs[:user]),
]
) )
end end
raw "Ssshh! This is our secret conversation!" raw "Ssshh! This is our secret conversation!"
@ -188,10 +181,6 @@ Fabricator(:post_via_email, from: :post) do
end end
end end
Fabricator(:whisper, from: :post) do Fabricator(:whisper, from: :post) { post_type Post.types[:whisper] }
post_type Post.types[:whisper]
end
Fabricator(:small_action, from: :post) do Fabricator(:small_action, from: :post) { post_type Post.types[:small_action] }
post_type Post.types[:small_action]
end

View File

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

View File

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

View File

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

View File

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

View File

@ -5,5 +5,7 @@ Fabricator(:single_sign_on_record) do
external_id { sequence(:external_id) { |i| "ext_#{i}" } } external_id { sequence(:external_id) { |i| "ext_#{i}" } }
external_username { sequence(:username) { |i| "bruce#{i}" } } external_username { sequence(:username) { |i| "bruce#{i}" } }
external_email { sequence(:email) { |i| "bruce#{i}@wayne.com" } } 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 end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,15 +3,15 @@
Fabricator(:user_api_key) do Fabricator(:user_api_key) do
user user
client_id { SecureRandom.hex } client_id { SecureRandom.hex }
application_name 'some app' application_name "some app"
end end
Fabricator(:user_api_key_scope) Fabricator(:user_api_key_scope)
Fabricator(:readonly_user_api_key, from: :user_api_key) do 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 end
Fabricator(:bookmarks_calendar_user_api_key, from: :user_api_key) do 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 end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
Fabricator(:user_second_factor_totp, from: :user_second_factor) do Fabricator(:user_second_factor_totp, from: :user_second_factor) do
user user
data 'rcyryaqage3jexfj' data "rcyryaqage3jexfj"
enabled true enabled true
method UserSecondFactor.methods[:totp] method UserSecondFactor.methods[:totp]
end 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 # 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 # HOWEVER they are largely useless unless you have the device that created
# them. It is nice to have an approximation though. # them. It is nice to have an approximation though.
credential_id { 'mJAJ4CznTO0SuLkJbYwpgK75ao4KMNIPlU5KWM92nq39kRbXzI9mSv6GxTcsMYoiPgaouNw7b7zBiS4vsQaO6A==' } credential_id do
public_key { 'pQECAyYgASFYIMNgw4GCpwBUlR2SznJ1yY7B9yFvsuxhfo+C9kcA4IitIlggRdofrCezymy2B/YarX+gfB6gZKg648/cHIMjf6wWmmU=' } "mJAJ4CznTO0SuLkJbYwpgK75ao4KMNIPlU5KWM92nq39kRbXzI9mSv6GxTcsMYoiPgaouNw7b7zBiS4vsQaO6A=="
end
public_key do
"pQECAyYgASFYIMNgw4GCpwBUlR2SznJ1yY7B9yFvsuxhfo+C9kcA4IitIlggRdofrCezymy2B/YarX+gfB6gZKg648/cHIMjf6wWmmU="
end
enabled true enabled true
factor_type { UserSecurityKey.factor_types[:second_factor] } factor_type { UserSecurityKey.factor_types[:second_factor] }
name { sequence(:name) { |i| "Security Key #{i + 1}" } } name { sequence(:name) { |i| "Security Key #{i + 1}" } }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,8 @@ RSpec.describe ApplicationHelper do
it "sends crawler content to old mobiles" do it "sends crawler content to old mobiles" do
controller.stubs(:use_crawler_layout?).returns(false) 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) expect(helper.include_crawler_content?).to eq(true)
end end
@ -21,7 +22,8 @@ RSpec.describe ApplicationHelper do
it "does not send crawler content to new mobiles" do it "does not send crawler content to new mobiles" do
controller.stubs(:use_crawler_layout?).returns(false) 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) expect(helper.include_crawler_content?).to eq(false)
end end
@ -29,94 +31,100 @@ RSpec.describe ApplicationHelper do
it "provides brotli links to brotli cdn" do it "provides brotli links to brotli cdn" do
set_cdn_url "https://awesome.com" set_cdn_url "https://awesome.com"
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'br' helper.request.env["HTTP_ACCEPT_ENCODING"] = "br"
link = helper.preload_script('discourse') link = helper.preload_script("discourse")
expect(link).to eq(script_tag("https://awesome.com/brotli_asset/discourse.js")) expect(link).to eq(script_tag("https://awesome.com/brotli_asset/discourse.js"))
end end
context "with s3 CDN" do context "with s3 CDN" do
before do before do
global_setting :s3_bucket, 'test_bucket' global_setting :s3_bucket, "test_bucket"
global_setting :s3_region, 'ap-australia' global_setting :s3_region, "ap-australia"
global_setting :s3_access_key_id, '123' global_setting :s3_access_key_id, "123"
global_setting :s3_secret_access_key, '123' global_setting :s3_secret_access_key, "123"
global_setting :s3_cdn_url, 'https://s3cdn.com' global_setting :s3_cdn_url, "https://s3cdn.com"
end end
it "deals correctly with subfolder" do it "deals correctly with subfolder" do
set_subfolder "/community" 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 end
it "replaces cdn URLs with s3 cdn subfolder paths" do 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_cdn_url "https://awesome.com"
set_subfolder "/community" 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 end
it "returns magic brotli mangling for brotli requests" do it "returns magic brotli mangling for brotli requests" do
helper.request.env["HTTP_ACCEPT_ENCODING"] = "br"
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'br' link = helper.preload_script("discourse")
link = helper.preload_script('discourse')
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.br.js")) expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.br.js"))
end end
it "gives s3 cdn if asset host is not set" do 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")) expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.js"))
end end
it "can fall back to gzip compression" do it "can fall back to gzip compression" do
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'gzip' helper.request.env["HTTP_ACCEPT_ENCODING"] = "gzip"
link = helper.preload_script('discourse') link = helper.preload_script("discourse")
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.gz.js")) expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.gz.js"))
end end
it "gives s3 cdn even if asset host is set" do it "gives s3 cdn even if asset host is set" do
set_cdn_url "https://awesome.com" 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")) expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.js"))
end end
it "gives s3 cdn but without brotli/gzip extensions for theme tests assets" do it "gives s3 cdn but without brotli/gzip extensions for theme tests assets" do
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'gzip, br' helper.request.env["HTTP_ACCEPT_ENCODING"] = "gzip, br"
link = helper.preload_script('discourse/tests/theme_qunit_ember_jquery') 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")) expect(link).to eq(
script_tag("https://s3cdn.com/assets/discourse/tests/theme_qunit_ember_jquery.js"),
)
end end
it "uses separate asset CDN if configured" do it "uses separate asset CDN if configured" do
global_setting :s3_asset_cdn_url, "https://s3-asset-cdn.example.com" 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 end
end end
describe "add_resource_preload_list" do describe "add_resource_preload_list" do
it "adds resources to the preload list when it's available" do it "adds resources to the preload list when it's available" do
@links_to_preload = [] @links_to_preload = []
add_resource_preload_list('/assets/discourse.js', 'script') add_resource_preload_list("/assets/discourse.js", "script")
add_resource_preload_list('/assets/discourse.css', 'style') add_resource_preload_list("/assets/discourse.css", "style")
expect(@links_to_preload.size).to eq(2) expect(@links_to_preload.size).to eq(2)
end end
it "doesn't add resources to the preload list when it's not available" do it "doesn't add resources to the preload list when it's not available" do
@links_to_preload = nil @links_to_preload = nil
add_resource_preload_list('/assets/discourse.js', 'script') add_resource_preload_list("/assets/discourse.js", "script")
add_resource_preload_list('/assets/discourse.css', 'style') add_resource_preload_list("/assets/discourse.css", "style")
expect(@links_to_preload).to eq(nil) expect(@links_to_preload).to eq(nil)
end end
it "adds resources to the preload list when preload_script is called" do it "adds resources to the preload list when preload_script is called" do
@links_to_preload = [] @links_to_preload = []
helper.preload_script('discourse') helper.preload_script("discourse")
expect(@links_to_preload.size).to eq(1) expect(@links_to_preload.size).to eq(1)
end end
@ -131,7 +139,7 @@ RSpec.describe ApplicationHelper do
it "adds resources as the correct type" do it "adds resources as the correct type" do
@links_to_preload = [] @links_to_preload = []
helper.discourse_stylesheet_link_tag(:desktop) 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[0]).to match(/as="style"/)
expect(@links_to_preload[1]).to match(/as="script"/) expect(@links_to_preload[1]).to match(/as="script"/)
@ -152,14 +160,14 @@ RSpec.describe ApplicationHelper do
context "when on homepage" do context "when on homepage" do
it "will return sitelinks search tag" do it "will return sitelinks search tag" do
helper.stubs(:current_page?).returns(false) 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"') expect(helper.render_sitelinks_search_tag).to include('"@type":"SearchAction"')
end end
end end
context "when not on homepage" do context "when not on homepage" do
it "will not return sitelinks search tag" do it "will not return sitelinks search tag" do
helper.stubs(:current_page?).returns(true) 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) helper.stubs(:current_page?).with(Discourse.base_path).returns(false)
expect(helper.render_sitelinks_search_tag).to be_nil expect(helper.render_sitelinks_search_tag).to be_nil
end end
@ -168,7 +176,7 @@ RSpec.describe ApplicationHelper do
context "for subfolder install" do context "for subfolder install" do
context "when on homepage" do context "when on homepage" do
it "will return sitelinks search tag" 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?).returns(false)
helper.stubs(:current_page?).with(Discourse.base_path).returns(true) helper.stubs(:current_page?).with(Discourse.base_path).returns(true)
expect(helper.render_sitelinks_search_tag).to include('"@type":"SearchAction"') expect(helper.render_sitelinks_search_tag).to include('"@type":"SearchAction"')
@ -176,9 +184,9 @@ RSpec.describe ApplicationHelper do
end end
context "when not on homepage" do context "when not on homepage" do
it "will not return sitelinks search tag" 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?).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) helper.stubs(:current_page?).with(Discourse.base_path).returns(false)
expect(helper.render_sitelinks_search_tag).to be_nil expect(helper.render_sitelinks_search_tag).to be_nil
end end
@ -189,18 +197,17 @@ RSpec.describe ApplicationHelper do
describe "application_logo_url" do describe "application_logo_url" do
context "when a dark color scheme is active" do context "when a dark color scheme is active" do
before do before do
dark_theme = Theme.create( dark_theme =
name: "Dark", Theme.create(
user_id: -1, name: "Dark",
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id user_id: -1,
) color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
)
helper.request.env[:resolved_theme_id] = dark_theme.id helper.request.env[:resolved_theme_id] = dark_theme.id
end end
context "when on desktop" do context "when on desktop" do
before do before { session[:mobile_view] = "0" }
session[:mobile_view] = '0'
end
context "when logo_dark is not set" do context "when logo_dark is not set" do
it "will return site_logo_url instead" do it "will return site_logo_url instead" do
@ -209,9 +216,7 @@ RSpec.describe ApplicationHelper do
end end
context "when logo_dark is set" do context "when logo_dark is set" do
before do before { SiteSetting.logo_dark = Fabricate(:upload, url: "/images/logo-dark.png") }
SiteSetting.logo_dark = Fabricate(:upload, url: '/images/logo-dark.png')
end
it "will return site_logo_dark_url" do it "will return site_logo_dark_url" do
expect(helper.application_logo_url).to eq(SiteSetting.site_logo_dark_url) expect(helper.application_logo_url).to eq(SiteSetting.site_logo_dark_url)
@ -220,9 +225,7 @@ RSpec.describe ApplicationHelper do
end end
context "when on mobile" do context "when on mobile" do
before do before { session[:mobile_view] = "1" }
session[:mobile_view] = '1'
end
context "when mobile_logo_dark is not set" do context "when mobile_logo_dark is not set" do
it "will return site_mobile_logo_url instead" 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 context "when mobile_logo_dark is set" do
before 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 end
it "will return site_mobile_logo_dark_url" do it "will return site_mobile_logo_dark_url" do
@ -254,11 +257,12 @@ RSpec.describe ApplicationHelper do
context "when dark theme is present" do context "when dark theme is present" do
before do before do
_dark_theme = Theme.create( _dark_theme =
name: "Dark", Theme.create(
user_id: -1, name: "Dark",
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id user_id: -1,
) color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
)
end end
context "when dark logo is not present" do context "when dark logo is not present" do
@ -268,9 +272,7 @@ RSpec.describe ApplicationHelper do
end end
context "when dark logo is present" do context "when dark logo is present" do
before do before { SiteSetting.logo_dark = Fabricate(:upload, url: "/images/logo-dark.png") }
SiteSetting.logo_dark = Fabricate(:upload, url: '/images/logo-dark.png')
end
it "should return correct url" do it "should return correct url" do
expect(helper.application_logo_dark_url).to eq(SiteSetting.site_logo_dark_url) 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 context "when dark theme is present and selected" do
before do before do
dark_theme = Theme.create( dark_theme =
name: "Dark", Theme.create(
user_id: -1, name: "Dark",
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id user_id: -1,
) color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
)
helper.request.env[:resolved_theme_id] = dark_theme.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 end
it "should return nothing" do it "should return nothing" do
@ -298,119 +301,168 @@ RSpec.describe ApplicationHelper do
describe "#mobile_view?" do describe "#mobile_view?" do
context "when enable_mobile_theme is true" do context "when enable_mobile_theme is true" do
before do before { SiteSetting.enable_mobile_theme = true }
SiteSetting.enable_mobile_theme = true
end
it "is true if mobile_view is '1' in the session" do 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) expect(helper.mobile_view?).to eq(true)
end end
it "is false if mobile_view is '0' in the session" do 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) expect(helper.mobile_view?).to eq(false)
end end
context "when mobile_view session is cleared" do context "when mobile_view session is cleared" do
before do before { params[:mobile_view] = "auto" }
params[:mobile_view] = 'auto'
end
it "is false if user agent is not mobile" do it "is false if user agent is not mobile" do
session[:mobile_view] = '1' 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') 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 expect(helper.mobile_view?).to be_falsey
end end
it "is true for iPhone" do it "is true for iPhone" do
session[:mobile_view] = '0' 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') 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) expect(helper.mobile_view?).to eq(true)
end end
end end
context "when mobile_view is not set" do context "when mobile_view is not set" do
it "is false if user agent is not mobile" 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 expect(helper.mobile_view?).to be_falsey
end end
it "is true for iPhone" do 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) expect(helper.mobile_view?).to eq(true)
end end
it "is true for Android Samsung Galaxy" do 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) expect(helper.mobile_view?).to eq(true)
end end
it "is true for Android Google Nexus 5X" do 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) expect(helper.mobile_view?).to eq(true)
end end
it "is false for iPad" do 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) expect(helper.mobile_view?).to eq(false)
end end
it "is false for Nexus 10 tablet" do 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 expect(helper.mobile_view?).to be_falsey
end end
it "is false for Nexus 7 tablet" do 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 expect(helper.mobile_view?).to be_falsey
end end
end end
end end
context "when enable_mobile_theme is false" do context "when enable_mobile_theme is false" do
before do before { SiteSetting.enable_mobile_theme = false }
SiteSetting.enable_mobile_theme = false
end
it "is false if mobile_view is '1' in the session" do 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) expect(helper.mobile_view?).to eq(false)
end end
it "is false if mobile_view is '0' in the session" do 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) expect(helper.mobile_view?).to eq(false)
end end
context "when mobile_view is not set" do context "when mobile_view is not set" do
it "is false if user agent is not mobile" 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) expect(helper.mobile_view?).to eq(false)
end end
it "is false for iPhone" do 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) expect(helper.mobile_view?).to eq(false)
end end
end end
end end
end end
describe '#html_classes' do describe "#html_classes" do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
it "includes 'rtl' when the I18n.locale is rtl" do it "includes 'rtl' when the I18n.locale is rtl" do
I18n.stubs(:locale).returns(:he) I18n.stubs(:locale).returns(:he)
expect(helper.html_classes.split(" ")).to include('rtl') expect(helper.html_classes.split(" ")).to include("rtl")
end 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) 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 end
describe 'text size' do describe "text size" do
context "with a user option" do context "with a user option" do
before do before do
user.user_option.text_size = "larger" user.user_option.text_size = "larger"
@ -418,63 +470,65 @@ RSpec.describe ApplicationHelper do
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
end end
it 'ignores invalid text sizes' do it "ignores invalid text sizes" do
helper.request.cookies["text_size"] = "invalid" 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 end
it 'ignores missing text size' do it "ignores missing text size" do
helper.request.cookies["text_size"] = nil 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 end
it 'ignores cookies with lower sequence' do it "ignores cookies with lower sequence" do
user.user_option.update!(text_size_seq: 2) user.user_option.update!(text_size_seq: 2)
helper.request.cookies["text_size"] = "normal|1" 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 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) user.user_option.update!(text_size_seq: 2)
helper.request.cookies["text_size"] = "largest|4" 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 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 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
end end
it 'falls back to the default text size for anon' do it "falls back to the default text size for anon" do
expect(helper.html_classes.split(" ")).to include('text-size-normal') expect(helper.html_classes.split(" ")).to include("text-size-normal")
SiteSetting.default_text_size = "largest" 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
end end
it "includes 'anon' for anonymous users and excludes when logged in" do 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 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
end end
describe 'gsub_emoji_to_unicode' do describe "gsub_emoji_to_unicode" do
it "converts all 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
end end
describe 'preloaded_json' do describe "preloaded_json" do
it 'returns empty JSON if preloaded is empty' do it "returns empty JSON if preloaded is empty" do
@preloaded = nil @preloaded = nil
expect(helper.preloaded_json).to eq('{}') expect(helper.preloaded_json).to eq("{}")
end 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"]} } @preloaded = { test: %{["< \x80"]} }
expect(helper.preloaded_json).to eq(%{{"test":"[\\"\\u003c \uFFFD\\"]"}}) expect(helper.preloaded_json).to eq(%{{"test":"[\\"\\u003c \uFFFD\\"]"}})
end end
@ -482,9 +536,7 @@ RSpec.describe ApplicationHelper do
describe "client_side_setup_data" do describe "client_side_setup_data" do
context "when Rails.env.development? is true" do context "when Rails.env.development? is true" do
before do before { Rails.env.stubs(:development?).returns(true) }
Rails.env.stubs(:development?).returns(true)
end
it "returns the correct service worker url" do it "returns the correct service worker url" do
expect(helper.client_side_setup_data[:service_worker_url]).to eq("service-worker.js") expect(helper.client_side_setup_data[:service_worker_url]).to eq("service-worker.js")
@ -499,9 +551,7 @@ RSpec.describe ApplicationHelper do
end end
context "if the DEBUG_PRELOADED_APP_DATA env var is provided" do context "if the DEBUG_PRELOADED_APP_DATA env var is provided" do
before do before { ENV["DEBUG_PRELOADED_APP_DATA"] = "true" }
ENV['DEBUG_PRELOADED_APP_DATA'] = 'true'
end
it "returns that key as true" do it "returns that key as true" do
expect(helper.client_side_setup_data[:debug_preloaded_app_data]).to eq(true) expect(helper.client_side_setup_data[:debug_preloaded_app_data]).to eq(true)
@ -510,54 +560,42 @@ RSpec.describe ApplicationHelper do
end end
end end
describe 'crawlable_meta_data' do describe "crawlable_meta_data" do
it 'Supports ASCII URLs with odd chars' do it "Supports ASCII URLs with odd chars" do
result = helper.crawlable_meta_data( result =
url: (+"http://localhost/ión").force_encoding("ASCII-8BIT").freeze helper.crawlable_meta_data(
) url: (+"http://localhost/ión").force_encoding("ASCII-8BIT").freeze,
)
expect(result).to include("ión") expect(result).to include("ión")
end end
context "with opengraph image" do context "with opengraph image" do
it 'returns the correct image' do it "returns the correct image" do
SiteSetting.opengraph_image = Fabricate(:upload, SiteSetting.opengraph_image = Fabricate(:upload, url: "/images/og-image.png")
url: '/images/og-image.png'
)
SiteSetting.twitter_summary_large_image = Fabricate(:upload, SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.png")
url: '/images/twitter.png'
)
SiteSetting.large_icon = Fabricate(:upload, SiteSetting.large_icon = Fabricate(:upload, url: "/images/large_icon.png")
url: '/images/large_icon.png'
)
SiteSetting.apple_touch_icon = Fabricate(:upload, SiteSetting.apple_touch_icon =
url: '/images/default-apple-touch-icon.png' 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( expect(helper.crawlable_meta_data(image: "some-image.png")).to include("some-image.png")
helper.crawlable_meta_data(image: "some-image.png")
).to include("some-image.png")
expect(helper.crawlable_meta_data).to include( expect(helper.crawlable_meta_data).to include(SiteSetting.site_opengraph_image_url)
SiteSetting.site_opengraph_image_url
)
SiteSetting.opengraph_image = nil SiteSetting.opengraph_image = nil
expect(helper.crawlable_meta_data).to include( 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 SiteSetting.twitter_summary_large_image = nil
expect(helper.crawlable_meta_data).to include( expect(helper.crawlable_meta_data).to include(SiteSetting.site_large_icon_url)
SiteSetting.site_large_icon_url
)
SiteSetting.large_icon = nil SiteSetting.large_icon = nil
SiteSetting.logo_small = nil SiteSetting.logo_small = nil
@ -566,67 +604,62 @@ RSpec.describe ApplicationHelper do
SiteSetting.logo = nil 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 end
it "does not allow SVG images for twitter:image, falls back to site logo or nothing if site logo is SVG too" do 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.logo = Fabricate(:upload, url: "/images/d-logo-sketch.png")
SiteSetting.opengraph_image = Fabricate(:upload, SiteSetting.opengraph_image = Fabricate(:upload, url: "/images/og-image.png")
url: '/images/og-image.png'
)
expect(helper.crawlable_meta_data).to include(<<~HTML) expect(helper.crawlable_meta_data).to include(<<~HTML)
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_opengraph_image_url}\" /> <meta name=\"twitter:image\" content=\"#{SiteSetting.site_opengraph_image_url}\" />
HTML HTML
SiteSetting.opengraph_image = Fabricate(:upload, SiteSetting.opengraph_image = Fabricate(:upload, url: "/images/og-image.svg")
url: '/images/og-image.svg'
)
expect(helper.crawlable_meta_data).to include(<<~HTML) expect(helper.crawlable_meta_data).to include(<<~HTML)
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_logo_url}\" /> <meta name=\"twitter:image\" content=\"#{SiteSetting.site_logo_url}\" />
HTML HTML
SiteSetting.twitter_summary_large_image = Fabricate(:upload, SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.png")
url: '/images/twitter.png'
)
expect(helper.crawlable_meta_data).to include(<<~HTML) expect(helper.crawlable_meta_data).to include(<<~HTML)
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_twitter_summary_large_image_url}\" /> <meta name=\"twitter:image\" content=\"#{SiteSetting.site_twitter_summary_large_image_url}\" />
HTML HTML
SiteSetting.twitter_summary_large_image = Fabricate(:upload, SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.svg")
url: '/images/twitter.svg'
)
expect(helper.crawlable_meta_data).to include(<<~HTML) expect(helper.crawlable_meta_data).to include(<<~HTML)
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_logo_url}\" /> <meta name=\"twitter:image\" content=\"#{SiteSetting.site_logo_url}\" />
HTML 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") expect(helper.crawlable_meta_data).not_to include("twitter:image")
end end
end end
end end
describe 'discourse_color_scheme_stylesheets' do describe "discourse_color_scheme_stylesheets" do
fab!(:user) { Fabricate(:user) } 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 cs_stylesheets = helper.discourse_color_scheme_stylesheets
expect(cs_stylesheets).to include("stylesheets/color_definitions") expect(cs_stylesheets).to include("stylesheets/color_definitions")
end end
it 'returns two color scheme link tags when dark mode is enabled' do 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) SiteSetting.default_dark_mode_color_scheme_id =
ColorScheme.where(name: "Dark").pluck_first(:id)
cs_stylesheets = helper.discourse_color_scheme_stylesheets cs_stylesheets = helper.discourse_color_scheme_stylesheets
expect(cs_stylesheets).to include("(prefers-color-scheme: dark)") expect(cs_stylesheets).to include("(prefers-color-scheme: dark)")
expect(cs_stylesheets.scan("stylesheets/color_definitions").size).to eq(2) expect(cs_stylesheets.scan("stylesheets/color_definitions").size).to eq(2)
end end
it 'handles a missing dark color scheme gracefully' do it "handles a missing dark color scheme gracefully" do
scheme = ColorScheme.create!(name: "pyramid") scheme = ColorScheme.create!(name: "pyramid")
SiteSetting.default_dark_mode_color_scheme_id = scheme.id SiteSetting.default_dark_mode_color_scheme_id = scheme.id
scheme.destroy! scheme.destroy!
@ -638,7 +671,7 @@ RSpec.describe ApplicationHelper do
context "with custom light scheme" do context "with custom light scheme" do
before 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.color_scheme_id = @new_cs.id
user.user_option.save! user.user_option.save!
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user 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.dark_scheme_id = -1
user.user_option.save! user.user_option.save!
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user 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 end
it "returns no dark scheme stylesheet when user has disabled that option" do it "returns no dark scheme stylesheet when user has disabled that option" do
@ -711,23 +745,24 @@ RSpec.describe ApplicationHelper do
end end
describe "dark_color_scheme?" do 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) expect(helper.dark_color_scheme?).to eq(false)
end end
it 'works correctly for a dark scheme' do it "works correctly for a dark scheme" do
dark_theme = Theme.create( dark_theme =
name: "Dark", Theme.create(
user_id: -1, name: "Dark",
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id user_id: -1,
) color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
)
helper.request.env[:resolved_theme_id] = dark_theme.id helper.request.env[:resolved_theme_id] = dark_theme.id
expect(helper.dark_color_scheme?).to eq(true) expect(helper.dark_color_scheme?).to eq(true)
end end
end end
describe 'html_lang' do describe "html_lang" do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
before do before do
@ -735,12 +770,12 @@ RSpec.describe ApplicationHelper do
SiteSetting.default_locale = :fr SiteSetting.default_locale = :fr
end end
it 'returns default locale if no request' do it "returns default locale if no request" do
helper.request = nil helper.request = nil
expect(helper.html_lang).to eq(SiteSetting.default_locale) expect(helper.html_lang).to eq(SiteSetting.default_locale)
end 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 helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
expect(helper.html_lang).to eq(I18n.locale.to_s) expect(helper.html_lang).to eq(I18n.locale.to_s)
end end

View File

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

View File

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

View File

@ -3,18 +3,17 @@
RSpec.describe UserNotificationsHelper do RSpec.describe UserNotificationsHelper do
let(:upload_path) { Discourse.store.upload_path } let(:upload_path) { Discourse.store.upload_path }
describe '#email_excerpt' do describe "#email_excerpt" do
let(:paragraphs) { [ 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>" "<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")
end end
let(:post_quote) do let(:cooked) { paragraphs.join("\n") }
<<~HTML
let(:post_quote) { <<~HTML }
<aside class="quote no-group" data-post="859" data-topic="30"> <aside class="quote no-group" data-post="859" data-topic="30">
<div class="title"> <div class="title">
<div class="quote-controls"></div> <div class="quote-controls"></div>
@ -24,19 +23,16 @@ RSpec.describe UserNotificationsHelper do
</blockquote> </blockquote>
</aside> </aside>
HTML HTML
end
let(:image_paragraph) do let(:image_paragraph) do
'<p><img src="//localhost:3000/uploads/b9.png" width="300" height="300"></p>' '<p><img src="//localhost:3000/uploads/b9.png" width="300" height="300"></p>'
end end
let(:lightbox_image) do let(:lightbox_image) { <<~HTML }
<<~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"> <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> <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> </div></a></div></p>
HTML HTML
end
let(:expected_lightbox_image) do 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>' '<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 end
it "doesn't count emoji images" do 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") arg = ([with_emoji] + paragraphs).join("\n")
SiteSetting.digest_min_excerpt_length = 50 SiteSetting.digest_min_excerpt_length = 50
expect(helper.email_excerpt(arg)).to eq([with_emoji, paragraphs[0]].join) expect(helper.email_excerpt(arg)).to eq([with_emoji, paragraphs[0]].join)
end end
it "only counts link text" do 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") arg = ([with_link] + paragraphs).join("\n")
SiteSetting.digest_min_excerpt_length = 50 SiteSetting.digest_min_excerpt_length = 50
expect(helper.email_excerpt(arg)).to eq([with_link, paragraphs[0]].join) expect(helper.email_excerpt(arg)).to eq([with_link, paragraphs[0]].join)
@ -81,11 +79,12 @@ RSpec.describe UserNotificationsHelper do
<p>AFTER</p> <p>AFTER</p>
HTML 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 end
it "defaults to content after post quote (image w/ no text)" do it "defaults to content after post quote (image w/ no text)" do
cooked = <<~HTML cooked = <<~HTML
#{post_quote} #{post_quote}
#{image_paragraph} #{image_paragraph}
@ -94,7 +93,8 @@ RSpec.describe UserNotificationsHelper do
end end
it "defaults to content after post quote (onebox)" do 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 cooked = <<~HTML
#{post_quote} #{post_quote}
#{aside_onebox} #{aside_onebox}
@ -120,44 +120,40 @@ RSpec.describe UserNotificationsHelper do
end end
end end
describe '#logo_url' do describe "#logo_url" do
describe 'local store' do describe "local store" do
let(:upload) { Fabricate(:upload, sha1: "somesha1") } let(:upload) { Fabricate(:upload, sha1: "somesha1") }
before do before { SiteSetting.logo = upload }
SiteSetting.logo = upload
end
it 'should return the right URL' do it "should return the right URL" do
expect(helper.logo_url).to eq( 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 end
describe 'when cdn path is configured' do describe "when cdn path is configured" do
before do before do
GlobalSetting.expects(:cdn_url) GlobalSetting.expects(:cdn_url).returns("https://some.localcdn.com").at_least_once
.returns('https://some.localcdn.com')
.at_least_once
end end
it 'should return the right URL' do it "should return the right URL" do
expect(helper.logo_url).to eq( 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
end end
describe 'when logo is an SVG' do describe "when logo is an SVG" do
let(:upload) { Fabricate(:upload, extension: "svg") } let(:upload) { Fabricate(:upload, extension: "svg") }
it 'should return nil' do it "should return nil" do
expect(helper.logo_url).to eq(nil) expect(helper.logo_url).to eq(nil)
end end
end end
end end
describe 's3 store' do describe "s3 store" do
let(:upload) { Fabricate(:upload_s3, sha1: "somesha1") } let(:upload) { Fabricate(:upload_s3, sha1: "somesha1") }
before do before do
@ -165,32 +161,27 @@ RSpec.describe UserNotificationsHelper do
SiteSetting.logo = upload SiteSetting.logo = upload
end end
it 'should return the right URL' do it "should return the right URL" do
expect(helper.logo_url).to eq( 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 global cdn path is configured' do describe "when global cdn path is configured" do
it 'should return the right url' do it "should return the right url" do
GlobalSetting.stubs(:cdn_url).returns('https://some.cdn.com/cluster') GlobalSetting.stubs(:cdn_url).returns("https://some.cdn.com/cluster")
expect(helper.logo_url).to eq( 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
end end
describe 'when cdn path is configured' do describe "when cdn path is configured" do
before do before { SiteSetting.s3_cdn_url = "https://some.cdn.com" }
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 end
end end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe 'Setting changes' do RSpec.describe "Setting changes" do
describe '#must_approve_users' do describe "#must_approve_users" do
before { SiteSetting.must_approve_users = false } 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 user_pending_approval = Fabricate(:reviewable_user).target
SiteSetting.must_approve_users = true SiteSetting.must_approve_users = true
@ -12,7 +12,7 @@ RSpec.describe 'Setting changes' do
expect(user_pending_approval.reload.approved?).to eq(false) expect(user_pending_approval.reload.approved?).to eq(false)
end 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) non_approved_user = Fabricate(:user, approved: false)
SiteSetting.must_approve_users = true SiteSetting.must_approve_users = true
@ -21,10 +21,10 @@ RSpec.describe 'Setting changes' do
end end
end end
describe '#reviewable_low_priority_threshold' do describe "#reviewable_low_priority_threshold" do
let(:new_threshold) { 5 } let(:new_threshold) { 5 }
it 'sets the low priority value' do it "sets the low priority value" do
medium_threshold = 10 medium_threshold = 10
Reviewable.set_priorities(medium: medium_threshold) Reviewable.set_priorities(medium: medium_threshold)

View File

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

View File

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

View File

@ -3,11 +3,20 @@
RSpec.describe "hotlinked media blocking" do RSpec.describe "hotlinked media blocking" do
let(:hotlinked_url) { "http://example.com/images/2/2e/Longcat1.png" } let(:hotlinked_url) { "http://example.com/images/2/2e/Longcat1.png" }
let(:onebox_url) { "http://example.com/onebox" } 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 before do
SiteSetting.download_remote_images_to_local = false 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 stub_image_size
end end
@ -19,38 +28,65 @@ RSpec.describe "hotlinked media blocking" do
context "with hotlinked media blocked, before post-processing" do context "with hotlinked media blocked, before post-processing" do
before do before do
SiteSetting.block_hotlinked_media = true 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 end
it "blocks hotlinked images" do it "blocks hotlinked images" do
post = Fabricate(:post, raw: "<img src='#{hotlinked_url}'>") post = Fabricate(:post, raw: "<img src='#{hotlinked_url}'>")
expect(post.cooked).not_to have_tag("img[src]") 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 end
it "blocks hotlinked videos with src" do it "blocks hotlinked videos with src" do
post = Fabricate(:post, raw: "![alt text|video](#{hotlinked_url})") post = Fabricate(:post, raw: "![alt text|video](#{hotlinked_url})")
expect(post.cooked).not_to have_tag("video source[src]") 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 end
it "blocks hotlinked videos with srcset" do it "blocks hotlinked videos with srcset" do
srcset = "#{hotlinked_url} 1x,https://example.com 2x" srcset = "#{hotlinked_url} 1x,https://example.com 2x"
post = Fabricate(:post, raw: "<video><source srcset='#{srcset}'></video>") post = Fabricate(:post, raw: "<video><source srcset='#{srcset}'></video>")
expect(post.cooked).not_to have_tag("video source[srcset]") 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 end
it "blocks hotlinked audio" do it "blocks hotlinked audio" do
post = Fabricate(:post, raw: "![alt text|audio](#{hotlinked_url})") post = Fabricate(:post, raw: "![alt text|audio](#{hotlinked_url})")
expect(post.cooked).not_to have_tag("audio source[src]") 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 end
it "blocks hotlinked onebox content when cached (post_analyzer)" do it "blocks hotlinked onebox content when cached (post_analyzer)" do
post = Fabricate(:post, raw: "#{onebox_url}") post = Fabricate(:post, raw: "#{onebox_url}")
expect(post.cooked).not_to have_tag("img[src]") 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 end
it "allows relative URLs" do it "allows relative URLs" do
@ -81,8 +117,18 @@ RSpec.describe "hotlinked media blocking" do
post.reload 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" })
expect(post.cooked).to have_tag("img", with: { "src" => "https://example.com/myimage.png" }) 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(
expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://malicious.invalid/https://example.com" }) "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 end
it "allows multiple exceptions" do 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).not_to have_tag("audio")
expect(post.cooked).to have_tag( expect(post.cooked).to have_tag(
"a.blocked-hotlinked-placeholder[href^='http://example.com'][rel='noopener nofollow ugc']", "a.blocked-hotlinked-placeholder[href^='http://example.com'][rel='noopener nofollow ugc']",
count: 4 count: 4,
) )
end end
end end

View File

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

View File

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

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
describe 'Discord OAuth2' do describe "Discord OAuth2" do
let(:access_token) { "discord_access_token_448" } let(:access_token) { "discord_access_token_448" }
let(:client_id) { "abcdef11223344" } let(:client_id) { "abcdef11223344" }
let(:client_secret) { "adddcccdddd99922" } let(:client_secret) { "adddcccdddd99922" }
@ -9,15 +9,14 @@ describe 'Discord OAuth2' do
fab!(:user1) { Fabricate(:user) } fab!(:user1) { Fabricate(:user) }
def setup_discord_email_stub(email, verified:) def setup_discord_email_stub(email, verified:)
stub_request(:get, "https://discord.com/api/users/@me") stub_request(:get, "https://discord.com/api/users/@me").with(
.with( headers: {
headers: { "Authorization" => "Bearer #{access_token}",
"Authorization" => "Bearer #{access_token}" },
} ).to_return(
) status: 200,
.to_return( body:
status: 200, JSON.dump(
body: JSON.dump(
id: "80351110224678912", id: "80351110224678912",
username: "Nelly", username: "Nelly",
discriminator: "1337", discriminator: "1337",
@ -26,14 +25,14 @@ describe 'Discord OAuth2' do
email: email, email: email,
flags: 64, flags: 64,
banner: "06c16474723fe537c283b8efa61a30c8", banner: "06c16474723fe537c283b8efa61a30c8",
accent_color: 16711680, accent_color: 16_711_680,
premium_type: 1, premium_type: 1,
public_flags: 64 public_flags: 64,
), ),
headers: { headers: {
"Content-Type" => "application/json" "Content-Type" => "application/json",
} },
) )
end end
before do before do
@ -41,50 +40,49 @@ describe 'Discord OAuth2' do
SiteSetting.discord_client_id = client_id SiteSetting.discord_client_id = client_id
SiteSetting.discord_secret = client_secret SiteSetting.discord_secret = client_secret
stub_request(:post, "https://discord.com/api/oauth2/token") stub_request(:post, "https://discord.com/api/oauth2/token").with(
.with( body:
body: hash_including( hash_including(
"client_id" => client_id, "client_id" => client_id,
"client_secret" => client_secret, "client_secret" => client_secret,
"code" => temp_code, "code" => temp_code,
"grant_type" => "authorization_code", "grant_type" => "authorization_code",
"redirect_uri" => "http://test.localhost/auth/discord/callback" "redirect_uri" => "http://test.localhost/auth/discord/callback",
) ),
) ).to_return(
.to_return( status: 200,
status: 200, body:
body: Rack::Utils.build_query( Rack::Utils.build_query(
access_token: access_token, access_token: access_token,
scope: "identify emails guilds", scope: "identify emails guilds",
token_type: "Bearer", token_type: "Bearer",
expires_in: 604800, expires_in: 604_800,
refresh_token: "D43f5y0ahjqew82jZ4NViEr2YafMKhue", refresh_token: "D43f5y0ahjqew82jZ4NViEr2YafMKhue",
), ),
headers: { headers: {
"Content-Type" => "application/x-www-form-urlencoded" "Content-Type" => "application/x-www-form-urlencoded",
} },
) )
stub_request(:get, "https://discord.com/api/users/@me/guilds") stub_request(:get, "https://discord.com/api/users/@me/guilds").with(
.with( headers: {
headers: { "Authorization" => "Bearer #{access_token}",
"Authorization" => "Bearer #{access_token}" },
} ).to_return(
) status: 200,
.to_return( body:
status: 200, JSON.dump(
body: JSON.dump(
id: "80351110224678912", id: "80351110224678912",
name: "1337 Krew", name: "1337 Krew",
icon: "8342729096ea3675442027381ff50dfe", icon: "8342729096ea3675442027381ff50dfe",
owner: true, owner: true,
permissions: "36953089", permissions: "36953089",
features: ["COMMUNITY", "NEWS"] features: %w[COMMUNITY NEWS],
), ),
headers: { headers: {
"Content-Type" => "application/json" "Content-Type" => "application/json",
} },
) )
end end
it "doesn't sign in anyone if the email from discord is not verified" do 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) setup_discord_email_stub(user1.email, verified: false)
post "/auth/discord/callback", params: { post "/auth/discord/callback", params: { state: session["omniauth.state"], code: temp_code }
state: session["omniauth.state"],
code: temp_code
}
expect(response.status).to eq(302) expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/") expect(response.location).to eq("http://test.localhost/")
@ -111,10 +106,7 @@ describe 'Discord OAuth2' do
setup_discord_email_stub(user1.email, verified: true) setup_discord_email_stub(user1.email, verified: true)
post "/auth/discord/callback", params: { post "/auth/discord/callback", params: { state: session["omniauth.state"], code: temp_code }
state: session["omniauth.state"],
code: temp_code
}
expect(response.status).to eq(302) expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/") 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) %>" SiteSetting.email_custom_template = "<hello>%{email_content}</hello><%= (111 * 333) %>"
html = Email::Renderer.new(UserNotifications.signup(Fabricate(:user))).html html = Email::Renderer.new(UserNotifications.signup(Fabricate(:user))).html
expect(html).not_to include("36963") expect(html).not_to include("36963")
expect(html).to include('<hello>') expect(html).to include("<hello>")
end end
end end
context "with a custom template" do context "with a custom template" do
before do before do
SiteSetting.email_custom_template = "<body><h1>FOR YOU</h1><div>%{email_content}</div></body>" 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 SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css
end end
@ -22,36 +22,36 @@ RSpec.describe EmailStyle do
SiteSetting.remove_override!(:email_custom_css) SiteSetting.remove_override!(:email_custom_css)
end end
context 'with invite' do context "with invite" do
fab!(:invite) { Fabricate(:invite) } fab!(:invite) { Fabricate(:invite) }
let(:invite_mail) { InviteMailer.send_invite(invite) } let(:invite_mail) { InviteMailer.send_invite(invite) }
subject(:mail_html) { Email::Renderer.new(invite_mail).html } 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.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}") expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}")
end end
it 'applies customizations if compiled is missing' do it "applies customizations if compiled is missing" do
SiteSetting.remove_override!(:email_custom_css_compiled) 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.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}") expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}")
end end
it 'can apply RTL attrs' do it "can apply RTL attrs" do
SiteSetting.default_locale = 'he' SiteSetting.default_locale = "he"
body_attrs = mail_html.match(/<body ([^>])+/) body_attrs = mail_html.match(/<body ([^>])+/)
expect(body_attrs[0]&.downcase).to match(/text-align:\s*right/) expect(body_attrs[0]&.downcase).to match(/text-align:\s*right/)
expect(body_attrs[0]&.downcase).to include('dir="rtl"') expect(body_attrs[0]&.downcase).to include('dir="rtl"')
end end
end end
context 'when user_replied' do context "when user_replied" do
let(:response_by_user) { Fabricate(:user, name: "John Doe") } 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(: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(:response) { Fabricate(:basic_reply, topic: post.topic, user: response_by_user) }
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
let(:notification) { Fabricate(:replied_notification, user: user, post: response) } let(:notification) { Fabricate(:replied_notification, user: user, post: response) }
@ -61,7 +61,7 @@ RSpec.describe EmailStyle do
user, user,
post: response, post: response,
notification_type: notification.notification_type, notification_type: notification.notification_type,
notification_data_hash: notification.data_hash notification_data_hash: notification.data_hash,
) )
end end
@ -72,42 +72,45 @@ RSpec.describe EmailStyle do
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1) 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}/) matches = mail_html.match(/<div style="([^"]+)" dm=\"body\">#{post.raw}/)
expect(matches[1]).to include('color: #FAB;') # custom expect(matches[1]).to include("color: #FAB;") # custom
expect(matches[1]).to include('padding-top:5px;') # div.body expect(matches[1]).to include("padding-top:5px;") # div.body
end end
# TODO: translation override # TODO: translation override
end end
context 'with signup' do context "with signup" do
let(:signup_mail) { UserNotifications.signup(Fabricate(:user)) } let(:signup_mail) { UserNotifications.signup(Fabricate(:user)) }
subject(:mail_html) { Email::Renderer.new(signup_mail).html } subject(:mail_html) { Email::Renderer.new(signup_mail).html }
it "customizations are applied to html part of emails" do 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.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 end
context 'with translation override' do context "with translation override" do
before do before do
TranslationOverride.upsert!( TranslationOverride.upsert!(
SiteSetting.default_locale, SiteSetting.default_locale,
'user_notifications.signup.text_body_template', "user_notifications.signup.text_body_template",
"CLICK THAT LINK: %{base_url}/u/activate-account/%{email_token}" "CLICK THAT LINK: %{base_url}/u/activate-account/%{email_token}",
) )
end end
after do 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 end
it "applies customizations when translation override exists" do 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('<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
end end
context 'with some bad css' do context "with some bad css" do
before do before do
SiteSetting.email_custom_css = '@import "nope.css"; h1 {{{ size: really big; ' SiteSetting.email_custom_css = '@import "nope.css"; h1 {{{ size: really big; '
SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css
@ -115,13 +118,15 @@ RSpec.describe EmailStyle do
it "can render the html" 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.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 end
end end
context 'with digest' do context "with digest" do
fab!(:popular_topic) { Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago) } fab!(:popular_topic) do
Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago)
end
let(:summary_email) { UserNotifications.digest(Fabricate(:user)) } let(:summary_email) { UserNotifications.digest(Fabricate(:user)) }
subject(:mail_html) { Email::Renderer.new(summary_email).html } 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 it "doesn't apply customizations if apply_custom_styles_to_digest is disabled" do
SiteSetting.apply_custom_styles_to_digest = false 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('<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) expect(mail_html).to include(popular_topic.title)
end end
end end

View File

@ -1,11 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
describe 'Facebook OAuth2' do describe "Facebook OAuth2" do
let(:access_token) { "facebook_access_token_448" } let(:access_token) { "facebook_access_token_448" }
let(:app_id) { "432489234823984" } let(:app_id) { "432489234823984" }
let(:app_secret) { "adddcccdddd99922" } let(:app_secret) { "adddcccdddd99922" }
let(:temp_code) { "facebook_temp_code_544254" } 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) } fab!(:user1) { Fabricate(:user) }
@ -18,19 +20,16 @@ describe 'Facebook OAuth2' do
} }
body[:email] = email if email 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") stub_request(
.with( :get,
headers: { "https://graph.facebook.com/v5.0/me?appsecret_proof=#{appsecret_proof}&fields=name,first_name,last_name,email",
"Authorization" => "OAuth #{access_token}" ).with(headers: { "Authorization" => "OAuth #{access_token}" }).to_return(
} status: 200,
) body: JSON.dump(body),
.to_return( headers: {
status: 200, "Content-Type" => "application/json",
body: JSON.dump(body), },
headers: { )
"Content-Type" => "application/json"
}
)
end end
before do before do
@ -38,27 +37,23 @@ describe 'Facebook OAuth2' do
SiteSetting.facebook_app_id = app_id SiteSetting.facebook_app_id = app_id
SiteSetting.facebook_app_secret = app_secret SiteSetting.facebook_app_secret = app_secret
stub_request(:post, "https://graph.facebook.com/v5.0/oauth/access_token") stub_request(:post, "https://graph.facebook.com/v5.0/oauth/access_token").with(
.with( body:
body: hash_including( hash_including(
"client_id" => app_id, "client_id" => app_id,
"client_secret" => app_secret, "client_secret" => app_secret,
"code" => temp_code, "code" => temp_code,
"grant_type" => "authorization_code", "grant_type" => "authorization_code",
"redirect_uri" => "http://test.localhost/auth/facebook/callback" "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",
), ),
headers: { ).to_return(
"Content-Type" => "application/x-www-form-urlencoded" 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 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 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) setup_facebook_email_stub(email: user1.email)
post "/auth/facebook/callback", params: { post "/auth/facebook/callback", params: { state: session["omniauth.state"], code: temp_code }
state: session["omniauth.state"],
code: temp_code
}
expect(response.status).to eq(302) expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/") expect(response.location).to eq("http://test.localhost/")
@ -85,10 +77,7 @@ describe 'Facebook OAuth2' do
setup_facebook_email_stub(email: nil) setup_facebook_email_stub(email: nil)
post "/auth/facebook/callback", params: { post "/auth/facebook/callback", params: { state: session["omniauth.state"], code: temp_code }
state: session["omniauth.state"],
code: temp_code
}
expect(response.status).to eq(302) expect(response.status).to eq(302)
expect(response.location).to eq("http://test.localhost/") expect(response.location).to eq("http://test.localhost/")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,36 +4,34 @@
RSpec.describe Topic do RSpec.describe Topic do
let(:job_klass) { Jobs::CloseTopic } 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) } let(:topic) { Fabricate(:topic, category: category) }
context 'when uncategorized' do context "when uncategorized" do
let(:category) { nil } 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(topic.public_topic_timer).to eq(nil)
expect(job_klass.jobs).to eq([]) expect(job_klass.jobs).to eq([])
end end
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) } 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(topic.public_topic_timer).to eq(nil)
expect(job_klass.jobs).to eq([]) expect(job_klass.jobs).to eq([])
end end
end end
context 'when jobs may be queued' do context "when jobs may be queued" do
before do before { freeze_time }
freeze_time
end
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) } 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
topic_status_update = TopicTimer.last 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) expect(topic.public_topic_timer.execute_at).to be_within_one_second_of(2.hours.from_now)
end end
context 'when topic was created by staff user' do context "when topic was created by staff user" do
let(:admin) { Fabricate(:admin) } let(:admin) { Fabricate(:admin) }
let(:staff_topic) { Fabricate(:topic, user: admin, category: category) } 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 staff_topic
topic_status_update = TopicTimer.last topic_status_update = TopicTimer.last
@ -56,23 +54,24 @@ RSpec.describe Topic do
expect(topic_status_update.user).to eq(Discourse.system_user) expect(topic_status_update.user).to eq(Discourse.system_user)
end end
context 'when topic is closed manually' do context "when topic is closed manually" do
it 'should remove the schedule to auto-close the topic' do it "should remove the schedule to auto-close the topic" do
topic_timer_id = staff_topic.public_topic_timer.id 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) expect(
.to be_within_one_second_of(Time.zone.now) TopicTimer.with_deleted.find(topic_timer_id).deleted_at,
).to be_within_one_second_of(Time.zone.now)
end end
end 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) { Fabricate(:user) }
let(:regular_user_topic) { Fabricate(:topic, user: regular_user, category: category) } 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 regular_user_topic
topic_status_update = TopicTimer.last 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!(:topic) { Fabricate(:topic, image_upload_id: image.id) }
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
describe 'latest' do describe "latest" do
def get_topic def get_topic
Discourse.redis.del(topic.thumbnail_job_redis_key(Topic.thumbnail_sizes)) Discourse.redis.del(topic.thumbnail_job_redis_key(Topic.thumbnail_sizes))
Discourse.redis.del(topic.thumbnail_job_redis_key([])) Discourse.redis.del(topic.thumbnail_job_redis_key([]))
get '/latest.json' get "/latest.json"
expect(response.status).to eq(200) expect(response.status).to eq(200)
response.parsed_body["topic_list"]["topics"][0] response.parsed_body["topic_list"]["topics"][0]
end end
@ -27,11 +27,7 @@ RSpec.describe "Topic Thumbnails" do
context "with a theme" do context "with a theme" do
before do before do
theme = Fabricate(:theme) theme = Fabricate(:theme)
theme.theme_modifier_set.topic_thumbnail_sizes = [ theme.theme_modifier_set.topic_thumbnail_sizes = [[10, 10], [20, 20], [30, 30]]
[10, 10],
[20, 20],
[30, 30]
]
theme.theme_modifier_set.save! theme.theme_modifier_set.save!
theme.set_default! theme.set_default!
end end
@ -39,15 +35,15 @@ RSpec.describe "Topic Thumbnails" do
it "includes the theme specified resolutions" do it "includes the theme specified resolutions" do
topic_json = nil topic_json = nil
expect do expect do topic_json = get_topic end.to change {
topic_json = get_topic Jobs::GenerateTopicThumbnails.jobs.size
end.to change { Jobs::GenerateTopicThumbnails.jobs.size }.by(2) }.by(2)
expect( expect(Jobs::GenerateTopicThumbnails.jobs.map { |j| j["args"][0]["extra_sizes"] }).to eq(
Jobs::GenerateTopicThumbnails.jobs.map { |j| j["args"][0]["extra_sizes"] } [
).to eq([ nil, # Job for core/plugin sizes
nil, # Job for core/plugin sizes [[10, 10], [20, 20], [30, 30]],
[[10, 10], [20, 20], [30, 30]]] # Job for theme sizes ], # Job for theme sizes
) )
thumbnails = topic_json["thumbnails"] thumbnails = topic_json["thumbnails"]
@ -67,9 +63,9 @@ RSpec.describe "Topic Thumbnails" do
Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access) Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access)
# Request again # Request again
expect do expect do topic_json = get_topic end.not_to change {
topic_json = get_topic Jobs::GenerateTopicThumbnails.jobs.size
end.not_to change { Jobs::GenerateTopicThumbnails.jobs.size } }
thumbnails = topic_json["thumbnails"] thumbnails = topic_json["thumbnails"]
@ -82,7 +78,6 @@ RSpec.describe "Topic Thumbnails" do
expect(thumbnails[1]["width"]).to eq(9) expect(thumbnails[1]["width"]).to eq(9)
expect(thumbnails[1]["height"]).to eq(9) expect(thumbnails[1]["height"]).to eq(9)
expect(thumbnails[1]["url"]).to include("/optimized/") expect(thumbnails[1]["url"]).to include("/optimized/")
end end
end end
@ -92,25 +87,23 @@ RSpec.describe "Topic Thumbnails" do
plugin.register_topic_thumbnail_size [512, 512] plugin.register_topic_thumbnail_size [512, 512]
end end
after do after { DiscoursePluginRegistry.reset! }
DiscoursePluginRegistry.reset!
end
it "includes the theme specified resolutions" do it "includes the theme specified resolutions" do
topic_json = nil topic_json = nil
expect do expect do topic_json = get_topic end.to change {
topic_json = get_topic Jobs::GenerateTopicThumbnails.jobs.size
end.to change { Jobs::GenerateTopicThumbnails.jobs.size }.by(1) }.by(1)
# Run the job # Run the job
args = Jobs::GenerateTopicThumbnails.jobs.last["args"].first args = Jobs::GenerateTopicThumbnails.jobs.last["args"].first
Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access) Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access)
# Request again # Request again
expect do expect do topic_json = get_topic end.not_to change {
topic_json = get_topic Jobs::GenerateTopicThumbnails.jobs.size
end.not_to change { Jobs::GenerateTopicThumbnails.jobs.size } }
thumbnails = topic_json["thumbnails"] thumbnails = topic_json["thumbnails"]

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
describe 'Twitter OAuth 1.0a' do describe "Twitter OAuth 1.0a" do
let(:access_token) { "twitter_access_token_448" } let(:access_token) { "twitter_access_token_448" }
let(:consumer_key) { "abcdef11223344" } let(:consumer_key) { "abcdef11223344" }
let(:consumer_secret) { "adddcccdddd99922" } let(:consumer_secret) { "adddcccdddd99922" }
@ -14,14 +14,15 @@ describe 'Twitter OAuth 1.0a' do
created_at: "Sat May 09 17:58:22 +0000 2009", created_at: "Sat May 09 17:58:22 +0000 2009",
default_profile: false, default_profile: false,
default_profile_image: 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, favourites_count: 588,
follow_request_sent: nil, follow_request_sent: nil,
followers_count: 10625, followers_count: 10_625,
following: nil, following: nil,
friends_count: 1181, friends_count: 1181,
geo_enabled: true, geo_enabled: true,
id: 38895958, id: 38_895_958,
id_str: "38895958", id_str: "38895958",
is_translator: false, is_translator: false,
lang: "en", lang: "en",
@ -30,11 +31,14 @@ describe 'Twitter OAuth 1.0a' do
name: "Sean Cook", name: "Sean Cook",
notifications: nil, notifications: nil,
profile_background_color: "1A1B1F", profile_background_color: "1A1B1F",
profile_background_image_url: "http://a0.twimg.com/profile_background_images/495742332/purty_wood.png", profile_background_image_url:
profile_background_image_url_https: "https://si0.twimg.com/profile_background_images/495742332/purty_wood.png", "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_background_tile: true,
profile_image_url: "http://a0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG", 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_link_color: "2FC2EF",
profile_sidebar_border_color: "181A1E", profile_sidebar_border_color: "181A1E",
profile_sidebar_fill_color: "252429", profile_sidebar_fill_color: "252429",
@ -46,19 +50,17 @@ describe 'Twitter OAuth 1.0a' do
statuses_count: 2609, statuses_count: 2609,
time_zone: "Pacific Time (US & Canada)", time_zone: "Pacific Time (US & Canada)",
url: nil, url: nil,
utc_offset: -28800, utc_offset: -28_800,
verified: true, verified: true,
email: email email: email,
} }
stub_request(:get, "https://api.twitter.com/1.1/account/verify_credentials.json") stub_request(:get, "https://api.twitter.com/1.1/account/verify_credentials.json").with(
.with( query: {
query: { include_email: true,
include_email: true, include_entities: false,
include_entities: false, skip_status: true,
skip_status: true },
} ).to_return(status: 200, body: JSON.dump(body))
)
.to_return(status: 200, body: JSON.dump(body))
end end
before do before do
@ -66,28 +68,28 @@ describe 'Twitter OAuth 1.0a' do
SiteSetting.twitter_consumer_key = consumer_key SiteSetting.twitter_consumer_key = consumer_key
SiteSetting.twitter_consumer_secret = consumer_secret SiteSetting.twitter_consumer_secret = consumer_secret
stub_request(:post, "https://api.twitter.com/oauth/request_token") stub_request(:post, "https://api.twitter.com/oauth/request_token").to_return(
.to_return( status: 200,
status: 200, body:
body: Rack::Utils.build_query( Rack::Utils.build_query(
oauth_token: access_token, oauth_token: access_token,
oauth_token_secret: oauth_token_secret, oauth_token_secret: oauth_token_secret,
oauth_callback_confirmed: true oauth_callback_confirmed: true,
), ),
headers: { headers: {
"Content-Type" => "application/x-www-form-urlencoded" "Content-Type" => "application/x-www-form-urlencoded",
} },
) )
stub_request(:post, "https://api.twitter.com/oauth/access_token") stub_request(:post, "https://api.twitter.com/oauth/access_token").to_return(
.to_return( status: 200,
status: 200, body:
body: Rack::Utils.build_query( Rack::Utils.build_query(
oauth_token: access_token, oauth_token: access_token,
oauth_token_secret: oauth_token_secret, oauth_token_secret: oauth_token_secret,
user_id: "43423432422", user_id: "43423432422",
screen_name: "twitterapi" screen_name: "twitterapi",
) ),
) )
end 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 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!(:topic) { Fabricate(:topic) }
fab!(:first_post) { Fabricate(:post, topic: 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(:flag_word) { Fabricate(:watched_word, action: WatchedWord.actions[:flag]) }
let(:block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) } let(:block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) }
let(:another_block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) } let(:another_block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) }
before_all do before_all { WordWatcher.clear_cache! }
WordWatcher.clear_cache!
end
after do after { WordWatcher.clear_cache! }
WordWatcher.clear_cache!
end
context "with block" do context "with block" do
def should_block_post(manager) def should_block_post(manager)
expect { expect {
result = manager.perform result = manager.perform
expect(result).to_not be_success 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 } }.to_not change { Post.count }
end end
it "escapes the blocked word in error message" do it "escapes the blocked word in error message" do
block_word = Fabricate(:watched_word, action: WatchedWord.actions[:block], word: "<a>") 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 result = manager.perform
expect(result).to_not be_success 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 end
it "should prevent the post from being created" do 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) should_block_post(manager)
end end
it "look at title too" do 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) should_block_post(manager)
end end
it "should block the post from admin" do 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) should_block_post(manager)
end end
it "should block the post from moderator" do 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) should_block_post(manager)
end end
it "should block the post if it contains multiple blocked words" do 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 { expect {
result = manager.perform result = manager.perform
expect(result).to_not be_success 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 } }.to_not change { Post.count }
end end
it "should block in a private message too" do it "should block in a private message too" do
manager = NewPostManager.new( manager =
tl2_user, NewPostManager.new(
raw: "Want some #{block_word.word} for cheap?", tl2_user,
title: 'this is a new title', raw: "Want some #{block_word.word} for cheap?",
archetype: Archetype.private_message, title: "this is a new title",
target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username archetype: Archetype.private_message,
) target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username,
)
should_block_post(manager) should_block_post(manager)
end end
it "blocks on revisions" do it "blocks on revisions" do
post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user) post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user)
expect { 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 expect(post.errors).to be_present
post.reload post.reload
}.to_not change { post.raw } }.to_not change { post.raw }
@ -90,27 +131,48 @@ RSpec.describe WatchedWord do
context "with require_approval" do context "with require_approval" do
it "should queue the post for 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 result = manager.perform
expect(result.action).to eq(:enqueued) expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:watched_word) expect(result.reason).to eq(:watched_word)
end end
it "looks at title too" do 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 result = manager.perform
expect(result.action).to eq(:enqueued) expect(result.action).to eq(:enqueued)
end end
it "should not queue posts from admin" do 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 result = manager.perform
expect(result).to be_success expect(result).to be_success
expect(result.action).to eq(:create_post) expect(result.action).to eq(:create_post)
end end
it "should not queue posts from moderator" do 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 result = manager.perform
expect(result).to be_success expect(result).to be_success
expect(result.action).to eq(:create_post) 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 it "doesn't need approval in a private message" do
Group.refresh_automatic_groups! Group.refresh_automatic_groups!
manager = NewPostManager.new( manager =
tl2_user, NewPostManager.new(
raw: "Want some #{require_approval_word.word} for cheap?", tl2_user,
title: 'this is a new title', raw: "Want some #{require_approval_word.word} for cheap?",
archetype: Archetype.private_message, title: "this is a new title",
target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username archetype: Archetype.private_message,
) target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username,
)
result = manager.perform result = manager.perform
expect(result).to be_success expect(result).to be_success
expect(result.action).to eq(:create_post) expect(result.action).to eq(:create_post)
@ -134,74 +197,122 @@ RSpec.describe WatchedWord do
context "with flag" do context "with flag" do
def should_flag_post(author, raw, topic) def should_flag_post(author, raw, topic)
post = Fabricate(:post, raw: raw, topic: topic, user: author) post = Fabricate(:post, raw: raw, topic: topic, user: author)
expect { expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to change { PostAction.count }.by(
Jobs::ProcessPost.new.execute(post_id: post.id) 1,
}.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 end
def should_not_flag_post(author, raw, topic) def should_not_flag_post(author, raw, topic)
post = Fabricate(:post, raw: raw, topic: topic, user: author) post = Fabricate(:post, raw: raw, topic: topic, user: author)
expect { expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to_not change { PostAction.count }
Jobs::ProcessPost.new.execute(post_id: post.id)
}.to_not change { PostAction.count }
end end
it "should flag the post as inappropriate" do it "should flag the post as inappropriate" do
topic = Fabricate(:topic, user: tl2_user) topic = Fabricate(:topic, user: tl2_user)
post = Fabricate(:post, raw: "I said.... #{flag_word.word}", topic: 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) 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) reviewable = ReviewableFlaggedPost.where(target: post)
expect(reviewable).to be_present 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 end
it "should look at the title too" do 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 end
it "shouldn't flag posts by admin" do 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 end
it "shouldn't flag posts by moderator" do 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 end
it "is compatible with flag_sockpuppets" do it "is compatible with flag_sockpuppets" do
SiteSetting.flag_sockpuppets = true 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) user1 = Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago)
user2 = Fabricate(:user, ip_address: ip_address) user2 = Fabricate(:user, ip_address: ip_address)
first = create_post(user: user1, created_at: 2.days.ago) 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) expect(PostAction.where(post_id: sockpuppet_post.id).count).to eq(1)
end end
it "flags in private message too" do it "flags in private message too" do
post = Fabricate(:private_message_post, raw: "Want some #{flag_word.word} for cheap?", user: tl2_user) post =
expect { Fabricate(
Jobs::ProcessPost.new.execute(post_id: post.id) :private_message_post,
}.to change { PostAction.count }.by(1) raw: "Want some #{flag_word.word} for cheap?",
expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true) 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 end
it "flags on revisions" do it "flags on revisions" do
Jobs.run_immediately! Jobs.run_immediately!
post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user) post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user)
expect { 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) }.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 end
it "should not flag on rebake" do 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") Fabricate(:watched_word, action: WatchedWord.actions[:flag], word: "coupon")
expect { expect { post.rebake! }.to_not change { PostAction.count }
post.rebake!
}.to_not change { PostAction.count }
end end
end end
end end

View File

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

View File

@ -1,76 +1,72 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe "CommonMark" do RSpec.describe "CommonMark" do
it 'passes spec' do it "passes spec" do
SiteSetting.traditional_markdown_linebreaks = true SiteSetting.traditional_markdown_linebreaks = true
SiteSetting.enable_markdown_typographer = false SiteSetting.enable_markdown_typographer = false
SiteSetting.highlighted_languages = 'ruby|aa' SiteSetting.highlighted_languages = "ruby|aa"
html, state, md = nil html, state, md = nil
failed = 0 failed = 0
File.readlines(Rails.root + 'spec/fixtures/md/spec.txt').each do |line| File
if line == "```````````````````````````````` example\n" .readlines(Rails.root + "spec/fixtures/md/spec.txt")
state = :example .each do |line|
next if line == "```````````````````````````````` example\n"
end state = :example
next
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
end end
html, state, md = nil
next
end
if state == :example && line == ".\n" if line == "````````````````````````````````\n"
state = :html md.gsub!("", "\t")
next html ||= String.new
end html.gsub!("", "\t")
html.strip!
if state == :example # normalize brs
md = (md || String.new) << line html.gsub!("<br />", "<br>")
end html.gsub!("<hr />", "<hr>")
html.gsub!(%r{<img([^>]+) />}, "<img\\1>")
if state == :html SiteSetting.enable_markdown_linkify = false
html = (html || String.new) << line cooked = PrettyText.markdown(md, sanitize: false)
end 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) expect(failed).to eq(0)
end end

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe "site setting integrity checks" do 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) } 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 it "set #{property} value as true or not set" do
yaml.each_value do |category| yaml.each_value do |category|
category.each_value do |setting| category.each_value do |setting|
@ -24,16 +24,16 @@ RSpec.describe "site setting integrity checks" do
yaml.each_value do |category| yaml.each_value do |category|
category.each do |setting_name, setting| category.each do |setting_name, setting|
next unless setting.is_a?(Hash) next unless setting.is_a?(Hash)
if setting['locale_default'] if setting["locale_default"]
setting['locale_default'].each_pair do |k, v| setting["locale_default"].each_pair do |k, v|
expect(LocaleSiteSetting.valid_value?(k.to_s)).to be_truthy, 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 when TrueClass, FalseClass
expect(v.class == TrueClass || v.class == FalseClass).to be_truthy expect(v.class == TrueClass || v.class == FalseClass).to be_truthy
else else
expect(v).to be_a_kind_of(setting['default'].class) expect(v).to be_a_kind_of(setting["default"].class)
end end
end end
end end

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe Jobs::AutomaticGroupMembership do RSpec.describe Jobs::AutomaticGroupMembership do
it "raises an error when the group id is missing" 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 end
it "updates the membership" do it "updates the membership" do
@ -14,19 +15,28 @@ RSpec.describe Jobs::AutomaticGroupMembership do
user4 = Fabricate(:user, email: "yes@wat.com") user4 = Fabricate(:user, email: "yes@wat.com")
EmailToken.confirm(Fabricate(:email_token, user: user4).token) EmailToken.confirm(Fabricate(:email_token, user: user4).token)
user5 = Fabricate(:user, email: "sso@wat.com") 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 = 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") group = Fabricate(:group, automatic_membership_email_domains: "wat.com")
automatic = nil automatic = nil
called = false called = false
blk = Proc.new do |_u, _g, options| blk =
automatic = options[:automatic] Proc.new do |_u, _g, options|
called = true automatic = options[:automatic]
end called = true
end
begin begin
DiscourseEvent.on(:user_added_to_group, &blk) 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.users.include?(user6)).to eq(true)
expect(group.user_count).to eq(2) expect(group.user_count).to eq(2)
end end
end end

View File

@ -8,13 +8,7 @@ RSpec.describe Jobs::BookmarkReminderNotifications do
let(:bookmark1) { Fabricate(:bookmark, user: user) } let(:bookmark1) { Fabricate(:bookmark, user: user) }
let(:bookmark2) { Fabricate(:bookmark, user: user) } let(:bookmark2) { Fabricate(:bookmark, user: user) }
let(:bookmark3) { Fabricate(:bookmark, user: user) } let(:bookmark3) { Fabricate(:bookmark, user: user) }
let!(:bookmarks) do let!(:bookmarks) { [bookmark1, bookmark2, bookmark3] }
[
bookmark1,
bookmark2,
bookmark3
]
end
before do before do
# this is done to avoid model validations on Bookmark # this is done to avoid model validations on Bookmark
@ -66,10 +60,12 @@ RSpec.describe Jobs::BookmarkReminderNotifications do
end end
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 bookmark1.bookmarkable.topic.destroy
bookmark2.bookmarkable.topic.destroy bookmark2.bookmarkable.topic.destroy
bookmark3.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
end end

View File

@ -1,13 +1,16 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe Jobs::BulkGrantTrustLevel do RSpec.describe Jobs::BulkGrantTrustLevel do
it "raises an error when trust_level is missing" 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 end
it "raises an error when user_ids are missing" do 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 end
it "updates the trust_level" do it "updates the trust_level" do

View File

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

View File

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

View File

@ -5,13 +5,13 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do
fab!(:topic) { Fabricate(:topic, created_at: 5.hours.ago) } fab!(:topic) { Fabricate(:topic, created_at: 5.hours.ago) }
fab!(:dismissed_topic_user) { Fabricate(:dismissed_topic_user, user: user, topic: topic) } fab!(:dismissed_topic_user) { Fabricate(:dismissed_topic_user, user: user, topic: topic) }
describe '#delete_overdue_dismissals!' do describe "#delete_overdue_dismissals!" do
it 'does not delete when new_topic_duration_minutes is set to always' 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) user.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::ALWAYS)
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count } expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
end 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) user.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::LAST_VISIT)
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count } 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) expect { described_class.new.execute({}) }.to change { DismissedTopicUser.count }.by(-1)
end 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) user.user_option.update(new_topic_duration_minutes: 1440)
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count } expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
@ -28,7 +28,7 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do
end end
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!(:user2) { Fabricate(:user, created_at: 1.days.ago, previous_visit_at: 1.days.ago) }
fab!(:topic2) { Fabricate(:topic, created_at: 6.hours.ago) } fab!(:topic2) { Fabricate(:topic, created_at: 6.hours.ago) }
fab!(:topic3) { Fabricate(:topic, created_at: 2.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) user2.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::ALWAYS)
end end
it 'deletes over the limit dismissals' do it "deletes over the limit dismissals" do
described_class.new.execute({}) described_class.new.execute({})
expect(dismissed_topic_user.reload).to be_present expect(dismissed_topic_user.reload).to be_present
expect(dismissed_topic_user2.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 it "deletes the correct records" do
freeze_time freeze_time
last_week = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "1", updated_at: 7.days.ago) last_week =
today = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "12", updated_at: 12.hours.ago) UserAssociatedAccount.create!(
connected = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "123", user: Fabricate(:user), updated_at: 12.hours.ago) 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 { subject }.to change { UserAssociatedAccount.count }.by(-1)
expect(UserAssociatedAccount.all).to contain_exactly(today, connected) expect(UserAssociatedAccount.all).to contain_exactly(today, connected)
end end
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_log2) { Fabricate(:email_log, created_at: 2.weeks.ago) }
fab!(:email_log3) { Fabricate(:email_log, created_at: 2.days.ago) } fab!(:email_log3) { Fabricate(:email_log, created_at: 2.days.ago) }
let!(:skipped_email_log) do let!(:skipped_email_log) { Fabricate(:skipped_email_log, created_at: 2.years.ago) }
Fabricate(:skipped_email_log, created_at: 2.years.ago)
end
fab!(:skipped_email_log2) { Fabricate(:skipped_email_log) } 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(EmailLog.all).to contain_exactly(email_log, email_log2, email_log3)
expect(SkippedEmailLog.all).to contain_exactly( expect(SkippedEmailLog.all).to contain_exactly(skipped_email_log, skipped_email_log2)
skipped_email_log,
skipped_email_log2
)
end end
end end

View File

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

View File

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

View File

@ -37,7 +37,7 @@ RSpec.describe Jobs::CleanUpUnusedStagedUsers do
end end
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) } before { staged_user.update!(created_at: 5.months.ago) }
include_examples "does not delete" include_examples "does not delete"
end end

View File

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

View File

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

View File

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

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