package api_test import ( "encoding/json" "math" "testing" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/lxc/incus/v7/shared/api" ) func TestDevicesMap_UnmarshalJSON(t *testing.T) { tests := []struct { name string input map[string]any target api.DevicesMap assertErr require.ErrorAssertionFunc want api.DevicesMap }{ { name: "nil", input: nil, assertErr: require.NoError, want: nil, }, { name: "nil with defaults", input: nil, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.NoError, want: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, }, { name: "empty with defaults", input: map[string]any{}, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.NoError, want: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, }, { name: "only string", input: map[string]any{ "device": map[string]any{ "string": "string", "int": "5", "float64": "5.4", "bool": "true", "empty": "", "null": "null", }, }, assertErr: require.NoError, want: api.DevicesMap{ "device": map[string]string{ "string": "string", "int": "5", "float64": "5.4", "bool": "true", "empty": "", "null": "null", }, }, }, { name: "mixed", input: map[string]any{ "device": map[string]any{ "string": "string", "int": 5, "uint": uint64(math.MaxUint64), "float64": 5.4, "float64max": math.MaxFloat64, "bool": true, "empty": "", "null": nil, }, }, assertErr: require.NoError, want: api.DevicesMap{ "device": map[string]string{ "string": "string", "int": "5", "uint": "1.8446744073709552e+19", "float64": "5.4", "float64max": "1.7976931348623157e+308", "bool": "true", "empty": "", "null": "", }, }, }, { name: "mixed with defaults", input: map[string]any{ "device": map[string]any{ "string": "string", "int": 5, "uint": uint64(math.MaxUint64), "float64": 5.4, "float64max": math.MaxFloat64, "bool": true, "empty": "", "null": nil, }, }, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.NoError, want: api.DevicesMap{ "device": map[string]string{ "string": "string", "int": "5", "uint": "1.8446744073709552e+19", "float64": "5.4", "float64max": "1.7976931348623157e+308", "bool": "true", "empty": "", "null": "", "default_string": "string", }, }, }, // // errors { name: "error - unsupported type object", input: map[string]any{ "device": map[string]any{ "invalid": map[string]any{ // invalid "inner": "value", }, }, }, assertErr: require.Error, want: nil, }, { name: "error - unsupported type object with defaults", input: map[string]any{ "device": map[string]any{ "invalid": map[string]any{ // invalid "inner": "value", }, }, }, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.Error, want: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, }, { name: "error - unsupported type array", input: map[string]any{ "invalid": []string{ // invalid "inner", "value", }, }, assertErr: require.Error, want: nil, }, { name: "error - unsupported type array with defaults", input: map[string]any{ "device": map[string]any{ "invalid": []string{ // invalid "inner", "value", }, }, }, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.Error, want: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { in, err := json.Marshal(tc.input) require.NoError(t, err) err = json.Unmarshal(in, &tc.target) tc.assertErr(t, err) require.Equal(t, tc.want, tc.target) }) } } func TestDevicesMap_UnmarshalYAML(t *testing.T) { tests := []struct { name string input map[string]any target api.DevicesMap assertErr require.ErrorAssertionFunc want api.DevicesMap }{ { name: "nil", input: nil, assertErr: require.NoError, want: api.DevicesMap{}, }, { name: "nil with defaults", input: nil, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.NoError, want: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, }, { name: "empty", input: map[string]any{}, assertErr: require.NoError, want: api.DevicesMap{}, }, { name: "empty with defaults", input: map[string]any{}, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.NoError, want: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, }, { name: "only string", input: map[string]any{ "device": map[string]any{ "string": "string", "int": "5", "float64": "5.4", "bool": "true", "empty": "", "null": "null", }, }, assertErr: require.NoError, want: api.DevicesMap{ "device": map[string]string{ "string": "string", "int": "5", "float64": "5.4", "bool": "true", "empty": "", "null": "null", }, }, }, { name: "mixed", input: map[string]any{ "device": map[string]any{ "string": "string", "int": 5, "uint": uint64(math.MaxUint64), "float64": 5.4, "float64max": math.MaxFloat64, "bool": true, "empty": "", "null": nil, }, }, assertErr: require.NoError, want: api.DevicesMap{ "device": map[string]string{ "string": "string", "int": "5", "uint": "18446744073709551615", "float64": "5.4", "float64max": "1.7976931348623157e+308", "bool": "true", "empty": "", "null": "", }, }, }, { name: "mixed with defaults", input: map[string]any{ "device": map[string]any{ "string": "string", "int": 5, "uint": uint64(math.MaxUint64), "float64": 5.4, "float64max": math.MaxFloat64, "bool": true, "empty": "", "null": nil, }, }, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.NoError, want: api.DevicesMap{ "device": map[string]string{ "string": "string", "int": "5", "uint": "18446744073709551615", "float64": "5.4", "float64max": "1.7976931348623157e+308", "bool": "true", "empty": "", "null": "", "default_string": "string", }, }, }, // errors { name: "error - unsupported type object", input: map[string]any{ "device": map[string]any{ "invalid": map[string]any{ // invalid "inner": "value", }, }, }, assertErr: require.Error, want: nil, }, { name: "error - unsupported type object with defaults", input: map[string]any{ "device": map[string]any{ "invalid": map[string]any{ // invalid "inner": "value", }, }, }, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.Error, want: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, }, { name: "error - unsupported type array", input: map[string]any{ "device": map[string]any{ "invalid": []string{ // invalid "inner", "value", }, }, }, assertErr: require.Error, want: nil, }, { name: "error - unsupported type array with defaults", input: map[string]any{ "device": map[string]any{ "invalid": []string{ // invalid "inner", "value", }, }, }, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.Error, want: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, }, { name: "error - unsupported type int for key", input: map[string]any{ "device": map[any]any{ 100: map[string]any{ // invalid key "inner": "value", }, }, }, target: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, assertErr: require.Error, want: api.DevicesMap{ "device": map[string]string{ "default_string": "string", }, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { in, err := yaml.Dump(tc.input, yaml.WithV2Defaults()) require.NoError(t, err) err = yaml.Load(in, &tc.target) tc.assertErr(t, err) require.Equal(t, tc.want, tc.target) }) } }