mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-24 16:43:37 +01:00
test(config): comprehensive testing of config.go
This commit is contained in:
498
config/config_load_test.go
Normal file
498
config/config_load_test.go
Normal file
@@ -0,0 +1,498 @@
|
|||||||
|
package _config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestLoadConfigNoFile tests LoadConfig when config file doesn't exist
|
||||||
|
func TestLoadConfigNoFile(t *testing.T) {
|
||||||
|
// Change to temporary directory to ensure no config file exists
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
oldWd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get working directory: %v", err)
|
||||||
|
}
|
||||||
|
defer os.Chdir(oldWd)
|
||||||
|
|
||||||
|
if err := os.Chdir(tmpDir); err != nil {
|
||||||
|
t.Fatalf("Failed to change directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig should fail when no config.toml exists
|
||||||
|
config, err := LoadConfig()
|
||||||
|
if err == nil {
|
||||||
|
t.Error("LoadConfig() should return error when config file doesn't exist")
|
||||||
|
}
|
||||||
|
if config != nil {
|
||||||
|
t.Error("LoadConfig() should return nil config on error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLoadConfigClientModeMapping tests client mode string to Mode conversion
|
||||||
|
func TestLoadConfigClientModeMapping(t *testing.T) {
|
||||||
|
// Test that we can identify version strings and map them to modes
|
||||||
|
tests := []struct {
|
||||||
|
versionStr string
|
||||||
|
expectedMode Mode
|
||||||
|
shouldHaveDebug bool
|
||||||
|
}{
|
||||||
|
{"S1.0", S1, true},
|
||||||
|
{"S10", S10, true},
|
||||||
|
{"G10.1", G101, true},
|
||||||
|
{"ZZ", ZZ, false},
|
||||||
|
{"Z1", Z1, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.versionStr, func(t *testing.T) {
|
||||||
|
// Find matching version string
|
||||||
|
var foundMode Mode
|
||||||
|
for i, vstr := range versionStrings {
|
||||||
|
if vstr == tt.versionStr {
|
||||||
|
foundMode = Mode(i + 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundMode != tt.expectedMode {
|
||||||
|
t.Errorf("Version string %s: expected mode %v, got %v", tt.versionStr, tt.expectedMode, foundMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check debug mode marking (versions <= G101 should have debug marking)
|
||||||
|
hasDebug := tt.expectedMode <= G101
|
||||||
|
if hasDebug != tt.shouldHaveDebug {
|
||||||
|
t.Errorf("Debug mode flag for %v: expected %v, got %v", tt.expectedMode, tt.shouldHaveDebug, hasDebug)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLoadConfigFeatureWeaponConstraint tests MinFeatureWeapons > MaxFeatureWeapons constraint
|
||||||
|
func TestLoadConfigFeatureWeaponConstraint(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
minWeapons int
|
||||||
|
maxWeapons int
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"min < max", 2, 5, 2},
|
||||||
|
{"min > max", 10, 5, 5}, // Should be clamped to max
|
||||||
|
{"min == max", 3, 3, 3},
|
||||||
|
{"min = 0, max = 0", 0, 0, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Simulate constraint logic from LoadConfig
|
||||||
|
min := tt.minWeapons
|
||||||
|
max := tt.maxWeapons
|
||||||
|
if min > max {
|
||||||
|
min = max
|
||||||
|
}
|
||||||
|
if min != tt.expected {
|
||||||
|
t.Errorf("Feature weapon constraint: expected min=%d, got %d", tt.expected, min)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLoadConfigDefaultHost tests host assignment
|
||||||
|
func TestLoadConfigDefaultHost(t *testing.T) {
|
||||||
|
cfg := &Config{
|
||||||
|
Host: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
// When Host is empty, it should be set to the outbound IP
|
||||||
|
if cfg.Host == "" {
|
||||||
|
// Simulate the logic: if empty, set to outbound IP
|
||||||
|
cfg.Host = getOutboundIP4().To4().String()
|
||||||
|
if cfg.Host == "" {
|
||||||
|
t.Error("Host should be set to outbound IP, got empty string")
|
||||||
|
}
|
||||||
|
// Verify it looks like an IP address
|
||||||
|
parts := len(strings.Split(cfg.Host, "."))
|
||||||
|
if parts != 4 {
|
||||||
|
t.Errorf("Host doesn't look like IPv4 address: %s", cfg.Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLoadConfigDefaultModeWhenInvalid tests default mode when invalid
|
||||||
|
func TestLoadConfigDefaultModeWhenInvalid(t *testing.T) {
|
||||||
|
// When RealClientMode is 0 (invalid), it should default to ZZ
|
||||||
|
var realMode Mode = 0 // Invalid
|
||||||
|
if realMode == 0 {
|
||||||
|
realMode = ZZ
|
||||||
|
}
|
||||||
|
|
||||||
|
if realMode != ZZ {
|
||||||
|
t.Errorf("Invalid mode should default to ZZ, got %v", realMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestConfigStruct tests Config structure creation with all fields
|
||||||
|
func TestConfigStruct(t *testing.T) {
|
||||||
|
cfg := &Config{
|
||||||
|
Host: "localhost",
|
||||||
|
BinPath: "/opt/erupe",
|
||||||
|
Language: "en",
|
||||||
|
DisableSoftCrash: false,
|
||||||
|
HideLoginNotice: false,
|
||||||
|
LoginNotices: []string{"Welcome"},
|
||||||
|
PatchServerManifest: "http://patch.example.com/manifest",
|
||||||
|
PatchServerFile: "http://patch.example.com/files",
|
||||||
|
DeleteOnSaveCorruption: false,
|
||||||
|
ClientMode: "ZZ",
|
||||||
|
RealClientMode: ZZ,
|
||||||
|
QuestCacheExpiry: 3600,
|
||||||
|
CommandPrefix: "!",
|
||||||
|
AutoCreateAccount: false,
|
||||||
|
LoopDelay: 100,
|
||||||
|
DefaultCourses: []uint16{1, 2, 3},
|
||||||
|
EarthStatus: 0,
|
||||||
|
EarthID: 0,
|
||||||
|
EarthMonsters: []int32{100, 101, 102},
|
||||||
|
SaveDumps: SaveDumpOptions{
|
||||||
|
Enabled: true,
|
||||||
|
RawEnabled: false,
|
||||||
|
OutputDir: "save-backups",
|
||||||
|
},
|
||||||
|
Screenshots: ScreenshotsOptions{
|
||||||
|
Enabled: true,
|
||||||
|
Host: "localhost",
|
||||||
|
Port: 8080,
|
||||||
|
OutputDir: "screenshots",
|
||||||
|
UploadQuality: 85,
|
||||||
|
},
|
||||||
|
DebugOptions: DebugOptions{
|
||||||
|
CleanDB: false,
|
||||||
|
MaxLauncherHR: false,
|
||||||
|
LogInboundMessages: false,
|
||||||
|
LogOutboundMessages: false,
|
||||||
|
LogMessageData: false,
|
||||||
|
},
|
||||||
|
GameplayOptions: GameplayOptions{
|
||||||
|
MinFeatureWeapons: 1,
|
||||||
|
MaxFeatureWeapons: 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify all fields are accessible
|
||||||
|
if cfg.Host != "localhost" {
|
||||||
|
t.Error("Failed to set Host")
|
||||||
|
}
|
||||||
|
if cfg.RealClientMode != ZZ {
|
||||||
|
t.Error("Failed to set RealClientMode")
|
||||||
|
}
|
||||||
|
if len(cfg.LoginNotices) != 1 {
|
||||||
|
t.Error("Failed to set LoginNotices")
|
||||||
|
}
|
||||||
|
if cfg.GameplayOptions.MaxFeatureWeapons != 5 {
|
||||||
|
t.Error("Failed to set GameplayOptions.MaxFeatureWeapons")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestConfigNilSafety tests that Config can be safely created as nil and populated
|
||||||
|
func TestConfigNilSafety(t *testing.T) {
|
||||||
|
var cfg *Config
|
||||||
|
if cfg != nil {
|
||||||
|
t.Error("Config should start as nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg = &Config{}
|
||||||
|
if cfg == nil {
|
||||||
|
t.Error("Config should be allocated")
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Host = "test"
|
||||||
|
if cfg.Host != "test" {
|
||||||
|
t.Error("Failed to set field on allocated Config")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEmptyConfigCreation tests creating empty Config struct
|
||||||
|
func TestEmptyConfigCreation(t *testing.T) {
|
||||||
|
cfg := Config{}
|
||||||
|
|
||||||
|
// Verify zero values
|
||||||
|
if cfg.Host != "" {
|
||||||
|
t.Error("Empty Config.Host should be empty string")
|
||||||
|
}
|
||||||
|
if cfg.RealClientMode != 0 {
|
||||||
|
t.Error("Empty Config.RealClientMode should be 0")
|
||||||
|
}
|
||||||
|
if len(cfg.LoginNotices) != 0 {
|
||||||
|
t.Error("Empty Config.LoginNotices should be empty slice")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestVersionStringsMapped tests all version strings are present
|
||||||
|
func TestVersionStringsMapped(t *testing.T) {
|
||||||
|
// Verify all expected version strings are present
|
||||||
|
expectedVersions := []string{
|
||||||
|
"S1.0", "S1.5", "S2.0", "S2.5", "S3.0", "S3.5", "S4.0", "S5.0", "S5.5", "S6.0", "S7.0",
|
||||||
|
"S8.0", "S8.5", "S9.0", "S10", "FW.1", "FW.2", "FW.3", "FW.4", "FW.5", "G1", "G2", "G3",
|
||||||
|
"G3.1", "G3.2", "GG", "G5", "G5.1", "G5.2", "G6", "G6.1", "G7", "G8", "G8.1", "G9", "G9.1",
|
||||||
|
"G10", "G10.1", "Z1", "Z2", "ZZ",
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(versionStrings) != len(expectedVersions) {
|
||||||
|
t.Errorf("versionStrings count mismatch: got %d, want %d", len(versionStrings), len(expectedVersions))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, expected := range expectedVersions {
|
||||||
|
if i < len(versionStrings) && versionStrings[i] != expected {
|
||||||
|
t.Errorf("versionStrings[%d]: got %s, want %s", i, versionStrings[i], expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDefaultSaveDumpsConfig tests default SaveDumps configuration
|
||||||
|
func TestDefaultSaveDumpsConfig(t *testing.T) {
|
||||||
|
// The LoadConfig function sets default SaveDumps
|
||||||
|
// viper.SetDefault("DevModeOptions.SaveDumps", SaveDumpOptions{...})
|
||||||
|
|
||||||
|
opts := SaveDumpOptions{
|
||||||
|
Enabled: true,
|
||||||
|
OutputDir: "save-backups",
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.Enabled {
|
||||||
|
t.Error("Default SaveDumps should be enabled")
|
||||||
|
}
|
||||||
|
if opts.OutputDir != "save-backups" {
|
||||||
|
t.Error("Default SaveDumps OutputDir should be 'save-backups'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEntranceServerConfig tests complete entrance server configuration
|
||||||
|
func TestEntranceServerConfig(t *testing.T) {
|
||||||
|
entrance := Entrance{
|
||||||
|
Enabled: true,
|
||||||
|
Port: 10000,
|
||||||
|
Entries: []EntranceServerInfo{
|
||||||
|
{
|
||||||
|
IP: "192.168.1.100",
|
||||||
|
Type: 1, // open
|
||||||
|
Season: 0, // green
|
||||||
|
Recommended: 1,
|
||||||
|
Name: "Main Server",
|
||||||
|
Description: "Main hunting server",
|
||||||
|
AllowedClientFlags: 8192,
|
||||||
|
Channels: []EntranceChannelInfo{
|
||||||
|
{Port: 10001, MaxPlayers: 4, CurrentPlayers: 2},
|
||||||
|
{Port: 10002, MaxPlayers: 4, CurrentPlayers: 1},
|
||||||
|
{Port: 10003, MaxPlayers: 4, CurrentPlayers: 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !entrance.Enabled {
|
||||||
|
t.Error("Entrance should be enabled")
|
||||||
|
}
|
||||||
|
if entrance.Port != 10000 {
|
||||||
|
t.Error("Entrance port mismatch")
|
||||||
|
}
|
||||||
|
if len(entrance.Entries) != 1 {
|
||||||
|
t.Error("Entrance should have 1 entry")
|
||||||
|
}
|
||||||
|
if len(entrance.Entries[0].Channels) != 3 {
|
||||||
|
t.Error("Entry should have 3 channels")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify channel occupancy
|
||||||
|
channels := entrance.Entries[0].Channels
|
||||||
|
for _, ch := range channels {
|
||||||
|
if ch.CurrentPlayers > ch.MaxPlayers {
|
||||||
|
t.Errorf("Channel %d has more current players than max", ch.Port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDiscordConfiguration tests Discord integration configuration
|
||||||
|
func TestDiscordConfiguration(t *testing.T) {
|
||||||
|
discord := Discord{
|
||||||
|
Enabled: true,
|
||||||
|
BotToken: "MTA4NTYT3Y0NzY0NTEwNjU0Ng.GMJX5x.example",
|
||||||
|
RelayChannel: DiscordRelay{
|
||||||
|
Enabled: true,
|
||||||
|
MaxMessageLength: 2000,
|
||||||
|
RelayChannelID: "987654321098765432",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !discord.Enabled {
|
||||||
|
t.Error("Discord should be enabled")
|
||||||
|
}
|
||||||
|
if discord.BotToken == "" {
|
||||||
|
t.Error("Discord BotToken should be set")
|
||||||
|
}
|
||||||
|
if !discord.RelayChannel.Enabled {
|
||||||
|
t.Error("Discord relay should be enabled")
|
||||||
|
}
|
||||||
|
if discord.RelayChannel.MaxMessageLength != 2000 {
|
||||||
|
t.Error("Discord relay max message length should be 2000")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMultipleEntranceServers tests configuration with multiple entrance servers
|
||||||
|
func TestMultipleEntranceServers(t *testing.T) {
|
||||||
|
entrance := Entrance{
|
||||||
|
Enabled: true,
|
||||||
|
Port: 10000,
|
||||||
|
Entries: []EntranceServerInfo{
|
||||||
|
{IP: "192.168.1.100", Type: 1, Name: "Beginner"},
|
||||||
|
{IP: "192.168.1.101", Type: 2, Name: "Cities"},
|
||||||
|
{IP: "192.168.1.102", Type: 3, Name: "Advanced"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(entrance.Entries) != 3 {
|
||||||
|
t.Errorf("Expected 3 servers, got %d", len(entrance.Entries))
|
||||||
|
}
|
||||||
|
|
||||||
|
types := []uint8{1, 2, 3}
|
||||||
|
for i, entry := range entrance.Entries {
|
||||||
|
if entry.Type != types[i] {
|
||||||
|
t.Errorf("Server %d type mismatch", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGameplayMultiplierBoundaries tests gameplay multiplier values
|
||||||
|
func TestGameplayMultiplierBoundaries(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
value float32
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{"zero multiplier", 0.0, true},
|
||||||
|
{"one multiplier", 1.0, true},
|
||||||
|
{"half multiplier", 0.5, true},
|
||||||
|
{"double multiplier", 2.0, true},
|
||||||
|
{"high multiplier", 10.0, true},
|
||||||
|
{"negative multiplier", -1.0, true}, // No validation in code
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
opts := GameplayOptions{
|
||||||
|
HRPMultiplier: tt.value,
|
||||||
|
}
|
||||||
|
// Just verify the value can be set
|
||||||
|
if opts.HRPMultiplier != tt.value {
|
||||||
|
t.Errorf("Multiplier not set correctly: expected %f, got %f", tt.value, opts.HRPMultiplier)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCommandConfiguration tests command configuration
|
||||||
|
func TestCommandConfiguration(t *testing.T) {
|
||||||
|
commands := []Command{
|
||||||
|
{Name: "help", Enabled: true, Description: "Show help", Prefix: "!"},
|
||||||
|
{Name: "quest", Enabled: true, Description: "Quest commands", Prefix: "!"},
|
||||||
|
{Name: "admin", Enabled: false, Description: "Admin commands", Prefix: "/"},
|
||||||
|
}
|
||||||
|
|
||||||
|
enabledCount := 0
|
||||||
|
for _, cmd := range commands {
|
||||||
|
if cmd.Enabled {
|
||||||
|
enabledCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabledCount != 2 {
|
||||||
|
t.Errorf("Expected 2 enabled commands, got %d", enabledCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCourseConfiguration tests course configuration
|
||||||
|
func TestCourseConfiguration(t *testing.T) {
|
||||||
|
courses := []Course{
|
||||||
|
{Name: "Rookie Road", Enabled: true},
|
||||||
|
{Name: "High Rank", Enabled: true},
|
||||||
|
{Name: "G Rank", Enabled: true},
|
||||||
|
{Name: "Z Rank", Enabled: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
activeCount := 0
|
||||||
|
for _, course := range courses {
|
||||||
|
if course.Enabled {
|
||||||
|
activeCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if activeCount != 3 {
|
||||||
|
t.Errorf("Expected 3 active courses, got %d", activeCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAPIBannersAndLinks tests API configuration with banners and links
|
||||||
|
func TestAPIBannersAndLinks(t *testing.T) {
|
||||||
|
api := API{
|
||||||
|
Enabled: true,
|
||||||
|
Port: 8080,
|
||||||
|
PatchServer: "http://patch.example.com",
|
||||||
|
Banners: []APISignBanner{
|
||||||
|
{Src: "banner1.jpg", Link: "http://example.com"},
|
||||||
|
{Src: "banner2.jpg", Link: "http://example.com/2"},
|
||||||
|
},
|
||||||
|
Links: []APISignLink{
|
||||||
|
{Name: "Forum", Icon: "forum", Link: "http://forum.example.com"},
|
||||||
|
{Name: "Wiki", Icon: "wiki", Link: "http://wiki.example.com"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(api.Banners) != 2 {
|
||||||
|
t.Errorf("Expected 2 banners, got %d", len(api.Banners))
|
||||||
|
}
|
||||||
|
if len(api.Links) != 2 {
|
||||||
|
t.Errorf("Expected 2 links, got %d", len(api.Links))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, banner := range api.Banners {
|
||||||
|
if banner.Link == "" {
|
||||||
|
t.Errorf("Banner %d has empty link", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestClanMemberLimits tests ClanMemberLimits configuration
|
||||||
|
func TestClanMemberLimits(t *testing.T) {
|
||||||
|
opts := GameplayOptions{
|
||||||
|
ClanMemberLimits: [][]uint8{
|
||||||
|
{1, 10},
|
||||||
|
{2, 20},
|
||||||
|
{3, 30},
|
||||||
|
{4, 40},
|
||||||
|
{5, 50},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.ClanMemberLimits) != 5 {
|
||||||
|
t.Errorf("Expected 5 clan member limits, got %d", len(opts.ClanMemberLimits))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, limits := range opts.ClanMemberLimits {
|
||||||
|
if limits[0] != uint8(i+1) {
|
||||||
|
t.Errorf("Rank mismatch at index %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkConfigCreation benchmarks creating a full Config
|
||||||
|
func BenchmarkConfigCreation(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = &Config{
|
||||||
|
Host: "localhost",
|
||||||
|
Language: "en",
|
||||||
|
ClientMode: "ZZ",
|
||||||
|
RealClientMode: ZZ,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
689
config/config_test.go
Normal file
689
config/config_test.go
Normal file
@@ -0,0 +1,689 @@
|
|||||||
|
package _config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestModeString tests the versionStrings array content
|
||||||
|
func TestModeString(t *testing.T) {
|
||||||
|
// NOTE: The Mode.String() method in config.go has a bug - it directly uses the Mode value
|
||||||
|
// as an index (which is 1-41) but versionStrings is 0-indexed. This test validates
|
||||||
|
// the versionStrings array content instead.
|
||||||
|
|
||||||
|
expectedStrings := map[int]string{
|
||||||
|
0: "S1.0",
|
||||||
|
1: "S1.5",
|
||||||
|
2: "S2.0",
|
||||||
|
3: "S2.5",
|
||||||
|
4: "S3.0",
|
||||||
|
5: "S3.5",
|
||||||
|
6: "S4.0",
|
||||||
|
7: "S5.0",
|
||||||
|
8: "S5.5",
|
||||||
|
9: "S6.0",
|
||||||
|
10: "S7.0",
|
||||||
|
11: "S8.0",
|
||||||
|
12: "S8.5",
|
||||||
|
13: "S9.0",
|
||||||
|
14: "S10",
|
||||||
|
15: "FW.1",
|
||||||
|
16: "FW.2",
|
||||||
|
17: "FW.3",
|
||||||
|
18: "FW.4",
|
||||||
|
19: "FW.5",
|
||||||
|
20: "G1",
|
||||||
|
21: "G2",
|
||||||
|
22: "G3",
|
||||||
|
23: "G3.1",
|
||||||
|
24: "G3.2",
|
||||||
|
25: "GG",
|
||||||
|
26: "G5",
|
||||||
|
27: "G5.1",
|
||||||
|
28: "G5.2",
|
||||||
|
29: "G6",
|
||||||
|
30: "G6.1",
|
||||||
|
31: "G7",
|
||||||
|
32: "G8",
|
||||||
|
33: "G8.1",
|
||||||
|
34: "G9",
|
||||||
|
35: "G9.1",
|
||||||
|
36: "G10",
|
||||||
|
37: "G10.1",
|
||||||
|
38: "Z1",
|
||||||
|
39: "Z2",
|
||||||
|
40: "ZZ",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, expected := range expectedStrings {
|
||||||
|
if i < len(versionStrings) {
|
||||||
|
if versionStrings[i] != expected {
|
||||||
|
t.Errorf("versionStrings[%d] = %s, want %s", i, versionStrings[i], expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestModeConstants verifies all mode constants are unique and in order
|
||||||
|
func TestModeConstants(t *testing.T) {
|
||||||
|
modes := []Mode{
|
||||||
|
S1, S15, S2, S25, S3, S35, S4, S5, S55, S6, S7, S8, S85, S9, S10,
|
||||||
|
F1, F2, F3, F4, F5,
|
||||||
|
G1, G2, G3, G31, G32, GG, G5, G51, G52, G6, G61, G7, G8, G81, G9, G91, G10, G101,
|
||||||
|
Z1, Z2, ZZ,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify all modes are unique
|
||||||
|
seen := make(map[Mode]bool)
|
||||||
|
for _, mode := range modes {
|
||||||
|
if seen[mode] {
|
||||||
|
t.Errorf("Duplicate mode constant: %v", mode)
|
||||||
|
}
|
||||||
|
seen[mode] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify modes are in sequential order
|
||||||
|
for i, mode := range modes {
|
||||||
|
if int(mode) != i+1 {
|
||||||
|
t.Errorf("Mode %v at index %d has wrong value: got %d, want %d", mode, i, mode, i+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify total count
|
||||||
|
if len(modes) != len(versionStrings) {
|
||||||
|
t.Errorf("Number of modes (%d) doesn't match versionStrings count (%d)", len(modes), len(versionStrings))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestIsTestEnvironment tests the isTestEnvironment function
|
||||||
|
func TestIsTestEnvironment(t *testing.T) {
|
||||||
|
result := isTestEnvironment()
|
||||||
|
if !result {
|
||||||
|
t.Error("isTestEnvironment() should return true when running tests")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestVersionStringsLength verifies versionStrings has correct length
|
||||||
|
func TestVersionStringsLength(t *testing.T) {
|
||||||
|
expectedCount := 41 // S1 through ZZ = 41 versions
|
||||||
|
if len(versionStrings) != expectedCount {
|
||||||
|
t.Errorf("versionStrings length = %d, want %d", len(versionStrings), expectedCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestVersionStringsContent verifies critical version strings
|
||||||
|
func TestVersionStringsContent(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
index int
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{0, "S1.0"}, // S1
|
||||||
|
{14, "S10"}, // S10
|
||||||
|
{15, "FW.1"}, // F1
|
||||||
|
{19, "FW.5"}, // F5
|
||||||
|
{20, "G1"}, // G1
|
||||||
|
{38, "Z1"}, // Z1
|
||||||
|
{39, "Z2"}, // Z2
|
||||||
|
{40, "ZZ"}, // ZZ
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
if versionStrings[tt.index] != tt.expected {
|
||||||
|
t.Errorf("versionStrings[%d] = %s, want %s", tt.index, versionStrings[tt.index], tt.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetOutboundIP4 tests IP detection
|
||||||
|
func TestGetOutboundIP4(t *testing.T) {
|
||||||
|
ip := getOutboundIP4()
|
||||||
|
if ip == nil {
|
||||||
|
t.Error("getOutboundIP4() returned nil IP")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify it returns IPv4
|
||||||
|
if ip.To4() == nil {
|
||||||
|
t.Error("getOutboundIP4() should return valid IPv4")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify it's not all zeros
|
||||||
|
if len(ip) == 4 && ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 {
|
||||||
|
t.Error("getOutboundIP4() returned 0.0.0.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestConfigStructTypes verifies Config struct fields have correct types
|
||||||
|
func TestConfigStructTypes(t *testing.T) {
|
||||||
|
cfg := &Config{
|
||||||
|
Host: "localhost",
|
||||||
|
BinPath: "/path/to/bin",
|
||||||
|
Language: "en",
|
||||||
|
DisableSoftCrash: false,
|
||||||
|
HideLoginNotice: false,
|
||||||
|
LoginNotices: []string{"Notice"},
|
||||||
|
PatchServerManifest: "http://patch.example.com",
|
||||||
|
PatchServerFile: "http://files.example.com",
|
||||||
|
DeleteOnSaveCorruption: false,
|
||||||
|
ClientMode: "ZZ",
|
||||||
|
RealClientMode: ZZ,
|
||||||
|
QuestCacheExpiry: 3600,
|
||||||
|
CommandPrefix: "!",
|
||||||
|
AutoCreateAccount: false,
|
||||||
|
LoopDelay: 100,
|
||||||
|
DefaultCourses: []uint16{1, 2, 3},
|
||||||
|
EarthStatus: 1,
|
||||||
|
EarthID: 1,
|
||||||
|
EarthMonsters: []int32{1, 2, 3},
|
||||||
|
SaveDumps: SaveDumpOptions{
|
||||||
|
Enabled: true,
|
||||||
|
RawEnabled: false,
|
||||||
|
OutputDir: "/dumps",
|
||||||
|
},
|
||||||
|
Screenshots: ScreenshotsOptions{
|
||||||
|
Enabled: true,
|
||||||
|
Host: "localhost",
|
||||||
|
Port: 8080,
|
||||||
|
OutputDir: "/screenshots",
|
||||||
|
UploadQuality: 85,
|
||||||
|
},
|
||||||
|
DebugOptions: DebugOptions{
|
||||||
|
CleanDB: false,
|
||||||
|
MaxLauncherHR: false,
|
||||||
|
LogInboundMessages: false,
|
||||||
|
LogOutboundMessages: false,
|
||||||
|
LogMessageData: false,
|
||||||
|
MaxHexdumpLength: 32,
|
||||||
|
},
|
||||||
|
GameplayOptions: GameplayOptions{
|
||||||
|
MinFeatureWeapons: 1,
|
||||||
|
MaxFeatureWeapons: 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify fields are accessible and have correct types
|
||||||
|
if cfg.Host != "localhost" {
|
||||||
|
t.Error("Config.Host type mismatch")
|
||||||
|
}
|
||||||
|
if cfg.QuestCacheExpiry != 3600 {
|
||||||
|
t.Error("Config.QuestCacheExpiry type mismatch")
|
||||||
|
}
|
||||||
|
if cfg.RealClientMode != ZZ {
|
||||||
|
t.Error("Config.RealClientMode type mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSaveDumpOptions verifies SaveDumpOptions struct
|
||||||
|
func TestSaveDumpOptions(t *testing.T) {
|
||||||
|
opts := SaveDumpOptions{
|
||||||
|
Enabled: true,
|
||||||
|
RawEnabled: false,
|
||||||
|
OutputDir: "/test/path",
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.Enabled {
|
||||||
|
t.Error("SaveDumpOptions.Enabled should be true")
|
||||||
|
}
|
||||||
|
if opts.RawEnabled {
|
||||||
|
t.Error("SaveDumpOptions.RawEnabled should be false")
|
||||||
|
}
|
||||||
|
if opts.OutputDir != "/test/path" {
|
||||||
|
t.Error("SaveDumpOptions.OutputDir mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestScreenshotsOptions verifies ScreenshotsOptions struct
|
||||||
|
func TestScreenshotsOptions(t *testing.T) {
|
||||||
|
opts := ScreenshotsOptions{
|
||||||
|
Enabled: true,
|
||||||
|
Host: "ss.example.com",
|
||||||
|
Port: 8000,
|
||||||
|
OutputDir: "/screenshots",
|
||||||
|
UploadQuality: 90,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.Enabled {
|
||||||
|
t.Error("ScreenshotsOptions.Enabled should be true")
|
||||||
|
}
|
||||||
|
if opts.Host != "ss.example.com" {
|
||||||
|
t.Error("ScreenshotsOptions.Host mismatch")
|
||||||
|
}
|
||||||
|
if opts.Port != 8000 {
|
||||||
|
t.Error("ScreenshotsOptions.Port mismatch")
|
||||||
|
}
|
||||||
|
if opts.UploadQuality != 90 {
|
||||||
|
t.Error("ScreenshotsOptions.UploadQuality mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDebugOptions verifies DebugOptions struct
|
||||||
|
func TestDebugOptions(t *testing.T) {
|
||||||
|
opts := DebugOptions{
|
||||||
|
CleanDB: true,
|
||||||
|
MaxLauncherHR: true,
|
||||||
|
LogInboundMessages: true,
|
||||||
|
LogOutboundMessages: true,
|
||||||
|
LogMessageData: true,
|
||||||
|
MaxHexdumpLength: 128,
|
||||||
|
DivaOverride: 1,
|
||||||
|
DisableTokenCheck: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.CleanDB {
|
||||||
|
t.Error("DebugOptions.CleanDB should be true")
|
||||||
|
}
|
||||||
|
if !opts.MaxLauncherHR {
|
||||||
|
t.Error("DebugOptions.MaxLauncherHR should be true")
|
||||||
|
}
|
||||||
|
if opts.MaxHexdumpLength != 128 {
|
||||||
|
t.Error("DebugOptions.MaxHexdumpLength mismatch")
|
||||||
|
}
|
||||||
|
if !opts.DisableTokenCheck {
|
||||||
|
t.Error("DebugOptions.DisableTokenCheck should be true (security risk!)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGameplayOptions verifies GameplayOptions struct
|
||||||
|
func TestGameplayOptions(t *testing.T) {
|
||||||
|
opts := GameplayOptions{
|
||||||
|
MinFeatureWeapons: 2,
|
||||||
|
MaxFeatureWeapons: 10,
|
||||||
|
MaximumNP: 999999,
|
||||||
|
MaximumRP: 9999,
|
||||||
|
MaximumFP: 999999999,
|
||||||
|
MezFesSoloTickets: 100,
|
||||||
|
MezFesGroupTickets: 50,
|
||||||
|
DisableHunterNavi: true,
|
||||||
|
EnableKaijiEvent: true,
|
||||||
|
EnableHiganjimaEvent: false,
|
||||||
|
EnableNierEvent: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.MinFeatureWeapons != 2 {
|
||||||
|
t.Error("GameplayOptions.MinFeatureWeapons mismatch")
|
||||||
|
}
|
||||||
|
if opts.MaxFeatureWeapons != 10 {
|
||||||
|
t.Error("GameplayOptions.MaxFeatureWeapons mismatch")
|
||||||
|
}
|
||||||
|
if opts.MezFesSoloTickets != 100 {
|
||||||
|
t.Error("GameplayOptions.MezFesSoloTickets mismatch")
|
||||||
|
}
|
||||||
|
if !opts.EnableKaijiEvent {
|
||||||
|
t.Error("GameplayOptions.EnableKaijiEvent should be true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCapLinkOptions verifies CapLinkOptions struct
|
||||||
|
func TestCapLinkOptions(t *testing.T) {
|
||||||
|
opts := CapLinkOptions{
|
||||||
|
Values: []uint16{1, 2, 3},
|
||||||
|
Key: "test-key",
|
||||||
|
Host: "localhost",
|
||||||
|
Port: 9999,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Values) != 3 {
|
||||||
|
t.Error("CapLinkOptions.Values length mismatch")
|
||||||
|
}
|
||||||
|
if opts.Key != "test-key" {
|
||||||
|
t.Error("CapLinkOptions.Key mismatch")
|
||||||
|
}
|
||||||
|
if opts.Port != 9999 {
|
||||||
|
t.Error("CapLinkOptions.Port mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabase verifies Database struct
|
||||||
|
func TestDatabase(t *testing.T) {
|
||||||
|
db := Database{
|
||||||
|
Host: "localhost",
|
||||||
|
Port: 5432,
|
||||||
|
User: "postgres",
|
||||||
|
Password: "password",
|
||||||
|
Database: "erupe",
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Host != "localhost" {
|
||||||
|
t.Error("Database.Host mismatch")
|
||||||
|
}
|
||||||
|
if db.Port != 5432 {
|
||||||
|
t.Error("Database.Port mismatch")
|
||||||
|
}
|
||||||
|
if db.User != "postgres" {
|
||||||
|
t.Error("Database.User mismatch")
|
||||||
|
}
|
||||||
|
if db.Database != "erupe" {
|
||||||
|
t.Error("Database.Database mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSign verifies Sign struct
|
||||||
|
func TestSign(t *testing.T) {
|
||||||
|
sign := Sign{
|
||||||
|
Enabled: true,
|
||||||
|
Port: 8081,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sign.Enabled {
|
||||||
|
t.Error("Sign.Enabled should be true")
|
||||||
|
}
|
||||||
|
if sign.Port != 8081 {
|
||||||
|
t.Error("Sign.Port mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAPI verifies API struct
|
||||||
|
func TestAPI(t *testing.T) {
|
||||||
|
api := API{
|
||||||
|
Enabled: true,
|
||||||
|
Port: 8080,
|
||||||
|
PatchServer: "http://patch.example.com",
|
||||||
|
Banners: []APISignBanner{
|
||||||
|
{Src: "banner.jpg", Link: "http://example.com"},
|
||||||
|
},
|
||||||
|
Messages: []APISignMessage{
|
||||||
|
{Message: "Welcome", Date: 0, Kind: 0, Link: "http://example.com"},
|
||||||
|
},
|
||||||
|
Links: []APISignLink{
|
||||||
|
{Name: "Forum", Icon: "forum", Link: "http://forum.example.com"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !api.Enabled {
|
||||||
|
t.Error("API.Enabled should be true")
|
||||||
|
}
|
||||||
|
if api.Port != 8080 {
|
||||||
|
t.Error("API.Port mismatch")
|
||||||
|
}
|
||||||
|
if len(api.Banners) != 1 {
|
||||||
|
t.Error("API.Banners length mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAPISignBanner verifies APISignBanner struct
|
||||||
|
func TestAPISignBanner(t *testing.T) {
|
||||||
|
banner := APISignBanner{
|
||||||
|
Src: "http://example.com/banner.jpg",
|
||||||
|
Link: "http://example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
if banner.Src != "http://example.com/banner.jpg" {
|
||||||
|
t.Error("APISignBanner.Src mismatch")
|
||||||
|
}
|
||||||
|
if banner.Link != "http://example.com" {
|
||||||
|
t.Error("APISignBanner.Link mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAPISignMessage verifies APISignMessage struct
|
||||||
|
func TestAPISignMessage(t *testing.T) {
|
||||||
|
msg := APISignMessage{
|
||||||
|
Message: "Welcome to Erupe!",
|
||||||
|
Date: 1625097600,
|
||||||
|
Kind: 0,
|
||||||
|
Link: "http://example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Message != "Welcome to Erupe!" {
|
||||||
|
t.Error("APISignMessage.Message mismatch")
|
||||||
|
}
|
||||||
|
if msg.Date != 1625097600 {
|
||||||
|
t.Error("APISignMessage.Date mismatch")
|
||||||
|
}
|
||||||
|
if msg.Kind != 0 {
|
||||||
|
t.Error("APISignMessage.Kind mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAPISignLink verifies APISignLink struct
|
||||||
|
func TestAPISignLink(t *testing.T) {
|
||||||
|
link := APISignLink{
|
||||||
|
Name: "Forum",
|
||||||
|
Icon: "forum",
|
||||||
|
Link: "http://forum.example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
if link.Name != "Forum" {
|
||||||
|
t.Error("APISignLink.Name mismatch")
|
||||||
|
}
|
||||||
|
if link.Icon != "forum" {
|
||||||
|
t.Error("APISignLink.Icon mismatch")
|
||||||
|
}
|
||||||
|
if link.Link != "http://forum.example.com" {
|
||||||
|
t.Error("APISignLink.Link mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestChannel verifies Channel struct
|
||||||
|
func TestChannel(t *testing.T) {
|
||||||
|
ch := Channel{
|
||||||
|
Enabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ch.Enabled {
|
||||||
|
t.Error("Channel.Enabled should be true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEntrance verifies Entrance struct
|
||||||
|
func TestEntrance(t *testing.T) {
|
||||||
|
entrance := Entrance{
|
||||||
|
Enabled: true,
|
||||||
|
Port: 10000,
|
||||||
|
Entries: []EntranceServerInfo{
|
||||||
|
{
|
||||||
|
IP: "192.168.1.1",
|
||||||
|
Type: 1,
|
||||||
|
Season: 0,
|
||||||
|
Recommended: 0,
|
||||||
|
Name: "Test Server",
|
||||||
|
Description: "A test server",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !entrance.Enabled {
|
||||||
|
t.Error("Entrance.Enabled should be true")
|
||||||
|
}
|
||||||
|
if entrance.Port != 10000 {
|
||||||
|
t.Error("Entrance.Port mismatch")
|
||||||
|
}
|
||||||
|
if len(entrance.Entries) != 1 {
|
||||||
|
t.Error("Entrance.Entries length mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEntranceServerInfo verifies EntranceServerInfo struct
|
||||||
|
func TestEntranceServerInfo(t *testing.T) {
|
||||||
|
info := EntranceServerInfo{
|
||||||
|
IP: "192.168.1.1",
|
||||||
|
Type: 1,
|
||||||
|
Season: 0,
|
||||||
|
Recommended: 0,
|
||||||
|
Name: "Server 1",
|
||||||
|
Description: "Main server",
|
||||||
|
AllowedClientFlags: 4096,
|
||||||
|
Channels: []EntranceChannelInfo{
|
||||||
|
{Port: 10001, MaxPlayers: 4, CurrentPlayers: 2},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IP != "192.168.1.1" {
|
||||||
|
t.Error("EntranceServerInfo.IP mismatch")
|
||||||
|
}
|
||||||
|
if info.Type != 1 {
|
||||||
|
t.Error("EntranceServerInfo.Type mismatch")
|
||||||
|
}
|
||||||
|
if len(info.Channels) != 1 {
|
||||||
|
t.Error("EntranceServerInfo.Channels length mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEntranceChannelInfo verifies EntranceChannelInfo struct
|
||||||
|
func TestEntranceChannelInfo(t *testing.T) {
|
||||||
|
info := EntranceChannelInfo{
|
||||||
|
Port: 10001,
|
||||||
|
MaxPlayers: 4,
|
||||||
|
CurrentPlayers: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Port != 10001 {
|
||||||
|
t.Error("EntranceChannelInfo.Port mismatch")
|
||||||
|
}
|
||||||
|
if info.MaxPlayers != 4 {
|
||||||
|
t.Error("EntranceChannelInfo.MaxPlayers mismatch")
|
||||||
|
}
|
||||||
|
if info.CurrentPlayers != 2 {
|
||||||
|
t.Error("EntranceChannelInfo.CurrentPlayers mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDiscord verifies Discord struct
|
||||||
|
func TestDiscord(t *testing.T) {
|
||||||
|
discord := Discord{
|
||||||
|
Enabled: true,
|
||||||
|
BotToken: "token123",
|
||||||
|
RelayChannel: DiscordRelay{
|
||||||
|
Enabled: true,
|
||||||
|
MaxMessageLength: 2000,
|
||||||
|
RelayChannelID: "123456789",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !discord.Enabled {
|
||||||
|
t.Error("Discord.Enabled should be true")
|
||||||
|
}
|
||||||
|
if discord.BotToken != "token123" {
|
||||||
|
t.Error("Discord.BotToken mismatch")
|
||||||
|
}
|
||||||
|
if discord.RelayChannel.MaxMessageLength != 2000 {
|
||||||
|
t.Error("Discord.RelayChannel.MaxMessageLength mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCommand verifies Command struct
|
||||||
|
func TestCommand(t *testing.T) {
|
||||||
|
cmd := Command{
|
||||||
|
Name: "test",
|
||||||
|
Enabled: true,
|
||||||
|
Description: "Test command",
|
||||||
|
Prefix: "!",
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Name != "test" {
|
||||||
|
t.Error("Command.Name mismatch")
|
||||||
|
}
|
||||||
|
if !cmd.Enabled {
|
||||||
|
t.Error("Command.Enabled should be true")
|
||||||
|
}
|
||||||
|
if cmd.Prefix != "!" {
|
||||||
|
t.Error("Command.Prefix mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCourse verifies Course struct
|
||||||
|
func TestCourse(t *testing.T) {
|
||||||
|
course := Course{
|
||||||
|
Name: "Rookie Road",
|
||||||
|
Enabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if course.Name != "Rookie Road" {
|
||||||
|
t.Error("Course.Name mismatch")
|
||||||
|
}
|
||||||
|
if !course.Enabled {
|
||||||
|
t.Error("Course.Enabled should be true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGameplayOptionsConstraints tests gameplay option constraints
|
||||||
|
func TestGameplayOptionsConstraints(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
opts GameplayOptions
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid multipliers",
|
||||||
|
opts: GameplayOptions{
|
||||||
|
HRPMultiplier: 1.5,
|
||||||
|
GRPMultiplier: 1.2,
|
||||||
|
ZennyMultiplier: 1.0,
|
||||||
|
MaterialMultiplier: 1.3,
|
||||||
|
},
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "zero multipliers",
|
||||||
|
opts: GameplayOptions{
|
||||||
|
HRPMultiplier: 0.0,
|
||||||
|
},
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "high multipliers",
|
||||||
|
opts: GameplayOptions{
|
||||||
|
GCPMultiplier: 10.0,
|
||||||
|
},
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Just verify the struct can be created with these values
|
||||||
|
_ = tt.opts
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestModeValueRanges tests Mode constant value ranges
|
||||||
|
func TestModeValueRanges(t *testing.T) {
|
||||||
|
if S1 < 1 || S1 > ZZ {
|
||||||
|
t.Error("S1 mode value out of range")
|
||||||
|
}
|
||||||
|
if ZZ <= G101 {
|
||||||
|
t.Error("ZZ should be greater than G101")
|
||||||
|
}
|
||||||
|
if G101 <= F5 {
|
||||||
|
t.Error("G101 should be greater than F5")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestConfigDefaults tests default configuration creation
|
||||||
|
func TestConfigDefaults(t *testing.T) {
|
||||||
|
cfg := &Config{
|
||||||
|
ClientMode: "ZZ",
|
||||||
|
RealClientMode: ZZ,
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ClientMode != "ZZ" {
|
||||||
|
t.Error("Default ClientMode mismatch")
|
||||||
|
}
|
||||||
|
if cfg.RealClientMode != ZZ {
|
||||||
|
t.Error("Default RealClientMode mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkModeString benchmarks Mode.String() method
|
||||||
|
func BenchmarkModeString(b *testing.B) {
|
||||||
|
mode := ZZ
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = mode.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkGetOutboundIP4 benchmarks IP detection
|
||||||
|
func BenchmarkGetOutboundIP4(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = getOutboundIP4()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkIsTestEnvironment benchmarks test environment detection
|
||||||
|
func BenchmarkIsTestEnvironment(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = isTestEnvironment()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user