- server: Go 单二进制骨架,embed 内嵌前端,配置/迁移/HTTP 路由 - store: system.db + event.db 两套迁移,业务表 + 种子数据 - domain: 积分引擎(并列三口径/计分通道/流水/排名/破纪录)+ 68 个测试 - web: Vite + Vue 三入口骨架 + shared 层(类型/API/WebSocket 客户端) - console: Electron 宿主骨架(选屏/防熄屏/进程守护/托盘)
196 lines
5.9 KiB
Go
196 lines
5.9 KiB
Go
package domain
|
||
|
||
import "testing"
|
||
|
||
func bonusConfig(mode RecordBonusMode) RecordBonusConfig {
|
||
return RecordBonusConfig{
|
||
Enabled: true, Mode: mode,
|
||
Levels: []RecordLevel{
|
||
{Code: "meet", Name: "赛会纪录", Points: 2},
|
||
{Code: "city", Name: "市级纪录", Points: 5},
|
||
{Code: "province", Name: "省级纪录", Points: 8},
|
||
},
|
||
}
|
||
}
|
||
|
||
func TestRecordBonusDisabled(t *testing.T) {
|
||
cfg := bonusConfig(RecordBonusMax)
|
||
cfg.Enabled = false
|
||
got := RecordBonusPoints(cfg, []RecordBreak{{EntryID: 1, Level: "meet", State: RecordBreakApproved}})
|
||
if got != nil {
|
||
t.Fatalf("未启用时 = %+v,期望 nil", got)
|
||
}
|
||
}
|
||
|
||
func TestRecordBonusStateFilter(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
state RecordBreakState
|
||
want float64
|
||
}{
|
||
{"已通过计入", RecordBreakApproved, 2},
|
||
{"待审核不计入", RecordBreakPending, 0},
|
||
{"已驳回不计入", RecordBreakRejected, 0},
|
||
{"状态空值不计入", "", 0},
|
||
}
|
||
for _, tc := range cases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
got := RecordBonusPoints(bonusConfig(RecordBonusMax), []RecordBreak{{EntryID: 1, Level: "meet", State: tc.state}})
|
||
if tc.want == 0 {
|
||
if len(got) != 0 {
|
||
t.Fatalf("结果 = %+v,期望空", got)
|
||
}
|
||
return
|
||
}
|
||
if len(got) != 1 || !nearlyEqual(got[0].Points, tc.want) {
|
||
t.Fatalf("结果 = %+v,期望 %v 分", got, tc.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestRecordBonusUnknownLevel(t *testing.T) {
|
||
got := RecordBonusPoints(bonusConfig(RecordBonusMax), []RecordBreak{{EntryID: 1, Level: "galaxy", State: RecordBreakApproved}})
|
||
if len(got) != 0 {
|
||
t.Fatalf("未配置档位 = %+v,期望空(不能凭空加分)", got)
|
||
}
|
||
}
|
||
|
||
func TestRecordBonusModes(t *testing.T) {
|
||
breaks := []RecordBreak{
|
||
{ID: 1, EntryID: 1, Level: "meet", State: RecordBreakApproved},
|
||
{ID: 2, EntryID: 1, Level: "city", State: RecordBreakApproved},
|
||
{ID: 3, EntryID: 1, Level: "province", State: RecordBreakApproved},
|
||
}
|
||
cases := []struct {
|
||
name string
|
||
mode RecordBonusMode
|
||
want float64
|
||
}{
|
||
{"max 取最高档", RecordBonusMax, 8},
|
||
{"sum 累加", RecordBonusSum, 15},
|
||
{"模式空值按 max", "", 8},
|
||
}
|
||
for _, tc := range cases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
got := RecordBonusPoints(bonusConfig(tc.mode), breaks)
|
||
if len(got) != 1 || !nearlyEqual(got[0].Points, tc.want) {
|
||
t.Fatalf("结果 = %+v,期望 %v", got, tc.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestRecordBonusGroupsByEntry(t *testing.T) {
|
||
breaks := []RecordBreak{
|
||
{ID: 1, EntryID: 2, Level: "meet", State: RecordBreakApproved},
|
||
{ID: 2, EntryID: 1, Level: "city", State: RecordBreakApproved},
|
||
{ID: 3, EntryID: 1, Level: "meet", State: RecordBreakApproved},
|
||
}
|
||
got := RecordBonusPoints(bonusConfig(RecordBonusMax), breaks)
|
||
if len(got) != 2 {
|
||
t.Fatalf("条数 = %d,期望 2(按报名项分组)", len(got))
|
||
}
|
||
if got[0].EntryID != 1 || got[1].EntryID != 2 {
|
||
t.Fatalf("顺序 = %+v,期望按 EntryID 升序", got)
|
||
}
|
||
if !nearlyEqual(got[0].Points, 5) || !nearlyEqual(got[1].Points, 2) {
|
||
t.Fatalf("分值 = %v/%v,期望 5/2", got[0].Points, got[1].Points)
|
||
}
|
||
}
|
||
|
||
func TestRecordBonusDedupLevel(t *testing.T) {
|
||
breaks := []RecordBreak{
|
||
{ID: 1, EntryID: 1, Level: "meet", State: RecordBreakApproved},
|
||
{ID: 2, EntryID: 1, Level: "meet", State: RecordBreakApproved},
|
||
}
|
||
got := RecordBonusPoints(bonusConfig(RecordBonusSum), breaks)
|
||
if len(got) != 1 || !nearlyEqual(got[0].Points, 2) {
|
||
t.Fatalf("重复档位 = %+v,期望只计一次 2 分", got)
|
||
}
|
||
if len(got[0].Levels) != 1 {
|
||
t.Fatalf("档位列表 = %v,期望去重", got[0].Levels)
|
||
}
|
||
}
|
||
|
||
func TestRecordBonusNoBreaks(t *testing.T) {
|
||
if got := RecordBonusPoints(bonusConfig(RecordBonusMax), nil); got != nil {
|
||
t.Fatalf("无破纪录登记 = %+v,期望 nil", got)
|
||
}
|
||
}
|
||
|
||
func TestRecordBonusLedger(t *testing.T) {
|
||
entries, results := fixture(lineup(2))
|
||
base := EventScoreInput{
|
||
Event: individualEvent(1), Category: testCategory(),
|
||
Channels: []ScoreChannel{athleteResultChannel(1)},
|
||
Profiles: map[int64]ScoringProfile{1: individualTop6()},
|
||
Entries: entries, Results: results,
|
||
RecordBreaks: []RecordBreak{{
|
||
ID: 1, EventID: 1, EntryID: 1, AthleteID: 101, TeamID: 1001,
|
||
Level: "meet", State: RecordBreakApproved,
|
||
}},
|
||
}
|
||
|
||
t.Run("未启用不产生加分流水", func(t *testing.T) {
|
||
cfg := bonusConfig(RecordBonusMax)
|
||
cfg.Enabled = false
|
||
base.RecordBonus = &cfg
|
||
es := mustScore(t, base)
|
||
for _, e := range es.Ledger {
|
||
if e.SourceType == SourceRecordBonus {
|
||
t.Fatalf("不应有加分流水: %+v", e)
|
||
}
|
||
}
|
||
})
|
||
|
||
t.Run("启用后归属到人", func(t *testing.T) {
|
||
cfg := bonusConfig(RecordBonusMax)
|
||
base.RecordBonus = &cfg
|
||
es := mustScore(t, base)
|
||
var found bool
|
||
for _, e := range es.Ledger {
|
||
if e.SourceType != SourceRecordBonus {
|
||
continue
|
||
}
|
||
found = true
|
||
if e.TeamID != 1001 || e.AthleteID != 101 || !nearlyEqual(e.Points, 2) {
|
||
t.Fatalf("加分流水 = %+v,期望 1001/101 记 2 分", e)
|
||
}
|
||
}
|
||
if !found {
|
||
t.Fatal("未产生加分流水")
|
||
}
|
||
})
|
||
|
||
t.Run("联队按份额拆分加分", func(t *testing.T) {
|
||
ps := []participant{{
|
||
entryID: 1, athleteID: 101, teamID: 1001, value: 1,
|
||
shares: []TeamShare{{TeamID: 1001, Weight: 3}, {TeamID: 1002, Weight: 2}},
|
||
}}
|
||
e2, r2 := fixture(ps)
|
||
cfg := bonusConfig(RecordBonusMax)
|
||
es := mustScore(t, EventScoreInput{
|
||
Event: individualEvent(1), Category: testCategory(),
|
||
Channels: []ScoreChannel{athleteResultChannel(1)},
|
||
Profiles: map[int64]ScoringProfile{1: individualTop6()},
|
||
Entries: e2, Results: r2,
|
||
RecordBonus: &cfg,
|
||
RecordBreaks: []RecordBreak{{
|
||
ID: 1, EventID: 1, EntryID: 1, AthleteID: 101, TeamID: 1001,
|
||
Level: "meet", State: RecordBreakApproved,
|
||
}},
|
||
Options: ScoreOptions{UnionAttribution: UnionSplit},
|
||
})
|
||
var bonus float64
|
||
for _, e := range es.Ledger {
|
||
if e.SourceType == SourceRecordBonus && e.TeamID == 1002 {
|
||
bonus += e.Points
|
||
}
|
||
}
|
||
if !nearlyEqual(bonus, 0.8) {
|
||
t.Fatalf("单位1002 的加分 = %v,期望 2 × 0.4 = 0.8", bonus)
|
||
}
|
||
})
|
||
}
|