package voice import ( "bytes" "encoding/binary" "testing" ) // 帧头布局必须精确匹配火山协议(错一个 bit 服务端就拒)。 func TestEncodeFrame_HeaderLayout(t *testing.T) { f := jsonFrame(msgFullClientReq, flagNone, []byte(`{"a":1}`)) if f[0] != 0x11 { t.Fatalf("byte0 应 0x11,得 %#x", f[0]) } if f[1] != (msgFullClientReq<<4)|flagNone { // 0x10 t.Fatalf("byte1 应 0x10,得 %#x", f[1]) } if f[2] != (serialJSON<<4)|compNone { // 0x10 t.Fatalf("byte2(JSON) 应 0x10,得 %#x", f[2]) } if got := binary.BigEndian.Uint32(f[4:8]); got != 7 { t.Fatalf("payload 长度应 7,得 %d", got) } if !bytes.Equal(f[8:], []byte(`{"a":1}`)) { t.Fatalf("payload 错:%s", f[8:]) } } // 音频帧:raw 序列化 + 最终标记。 func TestAudioFrame_LastFlag(t *testing.T) { f := audioFrame([]byte{1, 2, 3}, true) if f[1] != (msgAudioReq<<4)|flagLast { // 0x22 t.Fatalf("音频最终帧 byte1 应 0x22,得 %#x", f[1]) } if f[2] != (serialRaw<<4)|compNone { // 0x00 t.Fatalf("音频 byte2 应 0x00(raw),得 %#x", f[2]) } non := audioFrame([]byte{1}, false) if non[1]&0x0F != flagNone { t.Fatalf("非最终帧 flags 应 0,得 %#x", non[1]&0x0F) } } // 服务端帧解析:跳 12 字节头(含序列号)取 payload;识别结果类型 0x09。 func TestParseServerFrame(t *testing.T) { // 造一个响应帧:header(4) + seq(4) + size(4) + payload body := []byte(`{"type":"final"}`) resp := make([]byte, 12+len(body)) resp[1] = (msgServerResp << 4) // 0x90 copy(resp[12:], body) mt, payload, ok := parseServerFrame(resp) if !ok || mt != msgServerResp { t.Fatalf("应解析出 msgServerResp,得 mt=%#x ok=%v", mt, ok) } if !bytes.Equal(payload, body) { t.Fatalf("payload 应从第 12 字节起,得 %s", payload) } // 过短帧不 panic if _, _, ok := parseServerFrame([]byte{0x11}); ok { t.Fatal("过短帧应 ok=false") } }