From c05ea427c7b1ef874d8904a3bd99729fbad385da Mon Sep 17 00:00:00 2001 From: gkits <56302678+gKits@users.noreply.github.com> Date: Wed, 23 Oct 2024 20:38:38 +0200 Subject: [PATCH] feat: add implementation for all sql.Null types --- bool.go | 41 +++++++++++++++++++++ bool_test.go | 59 ++++++++++++++++++++++++++++++ byte.go | 41 +++++++++++++++++++++ byte_test.go | 59 ++++++++++++++++++++++++++++++ float64.go | 41 +++++++++++++++++++++ float64_test.go | 59 ++++++++++++++++++++++++++++++ generic.go | 42 ++++++++++++++++++++++ generic_test.go | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 11 ++++++ go.sum | 10 ++++++ int16.go | 41 +++++++++++++++++++++ int16_test.go | 59 ++++++++++++++++++++++++++++++ int32.go | 41 +++++++++++++++++++++ int32_test.go | 59 ++++++++++++++++++++++++++++++ int64.go | 41 +++++++++++++++++++++ int64_test.go | 59 ++++++++++++++++++++++++++++++ string.go | 41 +++++++++++++++++++++ string_test.go | 59 ++++++++++++++++++++++++++++++ time.go | 42 ++++++++++++++++++++++ time_test.go | 60 +++++++++++++++++++++++++++++++ 20 files changed, 961 insertions(+) create mode 100644 bool.go create mode 100644 bool_test.go create mode 100644 byte.go create mode 100644 byte_test.go create mode 100644 float64.go create mode 100644 float64_test.go create mode 100644 generic.go create mode 100644 generic_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 int16.go create mode 100644 int16_test.go create mode 100644 int32.go create mode 100644 int32_test.go create mode 100644 int64.go create mode 100644 int64_test.go create mode 100644 string.go create mode 100644 string_test.go create mode 100644 time.go create mode 100644 time_test.go diff --git a/bool.go b/bool.go new file mode 100644 index 0000000..5fc13b9 --- /dev/null +++ b/bool.go @@ -0,0 +1,41 @@ +package sqlnull + +import ( + "database/sql" + "encoding/json" + "reflect" +) + +type NullBool sql.NullBool + +func (n NullBool) MarshalJSON() ([]byte, error) { + if !n.Valid { + return json.Marshal(nil) + } + return json.Marshal(n.Bool) +} + +func (n *NullBool) UnmarshalJSON(data []byte) error { + var target *bool + if err := json.Unmarshal(data, &target); err != nil { + return err + } + + n.Valid = target != nil + if n.Valid { + n.Bool = *target + } else { + n.Bool = false + } + return nil +} + +func (n *NullBool) Scan(src any) error { + var sqln sql.NullBool + if err := sqln.Scan(src); err != nil { + return err + } + n.Bool = sqln.Bool + n.Valid = reflect.TypeOf(src) == nil + return nil +} diff --git a/bool_test.go b/bool_test.go new file mode 100644 index 0000000..d84ae78 --- /dev/null +++ b/bool_test.go @@ -0,0 +1,59 @@ +package sqlnull_test + +import ( + "encoding/json" + "testing" + + "github.com/gkits/sqlnull" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Bool_MarshalJSON(t *testing.T) { + cases := []struct { + name string + in sqlnull.NullBool + want string + }{ + { + name: "marshal false bool to null", + in: sqlnull.NullBool{ + Bool: false, + Valid: false, + }, + want: "null", + }, + { + name: "marshal true bool to null", + in: sqlnull.NullBool{ + Bool: true, + Valid: false, + }, + want: "null", + }, + { + name: "marshal false bool", + in: sqlnull.NullBool{ + Bool: false, + Valid: true, + }, + want: "false", + }, + { + name: "marshal true bool", + in: sqlnull.NullBool{ + Bool: true, + Valid: true, + }, + want: "true", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, err := json.Marshal(c.in) + require.NoError(t, err) + assert.JSONEq(t, c.want, string(got)) + }) + } +} diff --git a/byte.go b/byte.go new file mode 100644 index 0000000..7094590 --- /dev/null +++ b/byte.go @@ -0,0 +1,41 @@ +package sqlnull + +import ( + "database/sql" + "encoding/json" + "reflect" +) + +type NullByte sql.NullByte + +func (n NullByte) MarshalJSON() ([]byte, error) { + if !n.Valid { + return json.Marshal(nil) + } + return json.Marshal(n.Byte) +} + +func (n *NullByte) UnmarshalJSON(data []byte) error { + var target *byte + if err := json.Unmarshal(data, &target); err != nil { + return err + } + + n.Valid = target != nil + if n.Valid { + n.Byte = *target + } else { + n.Byte = 0x00 + } + return nil +} + +func (n *NullByte) Scan(src any) error { + var sqln sql.NullByte + if err := sqln.Scan(src); err != nil { + return err + } + n.Byte = sqln.Byte + n.Valid = reflect.TypeOf(src) == nil + return nil +} diff --git a/byte_test.go b/byte_test.go new file mode 100644 index 0000000..384f76b --- /dev/null +++ b/byte_test.go @@ -0,0 +1,59 @@ +package sqlnull_test + +import ( + "encoding/json" + "testing" + + "github.com/gkits/sqlnull" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Byte_MarshalJSON(t *testing.T) { + cases := []struct { + name string + in sqlnull.NullByte + want string + }{ + { + name: "marshal zero byte to null", + in: sqlnull.NullByte{ + Byte: 0x00, + Valid: false, + }, + want: "null", + }, + { + name: "marshal non zero byte to null", + in: sqlnull.NullByte{ + Byte: 0x99, + Valid: false, + }, + want: "null", + }, + { + name: "marshal zero byte", + in: sqlnull.NullByte{ + Byte: 0x00, + Valid: true, + }, + want: "0", + }, + { + name: "marshal non zero byte", + in: sqlnull.NullByte{ + Byte: 0x69, + Valid: true, + }, + want: "105", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, err := json.Marshal(c.in) + require.NoError(t, err) + assert.JSONEq(t, c.want, string(got)) + }) + } +} diff --git a/float64.go b/float64.go new file mode 100644 index 0000000..f83e223 --- /dev/null +++ b/float64.go @@ -0,0 +1,41 @@ +package sqlnull + +import ( + "database/sql" + "encoding/json" + "reflect" +) + +type NullFloat64 sql.NullFloat64 + +func (n NullFloat64) MarshalJSON() ([]byte, error) { + if !n.Valid { + return json.Marshal(nil) + } + return json.Marshal(n.Float64) +} + +func (n *NullFloat64) UnmarshalJSON(data []byte) error { + var target *float64 + if err := json.Unmarshal(data, &target); err != nil { + return err + } + + n.Valid = target != nil + if n.Valid { + n.Float64 = *target + } else { + n.Float64 = 0 + } + return nil +} + +func (n *NullFloat64) Scan(src any) error { + var sqln sql.NullFloat64 + if err := sqln.Scan(src); err != nil { + return err + } + n.Float64 = sqln.Float64 + n.Valid = reflect.TypeOf(src) == nil + return nil +} diff --git a/float64_test.go b/float64_test.go new file mode 100644 index 0000000..8907894 --- /dev/null +++ b/float64_test.go @@ -0,0 +1,59 @@ +package sqlnull_test + +import ( + "encoding/json" + "testing" + + "github.com/gkits/sqlnull" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Float64_MarshalJSON(t *testing.T) { + cases := []struct { + name string + in sqlnull.NullFloat64 + want string + }{ + { + name: "marshal 0 float to null", + in: sqlnull.NullFloat64{ + Float64: 0, + Valid: false, + }, + want: "null", + }, + { + name: "marshal non 0 float to null", + in: sqlnull.NullFloat64{ + Float64: 1234.56789, + Valid: false, + }, + want: "null", + }, + { + name: "marshal 0 float", + in: sqlnull.NullFloat64{ + Float64: 0.000, + Valid: true, + }, + want: "0", + }, + { + name: "marshal non 0 float", + in: sqlnull.NullFloat64{ + Float64: 10101.0101, + Valid: true, + }, + want: "10101.0101", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, err := json.Marshal(c.in) + require.NoError(t, err) + assert.JSONEq(t, c.want, string(got)) + }) + } +} diff --git a/generic.go b/generic.go new file mode 100644 index 0000000..7bd12d4 --- /dev/null +++ b/generic.go @@ -0,0 +1,42 @@ +package sqlnull + +import ( + "database/sql" + "encoding/json" + "reflect" +) + +type Null[T any] sql.Null[T] + +func (n Null[T]) MarshalJSON() ([]byte, error) { + if !n.Valid { + return json.Marshal(nil) + } + return json.Marshal(n.V) +} + +func (n *Null[T]) UnmarshalJSON(data []byte) error { + var target *T + if err := json.Unmarshal(data, &target); err != nil { + return err + } + + n.Valid = target != nil + if n.Valid { + n.V = *target + } else { + var zero T + n.V = zero + } + return nil +} + +func (n *Null[T]) Scan(src any) error { + var sqln sql.Null[T] + if err := sqln.Scan(src); err != nil { + return err + } + n.V = sqln.V + n.Valid = reflect.TypeOf(src) == nil + return nil +} diff --git a/generic_test.go b/generic_test.go new file mode 100644 index 0000000..35ea3c5 --- /dev/null +++ b/generic_test.go @@ -0,0 +1,96 @@ +package sqlnull_test + +import ( + "database/sql" + "encoding/json" + "testing" + + "github.com/gkits/sqlnull" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type sqlNullable interface { + json.Marshaler + json.Unmarshaler + sql.Scanner +} + +func Test_Generic_MarshalJSON(t *testing.T) { + cases := []struct { + name string + in sqlNullable + want string + }{ + { + name: "marshal generic string to null", + in: &sqlnull.Null[string]{ + V: "", + Valid: false, + }, + want: "null", + }, + { + name: "marshal generic int to null", + in: &sqlnull.Null[int]{ + V: 0, + Valid: false, + }, + want: "null", + }, + { + name: "marshal generic string to null with set value", + in: &sqlnull.Null[string]{ + V: "this does not matter", + Valid: false, + }, + want: "null", + }, + { + name: "marshal generic string", + in: &sqlnull.Null[string]{ + V: "this is sooo important", + Valid: true, + }, + want: `"this is sooo important"`, + }, + { + name: "marshal generic int", + in: &sqlnull.Null[int]{ + V: 69, + Valid: true, + }, + want: "69", + }, + { + name: "marshal generic float64", + in: &sqlnull.Null[float64]{ + V: 420.001, + Valid: true, + }, + want: "420.001", + }, + { + name: "marshal generic struct", + in: &sqlnull.Null[struct { + Name string + Age int + }]{ + V: struct { + Name string + Age int + }{"Joe", 25}, + Valid: true, + }, + want: `{ "Name": "Joe", "Age": 25 }`, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, err := json.Marshal(c.in) + require.NoError(t, err) + assert.JSONEq(t, c.want, string(got)) + }) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2e7cbd8 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/gkits/sqlnull + +go 1.23.2 + +require github.com/stretchr/testify v1.9.0 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..60ce688 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/int16.go b/int16.go new file mode 100644 index 0000000..6a4f08a --- /dev/null +++ b/int16.go @@ -0,0 +1,41 @@ +package sqlnull + +import ( + "database/sql" + "encoding/json" + "reflect" +) + +type NullInt16 sql.NullInt16 + +func (n NullInt16) MarshalJSON() ([]byte, error) { + if !n.Valid { + return json.Marshal(nil) + } + return json.Marshal(n.Int16) +} + +func (n *NullInt16) UnmarshalJSON(data []byte) error { + var target *int16 + if err := json.Unmarshal(data, &target); err != nil { + return err + } + + n.Valid = target != nil + if n.Valid { + n.Int16 = *target + } else { + n.Int16 = 0 + } + return nil +} + +func (n *NullInt16) Scan(src any) error { + var sqln sql.NullInt16 + if err := sqln.Scan(src); err != nil { + return err + } + n.Int16 = sqln.Int16 + n.Valid = reflect.TypeOf(src) == nil + return nil +} diff --git a/int16_test.go b/int16_test.go new file mode 100644 index 0000000..d755a58 --- /dev/null +++ b/int16_test.go @@ -0,0 +1,59 @@ +package sqlnull_test + +import ( + "encoding/json" + "testing" + + "github.com/gkits/sqlnull" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Int16_MarshalJSON(t *testing.T) { + cases := []struct { + name string + in sqlnull.NullInt16 + want string + }{ + { + name: "marshal 0 int16 to null", + in: sqlnull.NullInt16{ + Int16: 0, + Valid: false, + }, + want: "null", + }, + { + name: "marshal non 0 int16 to null", + in: sqlnull.NullInt16{ + Int16: 32767, + Valid: false, + }, + want: "null", + }, + { + name: "marshal 0 int16", + in: sqlnull.NullInt16{ + Int16: 0, + Valid: true, + }, + want: "0", + }, + { + name: "marshal non 0 int16", + in: sqlnull.NullInt16{ + Int16: 32767, + Valid: true, + }, + want: "32767", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, err := json.Marshal(c.in) + require.NoError(t, err) + assert.JSONEq(t, c.want, string(got)) + }) + } +} diff --git a/int32.go b/int32.go new file mode 100644 index 0000000..64eb5fc --- /dev/null +++ b/int32.go @@ -0,0 +1,41 @@ +package sqlnull + +import ( + "database/sql" + "encoding/json" + "reflect" +) + +type NullInt32 sql.NullInt32 + +func (n NullInt32) MarshalJSON() ([]byte, error) { + if !n.Valid { + return json.Marshal(nil) + } + return json.Marshal(n.Int32) +} + +func (n *NullInt32) UnmarshalJSON(data []byte) error { + var target *int32 + if err := json.Unmarshal(data, &target); err != nil { + return err + } + + n.Valid = target != nil + if n.Valid { + n.Int32 = *target + } else { + n.Int32 = 0 + } + return nil +} + +func (n *NullInt32) Scan(src any) error { + var sqln sql.NullInt32 + if err := sqln.Scan(src); err != nil { + return err + } + n.Int32 = sqln.Int32 + n.Valid = reflect.TypeOf(src) == nil + return nil +} diff --git a/int32_test.go b/int32_test.go new file mode 100644 index 0000000..8e022e4 --- /dev/null +++ b/int32_test.go @@ -0,0 +1,59 @@ +package sqlnull_test + +import ( + "encoding/json" + "testing" + + "github.com/gkits/sqlnull" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Int32_MarshalJSON(t *testing.T) { + cases := []struct { + name string + in sqlnull.NullInt32 + want string + }{ + { + name: "marshal 0 int32 to null", + in: sqlnull.NullInt32{ + Int32: 0, + Valid: false, + }, + want: "null", + }, + { + name: "marshal non 0 int32 to null", + in: sqlnull.NullInt32{ + Int32: 2147483647, + Valid: false, + }, + want: "null", + }, + { + name: "marshal 0 int32", + in: sqlnull.NullInt32{ + Int32: 0, + Valid: true, + }, + want: "0", + }, + { + name: "marshal non 0 int32", + in: sqlnull.NullInt32{ + Int32: 2147483647, + Valid: true, + }, + want: "2147483647", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, err := json.Marshal(c.in) + require.NoError(t, err) + assert.JSONEq(t, c.want, string(got)) + }) + } +} diff --git a/int64.go b/int64.go new file mode 100644 index 0000000..1eb5acd --- /dev/null +++ b/int64.go @@ -0,0 +1,41 @@ +package sqlnull + +import ( + "database/sql" + "encoding/json" + "reflect" +) + +type NullInt64 sql.NullInt64 + +func (n NullInt64) MarshalJSON() ([]byte, error) { + if !n.Valid { + return json.Marshal(nil) + } + return json.Marshal(n.Int64) +} + +func (n *NullInt64) UnmarshalJSON(data []byte) error { + var target *int64 + if err := json.Unmarshal(data, &target); err != nil { + return err + } + + n.Valid = target != nil + if n.Valid { + n.Int64 = *target + } else { + n.Int64 = 0 + } + return nil +} + +func (n *NullInt64) Scan(src any) error { + var sqln sql.NullInt64 + if err := sqln.Scan(src); err != nil { + return err + } + n.Int64 = sqln.Int64 + n.Valid = reflect.TypeOf(src) == nil + return nil +} diff --git a/int64_test.go b/int64_test.go new file mode 100644 index 0000000..1332ec1 --- /dev/null +++ b/int64_test.go @@ -0,0 +1,59 @@ +package sqlnull_test + +import ( + "encoding/json" + "testing" + + "github.com/gkits/sqlnull" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Int64_MarshalJSON(t *testing.T) { + cases := []struct { + name string + in sqlnull.NullInt64 + want string + }{ + { + name: "marshal 0 int64 to null", + in: sqlnull.NullInt64{ + Int64: 0, + Valid: false, + }, + want: "null", + }, + { + name: "marshal non 0 int64 to null", + in: sqlnull.NullInt64{ + Int64: 9223372036854775807, + Valid: false, + }, + want: "null", + }, + { + name: "marshal 0 int64", + in: sqlnull.NullInt64{ + Int64: 0, + Valid: true, + }, + want: "0", + }, + { + name: "marshal non 0 int64", + in: sqlnull.NullInt64{ + Int64: 9223372036854775807, + Valid: true, + }, + want: "9223372036854775807", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, err := json.Marshal(c.in) + require.NoError(t, err) + assert.JSONEq(t, c.want, string(got)) + }) + } +} diff --git a/string.go b/string.go new file mode 100644 index 0000000..1c34cb5 --- /dev/null +++ b/string.go @@ -0,0 +1,41 @@ +package sqlnull + +import ( + "database/sql" + "encoding/json" + "reflect" +) + +type NullString sql.NullString + +func (n NullString) MarshalJSON() ([]byte, error) { + if !n.Valid { + return json.Marshal(nil) + } + return json.Marshal(n.String) +} + +func (n *NullString) UnmarshalJSON(data []byte) error { + var target *string + if err := json.Unmarshal(data, &target); err != nil { + return err + } + + n.Valid = target != nil + if n.Valid { + n.String = *target + } else { + n.String = "" + } + return nil +} + +func (n *NullString) Scan(src any) error { + var sqln sql.NullString + if err := sqln.Scan(src); err != nil { + return err + } + n.String = sqln.String + n.Valid = reflect.TypeOf(src) == nil + return nil +} diff --git a/string_test.go b/string_test.go new file mode 100644 index 0000000..54333e7 --- /dev/null +++ b/string_test.go @@ -0,0 +1,59 @@ +package sqlnull_test + +import ( + "encoding/json" + "testing" + + "github.com/gkits/sqlnull" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_String_MarshalJSON(t *testing.T) { + cases := []struct { + name string + in sqlnull.NullString + want string + }{ + { + name: "marshal empty string to null", + in: sqlnull.NullString{ + String: "", + Valid: false, + }, + want: "null", + }, + { + name: "marshal non empty string to null", + in: sqlnull.NullString{ + String: "null", + Valid: false, + }, + want: "null", + }, + { + name: "marshal empty string", + in: sqlnull.NullString{ + String: "", + Valid: true, + }, + want: `""`, + }, + { + name: "marshal non empty string", + in: sqlnull.NullString{ + String: "null", + Valid: true, + }, + want: `"null"`, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, err := json.Marshal(c.in) + require.NoError(t, err) + assert.JSONEq(t, c.want, string(got)) + }) + } +} diff --git a/time.go b/time.go new file mode 100644 index 0000000..096dd75 --- /dev/null +++ b/time.go @@ -0,0 +1,42 @@ +package sqlnull + +import ( + "database/sql" + "encoding/json" + "reflect" + "time" +) + +type NullTime sql.NullTime + +func (n NullTime) MarshalJSON() ([]byte, error) { + if !n.Valid { + return json.Marshal(nil) + } + return json.Marshal(n.Time) +} + +func (n *NullTime) UnmarshalJSON(data []byte) error { + var target *time.Time + if err := json.Unmarshal(data, &target); err != nil { + return err + } + + n.Valid = target != nil + if n.Valid { + n.Time = *target + } else { + n.Time = time.Time{} + } + return nil +} + +func (n *NullTime) Scan(src any) error { + var sqln sql.NullTime + if err := sqln.Scan(src); err != nil { + return err + } + n.Time = sqln.Time + n.Valid = reflect.TypeOf(src) == nil + return nil +} diff --git a/time_test.go b/time_test.go new file mode 100644 index 0000000..f79efb4 --- /dev/null +++ b/time_test.go @@ -0,0 +1,60 @@ +package sqlnull_test + +import ( + "encoding/json" + "testing" + "time" + + "github.com/gkits/sqlnull" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Time_MarshalJSON(t *testing.T) { + cases := []struct { + name string + in sqlnull.NullTime + want string + }{ + { + name: "marshal zero timestamp to null", + in: sqlnull.NullTime{ + Time: time.Time{}, + Valid: false, + }, + want: "null", + }, + { + name: "marshal non zero timestamp to null", + in: sqlnull.NullTime{ + Time: time.Date(2024, 10, 23, 17, 50, 0, 0, time.UTC), + Valid: false, + }, + want: "null", + }, + { + name: "marshal zero timestamp", + in: sqlnull.NullTime{ + Time: time.Time{}, + Valid: true, + }, + want: `"0001-01-01T00:00:00Z"`, + }, + { + name: "marshal non zero timestamp", + in: sqlnull.NullTime{ + Time: time.Date(2024, 10, 23, 17, 50, 0, 0, time.UTC), + Valid: true, + }, + want: `"2024-10-23T17:50:00Z"`, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, err := json.Marshal(c.in) + require.NoError(t, err) + assert.JSONEq(t, c.want, string(got)) + }) + } +}