Files
plexgo/optionalnullable/optionalnullable_test.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)
})
}