mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
- Fix duplicate test declarations across coverage2, misc, mercenary, event files - Fix signserver TestHandlePacketDELETE timeout (multi-write pipe deadlock) - Fix entranceserver build error (invalid -1 for uint16 port) - Add handlers_reserve_test.go covering all 56 reserve handler stubs - Add handlers_coverage3_test.go with 115+ handler tests - Add handlers_register_test.go with 55 Raviente register/load tests - Add handlers_coverage_test.go, signserver, entranceserver, usercheck tests
1037 lines
27 KiB
Go
1037 lines
27 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"flag"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// escapeConnStringValue
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestEscapeConnStringValue_Empty(t *testing.T) {
|
|
got := escapeConnStringValue("")
|
|
if got != "" {
|
|
t.Errorf("expected empty string, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestEscapeConnStringValue_NoSpecialChars(t *testing.T) {
|
|
got := escapeConnStringValue("hello world")
|
|
if got != "hello world" {
|
|
t.Errorf("expected %q, got %q", "hello world", got)
|
|
}
|
|
}
|
|
|
|
func TestEscapeConnStringValue_SingleQuote(t *testing.T) {
|
|
got := escapeConnStringValue("it's")
|
|
want := "it''s"
|
|
if got != want {
|
|
t.Errorf("expected %q, got %q", want, got)
|
|
}
|
|
}
|
|
|
|
func TestEscapeConnStringValue_Backslash(t *testing.T) {
|
|
got := escapeConnStringValue(`path\to\file`)
|
|
want := `path\\to\\file`
|
|
if got != want {
|
|
t.Errorf("expected %q, got %q", want, got)
|
|
}
|
|
}
|
|
|
|
func TestEscapeConnStringValue_BothQuoteAndBackslash(t *testing.T) {
|
|
got := escapeConnStringValue(`it's\path`)
|
|
want := `it''s\\path`
|
|
if got != want {
|
|
t.Errorf("expected %q, got %q", want, got)
|
|
}
|
|
}
|
|
|
|
func TestEscapeConnStringValue_MultipleConsecutiveQuotes(t *testing.T) {
|
|
got := escapeConnStringValue("a'''b")
|
|
want := "a''''''b"
|
|
if got != want {
|
|
t.Errorf("expected %q, got %q", want, got)
|
|
}
|
|
}
|
|
|
|
func TestEscapeConnStringValue_OnlySpecialChars(t *testing.T) {
|
|
got := escapeConnStringValue(`'\`)
|
|
want := `''\\`
|
|
if got != want {
|
|
t.Errorf("expected %q, got %q", want, got)
|
|
}
|
|
}
|
|
|
|
func TestEscapeConnStringValue_Table(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
want string
|
|
}{
|
|
{"unicode", "p@$$w\u00f6rd", "p@$$w\u00f6rd"},
|
|
{"spaces only", " ", " "},
|
|
{"leading quote", "'start", "''start"},
|
|
{"trailing quote", "end'", "end''"},
|
|
{"trailing backslash", `end\`, `end\\`},
|
|
{"multiple backslashes", `a\\b`, `a\\\\b`},
|
|
{"mixed complex", `x'y\z'w\`, `x''y\\z''w\\`},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := escapeConnStringValue(tt.input)
|
|
if got != tt.want {
|
|
t.Errorf("escapeConnStringValue(%q) = %q, want %q", tt.input, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// loadConfigFile
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestLoadConfigFile_Valid(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "config.json")
|
|
content := `{
|
|
"Database": {
|
|
"Host": "myhost",
|
|
"Port": 1234,
|
|
"User": "myuser",
|
|
"Password": "mypass",
|
|
"Database": "mydb"
|
|
}
|
|
}`
|
|
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := loadConfigFile(path)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg.Database.Host != "myhost" {
|
|
t.Errorf("Host = %q, want %q", cfg.Database.Host, "myhost")
|
|
}
|
|
if cfg.Database.Port != 1234 {
|
|
t.Errorf("Port = %d, want %d", cfg.Database.Port, 1234)
|
|
}
|
|
if cfg.Database.User != "myuser" {
|
|
t.Errorf("User = %q, want %q", cfg.Database.User, "myuser")
|
|
}
|
|
if cfg.Database.Password != "mypass" {
|
|
t.Errorf("Password = %q, want %q", cfg.Database.Password, "mypass")
|
|
}
|
|
if cfg.Database.Database != "mydb" {
|
|
t.Errorf("Database = %q, want %q", cfg.Database.Database, "mydb")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_NonExistent(t *testing.T) {
|
|
_, err := loadConfigFile("/tmp/nonexistent_config_test_12345.json")
|
|
if err == nil {
|
|
t.Fatal("expected error for non-existent file, got nil")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_InvalidJSON(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "config.json")
|
|
if err := os.WriteFile(path, []byte("not valid json {{{"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err := loadConfigFile(path)
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid JSON, got nil")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_EmptyFile(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "config.json")
|
|
if err := os.WriteFile(path, []byte(""), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err := loadConfigFile(path)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty file, got nil")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_NoDatabaseField(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "config.json")
|
|
if err := os.WriteFile(path, []byte(`{"SomeOther": "field"}`), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := loadConfigFile(path)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
// Database fields should be zero-values
|
|
if cfg.Database.Host != "" {
|
|
t.Errorf("expected empty Host, got %q", cfg.Database.Host)
|
|
}
|
|
if cfg.Database.Port != 0 {
|
|
t.Errorf("expected Port 0, got %d", cfg.Database.Port)
|
|
}
|
|
if cfg.Database.Password != "" {
|
|
t.Errorf("expected empty Password, got %q", cfg.Database.Password)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_PartialDatabase(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "config.json")
|
|
content := `{"Database": {"Host": "partial", "Port": 9999}}`
|
|
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := loadConfigFile(path)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg.Database.Host != "partial" {
|
|
t.Errorf("Host = %q, want %q", cfg.Database.Host, "partial")
|
|
}
|
|
if cfg.Database.Port != 9999 {
|
|
t.Errorf("Port = %d, want %d", cfg.Database.Port, 9999)
|
|
}
|
|
if cfg.Database.User != "" {
|
|
t.Errorf("User = %q, want empty", cfg.Database.User)
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// findConfigFile
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestFindConfigFile_NotFound(t *testing.T) {
|
|
// Run in a temp directory where no config.json exists.
|
|
// We save and restore the working directory so other tests are not affected.
|
|
origDir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
dir := t.TempDir()
|
|
if err := os.Chdir(dir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
result := findConfigFile()
|
|
if result != "" {
|
|
t.Errorf("expected empty string when no config.json exists, got %q", result)
|
|
}
|
|
}
|
|
|
|
func TestFindConfigFile_InCurrentDir(t *testing.T) {
|
|
origDir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
dir := t.TempDir()
|
|
configPath := filepath.Join(dir, "config.json")
|
|
if err := os.WriteFile(configPath, []byte(`{}`), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := os.Chdir(dir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
result := findConfigFile()
|
|
if result == "" {
|
|
t.Error("expected findConfigFile to find config.json in current directory")
|
|
}
|
|
}
|
|
|
|
func TestFindConfigFile_TwoLevelsUp(t *testing.T) {
|
|
origDir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
// Simulate tools/usercheck/ structure: config.json is ../../config.json
|
|
dir := t.TempDir()
|
|
subDir := filepath.Join(dir, "tools", "usercheck")
|
|
if err := os.MkdirAll(subDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
configPath := filepath.Join(dir, "config.json")
|
|
if err := os.WriteFile(configPath, []byte(`{}`), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := os.Chdir(subDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
result := findConfigFile()
|
|
if result == "" {
|
|
t.Error("expected findConfigFile to find config.json two levels up")
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// addDBFlags
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestAddDBFlags_RegistersAllFlags(t *testing.T) {
|
|
fs := flag.NewFlagSet("test", flag.ContinueOnError)
|
|
cfg := &DBConfig{}
|
|
addDBFlags(fs, cfg)
|
|
|
|
expectedFlags := []string{"config", "host", "port", "user", "password", "dbname"}
|
|
for _, name := range expectedFlags {
|
|
f := fs.Lookup(name)
|
|
if f == nil {
|
|
t.Errorf("expected flag %q to be registered, but it was not found", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAddDBFlags_ParseFlags(t *testing.T) {
|
|
fs := flag.NewFlagSet("test", flag.ContinueOnError)
|
|
cfg := &DBConfig{}
|
|
addDBFlags(fs, cfg)
|
|
|
|
args := []string{
|
|
"-host", "dbhost",
|
|
"-port", "6543",
|
|
"-user", "dbuser",
|
|
"-password", "dbpass",
|
|
"-dbname", "testdb",
|
|
"-config", "/some/path.json",
|
|
}
|
|
if err := fs.Parse(args); err != nil {
|
|
t.Fatalf("unexpected parse error: %v", err)
|
|
}
|
|
|
|
if cfg.Host != "dbhost" {
|
|
t.Errorf("Host = %q, want %q", cfg.Host, "dbhost")
|
|
}
|
|
if cfg.Port != 6543 {
|
|
t.Errorf("Port = %d, want %d", cfg.Port, 6543)
|
|
}
|
|
if cfg.User != "dbuser" {
|
|
t.Errorf("User = %q, want %q", cfg.User, "dbuser")
|
|
}
|
|
if cfg.Password != "dbpass" {
|
|
t.Errorf("Password = %q, want %q", cfg.Password, "dbpass")
|
|
}
|
|
if cfg.DBName != "testdb" {
|
|
t.Errorf("DBName = %q, want %q", cfg.DBName, "testdb")
|
|
}
|
|
if cfg.ConfigPath != "/some/path.json" {
|
|
t.Errorf("ConfigPath = %q, want %q", cfg.ConfigPath, "/some/path.json")
|
|
}
|
|
}
|
|
|
|
func TestAddDBFlags_DefaultValues(t *testing.T) {
|
|
fs := flag.NewFlagSet("test", flag.ContinueOnError)
|
|
cfg := &DBConfig{}
|
|
addDBFlags(fs, cfg)
|
|
|
|
// Parse with no arguments
|
|
if err := fs.Parse(nil); err != nil {
|
|
t.Fatalf("unexpected parse error: %v", err)
|
|
}
|
|
|
|
// All fields should be zero values (defaults from flag package)
|
|
if cfg.Host != "" {
|
|
t.Errorf("Host = %q, want empty", cfg.Host)
|
|
}
|
|
if cfg.Port != 0 {
|
|
t.Errorf("Port = %d, want 0", cfg.Port)
|
|
}
|
|
if cfg.User != "" {
|
|
t.Errorf("User = %q, want empty", cfg.User)
|
|
}
|
|
if cfg.Password != "" {
|
|
t.Errorf("Password = %q, want empty", cfg.Password)
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// resolveDBConfig
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestResolveDBConfig_AllPreset(t *testing.T) {
|
|
// Clear environment variables that could interfere
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_USER")
|
|
os.Unsetenv("ERUPE_DB_PASSWORD")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
cfg := &DBConfig{
|
|
Host: "myhost",
|
|
Port: 5555,
|
|
User: "myuser",
|
|
Password: "mypass",
|
|
DBName: "mydb",
|
|
}
|
|
|
|
if err := resolveDBConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if cfg.Host != "myhost" {
|
|
t.Errorf("Host = %q, want %q", cfg.Host, "myhost")
|
|
}
|
|
if cfg.Port != 5555 {
|
|
t.Errorf("Port = %d, want %d", cfg.Port, 5555)
|
|
}
|
|
if cfg.User != "myuser" {
|
|
t.Errorf("User = %q, want %q", cfg.User, "myuser")
|
|
}
|
|
if cfg.Password != "mypass" {
|
|
t.Errorf("Password = %q, want %q", cfg.Password, "mypass")
|
|
}
|
|
if cfg.DBName != "mydb" {
|
|
t.Errorf("DBName = %q, want %q", cfg.DBName, "mydb")
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_MissingPassword(t *testing.T) {
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_USER")
|
|
os.Unsetenv("ERUPE_DB_PASSWORD")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
// Change to a temp dir so no config.json is found
|
|
origDir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
if err := os.Chdir(t.TempDir()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg := &DBConfig{}
|
|
err = resolveDBConfig(cfg)
|
|
if err == nil {
|
|
t.Fatal("expected error for missing password, got nil")
|
|
}
|
|
if got := err.Error(); got != "database password is required (set in config.json, use -password flag, or ERUPE_DB_PASSWORD env var)" {
|
|
t.Errorf("unexpected error message: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_PasswordFromEnv(t *testing.T) {
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_USER")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
os.Setenv("ERUPE_DB_PASSWORD", "envpass")
|
|
|
|
// Change to a temp dir so no config.json is found
|
|
origDir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
if err := os.Chdir(t.TempDir()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg := &DBConfig{}
|
|
if err := resolveDBConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg.Password != "envpass" {
|
|
t.Errorf("Password = %q, want %q", cfg.Password, "envpass")
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_HostFromEnv(t *testing.T) {
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_USER")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
os.Setenv("ERUPE_DB_HOST", "envhost")
|
|
os.Setenv("ERUPE_DB_PASSWORD", "envpass")
|
|
|
|
origDir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
if err := os.Chdir(t.TempDir()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg := &DBConfig{}
|
|
if err := resolveDBConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg.Host != "envhost" {
|
|
t.Errorf("Host = %q, want %q", cfg.Host, "envhost")
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_UserFromEnv(t *testing.T) {
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
os.Setenv("ERUPE_DB_USER", "envuser")
|
|
os.Setenv("ERUPE_DB_PASSWORD", "envpass")
|
|
|
|
origDir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
if err := os.Chdir(t.TempDir()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg := &DBConfig{}
|
|
if err := resolveDBConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg.User != "envuser" {
|
|
t.Errorf("User = %q, want %q", cfg.User, "envuser")
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_DBNameFromEnv(t *testing.T) {
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_USER")
|
|
|
|
os.Setenv("ERUPE_DB_PASSWORD", "envpass")
|
|
os.Setenv("ERUPE_DB_NAME", "envdb")
|
|
|
|
origDir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
if err := os.Chdir(t.TempDir()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg := &DBConfig{}
|
|
if err := resolveDBConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg.DBName != "envdb" {
|
|
t.Errorf("DBName = %q, want %q", cfg.DBName, "envdb")
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_DefaultsApplied(t *testing.T) {
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_USER")
|
|
os.Unsetenv("ERUPE_DB_PASSWORD")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
// Change to a temp dir so no config.json is found
|
|
origDir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
if err := os.Chdir(t.TempDir()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg := &DBConfig{Password: "provided"}
|
|
if err := resolveDBConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if cfg.Host != "localhost" {
|
|
t.Errorf("Host = %q, want %q", cfg.Host, "localhost")
|
|
}
|
|
if cfg.Port != 5432 {
|
|
t.Errorf("Port = %d, want %d", cfg.Port, 5432)
|
|
}
|
|
if cfg.User != "postgres" {
|
|
t.Errorf("User = %q, want %q", cfg.User, "postgres")
|
|
}
|
|
if cfg.DBName != "erupe" {
|
|
t.Errorf("DBName = %q, want %q", cfg.DBName, "erupe")
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_ConfigFileOverrides(t *testing.T) {
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_USER")
|
|
os.Unsetenv("ERUPE_DB_PASSWORD")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
dir := t.TempDir()
|
|
configPath := filepath.Join(dir, "config.json")
|
|
content := `{
|
|
"Database": {
|
|
"Host": "filehost",
|
|
"Port": 7777,
|
|
"User": "fileuser",
|
|
"Password": "filepass",
|
|
"Database": "filedb"
|
|
}
|
|
}`
|
|
if err := os.WriteFile(configPath, []byte(content), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg := &DBConfig{ConfigPath: configPath}
|
|
if err := resolveDBConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if cfg.Host != "filehost" {
|
|
t.Errorf("Host = %q, want %q", cfg.Host, "filehost")
|
|
}
|
|
if cfg.Port != 7777 {
|
|
t.Errorf("Port = %d, want %d", cfg.Port, 7777)
|
|
}
|
|
if cfg.User != "fileuser" {
|
|
t.Errorf("User = %q, want %q", cfg.User, "fileuser")
|
|
}
|
|
if cfg.Password != "filepass" {
|
|
t.Errorf("Password = %q, want %q", cfg.Password, "filepass")
|
|
}
|
|
if cfg.DBName != "filedb" {
|
|
t.Errorf("DBName = %q, want %q", cfg.DBName, "filedb")
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_FlagsOverrideConfigFile(t *testing.T) {
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_USER")
|
|
os.Unsetenv("ERUPE_DB_PASSWORD")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
dir := t.TempDir()
|
|
configPath := filepath.Join(dir, "config.json")
|
|
content := `{
|
|
"Database": {
|
|
"Host": "filehost",
|
|
"Port": 7777,
|
|
"User": "fileuser",
|
|
"Password": "filepass",
|
|
"Database": "filedb"
|
|
}
|
|
}`
|
|
if err := os.WriteFile(configPath, []byte(content), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// CLI flags take priority since they're already set in cfg
|
|
cfg := &DBConfig{
|
|
ConfigPath: configPath,
|
|
Host: "clihost",
|
|
Port: 8888,
|
|
User: "cliuser",
|
|
Password: "clipass",
|
|
DBName: "clidb",
|
|
}
|
|
if err := resolveDBConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if cfg.Host != "clihost" {
|
|
t.Errorf("Host = %q, want %q", cfg.Host, "clihost")
|
|
}
|
|
if cfg.Port != 8888 {
|
|
t.Errorf("Port = %d, want %d", cfg.Port, 8888)
|
|
}
|
|
if cfg.User != "cliuser" {
|
|
t.Errorf("User = %q, want %q", cfg.User, "cliuser")
|
|
}
|
|
if cfg.Password != "clipass" {
|
|
t.Errorf("Password = %q, want %q", cfg.Password, "clipass")
|
|
}
|
|
if cfg.DBName != "clidb" {
|
|
t.Errorf("DBName = %q, want %q", cfg.DBName, "clidb")
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_ExplicitConfigPathInvalid(t *testing.T) {
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_USER")
|
|
os.Unsetenv("ERUPE_DB_PASSWORD")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
cfg := &DBConfig{
|
|
ConfigPath: "/nonexistent/path/config.json",
|
|
Password: "pass",
|
|
}
|
|
err := resolveDBConfig(cfg)
|
|
if err == nil {
|
|
t.Fatal("expected error when explicitly specifying non-existent config path")
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_AutoDetectedConfigPathInvalid(t *testing.T) {
|
|
// When config.json is found by findConfigFile but is invalid JSON,
|
|
// resolveDBConfig should silently ignore it (because user didn't explicitly specify it)
|
|
origDir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_USER")
|
|
os.Unsetenv("ERUPE_DB_PASSWORD")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
dir := t.TempDir()
|
|
// Write a broken config.json
|
|
if err := os.WriteFile(filepath.Join(dir, "config.json"), []byte("BROKEN"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Chdir(dir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg := &DBConfig{Password: "pass"}
|
|
// Should not error -- broken auto-detected config is silently ignored
|
|
if err := resolveDBConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Defaults should be applied
|
|
if cfg.Host != "localhost" {
|
|
t.Errorf("Host = %q, want %q", cfg.Host, "localhost")
|
|
}
|
|
if cfg.Port != 5432 {
|
|
t.Errorf("Port = %d, want %d", cfg.Port, 5432)
|
|
}
|
|
}
|
|
|
|
func TestResolveDBConfig_EnvOverridesConfig(t *testing.T) {
|
|
origHost := os.Getenv("ERUPE_DB_HOST")
|
|
origUser := os.Getenv("ERUPE_DB_USER")
|
|
origPass := os.Getenv("ERUPE_DB_PASSWORD")
|
|
origName := os.Getenv("ERUPE_DB_NAME")
|
|
defer func() {
|
|
os.Setenv("ERUPE_DB_HOST", origHost)
|
|
os.Setenv("ERUPE_DB_USER", origUser)
|
|
os.Setenv("ERUPE_DB_PASSWORD", origPass)
|
|
os.Setenv("ERUPE_DB_NAME", origName)
|
|
}()
|
|
|
|
// Config file provides some values, env provides others
|
|
dir := t.TempDir()
|
|
configPath := filepath.Join(dir, "config.json")
|
|
content := `{"Database": {"Host": "filehost", "Port": 7777}}`
|
|
if err := os.WriteFile(configPath, []byte(content), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Env provides password and user, which config file doesn't set
|
|
os.Setenv("ERUPE_DB_PASSWORD", "envpass")
|
|
os.Setenv("ERUPE_DB_USER", "envuser")
|
|
os.Unsetenv("ERUPE_DB_HOST")
|
|
os.Unsetenv("ERUPE_DB_NAME")
|
|
|
|
cfg := &DBConfig{ConfigPath: configPath}
|
|
if err := resolveDBConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Host comes from config file
|
|
if cfg.Host != "filehost" {
|
|
t.Errorf("Host = %q, want %q", cfg.Host, "filehost")
|
|
}
|
|
// User comes from env (config file didn't set it)
|
|
if cfg.User != "envuser" {
|
|
t.Errorf("User = %q, want %q", cfg.User, "envuser")
|
|
}
|
|
// Password comes from env
|
|
if cfg.Password != "envpass" {
|
|
t.Errorf("Password = %q, want %q", cfg.Password, "envpass")
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Struct types construction
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestConnectedUser_Construction(t *testing.T) {
|
|
now := time.Now()
|
|
u := ConnectedUser{
|
|
CharID: 42,
|
|
CharName: "Hunter",
|
|
ServerID: 1,
|
|
ServerName: "World1",
|
|
UserID: 10,
|
|
Username: "player1",
|
|
LastLogin: sql.NullTime{Time: now, Valid: true},
|
|
HR: 999,
|
|
GR: 50,
|
|
}
|
|
|
|
if u.CharID != 42 {
|
|
t.Errorf("CharID = %d, want 42", u.CharID)
|
|
}
|
|
if u.CharName != "Hunter" {
|
|
t.Errorf("CharName = %q, want %q", u.CharName, "Hunter")
|
|
}
|
|
if u.ServerID != 1 {
|
|
t.Errorf("ServerID = %d, want 1", u.ServerID)
|
|
}
|
|
if u.ServerName != "World1" {
|
|
t.Errorf("ServerName = %q, want %q", u.ServerName, "World1")
|
|
}
|
|
if u.UserID != 10 {
|
|
t.Errorf("UserID = %d, want 10", u.UserID)
|
|
}
|
|
if u.Username != "player1" {
|
|
t.Errorf("Username = %q, want %q", u.Username, "player1")
|
|
}
|
|
if !u.LastLogin.Valid {
|
|
t.Error("LastLogin.Valid = false, want true")
|
|
}
|
|
if u.HR != 999 {
|
|
t.Errorf("HR = %d, want 999", u.HR)
|
|
}
|
|
if u.GR != 50 {
|
|
t.Errorf("GR = %d, want 50", u.GR)
|
|
}
|
|
}
|
|
|
|
func TestConnectedUser_NullLastLogin(t *testing.T) {
|
|
u := ConnectedUser{
|
|
CharID: 1,
|
|
CharName: "Test",
|
|
}
|
|
if u.LastLogin.Valid {
|
|
t.Error("LastLogin.Valid = true, want false for zero value")
|
|
}
|
|
}
|
|
|
|
func TestServerStatus_Construction(t *testing.T) {
|
|
s := ServerStatus{
|
|
ServerID: 5,
|
|
WorldName: "Frontier",
|
|
WorldDesc: "A great server",
|
|
Land: 2,
|
|
CurrentPlayers: 100,
|
|
Season: 1,
|
|
}
|
|
|
|
if s.ServerID != 5 {
|
|
t.Errorf("ServerID = %d, want 5", s.ServerID)
|
|
}
|
|
if s.WorldName != "Frontier" {
|
|
t.Errorf("WorldName = %q, want %q", s.WorldName, "Frontier")
|
|
}
|
|
if s.WorldDesc != "A great server" {
|
|
t.Errorf("WorldDesc = %q, want %q", s.WorldDesc, "A great server")
|
|
}
|
|
if s.Land != 2 {
|
|
t.Errorf("Land = %d, want 2", s.Land)
|
|
}
|
|
if s.CurrentPlayers != 100 {
|
|
t.Errorf("CurrentPlayers = %d, want 100", s.CurrentPlayers)
|
|
}
|
|
if s.Season != 1 {
|
|
t.Errorf("Season = %d, want 1", s.Season)
|
|
}
|
|
}
|
|
|
|
func TestLoginHistory_Construction(t *testing.T) {
|
|
now := time.Now()
|
|
h := LoginHistory{
|
|
CharID: 7,
|
|
CharName: "Veteran",
|
|
LastLogin: sql.NullTime{Time: now, Valid: true},
|
|
HR: 500,
|
|
GR: 25,
|
|
Username: "vet_player",
|
|
}
|
|
|
|
if h.CharID != 7 {
|
|
t.Errorf("CharID = %d, want 7", h.CharID)
|
|
}
|
|
if h.CharName != "Veteran" {
|
|
t.Errorf("CharName = %q, want %q", h.CharName, "Veteran")
|
|
}
|
|
if !h.LastLogin.Valid {
|
|
t.Error("LastLogin.Valid = false, want true")
|
|
}
|
|
if h.HR != 500 {
|
|
t.Errorf("HR = %d, want 500", h.HR)
|
|
}
|
|
if h.GR != 25 {
|
|
t.Errorf("GR = %d, want 25", h.GR)
|
|
}
|
|
if h.Username != "vet_player" {
|
|
t.Errorf("Username = %q, want %q", h.Username, "vet_player")
|
|
}
|
|
}
|
|
|
|
func TestLoginHistory_NullLastLogin(t *testing.T) {
|
|
h := LoginHistory{
|
|
CharID: 1,
|
|
CharName: "NewPlayer",
|
|
}
|
|
if h.LastLogin.Valid {
|
|
t.Error("LastLogin.Valid = true, want false for zero value")
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DBConfig and ErupeConfig struct construction
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestDBConfig_ZeroValue(t *testing.T) {
|
|
cfg := DBConfig{}
|
|
if cfg.Host != "" {
|
|
t.Errorf("Host = %q, want empty", cfg.Host)
|
|
}
|
|
if cfg.Port != 0 {
|
|
t.Errorf("Port = %d, want 0", cfg.Port)
|
|
}
|
|
if cfg.User != "" {
|
|
t.Errorf("User = %q, want empty", cfg.User)
|
|
}
|
|
if cfg.Password != "" {
|
|
t.Errorf("Password = %q, want empty", cfg.Password)
|
|
}
|
|
if cfg.DBName != "" {
|
|
t.Errorf("DBName = %q, want empty", cfg.DBName)
|
|
}
|
|
if cfg.ConfigPath != "" {
|
|
t.Errorf("ConfigPath = %q, want empty", cfg.ConfigPath)
|
|
}
|
|
}
|
|
|
|
func TestErupeConfig_ZeroValue(t *testing.T) {
|
|
cfg := ErupeConfig{}
|
|
if cfg.Database.Host != "" {
|
|
t.Errorf("Database.Host = %q, want empty", cfg.Database.Host)
|
|
}
|
|
if cfg.Database.Port != 0 {
|
|
t.Errorf("Database.Port = %d, want 0", cfg.Database.Port)
|
|
}
|
|
}
|