Skip to content

Commit 091f91f

Browse files
authored
Merge pull request #453 from Kalbi/coerce-null-int
Coerce float64 to int32 in `NullInt` and vice versa in `NullFloat`
2 parents 7aec311 + 687d3ab commit 091f91f

File tree

2 files changed

+230
-0
lines changed

2 files changed

+230
-0
lines changed

nullable_types.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package graphql
22

33
import (
44
"fmt"
5+
"math"
56
)
67

78
// NullString is a string that can be null. Use it in input structs to
@@ -87,6 +88,13 @@ func (s *NullInt) UnmarshalGraphQL(input interface{}) error {
8788
case int32:
8889
s.Value = &v
8990
return nil
91+
case float64:
92+
coerced := int32(v)
93+
if v < math.MinInt32 || v > math.MaxInt32 || float64(coerced) != v {
94+
return fmt.Errorf("not a 32-bit integer")
95+
}
96+
s.Value = &coerced
97+
return nil
9098
default:
9199
return fmt.Errorf("wrong type for Int: %T", v)
92100
}
@@ -117,6 +125,14 @@ func (s *NullFloat) UnmarshalGraphQL(input interface{}) error {
117125
case float64:
118126
s.Value = &v
119127
return nil
128+
case int32:
129+
coerced := float64(v)
130+
s.Value = &coerced
131+
return nil
132+
case int:
133+
coerced := float64(v)
134+
s.Value = &coerced
135+
return nil
120136
default:
121137
return fmt.Errorf("wrong type for Float: %T", v)
122138
}

nullable_types_test.go

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package graphql_test
2+
3+
import (
4+
"math"
5+
"testing"
6+
7+
. "github.com/graph-gophers/graphql-go"
8+
"github.com/graph-gophers/graphql-go/decode"
9+
)
10+
11+
func TestNullInt_ImplementsUnmarshaler(t *testing.T) {
12+
defer func() {
13+
if err := recover(); err != nil {
14+
t.Error(err)
15+
}
16+
}()
17+
18+
// assert *NullInt implements decode.Unmarshaler interface
19+
var _ decode.Unmarshaler = (*NullInt)(nil)
20+
}
21+
22+
func TestNullInt_UnmarshalGraphQL(t *testing.T) {
23+
type args struct {
24+
input interface{}
25+
}
26+
27+
a := float64(math.MaxInt32 + 1)
28+
b := float64(math.MinInt32 - 1)
29+
c := 1234.6
30+
good := int32(1234)
31+
ref := NullInt{
32+
Value: &good,
33+
Set: true,
34+
}
35+
36+
t.Run("invalid", func(t *testing.T) {
37+
tests := []struct {
38+
name string
39+
args args
40+
wantErr string
41+
}{
42+
{
43+
name: "boolean",
44+
args: args{input: true},
45+
wantErr: "wrong type for Int: bool",
46+
},
47+
{
48+
name: "int32 out of range (+)",
49+
args: args{
50+
input: a,
51+
},
52+
wantErr: "not a 32-bit integer",
53+
},
54+
{
55+
name: "int32 out of range (-)",
56+
args: args{
57+
input: b,
58+
},
59+
wantErr: "not a 32-bit integer",
60+
},
61+
{
62+
name: "non-integer",
63+
args: args{
64+
input: c,
65+
},
66+
wantErr: "not a 32-bit integer",
67+
},
68+
}
69+
70+
for _, tt := range tests {
71+
t.Run(tt.name, func(t *testing.T) {
72+
gt := new(NullInt)
73+
if err := gt.UnmarshalGraphQL(tt.args.input); err != nil {
74+
if err.Error() != tt.wantErr {
75+
t.Errorf("UnmarshalGraphQL() error = %v, want = %s", err, tt.wantErr)
76+
}
77+
78+
return
79+
}
80+
81+
t.Error("UnmarshalGraphQL() expected error not raised")
82+
})
83+
}
84+
})
85+
86+
tests := []struct {
87+
name string
88+
args args
89+
wantEq NullInt
90+
}{
91+
{
92+
name: "int32",
93+
args: args{
94+
input: good,
95+
},
96+
wantEq: ref,
97+
},
98+
{
99+
name: "float64",
100+
args: args{
101+
input: float64(good),
102+
},
103+
wantEq: ref,
104+
},
105+
}
106+
107+
for _, tt := range tests {
108+
t.Run(tt.name, func(t *testing.T) {
109+
gt := new(NullInt)
110+
if err := gt.UnmarshalGraphQL(tt.args.input); err != nil {
111+
t.Errorf("UnmarshalGraphQL() error = %v", err)
112+
return
113+
}
114+
115+
if *gt.Value != *tt.wantEq.Value {
116+
t.Errorf("UnmarshalGraphQL() got = %v, want = %v", *gt.Value, *tt.wantEq.Value)
117+
}
118+
})
119+
}
120+
}
121+
122+
func TestNullFloat_ImplementsUnmarshaler(t *testing.T) {
123+
defer func() {
124+
if err := recover(); err != nil {
125+
t.Error(err)
126+
}
127+
}()
128+
129+
// assert *NullFloat implements decode.Unmarshaler interface
130+
var _ decode.Unmarshaler = (*NullFloat)(nil)
131+
}
132+
133+
func TestNullFloat_UnmarshalGraphQL(t *testing.T) {
134+
type args struct {
135+
input interface{}
136+
}
137+
138+
good := float64(1234)
139+
ref := NullFloat{
140+
Value: &good,
141+
Set: true,
142+
}
143+
144+
t.Run("invalid", func(t *testing.T) {
145+
tests := []struct {
146+
name string
147+
args args
148+
wantErr string
149+
}{
150+
{
151+
name: "boolean",
152+
args: args{input: true},
153+
wantErr: "wrong type for Float: bool",
154+
},
155+
}
156+
157+
for _, tt := range tests {
158+
t.Run(tt.name, func(t *testing.T) {
159+
gt := new(NullFloat)
160+
if err := gt.UnmarshalGraphQL(tt.args.input); err != nil {
161+
if err.Error() != tt.wantErr {
162+
t.Errorf("UnmarshalGraphQL() error = %v, want = %s", err, tt.wantErr)
163+
}
164+
165+
return
166+
}
167+
168+
t.Error("UnmarshalGraphQL() expected error not raised")
169+
})
170+
}
171+
})
172+
173+
tests := []struct {
174+
name string
175+
args args
176+
wantEq NullFloat
177+
}{
178+
{
179+
name: "int",
180+
args: args{
181+
input: int(good),
182+
},
183+
wantEq: ref,
184+
},
185+
{
186+
name: "int32",
187+
args: args{
188+
input: int32(good),
189+
},
190+
wantEq: ref,
191+
},
192+
{
193+
name: "float64",
194+
args: args{
195+
input: good,
196+
},
197+
wantEq: ref,
198+
},
199+
}
200+
201+
for _, tt := range tests {
202+
t.Run(tt.name, func(t *testing.T) {
203+
gt := new(NullFloat)
204+
if err := gt.UnmarshalGraphQL(tt.args.input); err != nil {
205+
t.Errorf("UnmarshalGraphQL() error = %v", err)
206+
return
207+
}
208+
209+
if *gt.Value != *tt.wantEq.Value {
210+
t.Errorf("UnmarshalGraphQL() got = %v, want = %v", *gt.Value, *tt.wantEq.Value)
211+
}
212+
})
213+
}
214+
}

0 commit comments

Comments
 (0)