Skip to content

Commit 3e50f4c

Browse files
committed
add test cases
Signed-off-by: Ashutosh Kumar <sonasingh46@gmail.com>
1 parent 34d6233 commit 3e50f4c

File tree

2 files changed

+118
-15
lines changed

2 files changed

+118
-15
lines changed

types/nullable.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ var nullBytes = []byte("null")
1313
// provided `null` in JSON or not
1414
type Nullable[T any] struct {
1515
Value T
16+
Set bool
1617
Null bool
1718
}
1819

1920
// UnmarshalJSON implements the Unmarshaler interface.
2021
func (t *Nullable[T]) UnmarshalJSON(data []byte) error {
22+
t.Set = true
2123
if bytes.Equal(data, nullBytes) {
2224
t.Null = true
2325
return nil
@@ -32,7 +34,7 @@ func (t *Nullable[T]) UnmarshalJSON(data []byte) error {
3234
// MarshalJSON implements the Marshaler interface.
3335
func (t Nullable[T]) MarshalJSON() ([]byte, error) {
3436
if t.IsNull() {
35-
return []byte("null"), nil
37+
return nullBytes, nil
3638
}
3739
return json.Marshal(t.Value)
3840
}
@@ -42,6 +44,11 @@ func (t *Nullable[T]) IsNull() bool {
4244
return t.Null
4345
}
4446

47+
// IsSet returns true if the value is provided in json
48+
func (t *Nullable[T]) IsSet() bool {
49+
return t.Set
50+
}
51+
4552
func (t *Nullable[T]) Get() (value T, null bool) {
4653
return t.Value, t.IsNull()
4754
}

types/nullable_test.go

Lines changed: 110 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,42 +12,40 @@ type SimpleString struct {
1212
Name Nullable[string] `json:"name"`
1313
}
1414

15-
func TestSimpleString_IsDefined(t *testing.T) {
15+
func TestSimpleString(t *testing.T) {
1616
type testCase struct {
1717
name string
1818
jsonInput []byte
1919
wantNull bool
20+
wantSet bool
2021
}
2122
tests := []testCase{
2223
{
2324
name: "simple object: set name to some non null value",
2425
jsonInput: []byte(`{"name":"yolo"}`),
2526
wantNull: false,
27+
wantSet: true,
2628
},
2729

2830
{
2931
name: "simple object: set name to empty string value",
3032
jsonInput: []byte(`{"name":""}`),
31-
// since name field is present in JSON, want defined to be true
32-
wantNull: false,
33+
wantNull: false,
34+
wantSet: true,
3335
},
3436

3537
{
3638
name: "simple object: set name to null value",
3739
jsonInput: []byte(`{"name":null}`),
38-
// since name field is present in JSON, want defined to be true
39-
wantNull: true,
40+
wantNull: true,
41+
wantSet: true,
4042
},
41-
/*
42-
Note that it is not possible to differentiate b/w `{"name":""}` and `{"name":null}`
43-
as both will result in defined to be true but the value will always be the zero
44-
value and hence cannot tell which one was null
45-
*/
43+
4644
{
4745
name: "simple object: do not provide name in json data",
4846
jsonInput: []byte(`{}`),
49-
// since name field is present in JSON, want defined to be false
50-
wantNull: false,
47+
wantNull: false,
48+
wantSet: false,
5149
},
5250
}
5351
for _, tt := range tests {
@@ -56,6 +54,7 @@ func TestSimpleString_IsDefined(t *testing.T) {
5654
err := json.Unmarshal(tt.jsonInput, &obj)
5755
assert.NoError(t, err)
5856
assert.Equalf(t, tt.wantNull, obj.Name.IsNull(), "IsNull()")
57+
assert.Equalf(t, tt.wantSet, obj.Name.IsSet(), "IsSet()")
5958
fmt.Println(obj.Name.Get())
6059
})
6160
}
@@ -66,35 +65,40 @@ type SimpleInt struct {
6665
ReplicaCount Nullable[int] `json:"replicaCount"`
6766
}
6867

69-
func TestSimpleInt_IsDefined(t *testing.T) {
68+
func TestSimpleInt(t *testing.T) {
7069
type testCase struct {
7170
name string
7271
jsonInput []byte
7372
wantNull bool
73+
wantSet bool
7474
}
7575
tests := []testCase{
7676
{
7777
name: "simple object: set name to some non null value",
7878
jsonInput: []byte(`{"replicaCount":1}`),
7979
wantNull: false,
80+
wantSet: true,
8081
},
8182

8283
{
8384
name: "simple object: set name to empty value",
8485
jsonInput: []byte(`{"replicaCount":0}`),
8586
wantNull: false,
87+
wantSet: true,
8688
},
8789

8890
{
8991
name: "simple object: set name to null value",
9092
jsonInput: []byte(`{"replicaCount":null}`),
9193
wantNull: true,
94+
wantSet: true,
9295
},
9396

9497
{
9598
name: "simple object: do not provide name in json data",
9699
jsonInput: []byte(`{}`),
97100
wantNull: false,
101+
wantSet: false,
98102
},
99103
}
100104
for _, tt := range tests {
@@ -103,6 +107,7 @@ func TestSimpleInt_IsDefined(t *testing.T) {
103107
err := json.Unmarshal(tt.jsonInput, &obj)
104108
assert.NoError(t, err)
105109
assert.Equalf(t, tt.wantNull, obj.ReplicaCount.IsNull(), "IsNull()")
110+
assert.Equalf(t, tt.wantSet, obj.ReplicaCount.IsSet(), "IsSet()")
106111
})
107112
}
108113
}
@@ -112,35 +117,40 @@ type SimplePointerInt struct {
112117
ReplicaCount Nullable[*int] `json:"replicaCount"`
113118
}
114119

115-
func TestSimplePointerInt_IsDefined(t *testing.T) {
120+
func TestSimplePointerInt(t *testing.T) {
116121
type testCase struct {
117122
name string
118123
jsonInput []byte
119124
wantNull bool
125+
wantSet bool
120126
}
121127
tests := []testCase{
122128
{
123129
name: "simple object: set name to some non null value",
124130
jsonInput: []byte(`{"replicaCount":1}`),
125131
wantNull: false,
132+
wantSet: true,
126133
},
127134

128135
{
129136
name: "simple object: set name to empty value",
130137
jsonInput: []byte(`{"replicaCount":0}`),
131138
wantNull: false,
139+
wantSet: true,
132140
},
133141

134142
{
135143
name: "simple object: set name to null value",
136144
jsonInput: []byte(`{"replicaCount":null}`),
137145
wantNull: true,
146+
wantSet: true,
138147
},
139148

140149
{
141150
name: "simple object: do not provide name in json data",
142151
jsonInput: []byte(`{}`),
143152
wantNull: false,
153+
wantSet: false,
144154
},
145155
}
146156
for _, tt := range tests {
@@ -149,6 +159,92 @@ func TestSimplePointerInt_IsDefined(t *testing.T) {
149159
err := json.Unmarshal(tt.jsonInput, &obj)
150160
assert.NoError(t, err)
151161
assert.Equalf(t, tt.wantNull, obj.ReplicaCount.IsNull(), "IsNull()")
162+
assert.Equalf(t, tt.wantSet, obj.ReplicaCount.IsSet(), "IsSet()")
163+
})
164+
}
165+
}
166+
167+
type TestComplex struct {
168+
SimpleInt Nullable[SimpleInt] `json:"simple_int"`
169+
SimpleString Nullable[SimpleString] `json:"simple_string"`
170+
StringList Nullable[[]string] `json:"string_list"`
171+
}
172+
173+
func TestMixed(t *testing.T) {
174+
type testCase struct {
175+
name string
176+
jsonInput []byte
177+
assert func(obj TestComplex, t *testing.T)
178+
}
179+
tests := []testCase{
180+
{
181+
name: "empty json input",
182+
jsonInput: []byte(`{}`),
183+
assert: func(obj TestComplex, t *testing.T) {
184+
assert.Equalf(t, false, obj.SimpleInt.Value.ReplicaCount.IsSet(), "replica count should not be set")
185+
assert.Equalf(t, false, obj.SimpleInt.Value.ReplicaCount.IsNull(), "replica count should not be null")
186+
assert.Equalf(t, false, obj.SimpleString.Value.Name.IsSet(), "name should not be set")
187+
assert.Equalf(t, false, obj.SimpleString.Value.Name.IsNull(), "name should not be null")
188+
assert.Equalf(t, false, obj.StringList.IsSet(), "string list should not be set")
189+
assert.Equalf(t, false, obj.StringList.IsNull(), "string list should not be null")
190+
},
191+
},
192+
193+
{
194+
name: "replica count having non null value",
195+
jsonInput: []byte(`{"simple_int":{"replicaCount":1}}`),
196+
assert: func(obj TestComplex, t *testing.T) {
197+
assert.Equalf(t, false, obj.SimpleInt.Value.ReplicaCount.IsNull(), "replica count should NOT be null")
198+
assert.Equalf(t, true, obj.SimpleInt.Value.ReplicaCount.IsSet(), "replica count should be set")
199+
assert.Equalf(t, false, obj.SimpleString.Value.Name.IsSet(), "name should NOT be set")
200+
assert.Equalf(t, false, obj.SimpleString.Value.Name.IsNull(), "name should NOT be null")
201+
gotValue, isNull := obj.SimpleInt.Value.ReplicaCount.Get()
202+
assert.Equalf(t, false, isNull, "replica count should NOT be null")
203+
assert.Equalf(t, 1, gotValue, "replica count should be 1")
204+
},
205+
},
206+
207+
{
208+
name: "string list having null value",
209+
jsonInput: []byte(`{"string_list": null}`),
210+
assert: func(obj TestComplex, t *testing.T) {
211+
assert.Equalf(t, true, obj.StringList.IsSet(), "string_list should be set")
212+
assert.Equalf(t, true, obj.StringList.IsNull(), "string_list should be null")
213+
},
214+
},
215+
216+
{
217+
name: "string list having non null value",
218+
jsonInput: []byte(`{"string_list": ["foo", "bar"]}`),
219+
assert: func(obj TestComplex, t *testing.T) {
220+
assert.Equalf(t, true, obj.StringList.IsSet(), "string_list should be set")
221+
assert.Equalf(t, false, obj.StringList.IsNull(), "string_list should not be null")
222+
gotStringList, isNull := obj.StringList.Get()
223+
assert.Equalf(t, false, isNull, "string_list should not be null")
224+
assert.Equalf(t, []string{"foo", "bar"}, gotStringList, "string_list should have the values as provided in the jSON")
225+
226+
},
227+
},
228+
229+
{
230+
name: "set string list having empty value",
231+
jsonInput: []byte(`{"string_list":[]}`),
232+
assert: func(obj TestComplex, t *testing.T) {
233+
assert.Equalf(t, true, obj.StringList.IsSet(), "string_list should be set")
234+
assert.Equalf(t, false, obj.StringList.IsNull(), "string_list should not be null")
235+
gotStringList, isNull := obj.StringList.Get()
236+
assert.Equalf(t, false, isNull, "string_list should not be null")
237+
assert.Equalf(t, []string{}, gotStringList, "string_list should have the values as provided in the jSON")
238+
239+
},
240+
},
241+
}
242+
for _, tt := range tests {
243+
t.Run(tt.name, func(t1 *testing.T) {
244+
var obj TestComplex
245+
err := json.Unmarshal(tt.jsonInput, &obj)
246+
assert.NoError(t, err)
247+
tt.assert(obj, t)
152248
})
153249
}
154250
}

0 commit comments

Comments
 (0)