mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
196 lines
5.1 KiB
Go
196 lines
5.1 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package optional_test
|
|
|
|
import (
|
|
std_json "encoding/json" //nolint:depguard // for testing purpose
|
|
"testing"
|
|
|
|
"github.com/hanzoai/git/modules/json"
|
|
"github.com/hanzoai/git/modules/optional"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"go.yaml.in/yaml/v4"
|
|
)
|
|
|
|
type testSerializationStruct struct {
|
|
NormalString string `json:"normal_string" yaml:"normal_string"`
|
|
NormalBool bool `json:"normal_bool" yaml:"normal_bool"`
|
|
OptBool optional.Option[bool] `json:"optional_bool,omitempty" yaml:"optional_bool,omitempty"`
|
|
|
|
// It causes an undefined behavior: should the "omitempty" tag only omit "null", or also the empty string?
|
|
// The behavior is inconsistent between json and v2 packages, and there is no such use case in the forge.
|
|
// If anyone really needs it, they can use json.MarshalKeepOptionalEmpty to revert the v1 behavior
|
|
OptString optional.Option[string] `json:"optional_string,omitempty" yaml:"optional_string,omitempty"`
|
|
|
|
OptTwoBool optional.Option[bool] `json:"optional_two_bool" yaml:"optional_two_bool"`
|
|
OptTwoString optional.Option[string] `json:"optional_two_string" yaml:"optional_two_string"`
|
|
}
|
|
|
|
func TestOptionalToJson(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
obj *testSerializationStruct
|
|
want string
|
|
}{
|
|
{
|
|
name: "empty",
|
|
obj: new(testSerializationStruct),
|
|
want: `{"normal_string":"","normal_bool":false,"optional_two_bool":null,"optional_two_string":null}`,
|
|
},
|
|
{
|
|
name: "some",
|
|
obj: &testSerializationStruct{
|
|
NormalString: "a string",
|
|
NormalBool: true,
|
|
OptBool: optional.Some(false),
|
|
OptString: optional.Some(""),
|
|
OptTwoBool: optional.None[bool](),
|
|
OptTwoString: optional.None[string](),
|
|
},
|
|
want: `{"normal_string":"a string","normal_bool":true,"optional_bool":false,"optional_string":"","optional_two_bool":null,"optional_two_string":null}`,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
b, err := json.MarshalKeepOptionalEmpty(tc.obj)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.want, string(b), "forge json module returned unexpected")
|
|
|
|
b, err = std_json.Marshal(tc.obj)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.want, string(b), "std json module returned unexpected")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOptionalFromJson(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data string
|
|
want testSerializationStruct
|
|
}{
|
|
{
|
|
name: "empty",
|
|
data: `{}`,
|
|
want: testSerializationStruct{
|
|
NormalString: "",
|
|
},
|
|
},
|
|
{
|
|
name: "some",
|
|
data: `{"normal_string":"a string","normal_bool":true,"optional_bool":false,"optional_string":"","optional_two_bool":null,"optional_two_string":null}`,
|
|
want: testSerializationStruct{
|
|
NormalString: "a string",
|
|
NormalBool: true,
|
|
OptBool: optional.Some(false),
|
|
OptString: optional.Some(""),
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var obj1 testSerializationStruct
|
|
err := json.Unmarshal([]byte(tc.data), &obj1)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.want, obj1, "forge json module returned unexpected")
|
|
|
|
var obj2 testSerializationStruct
|
|
err = std_json.Unmarshal([]byte(tc.data), &obj2)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.want, obj2, "std json module returned unexpected")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOptionalToYaml(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
obj *testSerializationStruct
|
|
want string
|
|
}{
|
|
{
|
|
name: "empty",
|
|
obj: new(testSerializationStruct),
|
|
want: `normal_string: ""
|
|
normal_bool: false
|
|
optional_two_bool: null
|
|
optional_two_string: null
|
|
`,
|
|
},
|
|
{
|
|
name: "some",
|
|
obj: &testSerializationStruct{
|
|
NormalString: "a string",
|
|
NormalBool: true,
|
|
OptBool: optional.Some(false),
|
|
OptString: optional.Some(""),
|
|
},
|
|
want: `normal_string: a string
|
|
normal_bool: true
|
|
optional_bool: false
|
|
optional_string: ""
|
|
optional_two_bool: null
|
|
optional_two_string: null
|
|
`,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
b, err := yaml.Marshal(tc.obj)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.want, string(b), "yaml module returned unexpected")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOptionalFromYaml(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data string
|
|
want testSerializationStruct
|
|
}{
|
|
{
|
|
name: "empty",
|
|
data: ``,
|
|
want: testSerializationStruct{},
|
|
},
|
|
{
|
|
name: "empty but init",
|
|
data: `normal_string: ""
|
|
normal_bool: false
|
|
optional_bool:
|
|
optional_two_bool:
|
|
optional_two_string:
|
|
`,
|
|
want: testSerializationStruct{},
|
|
},
|
|
{
|
|
name: "some",
|
|
data: `
|
|
normal_string: a string
|
|
normal_bool: true
|
|
optional_bool: false
|
|
optional_string: ""
|
|
optional_two_bool: null
|
|
optional_two_string: null
|
|
`,
|
|
want: testSerializationStruct{
|
|
NormalString: "a string",
|
|
NormalBool: true,
|
|
OptBool: optional.Some(false),
|
|
OptString: optional.Some(""),
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var obj testSerializationStruct
|
|
err := yaml.Unmarshal([]byte(tc.data), &obj)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.want, obj, "yaml module returned unexpected")
|
|
})
|
|
}
|
|
}
|