Skip to content

Commit 8924738

Browse files
committed
Spec compliance: coercion of Int values
This fixes an issue with coercing Int values when provided a floating point value which disagrees with the spec definition. Updates test cases which were along the same incorrect line. Note: this is technically a breaking change Fixes #827
1 parent d7fe661 commit 8924738

File tree

4 files changed

+54
-24
lines changed

4 files changed

+54
-24
lines changed

src/type/__tests__/serialization-test.js

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,30 @@ describe('Type System: Scalar coercion', () => {
3333
GraphQLInt.serialize(-1)
3434
).to.equal(-1);
3535
expect(
36+
GraphQLInt.serialize(1e5)
37+
).to.equal(100000);
38+
// The GraphQL specification does not allow serializing non-integer values
39+
// as Int to avoid accidental data loss.
40+
expect(() =>
3641
GraphQLInt.serialize(0.1)
37-
).to.equal(0);
38-
expect(
42+
).to.throw(
43+
'Int cannot represent non-integer value: 0.1'
44+
);
45+
expect(() =>
3946
GraphQLInt.serialize(1.1)
40-
).to.equal(1);
41-
expect(
47+
).to.throw(
48+
'Int cannot represent non-integer value: 1.1'
49+
);
50+
expect(() =>
4251
GraphQLInt.serialize(-1.1)
43-
).to.equal(-1);
44-
expect(
45-
GraphQLInt.serialize(1e5)
46-
).to.equal(100000);
52+
).to.throw(
53+
'Int cannot represent non-integer value: -1.1'
54+
);
55+
expect(() =>
56+
GraphQLInt.serialize('-1.1')
57+
).to.throw(
58+
'Int cannot represent non-integer value: -1.1'
59+
);
4760
// Maybe a safe JavaScript int, but bigger than 2^32, so not
4861
// representable as a GraphQL Int
4962
expect(() =>
@@ -67,9 +80,6 @@ describe('Type System: Scalar coercion', () => {
6780
).to.throw(
6881
'Int cannot represent non 32-bit signed integer value: -1e+100'
6982
);
70-
expect(
71-
GraphQLInt.serialize('-1.1')
72-
).to.equal(-1);
7383
expect(() =>
7484
GraphQLInt.serialize('one')
7585
).to.throw(
@@ -86,6 +96,11 @@ describe('Type System: Scalar coercion', () => {
8696
).to.throw(
8797
'Int cannot represent non 32-bit signed integer value: (empty string)'
8898
);
99+
expect(() =>
100+
GraphQLInt.serialize(NaN)
101+
).to.throw(
102+
'Int cannot represent non 32-bit signed integer value: NaN'
103+
);
89104
});
90105

91106
it('serializes output float', () => {

src/type/scalars.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,18 @@ function coerceInt(value: mixed): ?number {
2626
);
2727
}
2828
const num = Number(value);
29-
if (num === num && num <= MAX_INT && num >= MIN_INT) {
30-
return (num < 0 ? Math.ceil : Math.floor)(num);
29+
if (num !== num || num > MAX_INT || num < MIN_INT) {
30+
throw new TypeError(
31+
'Int cannot represent non 32-bit signed integer value: ' + String(value)
32+
);
3133
}
32-
throw new TypeError(
33-
'Int cannot represent non 32-bit signed integer value: ' + String(value)
34-
);
34+
const int = Math.floor(num);
35+
if (int !== num) {
36+
throw new TypeError(
37+
'Int cannot represent non-integer value: ' + String(value)
38+
);
39+
}
40+
return int;
3541
}
3642

3743
export const GraphQLInt = new GraphQLScalarType({

src/utilities/__tests__/astFromValue-test.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ describe('astFromValue', () => {
3838
null
3939
);
4040

41+
expect(astFromValue(NaN, GraphQLInt)).to.deep.equal(null);
42+
4143
expect(astFromValue(null, GraphQLBoolean)).to.deep.equal(
4244
{ kind: 'NullValue' }
4345
);
@@ -61,14 +63,16 @@ describe('astFromValue', () => {
6163
{ kind: 'IntValue', value: '123' }
6264
);
6365

64-
expect(astFromValue(123.5, GraphQLInt)).to.deep.equal(
65-
{ kind: 'IntValue', value: '123' }
66-
);
67-
6866
expect(astFromValue(1e4, GraphQLInt)).to.deep.equal(
6967
{ kind: 'IntValue', value: '10000' }
7068
);
7169

70+
// GraphQL spec does not allow coercing non-integer values to Int to avoid
71+
// accidental data loss.
72+
expect(() => astFromValue(123.5, GraphQLInt)).to.throw(
73+
'Int cannot represent non-integer value: 123.5'
74+
);
75+
7276
// Note: outside the bounds of 32bit signed int.
7377
expect(() => astFromValue(1e40, GraphQLInt)).to.throw(
7478
'Int cannot represent non 32-bit signed integer value: 1e+40'

src/utilities/__tests__/isValidJSValue-test.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ describe('isValidJSValue for GraphQLInt', () => {
3131
expectNoErrors(result);
3232
});
3333

34-
it('returns no error for exponent input', () => {
35-
const result = isValidJSValue('1e3', GraphQLInt);
34+
it('returns no error for negative int input', () => {
35+
const result = isValidJSValue('-1', GraphQLInt);
3636
expectNoErrors(result);
3737
});
3838

39-
it('returns no error for float input', () => {
40-
const result = isValidJSValue('1.5', GraphQLInt);
39+
it('returns no error for exponent input', () => {
40+
const result = isValidJSValue('1e3', GraphQLInt);
4141
expectNoErrors(result);
4242
});
4343

@@ -51,6 +51,11 @@ describe('isValidJSValue for GraphQLInt', () => {
5151
expectErrorResult(result, 1);
5252
});
5353

54+
it('returns error for float input as int', () => {
55+
const result = isValidJSValue('1.5', GraphQLInt);
56+
expectErrorResult(result, 1);
57+
});
58+
5459
it('returns a single error for char input', () => {
5560
const result = isValidJSValue('a', GraphQLInt);
5661
expectErrorResult(result, 1);

0 commit comments

Comments
 (0)