// Copyright 2024 the libevm authors. // // The libevm additions to go-ethereum are free software: you can redistribute // them and/or modify them under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, either version 3 of the License, // or (at your option) any later version. // // The libevm additions are distributed in the hope that they will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser // General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see // . package params import ( "bytes" "encoding/json" "math/big" "testing" "github.com/stretchr/testify/require" "github.com/ethereum/go-ethereum/libevm/pseudo" ) type nestedChainConfigExtra struct { NestedFoo string `json:"foo"` NOOPHooks } type rootJSONChainConfigExtra struct { TopLevelFoo string `json:"foo"` NOOPHooks } func TestChainConfigJSONRoundTrip(t *testing.T) { tests := []struct { name string register func() jsonInput string want *ChainConfig }{ { name: "no registered extras", register: func() {}, jsonInput: `{ "chainId": 1234 }`, want: &ChainConfig{ ChainID: big.NewInt(1234), }, }, { name: "reuse top-level JSON with non-pointer", register: func() { RegisterExtras(Extras[rootJSONChainConfigExtra, NOOPHooks]{ ReuseJSONRoot: true, }) }, jsonInput: `{ "chainId": 5678, "foo": "hello" }`, want: &ChainConfig{ ChainID: big.NewInt(5678), extra: pseudo.From(rootJSONChainConfigExtra{TopLevelFoo: "hello"}).Type, }, }, { name: "reuse top-level JSON with pointer", register: func() { RegisterExtras(Extras[*rootJSONChainConfigExtra, NOOPHooks]{ ReuseJSONRoot: true, }) }, jsonInput: `{ "chainId": 5678, "foo": "hello" }`, want: &ChainConfig{ ChainID: big.NewInt(5678), extra: pseudo.From(&rootJSONChainConfigExtra{TopLevelFoo: "hello"}).Type, }, }, { name: "nested JSON with non-pointer", register: func() { RegisterExtras(Extras[nestedChainConfigExtra, NOOPHooks]{ ReuseJSONRoot: false, // explicit zero value only for tests }) }, jsonInput: `{ "chainId": 42, "extra": {"foo": "world"} }`, want: &ChainConfig{ ChainID: big.NewInt(42), extra: pseudo.From(nestedChainConfigExtra{NestedFoo: "world"}).Type, }, }, { name: "nested JSON with pointer", register: func() { RegisterExtras(Extras[*nestedChainConfigExtra, NOOPHooks]{ ReuseJSONRoot: false, // explicit zero value only for tests }) }, jsonInput: `{ "chainId": 42, "extra": {"foo": "world"} }`, want: &ChainConfig{ ChainID: big.NewInt(42), extra: pseudo.From(&nestedChainConfigExtra{NestedFoo: "world"}).Type, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { TestOnlyClearRegisteredExtras() t.Cleanup(TestOnlyClearRegisteredExtras) tt.register() t.Run("json.Unmarshal()", func(t *testing.T) { got := new(ChainConfig) require.NoError(t, json.Unmarshal([]byte(tt.jsonInput), got)) require.Equal(t, tt.want, got) }) t.Run("json.Marshal()", func(t *testing.T) { var want bytes.Buffer require.NoError(t, json.Compact(&want, []byte(tt.jsonInput)), "json.Compact()") got, err := json.Marshal(tt.want) require.NoError(t, err, "json.Marshal()") require.Equal(t, want.String(), string(got)) }) }) } }