mirror of
https://github.com/LukeHagar/plexgo.git
synced 2025-12-06 12:37:46 +00:00
1807 lines
46 KiB
Go
1807 lines
46 KiB
Go
// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
|
|
|
package optionalnullable
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Test helper function to create pointers from values
|
|
func ptrFrom[T any](value T) *T {
|
|
return &value
|
|
}
|
|
|
|
// Test helper types for comprehensive testing
|
|
type TestStruct struct {
|
|
Name string `json:"name"`
|
|
Age int `json:"age"`
|
|
}
|
|
|
|
type TestContainer struct {
|
|
StringField OptionalNullable[string] `json:"string_field,omitempty"`
|
|
IntField OptionalNullable[int] `json:"int_field,omitempty"`
|
|
SliceField OptionalNullable[[]string] `json:"slice_field,omitempty"`
|
|
StructField OptionalNullable[TestStruct] `json:"struct_field,omitempty"`
|
|
}
|
|
|
|
// TestNewNullable tests the From constructor
|
|
func TestNewNullable(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("with string value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("test"))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "test", got)
|
|
})
|
|
|
|
t.Run("with nil pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[string](nil)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.True(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "", got) // zero value for string
|
|
})
|
|
|
|
t.Run("with int value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom(42))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, 42, got)
|
|
})
|
|
|
|
t.Run("with slice value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom([]string{"a", "b", "c"}))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{"a", "b", "c"}, got)
|
|
})
|
|
|
|
t.Run("with empty slice", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom([]string{}))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{}, got)
|
|
})
|
|
|
|
t.Run("with struct value", func(t *testing.T) {
|
|
t.Parallel()
|
|
val := TestStruct{Name: "John", Age: 30}
|
|
nullable := From(&val)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
v, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, val, v)
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, TestStruct{Name: "John", Age: 30}, got)
|
|
})
|
|
}
|
|
|
|
// TestNewNullableUnset tests the NewNullableUnset constructor
|
|
func TestNewNullableUnset(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("string type", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
|
|
assert.False(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull()) // Unset is not null
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Equal(t, "", got) // zero value for string
|
|
})
|
|
|
|
t.Run("int type", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[int]
|
|
|
|
assert.False(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull()) // Unset is not null
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Equal(t, 0, got) // zero value for int
|
|
})
|
|
|
|
t.Run("slice type", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[[]string]
|
|
|
|
assert.False(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull()) // Unset is not null
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Nil(t, got) // zero value for slice is nil
|
|
})
|
|
}
|
|
|
|
// TestIsNull tests the IsNull method
|
|
func TestIsNull(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("with value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("test"))
|
|
assert.False(t, nullable.IsNull())
|
|
})
|
|
|
|
t.Run("with nil pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[string](nil)
|
|
assert.True(t, nullable.IsNull())
|
|
})
|
|
|
|
t.Run("unset", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
assert.False(t, nullable.IsNull())
|
|
})
|
|
}
|
|
|
|
// TestIsSet tests the IsSet method
|
|
func TestIsSet(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("with value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("test"))
|
|
assert.True(t, nullable.IsSet())
|
|
})
|
|
|
|
t.Run("with nil pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[string](nil)
|
|
assert.True(t, nullable.IsSet())
|
|
})
|
|
|
|
t.Run("unset", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
assert.False(t, nullable.IsSet())
|
|
})
|
|
}
|
|
|
|
// TestGet tests the Get method
|
|
func TestGet(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("with string value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("test"))
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "test", got)
|
|
})
|
|
|
|
t.Run("with nil pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[string](nil)
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "", got) // zero value
|
|
})
|
|
|
|
t.Run("unset", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Equal(t, "", got) // zero value
|
|
})
|
|
|
|
t.Run("with slice value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom([]string{"a", "b"}))
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{"a", "b"}, got)
|
|
})
|
|
|
|
t.Run("with nil slice pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[[]string](nil)
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Nil(t, got) // zero value for slice is nil
|
|
})
|
|
}
|
|
|
|
// TestPointer tests the Pointer method
|
|
func TestPointer(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("with value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("test"))
|
|
|
|
ptr, ok := nullable.Get()
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, ptr)
|
|
assert.Equal(t, "test", *ptr)
|
|
})
|
|
|
|
t.Run("with nil pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[string](nil)
|
|
|
|
ptr, ok := nullable.Get()
|
|
assert.True(t, ok)
|
|
assert.Nil(t, ptr)
|
|
})
|
|
|
|
t.Run("unset", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
|
|
ptr, ok := nullable.Get()
|
|
assert.False(t, ok)
|
|
assert.Nil(t, ptr)
|
|
})
|
|
}
|
|
|
|
// TestSet tests the Set method
|
|
func TestSet(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("set string value", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
|
|
// Initially unset
|
|
assert.False(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull()) // Unset is not null
|
|
|
|
// Set a value
|
|
nullable.Set(ptrFrom("test"))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "test", got)
|
|
})
|
|
|
|
t.Run("set int value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := OptionalNullable[int]{}
|
|
|
|
nullable.Set(ptrFrom(42))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, 42, got)
|
|
})
|
|
|
|
t.Run("set slice value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := OptionalNullable[[]string]{}
|
|
|
|
slice := []string{"a", "b"}
|
|
nullable.Set(ptrFrom(slice))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{"a", "b"}, got)
|
|
})
|
|
|
|
t.Run("set empty slice", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := OptionalNullable[[]string]{}
|
|
|
|
slice := []string{}
|
|
nullable.Set(ptrFrom(slice))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{}, got)
|
|
})
|
|
|
|
t.Run("overwrite existing value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("original"))
|
|
|
|
// Verify original value
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "original", got)
|
|
|
|
// Set new value
|
|
nullable.Set(ptrFrom("new"))
|
|
|
|
got, ok = nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "new", got)
|
|
})
|
|
}
|
|
|
|
// TestUnset tests the Unset method
|
|
func TestUnset(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("unset from value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("test"))
|
|
|
|
// Initially set
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
// Unset
|
|
nullable.Unset()
|
|
|
|
assert.False(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull()) // After unset is not null
|
|
// Value is now internal to the map implementation
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Equal(t, "", got) // zero value
|
|
})
|
|
|
|
t.Run("unset from nil", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[string](nil)
|
|
|
|
// Initially set to nil
|
|
assert.True(t, nullable.IsSet())
|
|
assert.True(t, nullable.IsNull()) // Set to nil should be null
|
|
|
|
// Unset
|
|
nullable.Unset()
|
|
|
|
assert.False(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull()) // After unset is not null
|
|
})
|
|
|
|
t.Run("unset already unset", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
|
|
// Initially unset
|
|
assert.False(t, nullable.IsSet())
|
|
|
|
// Unset again
|
|
nullable.Unset()
|
|
|
|
assert.False(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull()) // Empty map is not null
|
|
})
|
|
}
|
|
|
|
// TestMarshalJSON tests JSON marshaling
|
|
func TestMarshalJSON(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("marshal string value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("test"))
|
|
|
|
data, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, `"test"`, string(data))
|
|
})
|
|
|
|
t.Run("marshal int value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom(42))
|
|
|
|
data, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, `42`, string(data))
|
|
})
|
|
|
|
t.Run("marshal nil value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[string](nil)
|
|
|
|
data, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, `null`, string(data))
|
|
})
|
|
|
|
t.Run("marshal slice value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom([]string{"a", "b", "c"}))
|
|
|
|
data, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, `["a","b","c"]`, string(data))
|
|
})
|
|
|
|
t.Run("marshal empty slice", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom([]string{}))
|
|
|
|
data, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, `[]`, string(data))
|
|
})
|
|
|
|
t.Run("marshal struct value", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom(TestStruct{Name: "John", Age: 30}))
|
|
|
|
data, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, `{"name":"John","age":30}`, string(data))
|
|
})
|
|
|
|
// Note: Unset values are not tested here because the current implementation
|
|
// doesn't handle unset fields in marshaling (see TODO in the code)
|
|
}
|
|
|
|
// TestUnmarshalJSON tests JSON unmarshaling
|
|
func TestUnmarshalJSON(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("unmarshal string value", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
err := json.Unmarshal([]byte(`"test"`), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "test", got)
|
|
})
|
|
|
|
t.Run("unmarshal int value", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[int]
|
|
err := json.Unmarshal([]byte(`42`), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, 42, got)
|
|
})
|
|
|
|
t.Run("unmarshal null value", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
err := json.Unmarshal([]byte(`null`), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.True(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "", got) // zero value
|
|
})
|
|
|
|
t.Run("unmarshal slice value", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[[]string]
|
|
err := json.Unmarshal([]byte(`["a","b","c"]`), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{"a", "b", "c"}, got)
|
|
})
|
|
|
|
t.Run("unmarshal empty slice", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[[]string]
|
|
err := json.Unmarshal([]byte(`[]`), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{}, got)
|
|
})
|
|
|
|
t.Run("unmarshal struct value", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[TestStruct]
|
|
err := json.Unmarshal([]byte(`{"name":"John","age":30}`), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, TestStruct{Name: "John", Age: 30}, got)
|
|
})
|
|
|
|
t.Run("unmarshal invalid JSON", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
err := json.Unmarshal([]byte(`invalid`), &nullable)
|
|
assert.Error(t, err)
|
|
|
|
// Ensure the nullable remains unset after error
|
|
assert.False(t, nullable.IsSet())
|
|
})
|
|
|
|
t.Run("unmarshal invalid JSON for int", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[int]
|
|
err := json.Unmarshal([]byte(`"not_a_number"`), &nullable)
|
|
assert.Error(t, err)
|
|
|
|
// Ensure the nullable remains unset after error
|
|
assert.False(t, nullable.IsSet())
|
|
})
|
|
|
|
t.Run("unmarshal malformed JSON", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[TestStruct]
|
|
err := json.Unmarshal([]byte(`{invalid json`), &nullable)
|
|
assert.Error(t, err)
|
|
|
|
// Ensure the nullable remains unset after error
|
|
assert.False(t, nullable.IsSet())
|
|
})
|
|
}
|
|
|
|
// TestJSONRoundTrip tests marshaling and unmarshaling together
|
|
func TestJSONRoundTrip(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("string value round trip", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable1 := From(ptrFrom("test value"))
|
|
|
|
// Marshal
|
|
data, err := json.Marshal(nullable1)
|
|
require.NoError(t, err)
|
|
|
|
// Unmarshal
|
|
var nullable2 OptionalNullable[string]
|
|
err = json.Unmarshal(data, &nullable2)
|
|
require.NoError(t, err)
|
|
|
|
// Compare
|
|
assert.Equal(t, nullable1.IsSet(), nullable2.IsSet())
|
|
assert.Equal(t, nullable1.IsNull(), nullable2.IsNull())
|
|
|
|
got1, ok1 := nullable1.GetOrZero()
|
|
got2, ok2 := nullable2.GetOrZero()
|
|
assert.Equal(t, ok1, ok2)
|
|
assert.Equal(t, got1, got2)
|
|
})
|
|
|
|
t.Run("nil value round trip", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable1 := From[string](nil)
|
|
|
|
// Marshal
|
|
data, err := json.Marshal(nullable1)
|
|
require.NoError(t, err)
|
|
|
|
// Unmarshal
|
|
var nullable2 OptionalNullable[string]
|
|
err = json.Unmarshal(data, &nullable2)
|
|
require.NoError(t, err)
|
|
|
|
// Compare
|
|
assert.Equal(t, nullable1.IsSet(), nullable2.IsSet())
|
|
assert.Equal(t, nullable1.IsNull(), nullable2.IsNull())
|
|
|
|
got1, ok1 := nullable1.GetOrZero()
|
|
got2, ok2 := nullable2.GetOrZero()
|
|
assert.Equal(t, ok1, ok2)
|
|
assert.Equal(t, got1, got2)
|
|
})
|
|
|
|
t.Run("slice round trip", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable1 := From(ptrFrom([]string{"a", "b", "c"}))
|
|
|
|
// Marshal
|
|
data, err := json.Marshal(nullable1)
|
|
require.NoError(t, err)
|
|
|
|
// Unmarshal
|
|
var nullable2 OptionalNullable[[]string]
|
|
err = json.Unmarshal(data, &nullable2)
|
|
require.NoError(t, err)
|
|
|
|
// Compare
|
|
assert.Equal(t, nullable1.IsSet(), nullable2.IsSet())
|
|
assert.Equal(t, nullable1.IsNull(), nullable2.IsNull())
|
|
|
|
got1, ok1 := nullable1.GetOrZero()
|
|
got2, ok2 := nullable2.GetOrZero()
|
|
assert.Equal(t, ok1, ok2)
|
|
assert.Equal(t, got1, got2)
|
|
})
|
|
}
|
|
|
|
// TestJSONToJSONRoundTrip tests starting with JSON and ensuring we can serialize back to the same JSON
|
|
func TestJSONToJSONRoundTrip(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("string value JSON round trip", func(t *testing.T) {
|
|
t.Parallel()
|
|
originalJSON := `"hello world"`
|
|
|
|
// Unmarshal from JSON
|
|
var nullable OptionalNullable[string]
|
|
err := json.Unmarshal([]byte(originalJSON), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
// Verify state
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "hello world", got)
|
|
|
|
// Marshal back to JSON
|
|
resultJSON, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, originalJSON, string(resultJSON))
|
|
})
|
|
|
|
t.Run("null value JSON round trip", func(t *testing.T) {
|
|
t.Parallel()
|
|
originalJSON := `null`
|
|
|
|
// Unmarshal from JSON
|
|
var nullable OptionalNullable[string]
|
|
err := json.Unmarshal([]byte(originalJSON), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
// Verify state
|
|
assert.True(t, nullable.IsSet())
|
|
assert.True(t, nullable.IsNull())
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "", got) // zero value
|
|
|
|
// Marshal back to JSON
|
|
resultJSON, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, originalJSON, string(resultJSON))
|
|
})
|
|
|
|
t.Run("int value JSON round trip", func(t *testing.T) {
|
|
t.Parallel()
|
|
originalJSON := `42`
|
|
|
|
// Unmarshal from JSON
|
|
var nullable OptionalNullable[int]
|
|
err := json.Unmarshal([]byte(originalJSON), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
// Verify state
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, 42, got)
|
|
|
|
// Marshal back to JSON
|
|
resultJSON, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, originalJSON, string(resultJSON))
|
|
})
|
|
|
|
t.Run("slice value JSON round trip", func(t *testing.T) {
|
|
t.Parallel()
|
|
originalJSON := `["a","b","c"]`
|
|
|
|
// Unmarshal from JSON
|
|
var nullable OptionalNullable[[]string]
|
|
err := json.Unmarshal([]byte(originalJSON), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
// Verify state
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{"a", "b", "c"}, got)
|
|
|
|
// Marshal back to JSON
|
|
resultJSON, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, originalJSON, string(resultJSON))
|
|
})
|
|
|
|
t.Run("empty slice JSON round trip", func(t *testing.T) {
|
|
t.Parallel()
|
|
originalJSON := `[]`
|
|
|
|
// Unmarshal from JSON
|
|
var nullable OptionalNullable[[]string]
|
|
err := json.Unmarshal([]byte(originalJSON), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
// Verify state
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{}, got)
|
|
|
|
// Marshal back to JSON
|
|
resultJSON, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, originalJSON, string(resultJSON))
|
|
})
|
|
|
|
t.Run("struct value JSON round trip", func(t *testing.T) {
|
|
t.Parallel()
|
|
originalJSON := `{"name":"Alice","age":25}`
|
|
|
|
// Unmarshal from JSON
|
|
var nullable OptionalNullable[TestStruct]
|
|
err := json.Unmarshal([]byte(originalJSON), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
// Verify state
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, TestStruct{Name: "Alice", Age: 25}, got)
|
|
|
|
// Marshal back to JSON
|
|
resultJSON, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, originalJSON, string(resultJSON))
|
|
})
|
|
}
|
|
|
|
// TestContainerStates tests comprehensive state detection and serialization with TestContainer
|
|
func TestContainerStates(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("all fields set to values", func(t *testing.T) {
|
|
t.Parallel()
|
|
container := TestContainer{
|
|
StringField: From(ptrFrom("hello")),
|
|
IntField: From(ptrFrom(42)),
|
|
SliceField: From(ptrFrom([]string{"a", "b"})),
|
|
StructField: From(ptrFrom(TestStruct{Name: "John", Age: 30})),
|
|
}
|
|
|
|
// Verify all fields are set and not null
|
|
assert.True(t, container.StringField.IsSet())
|
|
assert.False(t, container.StringField.IsNull())
|
|
assert.True(t, container.IntField.IsSet())
|
|
assert.False(t, container.IntField.IsNull())
|
|
assert.True(t, container.SliceField.IsSet())
|
|
assert.False(t, container.SliceField.IsNull())
|
|
assert.True(t, container.StructField.IsSet())
|
|
assert.False(t, container.StructField.IsNull())
|
|
|
|
// Verify values
|
|
stringVal, ok := container.StringField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "hello", stringVal)
|
|
|
|
intVal, ok := container.IntField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, 42, intVal)
|
|
|
|
sliceVal, ok := container.SliceField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{"a", "b"}, sliceVal)
|
|
|
|
structVal, ok := container.StructField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, TestStruct{Name: "John", Age: 30}, structVal)
|
|
|
|
// Test JSON serialization
|
|
data, err := json.Marshal(container)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(data, &result)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "hello", result["string_field"])
|
|
assert.Equal(t, float64(42), result["int_field"]) // JSON numbers are float64
|
|
assert.Equal(t, []interface{}{"a", "b"}, result["slice_field"])
|
|
structResult := result["struct_field"].(map[string]interface{})
|
|
assert.Equal(t, "John", structResult["name"])
|
|
assert.Equal(t, float64(30), structResult["age"])
|
|
})
|
|
|
|
t.Run("all fields set to nil", func(t *testing.T) {
|
|
t.Parallel()
|
|
container := TestContainer{
|
|
StringField: From[string](nil),
|
|
IntField: From[int](nil),
|
|
SliceField: From[[]string](nil),
|
|
StructField: From[TestStruct](nil),
|
|
}
|
|
|
|
// Verify all fields are set but null
|
|
assert.True(t, container.StringField.IsSet())
|
|
assert.True(t, container.StringField.IsNull())
|
|
assert.True(t, container.IntField.IsSet())
|
|
assert.True(t, container.IntField.IsNull())
|
|
assert.True(t, container.SliceField.IsSet())
|
|
assert.True(t, container.SliceField.IsNull())
|
|
assert.True(t, container.StructField.IsSet())
|
|
assert.True(t, container.StructField.IsNull())
|
|
|
|
// Verify GetOrZero() behavior for nil values
|
|
stringVal, ok := container.StringField.GetOrZero()
|
|
assert.True(t, ok) // set to nil still returns true
|
|
assert.Equal(t, "", stringVal) // zero value
|
|
|
|
intVal, ok := container.IntField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, 0, intVal) // zero value
|
|
|
|
sliceVal, ok := container.SliceField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Nil(t, sliceVal) // zero value for slice is nil
|
|
|
|
structVal, ok := container.StructField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, TestStruct{}, structVal) // zero value
|
|
|
|
// Test JSON serialization - all should be null
|
|
data, err := json.Marshal(container)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(data, &result)
|
|
require.NoError(t, err)
|
|
|
|
assert.Nil(t, result["string_field"])
|
|
assert.Nil(t, result["int_field"])
|
|
assert.Nil(t, result["slice_field"])
|
|
assert.Nil(t, result["struct_field"])
|
|
})
|
|
|
|
t.Run("all fields unset", func(t *testing.T) {
|
|
t.Parallel()
|
|
container := TestContainer{}
|
|
|
|
// Verify all fields are unset
|
|
assert.False(t, container.StringField.IsSet())
|
|
assert.False(t, container.StringField.IsNull()) // unset is not null in new implementation
|
|
assert.False(t, container.IntField.IsSet())
|
|
assert.False(t, container.IntField.IsNull())
|
|
assert.False(t, container.SliceField.IsSet())
|
|
assert.False(t, container.SliceField.IsNull())
|
|
assert.False(t, container.StructField.IsSet())
|
|
assert.False(t, container.StructField.IsNull())
|
|
|
|
// Verify GetOrZero() behavior for unset values
|
|
stringVal, ok := container.StringField.GetOrZero()
|
|
assert.False(t, ok) // unset returns false
|
|
assert.Equal(t, "", stringVal) // zero value
|
|
|
|
intVal, ok := container.IntField.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Equal(t, 0, intVal) // zero value
|
|
|
|
sliceVal, ok := container.SliceField.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Nil(t, sliceVal) // zero value
|
|
|
|
structVal, ok := container.StructField.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Equal(t, TestStruct{}, structVal) // zero value
|
|
|
|
// Test JSON serialization - unset fields should be omitted due to omitempty
|
|
data, err := json.Marshal(container)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(data, &result)
|
|
require.NoError(t, err)
|
|
|
|
// With omitempty, unset fields should not appear in JSON
|
|
assert.NotContains(t, result, "string_field")
|
|
assert.NotContains(t, result, "int_field")
|
|
assert.NotContains(t, result, "slice_field")
|
|
assert.NotContains(t, result, "struct_field")
|
|
})
|
|
|
|
t.Run("slice field states: nil vs unset vs empty vs set", func(t *testing.T) {
|
|
t.Parallel()
|
|
// Test all possible slice states
|
|
nilSlice := TestContainer{
|
|
SliceField: From[[]string](nil), // explicitly set to nil
|
|
}
|
|
unsetSlice := TestContainer{} // unset
|
|
emptySlice := TestContainer{
|
|
SliceField: From(ptrFrom([]string{})), // empty slice
|
|
}
|
|
setSlice := TestContainer{
|
|
SliceField: From(ptrFrom([]string{"a", "b"})), // slice with values
|
|
}
|
|
|
|
// Verify nil slice
|
|
assert.True(t, nilSlice.SliceField.IsSet())
|
|
assert.True(t, nilSlice.SliceField.IsNull())
|
|
val, ok := nilSlice.SliceField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Nil(t, val)
|
|
|
|
// Verify unset slice
|
|
assert.False(t, unsetSlice.SliceField.IsSet())
|
|
assert.False(t, unsetSlice.SliceField.IsNull()) // Unset is not null
|
|
val, ok = unsetSlice.SliceField.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Nil(t, val)
|
|
|
|
// Verify empty slice
|
|
assert.True(t, emptySlice.SliceField.IsSet())
|
|
assert.False(t, emptySlice.SliceField.IsNull())
|
|
val, ok = emptySlice.SliceField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{}, val)
|
|
|
|
// Verify set slice
|
|
assert.True(t, setSlice.SliceField.IsSet())
|
|
assert.False(t, setSlice.SliceField.IsNull())
|
|
val, ok = setSlice.SliceField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, []string{"a", "b"}, val)
|
|
|
|
// Test JSON serialization for each state
|
|
nilData, err := json.Marshal(nilSlice)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(nilData), `"slice_field":null`)
|
|
|
|
unsetData, err := json.Marshal(unsetSlice)
|
|
require.NoError(t, err)
|
|
assert.NotContains(t, string(unsetData), "slice_field") // omitted due to omitempty
|
|
|
|
emptyData, err := json.Marshal(emptySlice)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(emptyData), `"slice_field":[]`)
|
|
|
|
setData, err := json.Marshal(setSlice)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(setData), `"slice_field":["a","b"]`)
|
|
})
|
|
|
|
t.Run("mixed states container", func(t *testing.T) {
|
|
t.Parallel()
|
|
container := TestContainer{
|
|
StringField: From(ptrFrom("hello")), // set to value
|
|
IntField: From[int](nil), // set to nil
|
|
StructField: From(ptrFrom(TestStruct{Name: "Alice", Age: 25})), // set to value
|
|
}
|
|
|
|
// Verify states
|
|
assert.True(t, container.StringField.IsSet())
|
|
assert.False(t, container.StringField.IsNull())
|
|
|
|
assert.True(t, container.IntField.IsSet())
|
|
assert.True(t, container.IntField.IsNull())
|
|
|
|
assert.False(t, container.SliceField.IsSet())
|
|
assert.False(t, container.SliceField.IsNull()) // Unset is not null
|
|
|
|
assert.True(t, container.StructField.IsSet())
|
|
assert.False(t, container.StructField.IsNull())
|
|
|
|
// Test JSON serialization
|
|
data, err := json.Marshal(container)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(data, &result)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "hello", result["string_field"])
|
|
assert.Nil(t, result["int_field"])
|
|
assert.NotContains(t, result, "slice_field") // unset, so omitted
|
|
structResult := result["struct_field"].(map[string]interface{})
|
|
assert.Equal(t, "Alice", structResult["name"])
|
|
assert.Equal(t, float64(25), structResult["age"])
|
|
})
|
|
|
|
t.Run("JSON unmarshaling preserves states", func(t *testing.T) {
|
|
t.Parallel()
|
|
// JSON with some fields missing, some null, some with values
|
|
jsonData := `{
|
|
"string_field": "test",
|
|
"int_field": null,
|
|
"struct_field": {"name": "Bob", "age": 35}
|
|
}`
|
|
|
|
var container TestContainer
|
|
err := json.Unmarshal([]byte(jsonData), &container)
|
|
require.NoError(t, err)
|
|
|
|
// string_field: present with value
|
|
assert.True(t, container.StringField.IsSet())
|
|
assert.False(t, container.StringField.IsNull())
|
|
stringVal, ok := container.StringField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "test", stringVal)
|
|
|
|
// int_field: present but null
|
|
assert.True(t, container.IntField.IsSet())
|
|
assert.True(t, container.IntField.IsNull())
|
|
intVal, ok := container.IntField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, 0, intVal) // zero value
|
|
|
|
// slice_field: missing from JSON, should remain unset
|
|
assert.False(t, container.SliceField.IsSet())
|
|
assert.False(t, container.SliceField.IsNull()) // Unset is not null
|
|
sliceVal, ok := container.SliceField.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Nil(t, sliceVal)
|
|
|
|
// struct_field: present with value
|
|
assert.True(t, container.StructField.IsSet())
|
|
assert.False(t, container.StructField.IsNull())
|
|
structVal, ok := container.StructField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, TestStruct{Name: "Bob", Age: 35}, structVal)
|
|
})
|
|
}
|
|
|
|
// TestNilVsUnsetDistinction tests the key feature of distinguishing nil from unset
|
|
func TestNilVsUnsetDistinction(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("explicit nil vs unset", func(t *testing.T) {
|
|
t.Parallel()
|
|
// Explicitly set to nil
|
|
explicitNil := From[string](nil)
|
|
|
|
// Unset
|
|
var unset OptionalNullable[string]
|
|
|
|
// Both are null, but only one is set
|
|
assert.True(t, explicitNil.IsNull())
|
|
assert.True(t, explicitNil.IsSet())
|
|
|
|
assert.False(t, unset.IsNull()) // Unset is not null
|
|
assert.False(t, unset.IsSet())
|
|
|
|
// Get behavior differs
|
|
got1, ok1 := explicitNil.GetOrZero()
|
|
got2, ok2 := unset.GetOrZero()
|
|
|
|
assert.True(t, ok1) // explicitly set to nil returns true
|
|
assert.False(t, ok2) // unset returns false
|
|
assert.Equal(t, "", got1) // both return zero value
|
|
assert.Equal(t, "", got2)
|
|
|
|
// Get behavior differs
|
|
ptr1, ok1 := explicitNil.Get()
|
|
ptr2, ok2 := unset.Get()
|
|
|
|
assert.True(t, ok1) // explicitly set to nil returns true
|
|
assert.False(t, ok2) // unset returns false
|
|
assert.Nil(t, ptr1) // both return nil pointer
|
|
assert.Nil(t, ptr2)
|
|
})
|
|
|
|
t.Run("empty slice vs nil slice vs unset", func(t *testing.T) {
|
|
t.Parallel()
|
|
// Empty slice
|
|
emptyNullable := From(ptrFrom([]string{}))
|
|
|
|
// Nil slice
|
|
nilNullable := From[[]string](nil)
|
|
|
|
// Unset
|
|
var unsetNullable OptionalNullable[[]string]
|
|
|
|
// All have different characteristics
|
|
assert.True(t, emptyNullable.IsSet())
|
|
assert.False(t, emptyNullable.IsNull())
|
|
|
|
assert.True(t, nilNullable.IsSet())
|
|
assert.True(t, nilNullable.IsNull())
|
|
|
|
assert.False(t, unsetNullable.IsSet())
|
|
assert.False(t, unsetNullable.IsNull()) // Unset is not null
|
|
|
|
// Get behavior
|
|
got1, ok1 := emptyNullable.GetOrZero()
|
|
got2, ok2 := nilNullable.GetOrZero()
|
|
got3, ok3 := unsetNullable.GetOrZero()
|
|
|
|
assert.True(t, ok1)
|
|
assert.Equal(t, []string{}, got1)
|
|
|
|
assert.True(t, ok2)
|
|
assert.Nil(t, got2)
|
|
|
|
assert.False(t, ok3)
|
|
assert.Nil(t, got3)
|
|
})
|
|
}
|
|
|
|
// TestJSONOmitEmpty tests behavior with omitempty tag
|
|
func TestJSONOmitEmpty(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("marshal with omitempty", func(t *testing.T) {
|
|
t.Parallel()
|
|
// Test container with various nullable states
|
|
container := TestContainer{
|
|
StringField: From(ptrFrom("test")),
|
|
IntField: From(ptrFrom(42)),
|
|
StructField: From[TestStruct](nil), // explicitly nil
|
|
}
|
|
|
|
data, err := json.Marshal(container)
|
|
require.NoError(t, err)
|
|
|
|
// Parse back to verify structure
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(data, &result)
|
|
require.NoError(t, err)
|
|
|
|
// Should contain set fields
|
|
assert.Contains(t, result, "string_field")
|
|
assert.Contains(t, result, "int_field")
|
|
assert.Contains(t, result, "struct_field")
|
|
|
|
// Should not contain unset field (due to omitempty)
|
|
// Note: This depends on how the marshaling handles unset fields
|
|
// The current implementation doesn't handle this case properly (see TODO)
|
|
})
|
|
|
|
t.Run("unmarshal missing fields", func(t *testing.T) {
|
|
t.Parallel()
|
|
// JSON with some fields missing
|
|
jsonData := `{"string_field": "test", "int_field": null}`
|
|
|
|
var container TestContainer
|
|
err := json.Unmarshal([]byte(jsonData), &container)
|
|
require.NoError(t, err)
|
|
|
|
// Present fields should be set
|
|
assert.True(t, container.StringField.IsSet())
|
|
assert.False(t, container.StringField.IsNull())
|
|
got, ok := container.StringField.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "test", got)
|
|
|
|
// Null field should be set to nil
|
|
assert.True(t, container.IntField.IsSet())
|
|
assert.True(t, container.IntField.IsNull())
|
|
|
|
// Missing fields should remain unset
|
|
assert.False(t, container.SliceField.IsSet())
|
|
assert.False(t, container.StructField.IsSet())
|
|
})
|
|
}
|
|
|
|
// TestEdgeCases tests various edge cases
|
|
func TestEdgeCases(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("zero values", func(t *testing.T) {
|
|
t.Parallel()
|
|
// Test with zero values that are not nil
|
|
intNullable := From(ptrFrom(0))
|
|
stringNullable := From(ptrFrom(""))
|
|
|
|
assert.True(t, intNullable.IsSet())
|
|
assert.False(t, intNullable.IsNull())
|
|
got, ok := intNullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, 0, got)
|
|
|
|
assert.True(t, stringNullable.IsSet())
|
|
assert.False(t, stringNullable.IsNull())
|
|
got2, ok2 := stringNullable.GetOrZero()
|
|
assert.True(t, ok2)
|
|
assert.Equal(t, "", got2)
|
|
})
|
|
|
|
t.Run("pointer to pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
// Test with pointer to pointer type
|
|
inner := "test"
|
|
nullable := From(ptrFrom(&inner))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, &inner, got)
|
|
assert.Equal(t, "test", *got)
|
|
})
|
|
|
|
t.Run("complex struct", func(t *testing.T) {
|
|
t.Parallel()
|
|
complexStruct := struct {
|
|
Name string
|
|
Values []int
|
|
Metadata map[string]string
|
|
}{
|
|
Name: "complex",
|
|
Values: []int{1, 2, 3},
|
|
Metadata: map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
},
|
|
}
|
|
|
|
nullable := From(ptrFrom(complexStruct))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, complexStruct, got)
|
|
})
|
|
}
|
|
|
|
// TestDoublePointers tests comprehensive double pointer scenarios
|
|
func TestDoublePointers(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("string double pointer with value", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := "hello world"
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, ptr, got)
|
|
assert.Equal(t, &inner, got)
|
|
assert.Equal(t, "hello world", *got)
|
|
})
|
|
|
|
t.Run("int double pointer with value", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := 42
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, ptr, got)
|
|
assert.Equal(t, &inner, got)
|
|
assert.Equal(t, 42, *got)
|
|
})
|
|
|
|
t.Run("double pointer to nil", func(t *testing.T) {
|
|
t.Parallel()
|
|
var ptr *string = nil
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, ptr, got)
|
|
assert.Nil(t, got)
|
|
})
|
|
|
|
t.Run("nil double pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[*string](nil)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.True(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Nil(t, got) // zero value for **string is nil
|
|
})
|
|
|
|
t.Run("unset double pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[*string]
|
|
|
|
assert.False(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Nil(t, got) // zero value for **string is nil
|
|
})
|
|
|
|
t.Run("double pointer modification", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := "original"
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
// Verify original value
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "original", *got)
|
|
|
|
// Modify through double pointer
|
|
*got = "modified"
|
|
assert.Equal(t, "modified", inner)
|
|
assert.Equal(t, "modified", *got)
|
|
})
|
|
|
|
t.Run("double pointer to struct", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := TestStruct{Name: "Alice", Age: 30}
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, ptr, got)
|
|
assert.Equal(t, TestStruct{Name: "Alice", Age: 30}, *got)
|
|
|
|
// Modify through double pointer
|
|
(*got).Name = "Bob"
|
|
assert.Equal(t, "Bob", inner.Name)
|
|
assert.Equal(t, "Bob", (*got).Name)
|
|
})
|
|
|
|
t.Run("double pointer to slice", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := []string{"a", "b", "c"}
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, ptr, got)
|
|
assert.Equal(t, []string{"a", "b", "c"}, *got)
|
|
|
|
// Modify through double pointer
|
|
*got = append(*got, "d")
|
|
assert.Equal(t, []string{"a", "b", "c", "d"}, inner)
|
|
assert.Equal(t, []string{"a", "b", "c", "d"}, *got)
|
|
})
|
|
|
|
t.Run("double pointer to empty slice", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := []string{}
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, ptr, got)
|
|
assert.Equal(t, []string{}, *got)
|
|
})
|
|
|
|
t.Run("double pointer to nil slice", func(t *testing.T) {
|
|
t.Parallel()
|
|
var inner []string = nil
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, ptr, got)
|
|
assert.Nil(t, *got)
|
|
})
|
|
|
|
t.Run("double pointer JSON marshaling", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := "json test"
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
data, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, `"json test"`, string(data))
|
|
})
|
|
|
|
t.Run("double pointer JSON unmarshaling", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[*string]
|
|
err := json.Unmarshal([]byte(`"json test"`), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, got)
|
|
assert.Equal(t, "json test", *got)
|
|
})
|
|
|
|
t.Run("double pointer JSON null marshaling", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[*string](nil)
|
|
|
|
data, err := json.Marshal(nullable)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, `null`, string(data))
|
|
})
|
|
|
|
t.Run("double pointer JSON null unmarshaling", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[*string]
|
|
err := json.Unmarshal([]byte(`null`), &nullable)
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.True(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Nil(t, got)
|
|
})
|
|
|
|
t.Run("double pointer round trip", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := "round trip test"
|
|
ptr := &inner
|
|
nullable1 := From(ptrFrom(ptr))
|
|
|
|
// Marshal
|
|
data, err := json.Marshal(nullable1)
|
|
require.NoError(t, err)
|
|
|
|
// Unmarshal
|
|
var nullable2 OptionalNullable[*string]
|
|
err = json.Unmarshal(data, &nullable2)
|
|
require.NoError(t, err)
|
|
|
|
// Compare states
|
|
assert.Equal(t, nullable1.IsSet(), nullable2.IsSet())
|
|
assert.Equal(t, nullable1.IsNull(), nullable2.IsNull())
|
|
|
|
got1, ok1 := nullable1.GetOrZero()
|
|
got2, ok2 := nullable2.GetOrZero()
|
|
assert.Equal(t, ok1, ok2)
|
|
|
|
// Values should be equal
|
|
assert.Equal(t, *got1, *got2)
|
|
})
|
|
|
|
t.Run("triple pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := "triple"
|
|
ptr1 := &inner
|
|
ptr2 := &ptr1
|
|
nullable := From(ptrFrom(ptr2))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, ptr2, got)
|
|
assert.Equal(t, ptr1, *got)
|
|
assert.Equal(t, "triple", **got)
|
|
})
|
|
|
|
t.Run("double pointer set and unset", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[*string]
|
|
|
|
// Initially unset
|
|
assert.False(t, nullable.IsSet())
|
|
|
|
// Set to double pointer
|
|
inner := "set test"
|
|
ptr := &inner
|
|
nullable.Set(ptrFrom(ptr))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "set test", *got)
|
|
|
|
// Set to nil
|
|
nullable.Set(nil)
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.True(t, nullable.IsNull())
|
|
|
|
got, ok = nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Nil(t, got)
|
|
|
|
// Unset
|
|
nullable.Unset()
|
|
|
|
assert.False(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok = nullable.GetOrZero()
|
|
assert.False(t, ok)
|
|
assert.Nil(t, got)
|
|
})
|
|
|
|
t.Run("double pointer Get method", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := "get test"
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
// Test Get method
|
|
gotPtr, ok := nullable.Get()
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, gotPtr)
|
|
assert.Equal(t, ptr, *gotPtr)
|
|
assert.Equal(t, "get test", **gotPtr)
|
|
|
|
// Test with nil
|
|
nilNullable := From[*string](nil)
|
|
gotPtr, ok = nilNullable.Get()
|
|
assert.True(t, ok)
|
|
assert.Nil(t, gotPtr)
|
|
|
|
// Test with unset
|
|
var unsetNullable OptionalNullable[*string]
|
|
gotPtr, ok = unsetNullable.Get()
|
|
assert.False(t, ok)
|
|
assert.Nil(t, gotPtr)
|
|
})
|
|
|
|
t.Run("double pointer zero values", func(t *testing.T) {
|
|
t.Parallel()
|
|
// Test with zero value string
|
|
inner := ""
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
|
|
assert.True(t, nullable.IsSet())
|
|
assert.False(t, nullable.IsNull())
|
|
|
|
got, ok := nullable.GetOrZero()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "", *got)
|
|
|
|
// Test with zero value int
|
|
innerInt := 0
|
|
ptrInt := &innerInt
|
|
nullableInt := From(ptrFrom(ptrInt))
|
|
|
|
assert.True(t, nullableInt.IsSet())
|
|
assert.False(t, nullableInt.IsNull())
|
|
|
|
gotInt, okInt := nullableInt.GetOrZero()
|
|
assert.True(t, okInt)
|
|
assert.Equal(t, 0, *gotInt)
|
|
})
|
|
}
|
|
|
|
// TestAsOptionalNullable tests the AsOptionalNullable helper function
|
|
func TestAsOptionalNullable(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("with nullable string", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("test"))
|
|
reflectValue := reflect.ValueOf(nullable)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, result)
|
|
|
|
value, isSet := result.GetUntyped()
|
|
assert.True(t, isSet)
|
|
assert.Equal(t, "test", value)
|
|
})
|
|
|
|
t.Run("with nullable int", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom(42))
|
|
reflectValue := reflect.ValueOf(nullable)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, result)
|
|
|
|
value, isSet := result.GetUntyped()
|
|
assert.True(t, isSet)
|
|
assert.Equal(t, 42, value)
|
|
})
|
|
|
|
t.Run("with nullable nil", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From[string](nil)
|
|
reflectValue := reflect.ValueOf(nullable)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, result)
|
|
|
|
value, isSet := result.GetUntyped()
|
|
assert.True(t, isSet)
|
|
assert.Nil(t, value)
|
|
})
|
|
|
|
t.Run("with unset nullable", func(t *testing.T) {
|
|
t.Parallel()
|
|
var nullable OptionalNullable[string]
|
|
reflectValue := reflect.ValueOf(nullable)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.False(t, ok)
|
|
assert.Nil(t, result)
|
|
})
|
|
|
|
t.Run("with non-nullable string", func(t *testing.T) {
|
|
t.Parallel()
|
|
regularString := "not nullable"
|
|
reflectValue := reflect.ValueOf(regularString)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.False(t, ok)
|
|
assert.Nil(t, result)
|
|
})
|
|
|
|
t.Run("with non-nullable int", func(t *testing.T) {
|
|
t.Parallel()
|
|
regularInt := 42
|
|
reflectValue := reflect.ValueOf(regularInt)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.False(t, ok)
|
|
assert.Nil(t, result)
|
|
})
|
|
|
|
t.Run("with non-nullable map", func(t *testing.T) {
|
|
t.Parallel()
|
|
regularMap := map[string]int{"key": 42}
|
|
reflectValue := reflect.ValueOf(regularMap)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.False(t, ok)
|
|
assert.Nil(t, result)
|
|
})
|
|
|
|
t.Run("with non-nullable struct", func(t *testing.T) {
|
|
t.Parallel()
|
|
regularStruct := TestStruct{Name: "test", Age: 30}
|
|
reflectValue := reflect.ValueOf(regularStruct)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.False(t, ok)
|
|
assert.Nil(t, result)
|
|
})
|
|
|
|
t.Run("with nullable double pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
inner := "test"
|
|
ptr := &inner
|
|
nullable := From(ptrFrom(ptr))
|
|
reflectValue := reflect.ValueOf(nullable)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, result)
|
|
|
|
value, isSet := result.GetUntyped()
|
|
assert.True(t, isSet)
|
|
assert.Equal(t, ptr, value)
|
|
assert.Equal(t, "test", *value.(*string))
|
|
})
|
|
|
|
t.Run("with nullable slice", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom([]string{"a", "b", "c"}))
|
|
reflectValue := reflect.ValueOf(nullable)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, result)
|
|
|
|
value, isSet := result.GetUntyped()
|
|
assert.True(t, isSet)
|
|
assert.Equal(t, []string{"a", "b", "c"}, value)
|
|
})
|
|
|
|
t.Run("with nullable struct", func(t *testing.T) {
|
|
t.Parallel()
|
|
testStruct := TestStruct{Name: "Alice", Age: 25}
|
|
nullable := From(ptrFrom(testStruct))
|
|
reflectValue := reflect.ValueOf(nullable)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, result)
|
|
|
|
value, isSet := result.GetUntyped()
|
|
assert.True(t, isSet)
|
|
assert.Equal(t, testStruct, value)
|
|
})
|
|
|
|
t.Run("with pointer to nullable", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("test"))
|
|
ptrToNullable := &nullable
|
|
reflectValue := reflect.ValueOf(ptrToNullable)
|
|
|
|
// This should work since the pointer to nullable still contains a nullable
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, result)
|
|
|
|
value, isSet := result.GetUntyped()
|
|
assert.True(t, isSet)
|
|
assert.Equal(t, "test", value)
|
|
})
|
|
|
|
t.Run("with interface containing nullable", func(t *testing.T) {
|
|
t.Parallel()
|
|
nullable := From(ptrFrom("test"))
|
|
var iface interface{} = nullable
|
|
reflectValue := reflect.ValueOf(iface)
|
|
|
|
result, ok := AsOptionalNullable(reflectValue)
|
|
assert.True(t, ok)
|
|
assert.NotNil(t, result)
|
|
|
|
value, isSet := result.GetUntyped()
|
|
assert.True(t, isSet)
|
|
assert.Equal(t, "test", value)
|
|
})
|
|
}
|