新增: - 书库导入导出(library_import/library_export)及测试 - 图片变体生成(image_variants)与响应式图片(responsiveImage) - 书籍搜索(bookSearch)+ BookSearch/LibrarySearchGrid 组件 - 小组件运行时(widgetRuntime)与静态检查(widgetLint) - 上传缓存中间件(upload_cache)与字体 CSS 提取脚本 其它: - 后端 handlers/services 全量调整 - 前端页面、组件、库函数与配置更新
68 lines
2.3 KiB
Go
68 lines
2.3 KiB
Go
package service
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/freefire/jiang13-bbs/model"
|
||
"github.com/glebarez/sqlite"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// 贡献值口径:earnedPoints 只累计窗口内正入账积分(排除 bounty_refund),
|
||
// 支出、悬赏退回、封禁用户、窗口外入账均不计
|
||
func TestEarnedPointsWindowAndExclusions(t *testing.T) {
|
||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||
if err != nil {
|
||
t.Fatalf("open sqlite: %v", err)
|
||
}
|
||
if err := db.AutoMigrate(&model.User{}, &model.PointLedger{}); err != nil {
|
||
t.Fatalf("migrate: %v", err)
|
||
}
|
||
|
||
now := time.Now()
|
||
weekAgo := now.AddDate(0, 0, -7)
|
||
users := []*model.User{
|
||
{Username: "earner", Password: "x"},
|
||
{Username: "refund_only", Password: "x"},
|
||
{Username: "banned", Password: "x", Banned: true},
|
||
{Username: "old_earner", Password: "x"},
|
||
}
|
||
for _, u := range users {
|
||
if err := db.Create(u).Error; err != nil {
|
||
t.Fatalf("seed user %s: %v", u.Username, err)
|
||
}
|
||
}
|
||
a, d := users[0].ID, users[3].ID
|
||
ledgers := []model.PointLedger{
|
||
{UserID: a, Delta: 5, Reason: model.PointReasonCheckin, Balance: 5, CreatedAt: now},
|
||
{UserID: a, Delta: 3, Reason: model.PointReasonReplyReward, Balance: 8, CreatedAt: now},
|
||
{UserID: a, Delta: 2, Reason: model.PointReasonStreakBonus, Balance: 10, CreatedAt: now.Add(-time.Hour)},
|
||
{UserID: a, Delta: -3, Reason: model.PointReasonUnlockPost, Balance: 7, CreatedAt: now}, // 支出不计
|
||
{UserID: users[1].ID, Delta: 8, Reason: model.PointReasonBountyRefund, Balance: 8, CreatedAt: now}, // 退回不计
|
||
{UserID: users[2].ID, Delta: 5, Reason: model.PointReasonCheckin, Balance: 5, CreatedAt: now}, // 封禁不计
|
||
{UserID: d, Delta: 5, Reason: model.PointReasonCheckin, Balance: 5, CreatedAt: weekAgo.Add(-time.Hour)}, // 窗口外
|
||
}
|
||
if err := db.Create(&ledgers).Error; err != nil {
|
||
t.Fatalf("seed ledgers: %v", err)
|
||
}
|
||
|
||
s := NewLeaderboardService(db)
|
||
from := weekAgo
|
||
pts, err := s.earnedPoints(&from, nil)
|
||
if err != nil {
|
||
t.Fatalf("earnedPoints: %v", err)
|
||
}
|
||
if len(pts) != 1 || pts[a] != 10 {
|
||
t.Fatalf("周窗内应仅 earner 计 10 分,got %v", pts)
|
||
}
|
||
|
||
all, err := s.earnedPoints(nil, nil)
|
||
if err != nil {
|
||
t.Fatalf("earnedPoints(all): %v", err)
|
||
}
|
||
if len(all) != 2 || all[a] != 10 || all[d] != 5 {
|
||
t.Fatalf("全窗口:earner=10、old_earner=5,退回/封禁不计,got %v", all)
|
||
}
|
||
}
|