- PlayerHandler with all CRUD routes and tournament player operations - Buy-in flow: register + financial engine + auto-seat suggestion - Bust flow: hitman selection + bounty transfer + re-ranking - Undo bust with full re-ranking and rankings response - Rankings API endpoint returning derived positions - QR code endpoint returns PNG image with Cache-Control header - CSV import via multipart upload (admin only) - Player merge endpoint (admin only) - CSV export safety: formula injection neutralization (tab-prefix =,+,-,@) - Ranking tests: bust order, undo re-ranking, early undo, re-entry, deal positions, auto-close, concurrent busts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
111 lines
2.6 KiB
Go
111 lines
2.6 KiB
Go
package player
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// ---------- CSV Export Safety Tests ----------
|
|
|
|
func TestSanitizeCSVField_FormulaInjection(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
expected string
|
|
name string
|
|
}{
|
|
{"=CMD()", "\t=CMD()", "equals command"},
|
|
{"+1+1", "\t+1+1", "plus prefix"},
|
|
{"-1+1", "\t-1+1", "minus prefix"},
|
|
{"@SUM(A1)", "\t@SUM(A1)", "at sign function"},
|
|
{"=HYPERLINK(\"http://evil.com\")", "\t=HYPERLINK(\"http://evil.com\")", "hyperlink formula"},
|
|
{"Normal text", "Normal text", "normal text"},
|
|
{"123", "123", "numeric"},
|
|
{"", "", "empty string"},
|
|
{"Hello World", "Hello World", "regular greeting"},
|
|
{"John Doe", "John Doe", "player name"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := SanitizeCSVField(tt.input)
|
|
if result != tt.expected {
|
|
t.Errorf("SanitizeCSVField(%q) = %q, want %q", tt.input, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSanitizeCSVRow(t *testing.T) {
|
|
row := []string{"John", "=SUM(A1)", "+1", "-1", "@SUM", "Normal"}
|
|
expected := []string{"John", "\t=SUM(A1)", "\t+1", "\t-1", "\t@SUM", "Normal"}
|
|
|
|
result := SanitizeCSVRow(row)
|
|
|
|
for i, v := range result {
|
|
if v != expected[i] {
|
|
t.Errorf("SanitizeCSVRow[%d] = %q, want %q", i, v, expected[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsFormulaInjection(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
expected bool
|
|
}{
|
|
{"=CMD()", true},
|
|
{"+1+1", true},
|
|
{"-1+1", true},
|
|
{"@SUM(A1)", true},
|
|
{"Normal text", false},
|
|
{"123", false},
|
|
{"", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
if result := IsFormulaInjection(tt.input); result != tt.expected {
|
|
t.Errorf("IsFormulaInjection(%q) = %v, want %v", tt.input, result, tt.expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------- CSV Import Safety Limit Tests ----------
|
|
|
|
func TestCSVImportLimits(t *testing.T) {
|
|
// These are constant assertions, not behavioral tests.
|
|
// They verify the safety constants are set to the documented values.
|
|
if MaxCSVRows != 10_000 {
|
|
t.Errorf("MaxCSVRows = %d, want 10000", MaxCSVRows)
|
|
}
|
|
if MaxCSVColumns != 20 {
|
|
t.Errorf("MaxCSVColumns = %d, want 20", MaxCSVColumns)
|
|
}
|
|
if MaxCSVFieldLen != 1_000 {
|
|
t.Errorf("MaxCSVFieldLen = %d, want 1000", MaxCSVFieldLen)
|
|
}
|
|
}
|
|
|
|
// ---------- UUID Generation Tests ----------
|
|
|
|
func TestGenerateUUID(t *testing.T) {
|
|
id := generateUUID()
|
|
if len(id) == 0 {
|
|
t.Error("generateUUID returned empty string")
|
|
}
|
|
|
|
// Check format: 8-4-4-4-12
|
|
parts := 0
|
|
for _, c := range id {
|
|
if c == '-' {
|
|
parts++
|
|
}
|
|
}
|
|
if parts != 4 {
|
|
t.Errorf("UUID should have 4 dashes, got %d: %s", parts, id)
|
|
}
|
|
|
|
// Check uniqueness
|
|
id2 := generateUUID()
|
|
if id == id2 {
|
|
t.Error("two UUIDs should not be equal")
|
|
}
|
|
}
|