- P0 慢客户端关闭码 1008 → 4000:前端把所有 1008 当「登录失效」并永久停止重连, 大屏被踢后再也不自愈(服务端 markSlow 改用自定义码,前端逻辑不动) - P1 REST 与 WS 的奖牌榜补 events.scored = 1 过滤,与导出成绩册口径一致 (否则「只排名不计分」项目只要挂着通道就会在大屏凭空出奖牌) - P1 聚合流水作废去掉 source_id 子查询:改通道会重建通道 id,旧聚合流水 永远匹配不上 → 再插一份 → 分数翻倍 - P1 审核页/历史页的折算系数兜底由 ?? 1 改为 >0 ? : 1:/ws 快照对非 admin 会把 individualToTeamWeight 置 0,recorder 看到的个人折算分全是 0 - P2 删掉 signature.local.txt 后清理残留的 signature.build.txt,避免继续注入旧署名 - 初始管理员密码:banner 加纯 ASCII 锚 INITIAL ADMIN CREATED,文档改为搜它 (原来教的 findstr "password=" 已经失效,且中文 findstr 本身不可靠) 自检:服务端 12 包全绿;前端 vue-tsc + build 通过、node --test 24/24;console 语法通过。
66 lines
2.2 KiB
Go
66 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// CLI 备份必须用 event 前缀(与备份服务一致),否则 PruneBackups 按 <prefix>-* 清理不到,
|
|
// 备份目录会只增不减。
|
|
func TestRunBackupUsesEventPrefix(t *testing.T) {
|
|
root := t.TempDir()
|
|
dataDir := filepath.Join(root, "data")
|
|
projDir := filepath.Join(dataDir, "event_projs", "default")
|
|
if err := os.MkdirAll(projDir, 0o755); err != nil {
|
|
t.Fatalf("创建赛事目录: %v", err)
|
|
}
|
|
// runBackup 只在 event.db 存在时备份赛事库;空库也走同一条 VACUUM INTO 路径。
|
|
if err := os.WriteFile(filepath.Join(projDir, "event.db"), nil, 0o644); err != nil {
|
|
t.Fatalf("创建赛事库: %v", err)
|
|
}
|
|
cfgPath := filepath.Join(root, "config.yaml")
|
|
cfg := "dataDir: " + dataDir + "\nserver:\n port: 19540\n"
|
|
if err := os.WriteFile(cfgPath, []byte(cfg), 0o644); err != nil {
|
|
t.Fatalf("写配置: %v", err)
|
|
}
|
|
|
|
if err := runBackup([]string{"-config", cfgPath, "-project", "default", "-reason", "manual"}); err != nil {
|
|
t.Fatalf("runBackup: %v", err)
|
|
}
|
|
|
|
entries, err := os.ReadDir(filepath.Join(dataDir, "backups"))
|
|
if err != nil {
|
|
t.Fatalf("读取备份目录: %v", err)
|
|
}
|
|
eventBackup := false
|
|
for _, e := range entries {
|
|
if strings.HasPrefix(e.Name(), "default-") {
|
|
t.Errorf("CLI 备份不应使用项目名前缀: %s", e.Name())
|
|
}
|
|
if strings.HasPrefix(e.Name(), "event-manual-") {
|
|
eventBackup = true
|
|
}
|
|
}
|
|
if !eventBackup {
|
|
t.Errorf("未生成 event- 前缀的赛事库备份,实际文件: %v", entries)
|
|
}
|
|
}
|
|
|
|
// 现场按文档用 findstr 搜纯 ASCII 锚找初始密码;中文匹配在 chcp 936/65001 下不可靠。
|
|
func TestInitialAdminBannerAsciiAnchor(t *testing.T) {
|
|
const anchor = "=== INITIAL ADMIN CREATED (password above) ==="
|
|
banner := initialAdminBanner("admin", "s3cret-pass")
|
|
if !strings.Contains(banner, anchor) {
|
|
t.Fatalf("banner 缺少可搜索的 ASCII 锚 %q:\n%s", anchor, banner)
|
|
}
|
|
if !strings.Contains(banner, "s3cret-pass") {
|
|
t.Fatalf("banner 应包含初始密码:\n%s", banner)
|
|
}
|
|
// 锚在密码之后,文案 password above 才成立。
|
|
if strings.Index(banner, "s3cret-pass") > strings.Index(banner, anchor) {
|
|
t.Fatalf("ASCII 锚应在密码行之后:\n%s", banner)
|
|
}
|
|
}
|