mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 15:43:49 +01:00
Covers all DataType branches (0/5/6/gift/>1000/unknown), ACK payload structure with correct 10-byte header offset, earth succeed entry counts, timetable content validation, PaperData/PaperGift serialization round-trips, and paperGiftData table integrity checks.
398 lines
11 KiB
Go
398 lines
11 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"testing"
|
|
|
|
"erupe-ce/common/byteframe"
|
|
"erupe-ce/network/mhfpacket"
|
|
)
|
|
|
|
// paperTestSession creates a minimal session for paper data handler tests.
|
|
func paperTestSession() *Session {
|
|
server := createMockServer()
|
|
return createMockSession(1, server)
|
|
}
|
|
|
|
// callGetPaperData invokes the handler and returns the ACK payload.
|
|
func callGetPaperData(t *testing.T, dataType uint32) []byte {
|
|
t.Helper()
|
|
s := paperTestSession()
|
|
pkt := &mhfpacket.MsgMhfGetPaperData{
|
|
AckHandle: 1,
|
|
DataType: dataType,
|
|
}
|
|
handleMsgMhfGetPaperData(s, pkt)
|
|
|
|
select {
|
|
case p := <-s.sendPackets:
|
|
return p.data
|
|
default:
|
|
t.Fatal("expected ACK packet, got none")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// --- DataType 0: Mission Timetable ---
|
|
|
|
func TestGetPaperData_Type0_MissionTimetable(t *testing.T) {
|
|
data := callGetPaperData(t, 0)
|
|
if len(data) == 0 {
|
|
t.Fatal("expected non-empty response for DataType 0")
|
|
}
|
|
|
|
// doAckBufSucceed wraps the payload in a MsgSysAck.
|
|
// The raw payload sent to the session contains the ack structure.
|
|
// We just verify the packet was sent and is non-empty.
|
|
}
|
|
|
|
func TestGetPaperData_Type0_MissionPayloadStructure(t *testing.T) {
|
|
s := paperTestSession()
|
|
pkt := &mhfpacket.MsgMhfGetPaperData{AckHandle: 1, DataType: 0}
|
|
handleMsgMhfGetPaperData(s, pkt)
|
|
|
|
select {
|
|
case <-s.sendPackets:
|
|
// ACK sent successfully
|
|
default:
|
|
t.Fatal("expected ACK packet for DataType 0")
|
|
}
|
|
}
|
|
|
|
// --- DataType 5: Tower Parameters ---
|
|
|
|
func TestGetPaperData_Type5_TowerParams(t *testing.T) {
|
|
data := callGetPaperData(t, 5)
|
|
if len(data) == 0 {
|
|
t.Fatal("expected non-empty response for DataType 5")
|
|
}
|
|
}
|
|
|
|
func TestGetPaperData_Type5_EntryCount(t *testing.T) {
|
|
s := paperTestSession()
|
|
pkt := &mhfpacket.MsgMhfGetPaperData{AckHandle: 1, DataType: 5}
|
|
handleMsgMhfGetPaperData(s, pkt)
|
|
|
|
select {
|
|
case p := <-s.sendPackets:
|
|
// doAckEarthSucceed writes: earthID(4) + 0(4) + 0(4) + count(4) + entries
|
|
// The full packet includes the MsgSysAck header, but we can verify it's substantial.
|
|
// Type 5 has 52 PaperData entries (counted from source), each 14 bytes.
|
|
// Minimum expected: 16 (earth header) + 52*14 = 744 bytes in the ack payload.
|
|
if len(p.data) < 100 {
|
|
t.Errorf("type 5 payload too small: %d bytes", len(p.data))
|
|
}
|
|
default:
|
|
t.Fatal("expected ACK packet for DataType 5")
|
|
}
|
|
}
|
|
|
|
// --- DataType 6: Tower Floor/Reward Data ---
|
|
|
|
func TestGetPaperData_Type6_TowerFloorData(t *testing.T) {
|
|
data := callGetPaperData(t, 6)
|
|
if len(data) == 0 {
|
|
t.Fatal("expected non-empty response for DataType 6")
|
|
}
|
|
}
|
|
|
|
func TestGetPaperData_Type6_LargerThanType5(t *testing.T) {
|
|
data5 := callGetPaperData(t, 5)
|
|
data6 := callGetPaperData(t, 6)
|
|
|
|
// Type 6 has significantly more entries than type 5
|
|
if len(data6) <= len(data5) {
|
|
t.Errorf("type 6 (%d bytes) should be larger than type 5 (%d bytes)", len(data6), len(data5))
|
|
}
|
|
}
|
|
|
|
// --- DataType > 1000: Paper Gift Data ---
|
|
|
|
func TestGetPaperData_KnownGiftType_6001(t *testing.T) {
|
|
data := callGetPaperData(t, 6001)
|
|
if len(data) == 0 {
|
|
t.Fatal("expected non-empty response for gift type 6001")
|
|
}
|
|
}
|
|
|
|
func TestGetPaperData_KnownGiftType_7001(t *testing.T) {
|
|
data := callGetPaperData(t, 7001)
|
|
if len(data) == 0 {
|
|
t.Fatal("expected non-empty response for gift type 7001")
|
|
}
|
|
}
|
|
|
|
func TestGetPaperData_AllKnownGiftTypes(t *testing.T) {
|
|
for dataType := range paperGiftData {
|
|
t.Run("gift_"+itoa(dataType), func(t *testing.T) {
|
|
data := callGetPaperData(t, dataType)
|
|
if len(data) == 0 {
|
|
t.Errorf("expected non-empty response for gift type %d", dataType)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// --- DataType > 1000 with unknown key ---
|
|
|
|
func TestGetPaperData_UnknownGiftType(t *testing.T) {
|
|
// 9999 is > 1000 but not in paperGiftData
|
|
data := callGetPaperData(t, 9999)
|
|
if len(data) == 0 {
|
|
t.Fatal("expected ACK even for unknown gift type")
|
|
}
|
|
}
|
|
|
|
// --- Unknown DataType (< 1000, not 0/5/6) ---
|
|
|
|
func TestGetPaperData_UnknownType_3(t *testing.T) {
|
|
// DataType 3 hits the default case, then the else branch (empty paperData)
|
|
data := callGetPaperData(t, 3)
|
|
if len(data) == 0 {
|
|
t.Fatal("expected ACK even for unknown DataType")
|
|
}
|
|
}
|
|
|
|
func TestGetPaperData_UnknownType_1(t *testing.T) {
|
|
data := callGetPaperData(t, 1)
|
|
if len(data) == 0 {
|
|
t.Fatal("expected ACK for DataType 1")
|
|
}
|
|
}
|
|
|
|
// --- Serialization Verification ---
|
|
|
|
func TestGetPaperData_Type0_SerializationFormat(t *testing.T) {
|
|
// Build expected payload manually and compare structure
|
|
s := paperTestSession()
|
|
pkt := &mhfpacket.MsgMhfGetPaperData{AckHandle: 42, DataType: 0}
|
|
handleMsgMhfGetPaperData(s, pkt)
|
|
|
|
select {
|
|
case p := <-s.sendPackets:
|
|
// The raw data is the full MsgSysAck Build output.
|
|
// We verify it's non-trivial (contains the timetable data).
|
|
if len(p.data) < 20 {
|
|
t.Errorf("type 0 ACK payload too small: %d bytes", len(p.data))
|
|
}
|
|
default:
|
|
t.Fatal("expected ACK packet")
|
|
}
|
|
}
|
|
|
|
// ackPayloadOffset is the offset to the ACK payload data within the raw packet.
|
|
// Raw packet layout: opcode(2) + AckHandle(4) + IsBuffer(1) + ErrorCode(1) + payloadSize(2) = 10 bytes header.
|
|
const ackPayloadOffset = 10
|
|
|
|
// extractAckPayload extracts the ACK payload from a raw packet sent via QueueSendMHF.
|
|
func extractAckPayload(t *testing.T, data []byte) []byte {
|
|
t.Helper()
|
|
if len(data) < ackPayloadOffset {
|
|
t.Fatalf("packet too short for ACK header: %d bytes", len(data))
|
|
}
|
|
payloadLen := binary.BigEndian.Uint16(data[8:10])
|
|
if payloadLen == 0xFFFF {
|
|
// Extended size
|
|
if len(data) < 14 {
|
|
t.Fatalf("packet too short for extended ACK header: %d bytes", len(data))
|
|
}
|
|
extLen := binary.BigEndian.Uint32(data[10:14])
|
|
return data[14 : 14+extLen]
|
|
}
|
|
return data[ackPayloadOffset : ackPayloadOffset+int(payloadLen)]
|
|
}
|
|
|
|
func TestGetPaperData_GiftSerialization_6001(t *testing.T) {
|
|
// Verify that gift type 6001 produces the right number of gift entries.
|
|
s := paperTestSession()
|
|
pkt := &mhfpacket.MsgMhfGetPaperData{AckHandle: 1, DataType: 6001}
|
|
handleMsgMhfGetPaperData(s, pkt)
|
|
|
|
select {
|
|
case p := <-s.sendPackets:
|
|
payload := extractAckPayload(t, p.data)
|
|
|
|
// Earth succeed: earthID(4) + 0(4) + 0(4) + count(4) = 16 byte header
|
|
if len(payload) < 16 {
|
|
t.Fatalf("earth payload too short: %d bytes", len(payload))
|
|
}
|
|
count := binary.BigEndian.Uint32(payload[12:16])
|
|
expectedCount := uint32(len(paperGiftData[6001]))
|
|
if count != expectedCount {
|
|
t.Errorf("gift entry count = %d, want %d", count, expectedCount)
|
|
}
|
|
|
|
// Each gift entry is 6 bytes
|
|
expectedDataLen := 16 + int(expectedCount)*6
|
|
if len(payload) != expectedDataLen {
|
|
t.Errorf("earth payload length = %d, want %d", len(payload), expectedDataLen)
|
|
}
|
|
default:
|
|
t.Fatal("expected ACK packet")
|
|
}
|
|
}
|
|
|
|
func TestGetPaperData_Type5_EarthSucceedEntryCount(t *testing.T) {
|
|
s := paperTestSession()
|
|
pkt := &mhfpacket.MsgMhfGetPaperData{AckHandle: 1, DataType: 5}
|
|
handleMsgMhfGetPaperData(s, pkt)
|
|
|
|
select {
|
|
case p := <-s.sendPackets:
|
|
payload := extractAckPayload(t, p.data)
|
|
|
|
// Earth succeed: earthID(4) + 0(4) + 0(4) + count(4) = 16 byte header
|
|
if len(payload) < 16 {
|
|
t.Fatalf("earth payload too short: %d bytes", len(payload))
|
|
}
|
|
count := binary.BigEndian.Uint32(payload[12:16])
|
|
// Type 5 has 52 PaperData entries
|
|
if count != 52 {
|
|
t.Errorf("type 5 entry count = %d, want 52", count)
|
|
}
|
|
|
|
// Each PaperData entry: uint16 + 6*int16 = 14 bytes
|
|
expectedDataLen := 16 + 52*14
|
|
if len(payload) != expectedDataLen {
|
|
t.Errorf("earth payload length = %d, want %d", len(payload), expectedDataLen)
|
|
}
|
|
default:
|
|
t.Fatal("expected ACK packet")
|
|
}
|
|
}
|
|
|
|
func TestGetPaperData_Type0_TimetableContent(t *testing.T) {
|
|
s := paperTestSession()
|
|
pkt := &mhfpacket.MsgMhfGetPaperData{AckHandle: 1, DataType: 0}
|
|
handleMsgMhfGetPaperData(s, pkt)
|
|
|
|
select {
|
|
case p := <-s.sendPackets:
|
|
payload := extractAckPayload(t, p.data)
|
|
|
|
// Mission payload: uint16(numTimetables) + uint16(numData) + timetable entries
|
|
if len(payload) < 4 {
|
|
t.Fatalf("mission payload too short: %d bytes", len(payload))
|
|
}
|
|
numTimetables := binary.BigEndian.Uint16(payload[0:2])
|
|
numData := binary.BigEndian.Uint16(payload[2:4])
|
|
|
|
if numTimetables != 1 {
|
|
t.Errorf("timetable count = %d, want 1", numTimetables)
|
|
}
|
|
if numData != 0 {
|
|
t.Errorf("mission data count = %d, want 0", numData)
|
|
}
|
|
|
|
// 1 timetable = 8 bytes (start uint32 + end uint32)
|
|
expectedLen := 4 + 8 // header + 1 timetable entry
|
|
if len(payload) != expectedLen {
|
|
t.Errorf("mission payload length = %d, want %d", len(payload), expectedLen)
|
|
}
|
|
|
|
// Verify start < end (midnight < midnight+24h)
|
|
start := binary.BigEndian.Uint32(payload[4:8])
|
|
end := binary.BigEndian.Uint32(payload[8:12])
|
|
if start >= end {
|
|
t.Errorf("timetable start (%d) should be < end (%d)", start, end)
|
|
}
|
|
default:
|
|
t.Fatal("expected ACK packet")
|
|
}
|
|
}
|
|
|
|
// --- paperGiftData table integrity ---
|
|
|
|
func TestPaperGiftData_AllEntriesHaveData(t *testing.T) {
|
|
for dataType, gifts := range paperGiftData {
|
|
if len(gifts) == 0 {
|
|
t.Errorf("paperGiftData[%d] is empty", dataType)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPaperGiftData_KnownKeys(t *testing.T) {
|
|
expectedKeys := []uint32{6001, 6002, 6010, 6011, 6012, 7001, 7002, 7011, 7012}
|
|
for _, key := range expectedKeys {
|
|
if _, ok := paperGiftData[key]; !ok {
|
|
t.Errorf("paperGiftData missing expected key %d", key)
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- PaperData struct serialization ---
|
|
|
|
func TestPaperData_Serialization_RoundTrip(t *testing.T) {
|
|
pd := PaperData{Unk0: 1001, Unk1: 1, Unk2: 100, Unk3: 200, Unk4: 300, Unk5: 400, Unk6: 500}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint16(pd.Unk0)
|
|
bf.WriteInt16(pd.Unk1)
|
|
bf.WriteInt16(pd.Unk2)
|
|
bf.WriteInt16(pd.Unk3)
|
|
bf.WriteInt16(pd.Unk4)
|
|
bf.WriteInt16(pd.Unk5)
|
|
bf.WriteInt16(pd.Unk6)
|
|
|
|
data := bf.Data()
|
|
if len(data) != 14 {
|
|
t.Fatalf("PaperData serialized size = %d, want 14", len(data))
|
|
}
|
|
|
|
// Read back
|
|
rbf := byteframe.NewByteFrameFromBytes(data)
|
|
if rbf.ReadUint16() != 1001 {
|
|
t.Error("Unk0 mismatch")
|
|
}
|
|
if rbf.ReadInt16() != 1 {
|
|
t.Error("Unk1 mismatch")
|
|
}
|
|
if rbf.ReadInt16() != 100 {
|
|
t.Error("Unk2 mismatch")
|
|
}
|
|
}
|
|
|
|
func TestPaperGift_Serialization_RoundTrip(t *testing.T) {
|
|
pg := PaperGift{Unk0: 11159, Unk1: 1, Unk2: 1, Unk3: 5000}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint16(pg.Unk0)
|
|
bf.WriteUint8(pg.Unk1)
|
|
bf.WriteUint8(pg.Unk2)
|
|
bf.WriteUint16(pg.Unk3)
|
|
|
|
data := bf.Data()
|
|
if len(data) != 6 {
|
|
t.Fatalf("PaperGift serialized size = %d, want 6", len(data))
|
|
}
|
|
|
|
rbf := byteframe.NewByteFrameFromBytes(data)
|
|
if rbf.ReadUint16() != 11159 {
|
|
t.Error("Unk0 mismatch")
|
|
}
|
|
if rbf.ReadUint8() != 1 {
|
|
t.Error("Unk1 mismatch")
|
|
}
|
|
if rbf.ReadUint8() != 1 {
|
|
t.Error("Unk2 mismatch")
|
|
}
|
|
if rbf.ReadUint16() != 5000 {
|
|
t.Error("Unk3 mismatch")
|
|
}
|
|
}
|
|
|
|
// itoa is a tiny helper to avoid importing strconv for test names.
|
|
func itoa(n uint32) string {
|
|
if n == 0 {
|
|
return "0"
|
|
}
|
|
var buf [10]byte
|
|
i := len(buf)
|
|
for n > 0 {
|
|
i--
|
|
buf[i] = byte('0' + n%10)
|
|
n /= 10
|
|
}
|
|
return string(buf[i:])
|
|
}
|