- Leaderboard: rank-based percentile tiers (champion/gold/silver/bronze),
deterministic tie-breaking (xp DESC, user_id ASC)
- Mentor chat: md: breakpoints, safe-area-inset-bottom padding
- Learning progress: server-side progressPct, curriculum expansion safety
- Physical orders: hard-disabled with 410 Gone
- Certificate approval: pending/issued flow, admin issue/revoke endpoints,
certificate_requires_approval setting (migration 0031)
- Draft/publish: is_published on lessons and chapters, admin publish UI
(migration 0030)
- Video panel: static thumbnails (no preload), client-side pagination
- Rich editor: TipTap + tiptap-markdown for lesson descriptions,
marked + DOMPurify for SafeMarkdown rendering
32 lines
801 B
Go
32 lines
801 B
Go
package api
|
|
|
|
import "testing"
|
|
|
|
func TestTierFromRank(t *testing.T) {
|
|
cases := []struct {
|
|
rank, total int
|
|
want string
|
|
}{
|
|
{1, 1, "champion"},
|
|
{1, 100, "champion"},
|
|
{1, 0, "bronze"}, // zero total → bronze (guard)
|
|
{0, 100, "bronze"}, // zero rank → bronze (guard)
|
|
{10, 100, "gold"}, // exactly 10%
|
|
{11, 100, "silver"}, // 11%
|
|
{30, 100, "silver"}, // exactly 30%
|
|
{31, 100, "bronze"}, // 31%
|
|
{5, 50, "gold"}, // 10%
|
|
{15, 50, "silver"}, // exactly 30%
|
|
{16, 50, "bronze"}, // 32% → bronze
|
|
{50, 100, "bronze"},
|
|
{2, 10, "silver"}, // 20% → silver
|
|
{1, 10, "champion"},
|
|
}
|
|
for _, c := range cases {
|
|
got := tierFromRank(c.rank, c.total)
|
|
if got != c.want {
|
|
t.Errorf("tierFromRank(%d, %d) = %q; want %q", c.rank, c.total, got, c.want)
|
|
}
|
|
}
|
|
}
|