package export import ( "database/sql" "path/filepath" "testing" "git.yeij.top/AskaEth/Scoreboard/server/internal/store" ) // openEventDB 建一个临时赛事库并跑完迁移;DSN 与 store.Open 保持一致。 func openEventDB(t *testing.T) *sql.DB { t.Helper() path := filepath.Join(t.TempDir(), "event.db") dsn := "file:" + path + "?_pragma=journal_mode(WAL)" + "&_pragma=foreign_keys(1)" + "&_pragma=busy_timeout(5000)" db, err := sql.Open("sqlite", dsn) if err != nil { t.Fatalf("打开赛事库: %v", err) } db.SetMaxOpenConns(1) t.Cleanup(func() { _ = db.Close() }) if err := store.MigrateEvent(db); err != nil { t.Fatalf("迁移赛事库: %v", err) } return db } // seedEvent 造一场最小可用赛事:2 个单位、2 名运动员、1 个小项 1 个组别、 // 2 条已发布成绩与对应积分流水。 func seedEvent(t *testing.T, db *sql.DB) { t.Helper() stmts := []string{ `INSERT INTO teams(code, name, short_name, sort_order) VALUES ('T1', '第一分公司', '一公司', 1)`, `INSERT INTO teams(code, name, short_name, sort_order) VALUES ('T2', '第二分公司', '二公司', 2)`, `INSERT INTO athletes(name, gender, team_id) VALUES ('张三', '男', 1)`, `INSERT INTO athletes(name, gender, team_id) VALUES ('李四', '男', 2)`, `INSERT INTO sports(code, name) VALUES ('SP1', '田径')`, `INSERT INTO events(sport_id, code, name, event_type, result_mode, result_type, compare, status) VALUES (1, 'E1', '100米跑', 'individual', 'auto', 'time', 'asc', 'published')`, `INSERT INTO event_categories(event_id, name, sort_order) VALUES (1, '男子', 1)`, `INSERT INTO event_score_channels(event_id, scope, basis, profile_id) SELECT 1, 'athlete', 'result', id FROM scoring_profiles WHERE code = 'individual_top6'`, `INSERT INTO entries(event_id, category_id, team_id, athlete_id, bib) VALUES (1, 1, 1, 1, '101')`, `INSERT INTO entries(event_id, category_id, team_id, athlete_id, bib) VALUES (1, 1, 2, 2, '102')`, `INSERT INTO results(event_id, entry_id, rank, result_text, result_value, unit, state) VALUES (1, 1, 1, '11.58', 11.58, 's', 'published')`, `INSERT INTO results(event_id, entry_id, rank, result_text, result_value, unit, state) VALUES (1, 2, 2, '11.90', 11.90, 's', 'published')`, `INSERT INTO score_ledger(team_id, athlete_id, event_id, entry_id, result_id, source_type, source_id, points) VALUES (1, 1, 1, 1, 1, 'result', 1, 10)`, `INSERT INTO score_ledger(team_id, athlete_id, event_id, entry_id, result_id, source_type, source_id, points) VALUES (2, 2, 1, 2, 2, 'result', 2, 8)`, `UPDATE settings SET value = '测试运动会' WHERE key = 'event.name'`, `UPDATE settings SET value = '测试主办方' WHERE key = 'event.organizer'`, } for _, s := range stmts { if _, err := db.Exec(s); err != nil { t.Fatalf("造数据 %q: %v", s, err) } } } func countRows(t *testing.T, db *sql.DB, table string) int { t.Helper() var n int if err := db.QueryRow(`SELECT count(*) FROM ` + table).Scan(&n); err != nil { t.Fatalf("统计 %s: %v", table, err) } return n } // openSQLite 以只读方式打开一个已存在的库,用于校验备份 / 导入产物可读。 func openSQLite(t *testing.T, path string) *sql.DB { t.Helper() db, err := sql.Open("sqlite", "file:"+path+"?mode=ro") if err != nil { t.Fatalf("打开 %s: %v", path, err) } return db }