2019-04-29 19:27:42 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2022-07-27 21:27:38 -05:00
|
|
|
RSpec.describe Badge do
|
2023-04-27 08:09:23 -05:00
|
|
|
describe "Validations" do
|
|
|
|
subject(:badge) { Fabricate.build(:badge) }
|
|
|
|
|
|
|
|
it { is_expected.to validate_length_of(:name).is_at_most(100) }
|
2023-05-09 04:10:12 -05:00
|
|
|
it { is_expected.to validate_length_of(:description).is_at_most(500) }
|
2023-04-27 08:09:23 -05:00
|
|
|
it { is_expected.to validate_length_of(:long_description).is_at_most(1000) }
|
|
|
|
it { is_expected.to validate_presence_of(:name) }
|
|
|
|
it { is_expected.to validate_presence_of(:badge_type) }
|
|
|
|
it { is_expected.to validate_uniqueness_of(:name) }
|
|
|
|
end
|
|
|
|
|
2014-07-25 00:22:29 -05:00
|
|
|
it "has a valid system attribute for new badges" do
|
2014-12-31 08:55:03 -06:00
|
|
|
expect(Badge.create!(name: "test", badge_type_id: 1).system?).to be false
|
2014-07-25 00:22:29 -05:00
|
|
|
end
|
|
|
|
|
2016-03-28 02:38:38 -05:00
|
|
|
it "auto translates name" do
|
|
|
|
badge = Badge.find_by_name("Basic User")
|
|
|
|
name_english = badge.name
|
|
|
|
|
2016-09-29 00:42:56 -05:00
|
|
|
I18n.with_locale(:fr) { expect(badge.display_name).not_to eq(name_english) }
|
2016-03-28 02:38:38 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it "handles changes on badge description and long description correctly for system badges" do
|
|
|
|
badge = Badge.find_by_name("Basic User")
|
|
|
|
badge.description = badge.description.dup
|
|
|
|
badge.long_description = badge.long_description.dup
|
|
|
|
badge.save
|
|
|
|
badge.reload
|
|
|
|
|
|
|
|
expect(badge[:description]).to eq(nil)
|
|
|
|
expect(badge[:long_description]).to eq(nil)
|
|
|
|
|
|
|
|
badge.description = "testing"
|
|
|
|
badge.long_description = "testing it"
|
|
|
|
|
|
|
|
badge.save
|
|
|
|
badge.reload
|
|
|
|
|
|
|
|
expect(badge[:description]).to eq("testing")
|
|
|
|
expect(badge[:long_description]).to eq("testing it")
|
|
|
|
end
|
|
|
|
|
2016-03-29 00:41:03 -05:00
|
|
|
it "can ensure consistency" do
|
2023-05-31 19:00:01 -05:00
|
|
|
b = Badge.find_by_name("Basic User")
|
|
|
|
|
2016-03-29 00:41:03 -05:00
|
|
|
b.grant_count = 100
|
|
|
|
b.save
|
|
|
|
|
2023-05-31 19:00:01 -05:00
|
|
|
UserBadge.create!(
|
|
|
|
user_id: User.minimum(:id) - 1,
|
|
|
|
badge_id: b.id,
|
|
|
|
granted_at: 1.minute.ago,
|
|
|
|
granted_by_id: -1,
|
|
|
|
)
|
2016-03-29 07:54:18 -05:00
|
|
|
UserBadge.create!(
|
|
|
|
user_id: User.first.id,
|
|
|
|
badge_id: b.id,
|
|
|
|
granted_at: 1.minute.ago,
|
|
|
|
granted_by_id: -1,
|
|
|
|
)
|
2016-03-29 00:41:03 -05:00
|
|
|
|
|
|
|
Badge.ensure_consistency!
|
|
|
|
|
|
|
|
b.reload
|
2016-03-29 07:54:18 -05:00
|
|
|
expect(b.grant_count).to eq(1)
|
2016-03-29 00:41:03 -05:00
|
|
|
end
|
|
|
|
|
2021-10-27 09:33:07 -05:00
|
|
|
it "sanitizes the description" do
|
|
|
|
xss = "<b onmouseover=alert('Wufff!')>click me!</b><script>alert('TEST');</script>"
|
|
|
|
badge = Fabricate(:badge)
|
|
|
|
|
|
|
|
badge.update!(description: xss)
|
|
|
|
|
|
|
|
expect(badge.description).to eq("<b>click me!</b>alert('TEST');")
|
|
|
|
end
|
|
|
|
|
2018-01-21 21:10:53 -06:00
|
|
|
describe "#manually_grantable?" do
|
2019-05-06 22:12:20 -05:00
|
|
|
fab!(:badge) { Fabricate(:badge, name: "Test Badge") }
|
2018-01-21 21:10:53 -06:00
|
|
|
subject { badge.manually_grantable? }
|
|
|
|
|
|
|
|
context "when system badge" do
|
|
|
|
before { badge.system = true }
|
|
|
|
it { is_expected.to be false }
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when has query" do
|
|
|
|
before { badge.query = "SELECT id FROM users" }
|
|
|
|
it { is_expected.to be false }
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when neither system nor has query" do
|
|
|
|
before { badge.update_columns(system: false, query: nil) }
|
|
|
|
it { is_expected.to be true }
|
|
|
|
end
|
|
|
|
end
|
2018-09-20 21:06:08 -05:00
|
|
|
|
2021-03-17 00:55:23 -05:00
|
|
|
describe "#image_url" do
|
2024-07-03 21:03:09 -05:00
|
|
|
before do
|
2021-03-17 00:55:23 -05:00
|
|
|
SiteSetting.enable_s3_uploads = true
|
|
|
|
SiteSetting.s3_cdn_url = "https://some-s3-cdn.amzn.com"
|
2024-07-03 21:03:09 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
context "when the badge has an existing image" do
|
|
|
|
it "has a CDN url" do
|
|
|
|
upload = Fabricate(:upload_s3)
|
|
|
|
badge = Fabricate(:badge, image_upload_id: upload.id)
|
|
|
|
|
|
|
|
expect(badge.image_url).to start_with("https://some-s3-cdn.amzn.com")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when the badge does not have a related image" do
|
|
|
|
it "does not have a CDN url" do
|
|
|
|
upload = Fabricate(:upload_s3)
|
|
|
|
badge = Fabricate(:badge, image_upload_id: upload.id)
|
|
|
|
|
|
|
|
store = stub
|
|
|
|
store.expects(:remove_upload).returns(true)
|
|
|
|
Discourse.stubs(:store).returns(store)
|
|
|
|
|
|
|
|
upload.destroy!
|
|
|
|
|
|
|
|
expect(badge.reload.image_url).to eq(nil)
|
|
|
|
end
|
2021-03-17 00:55:23 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-09-20 21:06:08 -05:00
|
|
|
describe ".i18n_name" do
|
|
|
|
it "transforms to lower case letters, and replaces spaces with underscores" do
|
|
|
|
expect(Badge.i18n_name("Basic User")).to eq("basic_user")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe ".display_name" do
|
|
|
|
it "fetches from translations when i18n_name key exists" do
|
|
|
|
expect(Badge.display_name("basic_user")).to eq("Basic")
|
|
|
|
expect(Badge.display_name("Basic User")).to eq("Basic")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "fallbacks to argument value when translation does not exist" do
|
|
|
|
expect(Badge.display_name("Not In Translations")).to eq("Not In Translations")
|
|
|
|
end
|
|
|
|
end
|
2018-10-15 18:38:59 -05:00
|
|
|
|
2019-11-07 23:34:24 -06:00
|
|
|
describe ".find_system_badge_id_from_translation_key" do
|
|
|
|
let(:translation_key) { "badges.regular.name" }
|
|
|
|
|
|
|
|
it "uses a translation key to get a system badge id, mainly to find which badge a translation override corresponds to" do
|
|
|
|
expect(Badge.find_system_badge_id_from_translation_key(translation_key)).to eq(Badge::Regular)
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when the translation key is snake case" do
|
|
|
|
let(:translation_key) { "badges.crazy_in_love.name" }
|
|
|
|
|
|
|
|
it "works to get the badge" do
|
|
|
|
expect(Badge.find_system_badge_id_from_translation_key(translation_key)).to eq(
|
|
|
|
Badge::CrazyInLove,
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when a translation key not for a badge is provided" do
|
|
|
|
let(:translation_key) { "reports.flags.title" }
|
|
|
|
it "returns nil" do
|
|
|
|
expect(Badge.find_system_badge_id_from_translation_key(translation_key)).to eq(nil)
|
|
|
|
end
|
|
|
|
end
|
2020-04-28 13:33:58 -05:00
|
|
|
|
|
|
|
context "when translation key doesn't match its class" do
|
|
|
|
let(:translation_key) { "badges.licensed.long_description" }
|
|
|
|
|
|
|
|
it "returns nil" do
|
|
|
|
expect(Badge.find_system_badge_id_from_translation_key(translation_key)).to eq(nil)
|
|
|
|
end
|
|
|
|
end
|
2019-11-07 23:34:24 -06:00
|
|
|
end
|
|
|
|
|
2022-07-27 11:14:14 -05:00
|
|
|
describe "First Quote" do
|
2019-04-03 00:41:52 -05:00
|
|
|
let(:quoted_post_badge) { Badge.find(Badge::FirstQuote) }
|
|
|
|
|
|
|
|
it "Awards at the correct award date" do
|
|
|
|
freeze_time
|
|
|
|
post1 = create_post
|
|
|
|
|
|
|
|
raw = <<~RAW
|
|
|
|
[quote="#{post1.user.username}, post:#{post1.post_number}, topic:#{post1.topic_id}"]
|
|
|
|
lorem
|
|
|
|
[/quote]
|
|
|
|
RAW
|
|
|
|
|
|
|
|
post2 = create_post(raw: raw)
|
|
|
|
|
|
|
|
quoted_post = QuotedPost.find_by(post_id: post2.id)
|
|
|
|
freeze_time 1.year.from_now
|
|
|
|
quoted_post.update!(created_at: Time.now)
|
|
|
|
|
|
|
|
BadgeGranter.backfill(quoted_post_badge)
|
|
|
|
user_badge = post2.user.user_badges.find_by(badge_id: quoted_post_badge.id)
|
|
|
|
|
|
|
|
expect(user_badge.granted_at).to eq_time(post2.created_at)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-27 11:14:14 -05:00
|
|
|
describe "WikiEditor badge" do
|
2020-03-26 20:41:06 -05:00
|
|
|
it "is awarded" do
|
|
|
|
wiki_editor_badge = Badge.find(Badge::WikiEditor)
|
|
|
|
post = Fabricate(:post, wiki: true)
|
|
|
|
revisor = PostRevisor.new(post)
|
|
|
|
revisor.revise!(post.user, { raw: "I am editing a wiki" }, force_new_version: true)
|
|
|
|
|
|
|
|
BadgeGranter.backfill(wiki_editor_badge)
|
|
|
|
|
|
|
|
expect(UserBadge.where(user_id: post.user.id, badge_id: Badge::WikiEditor).count).to eq(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-27 11:14:14 -05:00
|
|
|
describe "PopularLink badge" do
|
2018-10-15 18:42:16 -05:00
|
|
|
let(:popular_link_badge) { Badge.find(Badge::PopularLink) }
|
|
|
|
|
2018-10-15 18:38:59 -05:00
|
|
|
before do
|
2018-10-15 18:42:16 -05:00
|
|
|
popular_link_badge.query = BadgeQueries.linking_badge(2)
|
|
|
|
popular_link_badge.save!
|
2018-10-15 18:38:59 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it "is awarded" do
|
|
|
|
post = create_post(raw: "https://www.discourse.org/")
|
|
|
|
|
|
|
|
TopicLinkClick.create_from(
|
|
|
|
url: "https://www.discourse.org/",
|
|
|
|
post_id: post.id,
|
|
|
|
topic_id: post.topic.id,
|
|
|
|
ip: "192.168.0.100",
|
|
|
|
)
|
2018-10-15 18:42:16 -05:00
|
|
|
BadgeGranter.backfill(popular_link_badge)
|
2018-10-15 18:38:59 -05:00
|
|
|
expect(UserBadge.where(user_id: post.user.id, badge_id: Badge::PopularLink).count).to eq(0)
|
|
|
|
|
|
|
|
TopicLinkClick.create_from(
|
|
|
|
url: "https://www.discourse.org/",
|
|
|
|
post_id: post.id,
|
|
|
|
topic_id: post.topic.id,
|
|
|
|
ip: "192.168.0.101",
|
|
|
|
)
|
2018-10-15 18:42:16 -05:00
|
|
|
BadgeGranter.backfill(popular_link_badge)
|
2018-10-15 18:38:59 -05:00
|
|
|
expect(UserBadge.where(user_id: post.user.id, badge_id: Badge::PopularLink).count).to eq(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "is not awarded for links in a restricted category" do
|
|
|
|
category = Fabricate(:category)
|
|
|
|
post = create_post(raw: "https://www.discourse.org/", category: category)
|
|
|
|
|
|
|
|
category.set_permissions({})
|
|
|
|
category.save!
|
|
|
|
|
|
|
|
TopicLinkClick.create_from(
|
|
|
|
url: "https://www.discourse.org/",
|
|
|
|
post_id: post.id,
|
|
|
|
topic_id: post.topic.id,
|
|
|
|
ip: "192.168.0.100",
|
|
|
|
)
|
|
|
|
TopicLinkClick.create_from(
|
|
|
|
url: "https://www.discourse.org/",
|
|
|
|
post_id: post.id,
|
|
|
|
topic_id: post.topic.id,
|
|
|
|
ip: "192.168.0.101",
|
|
|
|
)
|
2018-10-15 18:42:16 -05:00
|
|
|
BadgeGranter.backfill(popular_link_badge)
|
2018-10-15 18:38:59 -05:00
|
|
|
expect(UserBadge.where(user_id: post.user.id, badge_id: Badge::PopularLink).count).to eq(0)
|
|
|
|
end
|
|
|
|
end
|
2021-05-27 14:00:57 -05:00
|
|
|
|
2022-07-27 05:21:10 -05:00
|
|
|
describe "#seed" do
|
2022-11-24 06:44:26 -06:00
|
|
|
let(:badge_id) { Badge.maximum(:id) + 1 }
|
2021-05-27 14:00:57 -05:00
|
|
|
|
|
|
|
it "`allow_title` is not updated for existing records" do
|
2022-11-24 06:44:26 -06:00
|
|
|
Badge.seed do |b|
|
|
|
|
b.id = badge_id
|
|
|
|
b.name = "Foo"
|
|
|
|
b.badge_type_id = BadgeType::Bronze
|
|
|
|
b.default_allow_title = true
|
|
|
|
end
|
|
|
|
|
|
|
|
badge = Badge.find(badge_id)
|
|
|
|
expect(badge.allow_title).to eq(true)
|
|
|
|
|
|
|
|
badge.update!(allow_title: false)
|
|
|
|
|
|
|
|
Badge.seed do |b|
|
|
|
|
b.id = badge_id
|
|
|
|
b.name = "Foo"
|
|
|
|
b.badge_type_id = BadgeType::Bronze
|
|
|
|
b.default_allow_title = true
|
|
|
|
end
|
|
|
|
|
|
|
|
badge.reload
|
|
|
|
expect(badge.allow_title).to eq(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "`enabled` is not updated for existing records" do
|
|
|
|
Badge.seed do |b|
|
|
|
|
b.id = badge_id
|
|
|
|
b.name = "Foo"
|
|
|
|
b.badge_type_id = BadgeType::Bronze
|
|
|
|
b.default_enabled = false
|
|
|
|
end
|
|
|
|
|
|
|
|
badge = Badge.find(badge_id)
|
|
|
|
expect(badge.enabled).to eq(false)
|
|
|
|
|
|
|
|
badge.update!(enabled: true)
|
|
|
|
|
|
|
|
Badge.seed do |b|
|
|
|
|
b.id = badge_id
|
|
|
|
b.name = "Foo"
|
|
|
|
b.badge_type_id = BadgeType::Bronze
|
|
|
|
b.default_enabled = false
|
|
|
|
end
|
|
|
|
|
|
|
|
badge.reload
|
|
|
|
expect(badge.enabled).to eq(true)
|
2021-05-27 14:00:57 -05:00
|
|
|
end
|
|
|
|
end
|
2014-07-25 00:22:29 -05:00
|
|
|
end
|