discourse/plugins/discourse-gamification/spec/lib/directory_integration_spec.rb
Rafael dos Santos Silva 848c19ae48
FEATURE: Per-leaderboard scorable weights and category filters (#39062)
## Summary

- Moves scorable weight configuration and category filtering from global
site settings to individual leaderboards
- Each leaderboard can override any of the 15 score weight values and
specify its own scorable categories
- Score calculation now runs per leaderboard into a new
`gamification_leaderboard_scores` table
- Admin UI gains a "Scoring configuration" section on the leaderboard
edit form

## Details

Previously all leaderboards shared the same pre-calculated scores from
global site settings. This made it impossible to have e.g. a
"posts-only" leaderboard alongside a "likes-focused" one.

Now each leaderboard can optionally override:
- **Score weights**: Set per-action point values (empty = inherit global
default, 0 = disabled)
- **Scorable categories**: Restrict which categories count toward
scoring (empty = inherit global setting)

The old `gamification_scores` table is no longer written to — a
post-deploy migration to drop it will follow separately.

## Test plan

- [ ] Create a leaderboard with default scoring — scores match current
behavior
- [ ] Create a leaderboard with custom weights (e.g. `post_created` =
10, all others empty) — only overridden weights differ
- [ ] Set a weight to 0 — that scorable is disabled for the leaderboard
- [ ] Set per-leaderboard categories — only those categories count
- [ ] Clear categories — inherits global `scorable_categories` setting
- [ ] Existing leaderboards with no overrides behave identically after
upgrade
- [ ] Directory scores and user card scores work correctly (read from
default leaderboard)
- [ ] `bin/rspec plugins/discourse-gamification/spec/` — 120 examples, 0
failures
2026-04-23 11:23:01 -03:00

97 lines
2.5 KiB
Ruby
Vendored

# frozen_string_literal: true
describe DiscourseGamification::DirectoryIntegration do
fab!(:user_1, :admin)
fab!(:user_2, :user)
fab!(:leaderboard, :gamification_leaderboard)
fab!(:score_1) do
Fabricate(
:gamification_leaderboard_score,
leaderboard_id: leaderboard.id,
user_id: user_1.id,
score: 10,
date: 8.days.ago,
)
end
fab!(:score_2) do
Fabricate(
:gamification_leaderboard_score,
leaderboard_id: leaderboard.id,
user_id: user_1.id,
score: 40,
date: 3.days.ago,
)
end
fab!(:score_3) do
Fabricate(
:gamification_leaderboard_score,
leaderboard_id: leaderboard.id,
user_id: user_2.id,
score: 25,
date: 5.days.ago,
)
end
fab!(:score_4) do
Fabricate(
:gamification_leaderboard_score,
leaderboard_id: leaderboard.id,
user_id: user_2.id,
score: 5,
date: 2.days.ago,
)
end
before do
SiteSetting.discourse_gamification_enabled = true
DirectoryItem.refresh!
end
def all_time_score_for(user)
user.directory_items.find_by(period_type: 1).gamification_score
end
context "with a date-restricted default leaderboard" do
context "with only a 'from_date'" do
before do
leaderboard.update(from_date: 5.days.ago.to_date)
DirectoryItem.refresh!
end
it "returns sum of points earned from leaderboard's 'from_date'" do
expect(all_time_score_for(user_1)).to eq(40)
expect(all_time_score_for(user_2)).to eq(30)
end
end
context "with only a 'to_date'" do
before do
leaderboard.update(to_date: 4.days.ago.to_date)
DirectoryItem.refresh!
end
it "returns sum of points earned upto leaderboard's 'to_date'" do
expect(all_time_score_for(user_1)).to eq(10)
expect(all_time_score_for(user_2)).to eq(25)
end
end
context "with both 'from_date' and 'to_date'" do
before do
leaderboard.update(from_date: 5.days.ago.to_date, to_date: 3.days.ago.to_date)
DirectoryItem.refresh!
end
it "returns sum of points earned between leaderboard's 'from_date' and 'to_date'" do
expect(all_time_score_for(user_1)).to eq(40)
expect(all_time_score_for(user_2)).to eq(25)
end
end
end
context "without a date-restricted default leaderboard" do
it "returns sum of all scores for the period" do
expect(all_time_score_for(user_1)).to eq(50)
expect(all_time_score_for(user_2)).to eq(30)
end
end
end