mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
415 lines
8.7 KiB
Go
415 lines
8.7 KiB
Go
package codec
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type testStruct struct {
|
|
Name string `json:"name"`
|
|
Value int `json:"value"`
|
|
Data []byte `json:"data"`
|
|
}
|
|
|
|
type nestedStruct struct {
|
|
ID string `json:"id"`
|
|
Inner testStruct `json:"inner"`
|
|
List []int `json:"list"`
|
|
Map map[string]string `json:"map"`
|
|
}
|
|
|
|
func TestJSONCodec_Marshal(t *testing.T) {
|
|
codec := &JSONCodec{}
|
|
|
|
tests := []struct {
|
|
name string
|
|
version CodecVersion
|
|
input interface{}
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "marshal simple struct",
|
|
version: CurrentVersion,
|
|
input: testStruct{
|
|
Name: "test",
|
|
Value: 42,
|
|
Data: []byte("hello"),
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "marshal nested struct",
|
|
version: CurrentVersion,
|
|
input: nestedStruct{
|
|
ID: "test-id",
|
|
Inner: testStruct{
|
|
Name: "inner",
|
|
Value: 100,
|
|
Data: []byte("world"),
|
|
},
|
|
List: []int{1, 2, 3},
|
|
Map: map[string]string{"key": "value"},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "marshal nil",
|
|
version: CurrentVersion,
|
|
input: nil,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "marshal empty struct",
|
|
version: CurrentVersion,
|
|
input: testStruct{},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "marshal string",
|
|
version: CurrentVersion,
|
|
input: "test string",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "marshal number",
|
|
version: CurrentVersion,
|
|
input: 123.456,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "marshal bool",
|
|
version: CurrentVersion,
|
|
input: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "marshal slice",
|
|
version: CurrentVersion,
|
|
input: []string{"a", "b", "c"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "marshal map",
|
|
version: CurrentVersion,
|
|
input: map[string]int{"one": 1, "two": 2},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "unsupported version",
|
|
version: CodecVersion(999),
|
|
input: testStruct{Name: "test"},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "marshal channel (should fail)",
|
|
version: CurrentVersion,
|
|
input: make(chan int),
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "marshal function (should fail)",
|
|
version: CurrentVersion,
|
|
input: func() {},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
data, err := codec.Marshal(tt.version, tt.input)
|
|
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
|
|
// For valid marshals, verify it's valid JSON
|
|
if tt.input != nil {
|
|
var result interface{}
|
|
err := json.Unmarshal(data, &result)
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestJSONCodec_Unmarshal(t *testing.T) {
|
|
codec := &JSONCodec{}
|
|
|
|
tests := []struct {
|
|
name string
|
|
input []byte
|
|
target interface{}
|
|
wantErr bool
|
|
validate func(t *testing.T, v interface{})
|
|
}{
|
|
{
|
|
name: "unmarshal simple struct",
|
|
input: []byte(`{"name":"test","value":42,"data":"aGVsbG8="}`),
|
|
target: &testStruct{},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, v interface{}) {
|
|
s := v.(*testStruct)
|
|
require.Equal(t, "test", s.Name)
|
|
require.Equal(t, 42, s.Value)
|
|
require.Equal(t, []byte("hello"), s.Data)
|
|
},
|
|
},
|
|
{
|
|
name: "unmarshal nested struct",
|
|
input: []byte(`{
|
|
"id":"test-id",
|
|
"inner":{"name":"inner","value":100,"data":"d29ybGQ="},
|
|
"list":[1,2,3],
|
|
"map":{"key":"value"}
|
|
}`),
|
|
target: &nestedStruct{},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, v interface{}) {
|
|
s := v.(*nestedStruct)
|
|
require.Equal(t, "test-id", s.ID)
|
|
require.Equal(t, "inner", s.Inner.Name)
|
|
require.Equal(t, 100, s.Inner.Value)
|
|
require.Equal(t, []int{1, 2, 3}, s.List)
|
|
require.Equal(t, "value", s.Map["key"])
|
|
},
|
|
},
|
|
{
|
|
name: "unmarshal string",
|
|
input: []byte(`"test string"`),
|
|
target: new(string),
|
|
wantErr: false,
|
|
validate: func(t *testing.T, v interface{}) {
|
|
s := v.(*string)
|
|
require.Equal(t, "test string", *s)
|
|
},
|
|
},
|
|
{
|
|
name: "unmarshal number",
|
|
input: []byte(`123.456`),
|
|
target: new(float64),
|
|
wantErr: false,
|
|
validate: func(t *testing.T, v interface{}) {
|
|
n := v.(*float64)
|
|
require.Equal(t, 123.456, *n)
|
|
},
|
|
},
|
|
{
|
|
name: "unmarshal bool",
|
|
input: []byte(`true`),
|
|
target: new(bool),
|
|
wantErr: false,
|
|
validate: func(t *testing.T, v interface{}) {
|
|
b := v.(*bool)
|
|
require.True(t, *b)
|
|
},
|
|
},
|
|
{
|
|
name: "unmarshal array",
|
|
input: []byte(`["a","b","c"]`),
|
|
target: &[]string{},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, v interface{}) {
|
|
s := v.(*[]string)
|
|
require.Equal(t, []string{"a", "b", "c"}, *s)
|
|
},
|
|
},
|
|
{
|
|
name: "unmarshal null",
|
|
input: []byte(`null`),
|
|
target: new(interface{}),
|
|
wantErr: false,
|
|
validate: func(t *testing.T, v interface{}) {
|
|
p := v.(*interface{})
|
|
require.Nil(t, *p)
|
|
},
|
|
},
|
|
{
|
|
name: "unmarshal empty object",
|
|
input: []byte(`{}`),
|
|
target: &testStruct{},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, v interface{}) {
|
|
s := v.(*testStruct)
|
|
require.Empty(t, s.Name)
|
|
require.Zero(t, s.Value)
|
|
require.Nil(t, s.Data)
|
|
},
|
|
},
|
|
{
|
|
name: "unmarshal invalid JSON",
|
|
input: []byte(`{invalid json`),
|
|
target: &testStruct{},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "unmarshal type mismatch",
|
|
input: []byte(`"string value"`),
|
|
target: new(int),
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "unmarshal empty input",
|
|
input: []byte(``),
|
|
target: &testStruct{},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
version, err := codec.Unmarshal(tt.input, tt.target)
|
|
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, CurrentVersion, version)
|
|
|
|
if tt.validate != nil {
|
|
tt.validate(t, tt.target)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestJSONCodec_RoundTrip(t *testing.T) {
|
|
codec := &JSONCodec{}
|
|
|
|
tests := []struct {
|
|
name string
|
|
input interface{}
|
|
}{
|
|
{
|
|
name: "simple struct",
|
|
input: testStruct{
|
|
Name: "roundtrip",
|
|
Value: 999,
|
|
Data: []byte("test data"),
|
|
},
|
|
},
|
|
{
|
|
name: "nested struct",
|
|
input: nestedStruct{
|
|
ID: "nested-id",
|
|
Inner: testStruct{
|
|
Name: "inner-test",
|
|
Value: 777,
|
|
Data: []byte("inner data"),
|
|
},
|
|
List: []int{10, 20, 30},
|
|
Map: map[string]string{"foo": "bar", "baz": "qux"},
|
|
},
|
|
},
|
|
{
|
|
name: "slice of structs",
|
|
input: []testStruct{
|
|
{Name: "first", Value: 1},
|
|
{Name: "second", Value: 2},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Marshal
|
|
data, err := codec.Marshal(CurrentVersion, tt.input)
|
|
require.NoError(t, err)
|
|
|
|
// Create new instance of same type for unmarshaling
|
|
targetType := reflect.TypeOf(tt.input)
|
|
target := reflect.New(targetType).Interface()
|
|
|
|
// Unmarshal
|
|
version, err := codec.Unmarshal(data, target)
|
|
require.NoError(t, err)
|
|
require.Equal(t, CurrentVersion, version)
|
|
|
|
// Compare
|
|
require.Equal(t, tt.input, reflect.ValueOf(target).Elem().Interface())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCodecVersion(t *testing.T) {
|
|
// Test that CurrentVersion is 0
|
|
require.Equal(t, CodecVersion(0), CurrentVersion)
|
|
|
|
// Test version comparison
|
|
require.True(t, CurrentVersion == 0)
|
|
require.False(t, CurrentVersion != 0)
|
|
}
|
|
|
|
func TestCodec(t *testing.T) {
|
|
// Test that global Codec variable is initialized
|
|
require.NotNil(t, Codec)
|
|
require.IsType(t, &JSONCodec{}, Codec)
|
|
|
|
// Test using global codec
|
|
input := testStruct{Name: "global", Value: 100}
|
|
data, err := Codec.Marshal(CurrentVersion, input)
|
|
require.NoError(t, err)
|
|
|
|
var result testStruct
|
|
version, err := Codec.Unmarshal(data, &result)
|
|
require.NoError(t, err)
|
|
require.Equal(t, CurrentVersion, version)
|
|
require.Equal(t, input, result)
|
|
}
|
|
|
|
func BenchmarkJSONCodec_Marshal(b *testing.B) {
|
|
codec := &JSONCodec{}
|
|
input := nestedStruct{
|
|
ID: "bench-id",
|
|
Inner: testStruct{
|
|
Name: "benchmark",
|
|
Value: 42,
|
|
Data: []byte("benchmark data"),
|
|
},
|
|
List: []int{1, 2, 3, 4, 5},
|
|
Map: map[string]string{"key1": "value1", "key2": "value2"},
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = codec.Marshal(CurrentVersion, input)
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSONCodec_Unmarshal(b *testing.B) {
|
|
codec := &JSONCodec{}
|
|
data := []byte(`{
|
|
"id":"bench-id",
|
|
"inner":{"name":"benchmark","value":42,"data":"YmVuY2htYXJrIGRhdGE="},
|
|
"list":[1,2,3,4,5],
|
|
"map":{"key1":"value1","key2":"value2"}
|
|
}`)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var result nestedStruct
|
|
_, _ = codec.Unmarshal(data, &result)
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSONCodec_RoundTrip(b *testing.B) {
|
|
codec := &JSONCodec{}
|
|
input := testStruct{
|
|
Name: "benchmark",
|
|
Value: 42,
|
|
Data: []byte("benchmark data"),
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
data, _ := codec.Marshal(CurrentVersion, input)
|
|
var result testStruct
|
|
_, _ = codec.Unmarshal(data, &result)
|
|
}
|
|
}
|