test: improve test coverage for mhfpacket, channelserver, and server packages

Add comprehensive tests for:
- Pure time functions in channelserver (sys_time_test.go)
- Stage-related packet parsing (msg_sys_stage_test.go)
- Acquire packet family parsing (msg_mhf_acquire_test.go)
- Extended mhfpacket tests for login, logout, and stage packets
- Entrance server makeHeader structure and checksum tests
- SignV2 server request/response JSON structure tests
This commit is contained in:
Houmgaor
2026-02-01 23:28:19 +01:00
parent f83761c0b1
commit db3e0bccc7
6 changed files with 1715 additions and 0 deletions

View File

@@ -347,3 +347,374 @@ func TestServerConfig(t *testing.T) {
t.Error("Config.Logger should be nil when not set")
}
}
// Note: Tests that require database operations are skipped when no DB is available.
// The following tests validate the structure and JSON handling of endpoints.
// TestLoginRequestStructure tests that login request JSON structure is correct
func TestLoginRequestStructure(t *testing.T) {
// Test JSON marshaling/unmarshaling of request structure
reqData := struct {
Username string `json:"username"`
Password string `json:"password"`
}{
Username: "testuser",
Password: "testpass",
}
data, err := json.Marshal(reqData)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
var decoded struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
if decoded.Username != reqData.Username {
t.Errorf("Username = %s, want %s", decoded.Username, reqData.Username)
}
if decoded.Password != reqData.Password {
t.Errorf("Password = %s, want %s", decoded.Password, reqData.Password)
}
}
// TestRegisterRequestStructure tests that register request JSON structure is correct
func TestRegisterRequestStructure(t *testing.T) {
reqData := struct {
Username string `json:"username"`
Password string `json:"password"`
}{
Username: "newuser",
Password: "newpass",
}
data, err := json.Marshal(reqData)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
var decoded struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
if decoded.Username != reqData.Username {
t.Errorf("Username = %s, want %s", decoded.Username, reqData.Username)
}
if decoded.Password != reqData.Password {
t.Errorf("Password = %s, want %s", decoded.Password, reqData.Password)
}
}
// TestCreateCharacterRequestStructure tests that create character request JSON structure is correct
func TestCreateCharacterRequestStructure(t *testing.T) {
reqData := struct {
Token string `json:"token"`
}{
Token: "test-token-12345",
}
data, err := json.Marshal(reqData)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
var decoded struct {
Token string `json:"token"`
}
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
if decoded.Token != reqData.Token {
t.Errorf("Token = %s, want %s", decoded.Token, reqData.Token)
}
}
// TestDeleteCharacterRequestStructure tests that delete character request JSON structure is correct
func TestDeleteCharacterRequestStructure(t *testing.T) {
reqData := struct {
Token string `json:"token"`
CharID int `json:"id"`
}{
Token: "test-token",
CharID: 12345,
}
data, err := json.Marshal(reqData)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
var decoded struct {
Token string `json:"token"`
CharID int `json:"id"`
}
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
if decoded.Token != reqData.Token {
t.Errorf("Token = %s, want %s", decoded.Token, reqData.Token)
}
if decoded.CharID != reqData.CharID {
t.Errorf("CharID = %d, want %d", decoded.CharID, reqData.CharID)
}
}
// TestLoginResponseStructure tests the login response JSON structure
func TestLoginResponseStructure(t *testing.T) {
respData := struct {
Token string `json:"token"`
Characters []Character `json:"characters"`
}{
Token: "login-token-abc123",
Characters: []Character{
{ID: 1, Name: "Hunter1", IsFemale: false, Weapon: 3, HR: 100, GR: 10},
{ID: 2, Name: "Hunter2", IsFemale: true, Weapon: 7, HR: 200, GR: 20},
},
}
data, err := json.Marshal(respData)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
var decoded struct {
Token string `json:"token"`
Characters []Character `json:"characters"`
}
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
if decoded.Token != respData.Token {
t.Errorf("Token = %s, want %s", decoded.Token, respData.Token)
}
if len(decoded.Characters) != len(respData.Characters) {
t.Errorf("Characters count = %d, want %d", len(decoded.Characters), len(respData.Characters))
}
}
// TestRegisterResponseStructure tests the register response JSON structure
func TestRegisterResponseStructure(t *testing.T) {
respData := struct {
Token string `json:"token"`
}{
Token: "register-token-xyz789",
}
data, err := json.Marshal(respData)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
var decoded struct {
Token string `json:"token"`
}
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
if decoded.Token != respData.Token {
t.Errorf("Token = %s, want %s", decoded.Token, respData.Token)
}
}
// TestCreateCharacterResponseStructure tests the create character response JSON structure
func TestCreateCharacterResponseStructure(t *testing.T) {
respData := struct {
CharID int `json:"id"`
}{
CharID: 42,
}
data, err := json.Marshal(respData)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
var decoded struct {
CharID int `json:"id"`
}
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
if decoded.CharID != respData.CharID {
t.Errorf("CharID = %d, want %d", decoded.CharID, respData.CharID)
}
}
// TestLauncherContentType tests that Launcher sets correct content type
func TestLauncherContentType(t *testing.T) {
s := mockServer()
req := httptest.NewRequest("GET", "/launcher", nil)
w := httptest.NewRecorder()
s.Launcher(w, req)
// Note: The handler sets header after WriteHeader, so we check response body is JSON
resp := w.Result()
var data map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
t.Errorf("Launcher() response is not valid JSON: %v", err)
}
}
// TestLauncherMessageDates tests that launcher message dates are valid timestamps
func TestLauncherMessageDates(t *testing.T) {
s := mockServer()
req := httptest.NewRequest("GET", "/launcher", nil)
w := httptest.NewRecorder()
s.Launcher(w, req)
var data struct {
Important []LauncherMessage `json:"important"`
Normal []LauncherMessage `json:"normal"`
}
json.NewDecoder(w.Result().Body).Decode(&data)
// All dates should be positive unix timestamps
for _, msg := range data.Important {
if msg.Date <= 0 {
t.Errorf("Important message date should be positive, got %d", msg.Date)
}
}
for _, msg := range data.Normal {
if msg.Date <= 0 {
t.Errorf("Normal message date should be positive, got %d", msg.Date)
}
}
}
// TestLauncherMessageLinks tests that launcher message links are valid URLs
func TestLauncherMessageLinks(t *testing.T) {
s := mockServer()
req := httptest.NewRequest("GET", "/launcher", nil)
w := httptest.NewRecorder()
s.Launcher(w, req)
var data struct {
Important []LauncherMessage `json:"important"`
Normal []LauncherMessage `json:"normal"`
}
json.NewDecoder(w.Result().Body).Decode(&data)
// All links should start with http:// or https://
for _, msg := range data.Important {
if len(msg.Link) < 7 || (msg.Link[:7] != "http://" && msg.Link[:8] != "https://") {
t.Errorf("Important message link should be a URL, got %q", msg.Link)
}
}
for _, msg := range data.Normal {
if len(msg.Link) < 7 || (msg.Link[:7] != "http://" && msg.Link[:8] != "https://") {
t.Errorf("Normal message link should be a URL, got %q", msg.Link)
}
}
}
// TestCharacterStructJSONMarshal tests Character struct marshals correctly
func TestCharacterStructJSONMarshal(t *testing.T) {
char := Character{
ID: 42,
Name: "TestHunter",
IsFemale: true,
Weapon: 7,
HR: 999,
GR: 100,
LastLogin: 1609459200,
}
data, err := json.Marshal(char)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
var decoded Character
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
if decoded.ID != char.ID {
t.Errorf("ID = %d, want %d", decoded.ID, char.ID)
}
if decoded.Name != char.Name {
t.Errorf("Name = %s, want %s", decoded.Name, char.Name)
}
if decoded.IsFemale != char.IsFemale {
t.Errorf("IsFemale = %v, want %v", decoded.IsFemale, char.IsFemale)
}
if decoded.Weapon != char.Weapon {
t.Errorf("Weapon = %d, want %d", decoded.Weapon, char.Weapon)
}
if decoded.HR != char.HR {
t.Errorf("HR = %d, want %d", decoded.HR, char.HR)
}
if decoded.GR != char.GR {
t.Errorf("GR = %d, want %d", decoded.GR, char.GR)
}
if decoded.LastLogin != char.LastLogin {
t.Errorf("LastLogin = %d, want %d", decoded.LastLogin, char.LastLogin)
}
}
// TestLauncherMessageJSONMarshal tests LauncherMessage struct marshals correctly
func TestLauncherMessageJSONMarshal(t *testing.T) {
msg := LauncherMessage{
Message: "Test Announcement",
Date: 1609459200,
Link: "https://example.com/news",
}
data, err := json.Marshal(msg)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
var decoded LauncherMessage
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
if decoded.Message != msg.Message {
t.Errorf("Message = %s, want %s", decoded.Message, msg.Message)
}
if decoded.Date != msg.Date {
t.Errorf("Date = %d, want %d", decoded.Date, msg.Date)
}
if decoded.Link != msg.Link {
t.Errorf("Link = %s, want %s", decoded.Link, msg.Link)
}
}
// TestEndpointHTTPMethods tests that endpoints respond to correct HTTP methods
func TestEndpointHTTPMethods(t *testing.T) {
s := mockServer()
// Launcher should respond to GET
t.Run("Launcher GET", func(t *testing.T) {
req := httptest.NewRequest("GET", "/launcher", nil)
w := httptest.NewRecorder()
s.Launcher(w, req)
if w.Result().StatusCode != http.StatusOK {
t.Errorf("Launcher() GET status = %d, want %d", w.Result().StatusCode, http.StatusOK)
}
})
// Note: Login, Register, CreateCharacter, DeleteCharacter require database
// and cannot be tested without mocking the database connection
}