go-p2p/node/levin/storage_test.go
Virgil 7ce21cdba1
All checks were successful
Security Scan / security (push) Successful in 10s
Test / test (push) Successful in 1m30s
refactor(node): adopt AX-native protocol names
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-30 21:21:07 +00:00

337 lines
8.2 KiB
Go

// Copyright (c) 2024-2026 Lethean Contributors
// SPDX-License-Identifier: EUPL-1.2
package levin
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestStorage_EncodeStorage_EmptySection_Ugly(t *testing.T) {
s := Section{}
data, err := EncodeStorage(s)
require.NoError(t, err)
// 9-byte header + 1-byte varint(0) = 10 bytes.
assert.Len(t, data, 10)
// Verify storage header signatures.
assert.Equal(t, byte(0x01), data[0])
assert.Equal(t, byte(0x11), data[1])
assert.Equal(t, byte(0x01), data[2])
assert.Equal(t, byte(0x01), data[3])
assert.Equal(t, byte(0x01), data[4])
assert.Equal(t, byte(0x01), data[5])
assert.Equal(t, byte(0x02), data[6])
assert.Equal(t, byte(0x01), data[7])
// Version byte.
assert.Equal(t, byte(1), data[8])
// Entry count varint: 0.
assert.Equal(t, byte(0x00), data[9])
}
func TestStorage_PrimitivesRoundTrip_Ugly(t *testing.T) {
s := Section{
"u64": Uint64Value(0xDEADBEEFCAFEBABE),
"u32": Uint32Value(0xCAFEBABE),
"u16": Uint16Value(0xBEEF),
"u8": Uint8Value(42),
"i64": Int64Value(-9223372036854775808),
"i32": Int32Value(-2147483648),
"i16": Int16Value(-32768),
"i8": Int8Value(-128),
"flag": BoolValue(true),
"height": StringValue([]byte("hello world")),
"pi": DoubleValue(3.141592653589793),
}
data, err := EncodeStorage(s)
require.NoError(t, err)
decoded, err := DecodeStorage(data)
require.NoError(t, err)
// Unsigned integers.
u64, err := decoded["u64"].AsUint64()
require.NoError(t, err)
assert.Equal(t, uint64(0xDEADBEEFCAFEBABE), u64)
u32, err := decoded["u32"].AsUint32()
require.NoError(t, err)
assert.Equal(t, uint32(0xCAFEBABE), u32)
u16, err := decoded["u16"].AsUint16()
require.NoError(t, err)
assert.Equal(t, uint16(0xBEEF), u16)
u8, err := decoded["u8"].AsUint8()
require.NoError(t, err)
assert.Equal(t, uint8(42), u8)
// Signed integers.
i64, err := decoded["i64"].AsInt64()
require.NoError(t, err)
assert.Equal(t, int64(-9223372036854775808), i64)
i32, err := decoded["i32"].AsInt32()
require.NoError(t, err)
assert.Equal(t, int32(-2147483648), i32)
i16, err := decoded["i16"].AsInt16()
require.NoError(t, err)
assert.Equal(t, int16(-32768), i16)
i8, err := decoded["i8"].AsInt8()
require.NoError(t, err)
assert.Equal(t, int8(-128), i8)
// Bool.
flag, err := decoded["flag"].AsBool()
require.NoError(t, err)
assert.True(t, flag)
// String.
str, err := decoded["height"].AsString()
require.NoError(t, err)
assert.Equal(t, []byte("hello world"), str)
// Double.
pi, err := decoded["pi"].AsDouble()
require.NoError(t, err)
assert.Equal(t, 3.141592653589793, pi)
}
func TestStorage_NestedObject_Good(t *testing.T) {
inner := Section{
"port": Uint16Value(18080),
"host": StringValue([]byte("127.0.0.1")),
}
outer := Section{
"node_data": ObjectValue(inner),
"version": Uint32Value(1),
}
data, err := EncodeStorage(outer)
require.NoError(t, err)
decoded, err := DecodeStorage(data)
require.NoError(t, err)
ver, err := decoded["version"].AsUint32()
require.NoError(t, err)
assert.Equal(t, uint32(1), ver)
innerDec, err := decoded["node_data"].AsSection()
require.NoError(t, err)
port, err := innerDec["port"].AsUint16()
require.NoError(t, err)
assert.Equal(t, uint16(18080), port)
host, err := innerDec["host"].AsString()
require.NoError(t, err)
assert.Equal(t, []byte("127.0.0.1"), host)
}
func TestStorage_Uint64Array_Good(t *testing.T) {
s := Section{
"heights": Uint64ArrayValue([]uint64{10, 20, 30}),
}
data, err := EncodeStorage(s)
require.NoError(t, err)
decoded, err := DecodeStorage(data)
require.NoError(t, err)
arr, err := decoded["heights"].AsUint64Array()
require.NoError(t, err)
assert.Equal(t, []uint64{10, 20, 30}, arr)
}
func TestStorage_StringArray_Good(t *testing.T) {
s := Section{
"peers": StringArrayValue([][]byte{[]byte("foo"), []byte("bar")}),
}
data, err := EncodeStorage(s)
require.NoError(t, err)
decoded, err := DecodeStorage(data)
require.NoError(t, err)
arr, err := decoded["peers"].AsStringArray()
require.NoError(t, err)
require.Len(t, arr, 2)
assert.Equal(t, []byte("foo"), arr[0])
assert.Equal(t, []byte("bar"), arr[1])
}
func TestStorage_ObjectArray_Good(t *testing.T) {
sections := []Section{
{"id": Uint32Value(1), "name": StringValue([]byte("alice"))},
{"id": Uint32Value(2), "name": StringValue([]byte("bob"))},
}
s := Section{
"nodes": ObjectArrayValue(sections),
}
data, err := EncodeStorage(s)
require.NoError(t, err)
decoded, err := DecodeStorage(data)
require.NoError(t, err)
arr, err := decoded["nodes"].AsSectionArray()
require.NoError(t, err)
require.Len(t, arr, 2)
id1, err := arr[0]["id"].AsUint32()
require.NoError(t, err)
assert.Equal(t, uint32(1), id1)
name1, err := arr[0]["name"].AsString()
require.NoError(t, err)
assert.Equal(t, []byte("alice"), name1)
id2, err := arr[1]["id"].AsUint32()
require.NoError(t, err)
assert.Equal(t, uint32(2), id2)
name2, err := arr[1]["name"].AsString()
require.NoError(t, err)
assert.Equal(t, []byte("bob"), name2)
}
func TestStorage_DecodeStorage_BadSignature_Bad(t *testing.T) {
// Corrupt the first 4 bytes.
data := []byte{0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x01, 0x02, 0x01, 0x01, 0x00}
_, err := DecodeStorage(data)
require.Error(t, err)
assert.ErrorIs(t, err, ErrorStorageBadSignature)
}
func TestStorage_DecodeStorage_TooShort_Bad(t *testing.T) {
_, err := DecodeStorage([]byte{0x01, 0x11})
require.Error(t, err)
assert.ErrorIs(t, err, ErrorStorageTruncated)
}
func TestStorage_ByteIdenticalReencode_Ugly(t *testing.T) {
s := Section{
"alpha": Uint64Value(999),
"bravo": StringValue([]byte("deterministic")),
"charlie": BoolValue(false),
"delta": ObjectValue(Section{
"x": Int32Value(-42),
"y": Int32Value(100),
}),
"echo": Uint64ArrayValue([]uint64{1, 2, 3}),
}
data1, err := EncodeStorage(s)
require.NoError(t, err)
decoded, err := DecodeStorage(data1)
require.NoError(t, err)
data2, err := EncodeStorage(decoded)
require.NoError(t, err)
assert.Equal(t, data1, data2, "re-encoded bytes must be identical")
}
func TestStorage_TypeMismatchErrors_Bad(t *testing.T) {
v := Uint64Value(42)
_, err := v.AsUint32()
assert.ErrorIs(t, err, ErrorStorageTypeMismatch)
_, err = v.AsString()
assert.ErrorIs(t, err, ErrorStorageTypeMismatch)
_, err = v.AsBool()
assert.ErrorIs(t, err, ErrorStorageTypeMismatch)
_, err = v.AsSection()
assert.ErrorIs(t, err, ErrorStorageTypeMismatch)
_, err = v.AsUint64Array()
assert.ErrorIs(t, err, ErrorStorageTypeMismatch)
}
func TestStorage_Uint32Array_Good(t *testing.T) {
s := Section{
"ports": Uint32ArrayValue([]uint32{8080, 8443, 9090}),
}
data, err := EncodeStorage(s)
require.NoError(t, err)
decoded, err := DecodeStorage(data)
require.NoError(t, err)
arr, err := decoded["ports"].AsUint32Array()
require.NoError(t, err)
assert.Equal(t, []uint32{8080, 8443, 9090}, arr)
}
func TestStorage_DecodeStorage_BadVersion_Bad(t *testing.T) {
// Valid signatures but version 2 instead of 1.
data := []byte{0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x00}
_, err := DecodeStorage(data)
require.Error(t, err)
assert.ErrorIs(t, err, ErrorStorageBadVersion)
}
func TestStorage_EmptyArrays_Ugly(t *testing.T) {
s := Section{
"empty_u64": Uint64ArrayValue([]uint64{}),
"empty_str": StringArrayValue([][]byte{}),
"empty_obj": ObjectArrayValue([]Section{}),
}
data, err := EncodeStorage(s)
require.NoError(t, err)
decoded, err := DecodeStorage(data)
require.NoError(t, err)
u64arr, err := decoded["empty_u64"].AsUint64Array()
require.NoError(t, err)
assert.Empty(t, u64arr)
strarr, err := decoded["empty_str"].AsStringArray()
require.NoError(t, err)
assert.Empty(t, strarr)
objarr, err := decoded["empty_obj"].AsSectionArray()
require.NoError(t, err)
assert.Empty(t, objarr)
}
func TestStorage_BoolFalseRoundTrip_Ugly(t *testing.T) {
s := Section{
"off": BoolValue(false),
"on": BoolValue(true),
}
data, err := EncodeStorage(s)
require.NoError(t, err)
decoded, err := DecodeStorage(data)
require.NoError(t, err)
off, err := decoded["off"].AsBool()
require.NoError(t, err)
assert.False(t, off)
on, err := decoded["on"].AsBool()
require.NoError(t, err)
assert.True(t, on)
}