// 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) }) }