forked from quic-go/quic-go
205 lines
4.7 KiB
Go
205 lines
4.7 KiB
Go
package qlog
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"git.geeks-team.ru/gr1ffon/quic-go/qlogwriter/jsontext"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func check(t *testing.T, f any, expected map[string]any) {
|
|
t.Helper()
|
|
|
|
var buf bytes.Buffer
|
|
enc := jsontext.NewEncoder(&buf)
|
|
require.NoError(t, (Frame{Frame: f}).encode(enc))
|
|
data := buf.Bytes()
|
|
require.True(t, json.Valid(data), "invalid JSON: %s", string(data))
|
|
checkEncoding(t, data, expected)
|
|
}
|
|
|
|
func checkEncoding(t *testing.T, data []byte, expected map[string]any) {
|
|
t.Helper()
|
|
|
|
m := make(map[string]any)
|
|
require.NoError(t, json.Unmarshal(data, &m))
|
|
require.Len(t, m, len(expected))
|
|
|
|
for key, value := range expected {
|
|
switch v := value.(type) {
|
|
case bool, string, map[string]any:
|
|
require.Equal(t, v, m[key])
|
|
case int:
|
|
require.Equal(t, float64(v), m[key])
|
|
case float64:
|
|
require.Equal(t, v, m[key])
|
|
case []map[string]any: // used for header fields
|
|
require.Contains(t, m, key)
|
|
slice, ok := m[key].([]any)
|
|
require.True(t, ok)
|
|
require.Len(t, slice, len(v))
|
|
for i, expectedField := range v {
|
|
field, ok := slice[i].(map[string]any)
|
|
require.True(t, ok)
|
|
require.Equal(t, expectedField, field)
|
|
}
|
|
default:
|
|
t.Fatalf("unexpected type: %T", v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDataFrame(t *testing.T) {
|
|
check(t, DataFrame{}, map[string]any{
|
|
"frame_type": "data",
|
|
})
|
|
}
|
|
|
|
func TestHeadersFrame(t *testing.T) {
|
|
check(t, HeadersFrame{
|
|
HeaderFields: []HeaderField{
|
|
{Name: ":status", Value: "200"},
|
|
{Name: "content-type", Value: "application/json"},
|
|
},
|
|
}, map[string]any{
|
|
"frame_type": "headers",
|
|
"header_fields": []map[string]any{
|
|
{"name": ":status", "value": "200"},
|
|
{"name": "content-type", "value": "application/json"},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestGoAwayFrame(t *testing.T) {
|
|
check(t, GoAwayFrame{StreamID: 1337}, map[string]any{
|
|
"frame_type": "goaway",
|
|
"id": 1337,
|
|
})
|
|
}
|
|
|
|
func pointer[T any](v T) *T {
|
|
return &v
|
|
}
|
|
|
|
func TestSettingsFrame(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
frame SettingsFrame
|
|
expected map[string]any
|
|
}{
|
|
{
|
|
name: "datagram: true",
|
|
frame: SettingsFrame{
|
|
MaxFieldSectionSize: -1,
|
|
Datagram: pointer(true),
|
|
},
|
|
expected: map[string]any{
|
|
"frame_type": "settings",
|
|
"settings": []map[string]any{{
|
|
"name": "settings_h3_datagram",
|
|
"value": true,
|
|
}},
|
|
},
|
|
},
|
|
{
|
|
name: "extended_connect: false",
|
|
frame: SettingsFrame{
|
|
MaxFieldSectionSize: -1,
|
|
ExtendedConnect: pointer(false),
|
|
},
|
|
expected: map[string]any{
|
|
"frame_type": "settings",
|
|
"settings": []map[string]any{{
|
|
"name": "settings_enable_connect_protocol",
|
|
"value": false,
|
|
}},
|
|
},
|
|
},
|
|
{
|
|
name: "max_field_section_size",
|
|
frame: SettingsFrame{MaxFieldSectionSize: 1337},
|
|
expected: map[string]any{
|
|
"frame_type": "settings",
|
|
"settings": []map[string]any{{
|
|
"name": "settings_max_field_section_size",
|
|
"value": float64(1337),
|
|
}},
|
|
},
|
|
},
|
|
{
|
|
name: "datagram: false, extended_connect: false",
|
|
frame: SettingsFrame{
|
|
MaxFieldSectionSize: -1,
|
|
Datagram: pointer(false),
|
|
ExtendedConnect: pointer(false),
|
|
},
|
|
expected: map[string]any{
|
|
"frame_type": "settings",
|
|
"settings": []map[string]any{
|
|
{"name": "settings_h3_datagram", "value": false},
|
|
{"name": "settings_enable_connect_protocol", "value": false},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unknowns",
|
|
// Only test a single unknown setting.
|
|
// Testing multiple unknown settings doesn't add a lot of value,
|
|
// and would require us to deal with non-deterministic map iteration order.
|
|
frame: SettingsFrame{
|
|
MaxFieldSectionSize: -1,
|
|
Other: map[uint64]uint64{0xdead: 0xbeef},
|
|
},
|
|
expected: map[string]any{
|
|
"frame_type": "settings",
|
|
"settings": []map[string]any{{
|
|
"name": "unknown",
|
|
"name_bytes": float64(0xdead),
|
|
"value": float64(0xbeef),
|
|
}},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
check(t, tc.frame, tc.expected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPushPromiseFrame(t *testing.T) {
|
|
check(t, PushPromiseFrame{}, map[string]any{
|
|
"frame_type": "push_promise",
|
|
})
|
|
}
|
|
|
|
func TestCancelPushFrame(t *testing.T) {
|
|
check(t, CancelPushFrame{}, map[string]any{
|
|
"frame_type": "cancel_push",
|
|
})
|
|
}
|
|
|
|
func TestMaxPushIDFrame(t *testing.T) {
|
|
check(t, MaxPushIDFrame{}, map[string]any{
|
|
"frame_type": "max_push_id",
|
|
})
|
|
}
|
|
|
|
func TestReservedFrame(t *testing.T) {
|
|
check(t, ReservedFrame{Type: 0x1f}, map[string]any{
|
|
"frame_type": "reserved",
|
|
"frame_type_bytes": 0x1f,
|
|
})
|
|
}
|
|
|
|
func TestUnknownFrame(t *testing.T) {
|
|
check(t, UnknownFrame{Type: 0x2a}, map[string]any{
|
|
"frame_type": "unknown",
|
|
"frame_type_bytes": 0x2a,
|
|
})
|
|
}
|