package orderedmap import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) func TestMarshalYAML(t *testing.T) { t.Run("int key", func(t *testing.T) { om := New[int, any]() om.Set(1, "bar") om.Set(7, "baz") om.Set(2, 28) om.Set(3, 100) om.Set(4, "baz") om.Set(5, "28") om.Set(6, "100") om.Set(8, "baz") om.Set(8, "baz") om.Set(9, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque auctor augue accumsan mi maximus, quis viverra massa pretium. Phasellus imperdiet sapien a interdum sollicitudin. Duis at commodo lectus, a lacinia sem.") b, err := yaml.Marshal(om) expected := `1: bar 7: baz 2: 28 3: 100 4: baz 5: "28" 6: "100" 8: baz 9: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque auctor augue accumsan mi maximus, quis viverra massa pretium. Phasellus imperdiet sapien a interdum sollicitudin. Duis at commodo lectus, a lacinia sem. ` assert.NoError(t, err) assert.Equal(t, expected, string(b)) }) t.Run("string key", func(t *testing.T) { om := New[string, any]() om.Set("test", "bar") om.Set("abc", true) b, err := yaml.Marshal(om) assert.NoError(t, err) expected := `test: bar abc: true ` assert.Equal(t, expected, string(b)) }) t.Run("typed string key", func(t *testing.T) { type myString string om := New[myString, any]() om.Set("test", "bar") om.Set("abc", true) b, err := yaml.Marshal(om) assert.NoError(t, err) assert.Equal(t, `test: bar abc: true `, string(b)) }) t.Run("typed int key", func(t *testing.T) { type myInt uint32 om := New[myInt, any]() om.Set(1, "bar") om.Set(7, "baz") om.Set(2, 28) om.Set(3, 100) om.Set(4, "baz") b, err := yaml.Marshal(om) assert.NoError(t, err) assert.Equal(t, `1: bar 7: baz 2: 28 3: 100 4: baz `, string(b)) }) t.Run("TextMarshaller key", func(t *testing.T) { om := New[marshallable, any]() om.Set(marshallable(1), "bar") om.Set(marshallable(28), true) b, err := yaml.Marshal(om) assert.NoError(t, err) assert.Equal(t, `'#1#': bar '#28#': true `, string(b)) }) t.Run("empty map with 0 elements", func(t *testing.T) { om := New[string, any]() b, err := yaml.Marshal(om) assert.NoError(t, err) assert.Equal(t, "{}\n", string(b)) }) t.Run("empty map with no elements (null)", func(t *testing.T) { om := &OrderedMap[string, string]{} b, err := yaml.Marshal(om) assert.NoError(t, err) assert.Equal(t, "{}\n", string(b)) }) } func TestUnmarshallYAML(t *testing.T) { t.Run("int key", func(t *testing.T) { data := ` 1: bar 7: baz 2: 28 3: 100 4: baz 5: "28" 6: "100" 8: baz ` om := New[int, any]() require.NoError(t, yaml.Unmarshal([]byte(data), &om)) assertOrderedPairsEqual(t, om, []int{1, 7, 2, 3, 4, 5, 6, 8}, []any{"bar", "baz", 28, 100, "baz", "28", "100", "baz"}) // serialize back to yaml to make sure things are equal }) t.Run("string key", func(t *testing.T) { data := `{"test":"bar","abc":true}` om := New[string, any]() require.NoError(t, yaml.Unmarshal([]byte(data), &om)) assertOrderedPairsEqual(t, om, []string{"test", "abc"}, []any{"bar", true}) }) t.Run("typed string key", func(t *testing.T) { data := `{"test":"bar","abc":true}` type myString string om := New[myString, any]() require.NoError(t, yaml.Unmarshal([]byte(data), &om)) assertOrderedPairsEqual(t, om, []myString{"test", "abc"}, []any{"bar", true}) }) t.Run("typed int key", func(t *testing.T) { data := ` 1: bar 7: baz 2: 28 3: 100 4: baz 5: "28" 6: "100" 8: baz ` type myInt uint32 om := New[myInt, any]() require.NoError(t, yaml.Unmarshal([]byte(data), &om)) assertOrderedPairsEqual(t, om, []myInt{1, 7, 2, 3, 4, 5, 6, 8}, []any{"bar", "baz", 28, 100, "baz", "28", "100", "baz"}) }) t.Run("TextUnmarshaler key", func(t *testing.T) { data := `{"#1#":"bar","#28#":true}` om := New[marshallable, any]() require.NoError(t, yaml.Unmarshal([]byte(data), &om)) assertOrderedPairsEqual(t, om, []marshallable{1, 28}, []any{"bar", true}) }) t.Run("when fed with an input that's not an object", func(t *testing.T) { for _, data := range []string{"true", `["foo"]`, "42", `"foo"`} { om := New[int, any]() require.Error(t, yaml.Unmarshal([]byte(data), &om)) } }) t.Run("empty map", func(t *testing.T) { data := `{}` om := New[int, any]() require.NoError(t, yaml.Unmarshal([]byte(data), &om)) assertLenEqual(t, om, 0) }) } func TestYAMLSpecialCharacters(t *testing.T) { baselineMap := map[string]any{specialCharacters: specialCharacters} baselineData, err := yaml.Marshal(baselineMap) require.NoError(t, err) // baseline proves this key is supported by official yaml library t.Logf("specialCharacters: %#v as []rune:%v", specialCharacters, []rune(specialCharacters)) t.Logf("baseline yaml data: %s", baselineData) t.Run("marshal special characters", func(t *testing.T) { om := New[string, any]() om.Set(specialCharacters, specialCharacters) b, err := yaml.Marshal(om) require.NoError(t, err) require.Equal(t, baselineData, b) type myString string om2 := New[myString, myString]() om2.Set(specialCharacters, specialCharacters) b, err = yaml.Marshal(om2) require.NoError(t, err) require.Equal(t, baselineData, b) }) t.Run("unmarshall special characters", func(t *testing.T) { om := New[string, any]() require.NoError(t, yaml.Unmarshal(baselineData, &om)) assertOrderedPairsEqual(t, om, []string{specialCharacters}, []any{specialCharacters}) type myString string om2 := New[myString, myString]() require.NoError(t, yaml.Unmarshal(baselineData, &om2)) assertOrderedPairsEqual(t, om2, []myString{specialCharacters}, []myString{specialCharacters}) }) } func TestYAMLRoundTrip(t *testing.T) { for _, testCase := range []struct { name string input string targetFactory func() any }{ { name: "empty map", input: "{}\n", targetFactory: func() any { return &OrderedMap[string, any]{} }, }, { name: "", input: `x: 28 m: bar: - 5: foo: bar foo: - 12: b: true i: 12 m: a: b c: 28 "n": null 28: a: false b: - 1 - 2 - 3 - 3: c: null d: 87 4: e: true 5: f: 4 g: 5 h: 6 `, targetFactory: func() any { return &nestedMaps{} }, }, { name: "with UTF-8 special chars in key", input: "�: 0\n", targetFactory: func() any { return &OrderedMap[string, int]{} }, }, } { t.Run(testCase.name, func(t *testing.T) { target := testCase.targetFactory() require.NoError(t, yaml.Unmarshal([]byte(testCase.input), target)) var ( out []byte err error ) out, err = yaml.Marshal(target) if assert.NoError(t, err) { assert.Equal(t, testCase.input, string(out)) } }) } } func BenchmarkMarshalYAML(b *testing.B) { om := New[int, any]() om.Set(1, "bar") om.Set(7, "baz") om.Set(2, 28) om.Set(3, 100) om.Set(4, "baz") om.Set(5, "28") om.Set(6, "100") om.Set(8, "baz") om.Set(8, "baz") b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = yaml.Marshal(om) } }