test: add unit tests for core packages

Add comprehensive test coverage for:
- common/token: token generation and RNG tests
- common/stringsupport: string encoding, CSV operations
- common/byteframe: binary read/write operations
- common/mhfcourse: course/subscription logic
- network/crypt_packet: packet header parsing
- network/binpacket: binary packet round-trips
- network/mhfpacket: packet interface and opcode mapping
- config: configuration struct and loading
- server/entranceserver: response building
- server/signserver: response ID constants
- server/signv2server: HTTP endpoint validation
- server/channelserver: session, semaphore, and handler tests

All tests pass with race detector enabled.
This commit is contained in:
Houmgaor
2026-01-30 00:19:27 +01:00
parent 69cc84aa2f
commit e929346bf3
14 changed files with 4583 additions and 0 deletions

View File

@@ -0,0 +1,341 @@
package stringsupport
import (
"bytes"
"testing"
"golang.org/x/text/encoding/japanese"
)
func TestStringConverterDecode(t *testing.T) {
sc := &StringConverter{Encoding: japanese.ShiftJIS}
tests := []struct {
name string
input []byte
want string
wantErr bool
}{
{"empty", []byte{}, "", false},
{"ascii", []byte("Hello"), "Hello", false},
{"japanese hello", []byte{0x82, 0xb1, 0x82, 0xf1, 0x82, 0xc9, 0x82, 0xbf, 0x82, 0xcd}, "こんにちは", false},
{"mixed", []byte{0x41, 0x42, 0x43}, "ABC", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := sc.Decode(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Decode() = %q, want %q", got, tt.want)
}
})
}
}
func TestStringConverterEncode(t *testing.T) {
sc := &StringConverter{Encoding: japanese.ShiftJIS}
tests := []struct {
name string
input string
want []byte
wantErr bool
}{
{"empty", "", []byte{}, false},
{"ascii", "Hello", []byte("Hello"), false},
{"japanese hello", "こんにちは", []byte{0x82, 0xb1, 0x82, 0xf1, 0x82, 0xc9, 0x82, 0xbf, 0x82, 0xcd}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := sc.Encode(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("Encode() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !bytes.Equal(got, tt.want) {
t.Errorf("Encode() = %v, want %v", got, tt.want)
}
})
}
}
func TestStringConverterMustDecode(t *testing.T) {
sc := &StringConverter{Encoding: japanese.ShiftJIS}
// Valid input should not panic
result := sc.MustDecode([]byte("Hello"))
if result != "Hello" {
t.Errorf("MustDecode() = %q, want %q", result, "Hello")
}
}
func TestStringConverterMustEncode(t *testing.T) {
sc := &StringConverter{Encoding: japanese.ShiftJIS}
// Valid input should not panic
result := sc.MustEncode("Hello")
if !bytes.Equal(result, []byte("Hello")) {
t.Errorf("MustEncode() = %v, want %v", result, []byte("Hello"))
}
}
func TestUTF8ToSJIS(t *testing.T) {
tests := []struct {
name string
input string
want []byte
}{
{"empty", "", []byte{}},
{"ascii", "ABC", []byte("ABC")},
{"japanese", "こんにちは", []byte{0x82, 0xb1, 0x82, 0xf1, 0x82, 0xc9, 0x82, 0xbf, 0x82, 0xcd}},
{"mixed", "Hello世界", []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x90, 0xa2, 0x8a, 0x45}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := UTF8ToSJIS(tt.input)
if !bytes.Equal(got, tt.want) {
t.Errorf("UTF8ToSJIS(%q) = %v, want %v", tt.input, got, tt.want)
}
})
}
}
func TestSJISToUTF8(t *testing.T) {
tests := []struct {
name string
input []byte
want string
}{
{"empty", []byte{}, ""},
{"ascii", []byte("ABC"), "ABC"},
{"japanese", []byte{0x82, 0xb1, 0x82, 0xf1, 0x82, 0xc9, 0x82, 0xbf, 0x82, 0xcd}, "こんにちは"},
{"mixed", []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x90, 0xa2, 0x8a, 0x45}, "Hello世界"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := SJISToUTF8(tt.input)
if got != tt.want {
t.Errorf("SJISToUTF8(%v) = %q, want %q", tt.input, got, tt.want)
}
})
}
}
func TestUTF8ToSJISRoundTrip(t *testing.T) {
tests := []string{
"Hello",
"ABC123",
"こんにちは",
"テスト",
"モンスターハンター",
}
for _, input := range tests {
t.Run(input, func(t *testing.T) {
encoded := UTF8ToSJIS(input)
decoded := SJISToUTF8(encoded)
if decoded != input {
t.Errorf("Round trip failed: %q -> %v -> %q", input, encoded, decoded)
}
})
}
}
func TestPaddedString(t *testing.T) {
tests := []struct {
name string
input string
size uint
transform bool
wantLen int
wantEnd byte
}{
{"empty ascii", "", 10, false, 10, 0},
{"short ascii", "Hi", 10, false, 10, 0},
{"exact ascii", "1234567890", 10, false, 10, 0},
{"empty sjis", "", 10, true, 10, 0},
{"short sjis", "Hi", 10, true, 10, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := PaddedString(tt.input, tt.size, tt.transform)
if len(got) != tt.wantLen {
t.Errorf("PaddedString() len = %d, want %d", len(got), tt.wantLen)
}
if got[len(got)-1] != tt.wantEnd {
t.Errorf("PaddedString() last byte = %d, want %d", got[len(got)-1], tt.wantEnd)
}
})
}
}
func TestPaddedStringContent(t *testing.T) {
// Verify the content is correctly placed at the beginning
result := PaddedString("ABC", 10, false)
if result[0] != 'A' || result[1] != 'B' || result[2] != 'C' {
t.Errorf("PaddedString() content mismatch: got %v", result[:3])
}
// Rest should be zeros (except last which is forced to 0)
for i := 3; i < 10; i++ {
if result[i] != 0 {
t.Errorf("PaddedString() byte at %d = %d, want 0", i, result[i])
}
}
}
func TestCSVAdd(t *testing.T) {
tests := []struct {
name string
csv string
v int
want string
}{
{"empty add", "", 5, "5"},
{"add to existing", "1,2,3", 4, "1,2,3,4"},
{"add duplicate", "1,2,3", 2, "1,2,3"},
{"add to single", "1", 2, "1,2"},
{"add zero", "", 0, "0"},
{"add negative", "1,2", -5, "1,2,-5"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CSVAdd(tt.csv, tt.v)
if got != tt.want {
t.Errorf("CSVAdd(%q, %d) = %q, want %q", tt.csv, tt.v, got, tt.want)
}
})
}
}
func TestCSVRemove(t *testing.T) {
tests := []struct {
name string
csv string
v int
want string
}{
{"remove from middle", "1,2,3", 2, "1,3"},
{"remove first", "1,2,3", 1, "3,2"},
{"remove last", "1,2,3", 3, "1,2"},
{"remove only", "5", 5, ""},
{"remove nonexistent", "1,2,3", 99, "1,2,3"},
{"remove from empty", "", 5, ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CSVRemove(tt.csv, tt.v)
if got != tt.want {
t.Errorf("CSVRemove(%q, %d) = %q, want %q", tt.csv, tt.v, got, tt.want)
}
})
}
}
func TestCSVContains(t *testing.T) {
tests := []struct {
name string
csv string
v int
want bool
}{
{"contains first", "1,2,3", 1, true},
{"contains middle", "1,2,3", 2, true},
{"contains last", "1,2,3", 3, true},
{"not contains", "1,2,3", 99, false},
{"empty csv", "", 5, false},
{"single contains", "5", 5, true},
{"single not contains", "5", 3, false},
{"contains zero", "0,1,2", 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CSVContains(tt.csv, tt.v)
if got != tt.want {
t.Errorf("CSVContains(%q, %d) = %v, want %v", tt.csv, tt.v, got, tt.want)
}
})
}
}
func TestCSVLength(t *testing.T) {
tests := []struct {
name string
csv string
want int
}{
{"empty", "", 0},
{"single", "5", 1},
{"two", "1,2", 2},
{"three", "1,2,3", 3},
{"many", "1,2,3,4,5,6,7,8,9,10", 10},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CSVLength(tt.csv)
if got != tt.want {
t.Errorf("CSVLength(%q) = %d, want %d", tt.csv, got, tt.want)
}
})
}
}
func TestCSVElems(t *testing.T) {
tests := []struct {
name string
csv string
want []int
}{
{"empty", "", nil},
{"single", "5", []int{5}},
{"multiple", "1,2,3", []int{1, 2, 3}},
{"with zero", "0,1,2", []int{0, 1, 2}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CSVElems(tt.csv)
if len(got) != len(tt.want) {
t.Errorf("CSVElems(%q) len = %d, want %d", tt.csv, len(got), len(tt.want))
return
}
for i := range got {
if got[i] != tt.want[i] {
t.Errorf("CSVElems(%q)[%d] = %d, want %d", tt.csv, i, got[i], tt.want[i])
}
}
})
}
}
func TestCSVAddRemoveRoundTrip(t *testing.T) {
csv := ""
csv = CSVAdd(csv, 1)
csv = CSVAdd(csv, 2)
csv = CSVAdd(csv, 3)
if !CSVContains(csv, 1) || !CSVContains(csv, 2) || !CSVContains(csv, 3) {
t.Error("CSVAdd did not add all elements")
}
csv = CSVRemove(csv, 2)
if CSVContains(csv, 2) {
t.Error("CSVRemove did not remove element")
}
if CSVLength(csv) != 2 {
t.Errorf("CSVLength after remove = %d, want 2", CSVLength(csv))
}
}