Skip to content

Commit

Permalink
Update built-in scalars' parseLiteral method to throw TypeError
Browse files Browse the repository at this point in the history
  • Loading branch information
danielrearden committed May 3, 2019
1 parent 83ccbe2 commit 8eeee77
Show file tree
Hide file tree
Showing 6 changed files with 702 additions and 48 deletions.
3 changes: 2 additions & 1 deletion src/type/__tests__/enumType-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ describe('Type System: Enum Values', () => {
expect(result).to.deep.equal({
errors: [
{
message: 'Expected type Int, found GREEN.',
message:
'Expected type Int, found GREEN; Int cannot represent non-integer value: "GREEN"',
locations: [{ line: 1, column: 22 }],
},
],
Expand Down
376 changes: 376 additions & 0 deletions src/type/__tests__/input-coercion-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,376 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
*/

import { describe, it } from 'mocha';
import { expect } from 'chai';

import {
GraphQLInt,
GraphQLID,
GraphQLFloat,
GraphQLString,
GraphQLBoolean,
} from '..';
import { Kind } from '../../language/kinds';

import type {
IntValueNode,
FloatValueNode,
StringValueNode,
BooleanValueNode,
NullValueNode,
} from '../../language/ast';

function buildNode(kind: any, value: any): any {
switch (kind) {
case Kind.INT:
return ({ kind, value }: IntValueNode);
case Kind.FLOAT:
return ({ kind, value }: FloatValueNode);
case Kind.STRING:
return ({ kind, value }: StringValueNode);
case Kind.BOOLEAN:
return ({ kind, value }: BooleanValueNode);
case Kind.NULL:
return ({ kind }: NullValueNode);
}
}

describe('Type System: Scalar input coercion', () => {
describe('parseValue', () => {
it('parses input as Int', () => {
expect(GraphQLInt.parseValue(1)).to.equal(1);
expect(GraphQLInt.parseValue(0)).to.equal(0);
expect(GraphQLInt.parseValue(-1)).to.equal(-1);

expect(() => GraphQLInt.parseValue(0.1)).to.throw(
'Int cannot represent non-integer value: 0.1',
);
expect(() => GraphQLInt.parseValue(1.1)).to.throw(
'Int cannot represent non-integer value: 1.1',
);
expect(() => GraphQLInt.parseValue(-1.1)).to.throw(
'Int cannot represent non-integer value: -1.1',
);
expect(() => GraphQLInt.parseValue('-1.1')).to.throw(
'Int cannot represent non-integer value: "-1.1"',
);
expect(() => GraphQLInt.parseValue(9876504321)).to.throw(
'Int cannot represent non 32-bit signed integer value: 9876504321',
);
expect(() => GraphQLInt.parseValue(-9876504321)).to.throw(
'Int cannot represent non 32-bit signed integer value: -9876504321',
);
// Too big to represent as an Int in JavaScript or GraphQL
expect(() => GraphQLInt.parseValue(1e100)).to.throw(
'Int cannot represent non 32-bit signed integer value: 1e+100',
);
expect(() => GraphQLInt.parseValue(-1e100)).to.throw(
'Int cannot represent non 32-bit signed integer value: -1e+100',
);
expect(() => GraphQLInt.parseValue('')).to.throw(
'Int cannot represent non-integer value: ""',
);
expect(() => GraphQLInt.parseValue(NaN)).to.throw(
'Int cannot represent non-integer value: NaN',
);
expect(() => GraphQLInt.parseValue(Infinity)).to.throw(
'Int cannot represent non-integer value: Infinity',
);
expect(() => GraphQLInt.parseValue([5])).to.throw(
'Int cannot represent non-integer value: [5]',
);
expect(() => GraphQLInt.parseValue(false)).to.throw(
'Int cannot represent non-integer value: false',
);
expect(() => GraphQLInt.parseValue(true)).to.throw(
'Int cannot represent non-integer value: true',
);
expect(() => GraphQLInt.parseValue('123')).to.throw(
'Int cannot represent non-integer value: "123"',
);
});

it('parses input as Float', () => {
expect(GraphQLFloat.parseValue(1)).to.equal(1.0);
expect(GraphQLFloat.parseValue(0)).to.equal(0.0);
expect(GraphQLFloat.parseValue(-1)).to.equal(-1.0);
expect(GraphQLFloat.parseValue(0.1)).to.equal(0.1);
expect(GraphQLFloat.parseValue(1.1)).to.equal(1.1);
expect(GraphQLFloat.parseValue(-1.1)).to.equal(-1.1);

expect(() => GraphQLFloat.parseValue(NaN)).to.throw(
'Float cannot represent non numeric value: NaN',
);
expect(() => GraphQLFloat.parseValue(Infinity)).to.throw(
'Float cannot represent non numeric value: Infinity',
);
expect(() => GraphQLFloat.parseValue('one')).to.throw(
'Float cannot represent non numeric value: "one"',
);
expect(() => GraphQLFloat.parseValue('')).to.throw(
'Float cannot represent non numeric value: ""',
);
expect(() => GraphQLFloat.parseValue([5])).to.throw(
'Float cannot represent non numeric value: [5]',
);
expect(() => GraphQLFloat.parseValue(false)).to.throw(
'Float cannot represent non numeric value: false',
);
expect(() => GraphQLFloat.parseValue(true)).to.throw(
'Float cannot represent non numeric value: true',
);
expect(() => GraphQLFloat.parseValue('123.5')).to.throw(
'Float cannot represent non numeric value: "123.5"',
);
});

it('parses input as String', () => {
expect(GraphQLString.parseValue('string')).to.equal('string');

expect(() => GraphQLString.parseValue(NaN)).to.throw(
'String cannot represent a non string value: NaN',
);
expect(() => GraphQLString.parseValue([1])).to.throw(
'String cannot represent a non string value: [1]',
);
expect(() => GraphQLString.parseValue(1)).to.throw(
'String cannot represent a non string value: 1',
);
expect(() => GraphQLString.parseValue(-1.1)).to.throw(
'String cannot represent a non string value: -1.1',
);
expect(() => GraphQLString.parseValue(false)).to.throw(
'String cannot represent a non string value: false',
);
expect(() => GraphQLString.parseValue(true)).to.throw(
'String cannot represent a non string value: true',
);
});

it('parses input as Boolean', () => {
expect(GraphQLBoolean.parseValue(true)).to.equal(true);
expect(GraphQLBoolean.parseValue(false)).to.equal(false);

expect(() => GraphQLBoolean.parseValue(NaN)).to.throw(
'Boolean cannot represent a non boolean value: NaN',
);
expect(() => GraphQLBoolean.parseValue('')).to.throw(
'Boolean cannot represent a non boolean value: ""',
);
expect(() => GraphQLBoolean.parseValue('true')).to.throw(
'Boolean cannot represent a non boolean value: "true"',
);
expect(() => GraphQLBoolean.parseValue([false])).to.throw(
'Boolean cannot represent a non boolean value: [false]',
);
expect(() => GraphQLBoolean.parseValue({})).to.throw(
'Boolean cannot represent a non boolean value: {}',
);
expect(() => GraphQLBoolean.parseValue(0)).to.throw(
'Boolean cannot represent a non boolean value: 0',
);
expect(() => GraphQLBoolean.parseValue(1)).to.throw(
'Boolean cannot represent a non boolean value: 1',
);
});

it('parses input as ID', () => {
expect(GraphQLID.parseValue('string')).to.equal('string');
expect(GraphQLID.parseValue('false')).to.equal('false');
expect(GraphQLID.parseValue('')).to.equal('');
expect(GraphQLID.parseValue(123)).to.equal('123');
expect(GraphQLID.parseValue(0)).to.equal('0');
expect(GraphQLID.parseValue(-1)).to.equal('-1');

expect(() => GraphQLID.parseValue(true)).to.throw(
'ID cannot represent value: true',
);
expect(() => GraphQLID.parseValue(3.14)).to.throw(
'ID cannot represent value: 3.14',
);
expect(() => GraphQLID.parseValue({})).to.throw(
'ID cannot represent value: {}',
);
expect(() => GraphQLID.parseValue(['abc'])).to.throw(
'ID cannot represent value: ["abc"]',
);
});
});

describe('parseLiteral', () => {
it('parses input as Int', () => {
expect(GraphQLInt.parseLiteral(buildNode(Kind.INT, '1'))).to.equal(1);
expect(GraphQLInt.parseLiteral(buildNode(Kind.INT, '0'))).to.equal(0);
expect(GraphQLInt.parseLiteral(buildNode(Kind.INT, '-1'))).to.equal(-1);

// The GraphQL specification does not allow serializing non-integer values
// as Int to avoid accidental data loss.
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.FLOAT, '0.1')),
).to.throw('Int cannot represent non-integer value: "0.1"');
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.FLOAT, '1.1')),
).to.throw('Int cannot represent non-integer value: "1.1"');
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.FLOAT, '-1.1')),
).to.throw('Int cannot represent non-integer value: "-1.1"');
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.INT, '9876504321')),
).to.throw(
'Int cannot represent non 32-bit signed integer value: 9876504321',
);
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.INT, '-9876504321')),
).to.throw(
'Int cannot represent non 32-bit signed integer value: -9876504321',
);
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.INT, '10000000000000000')),
).to.throw(
'Int cannot represent non 32-bit signed integer value: 10000000000000000',
);
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.INT, '-10000000000000000')),
).to.throw(
'Int cannot represent non 32-bit signed integer value: -10000000000000000',
);
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.STRING, '123')),
).to.throw('Int cannot represent non-integer value: "123"');
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.BOOLEAN, true)),
).to.throw('Int cannot represent non-integer value: true');
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.BOOLEAN, false)),
).to.throw('Int cannot represent non-integer value: false');
expect(() =>
GraphQLInt.parseLiteral(buildNode(Kind.STRING, '')),
).to.throw('Int cannot represent non-integer value: ""');
expect(() => GraphQLInt.parseLiteral(buildNode(Kind.NULL))).to.throw(
'Int cannot represent non-integer value.',
);
});

it('parses input as Float', () => {
expect(GraphQLFloat.parseLiteral(buildNode(Kind.INT, '1'))).to.equal(1.0);
expect(GraphQLFloat.parseLiteral(buildNode(Kind.INT, '0'))).to.equal(0.0);
expect(GraphQLFloat.parseLiteral(buildNode(Kind.INT, '-1'))).to.equal(
-1.0,
);
expect(GraphQLFloat.parseLiteral(buildNode(Kind.FLOAT, '0.1'))).to.equal(
0.1,
);
expect(GraphQLFloat.parseLiteral(buildNode(Kind.FLOAT, '1.1'))).to.equal(
1.1,
);
expect(GraphQLFloat.parseLiteral(buildNode(Kind.FLOAT, '-1.1'))).to.equal(
-1.1,
);

expect(() =>
GraphQLFloat.parseLiteral(buildNode(Kind.STRING, '123')),
).to.throw('Float cannot represent non numeric value: "123"');
expect(() =>
GraphQLFloat.parseLiteral(buildNode(Kind.STRING, '123.5')),
).to.throw('Float cannot represent non numeric value: "123.5"');
expect(() =>
GraphQLFloat.parseLiteral(buildNode(Kind.STRING, '')),
).to.throw('Float cannot represent non numeric value: ""');
expect(() =>
GraphQLFloat.parseLiteral(buildNode(Kind.BOOLEAN, false)),
).to.throw('Float cannot represent non numeric value: false');
expect(() =>
GraphQLFloat.parseLiteral(buildNode(Kind.BOOLEAN, true)),
).to.throw('Float cannot represent non numeric value: true');
expect(() => GraphQLFloat.parseLiteral(buildNode(Kind.NULL))).to.throw(
'Float cannot represent non numeric value.',
);
});

it('parses input as String', () => {
expect(
GraphQLString.parseLiteral(buildNode(Kind.STRING, 'string')),
).to.equal('string');

expect(() =>
GraphQLString.parseLiteral(buildNode(Kind.INT, '1')),
).to.throw('String cannot represent a non string value: "1"');
expect(() =>
GraphQLString.parseLiteral(buildNode(Kind.FLOAT, '-1.1')),
).to.throw('String cannot represent a non string value: "-1.1"');
expect(() =>
GraphQLString.parseLiteral(buildNode(Kind.BOOLEAN, true)),
).to.throw('String cannot represent a non string value: true');
expect(() =>
GraphQLString.parseLiteral(buildNode(Kind.BOOLEAN, false)),
).to.throw('String cannot represent a non string value: false');
expect(() => GraphQLString.parseLiteral(buildNode(Kind.NULL))).to.throw(
'String cannot represent a non string value.',
);
});

it('parses input as Boolean', () => {
expect(
GraphQLBoolean.parseLiteral(buildNode(Kind.BOOLEAN, true)),
).to.equal(true);
expect(
GraphQLBoolean.parseLiteral(buildNode(Kind.BOOLEAN, false)),
).to.equal(false);

expect(() =>
GraphQLBoolean.parseLiteral(buildNode(Kind.STRING, '')),
).to.throw('Boolean cannot represent a non boolean value: ""');
expect(() =>
GraphQLBoolean.parseLiteral(buildNode(Kind.STRING, 'true')),
).to.throw('Boolean cannot represent a non boolean value: "true"');
expect(() =>
GraphQLBoolean.parseLiteral(buildNode(Kind.INT, '0')),
).to.throw('Boolean cannot represent a non boolean value: "0"');
expect(() =>
GraphQLBoolean.parseLiteral(buildNode(Kind.INT, '1')),
).to.throw('Boolean cannot represent a non boolean value: "1"');
expect(() =>
GraphQLBoolean.parseLiteral(buildNode(Kind.FLOAT, '1.1')),
).to.throw('Boolean cannot represent a non boolean value: "1.1"');
expect(() => GraphQLBoolean.parseLiteral(buildNode(Kind.NULL))).to.throw(
'Boolean cannot represent a non boolean value.',
);
});

it('parses input as ID', () => {
expect(GraphQLID.parseLiteral(buildNode(Kind.STRING, ''))).to.equal('');
expect(GraphQLID.parseLiteral(buildNode(Kind.STRING, 'string'))).to.equal(
'string',
);
expect(GraphQLID.parseLiteral(buildNode(Kind.STRING, 'false'))).to.equal(
'false',
);
expect(GraphQLID.parseLiteral(buildNode(Kind.INT, '123'))).to.equal(
'123',
);
expect(GraphQLID.parseLiteral(buildNode(Kind.INT, '0'))).to.equal('0');
expect(GraphQLID.parseLiteral(buildNode(Kind.INT, '-1'))).to.equal('-1');

expect(() =>
GraphQLID.parseLiteral(buildNode(Kind.BOOLEAN, true)),
).to.throw(
'ID cannot represent a non-string and non-integer value: true',
);
expect(() =>
GraphQLID.parseLiteral(buildNode(Kind.FLOAT, '3.14')),
).to.throw(
'ID cannot represent a non-string and non-integer value: "3.14"',
);
expect(() => GraphQLID.parseLiteral(buildNode(Kind.NULL))).to.throw(
'ID cannot represent a non-string and non-integer value.',
);
});
});
});
5 changes: 5 additions & 0 deletions src/type/__tests__/serialization-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ describe('Type System: Scalar coercion', () => {
expect(() => GraphQLString.serialize(badObjValue)).to.throw(
'String cannot represent value: {}',
);

const badValueOfObjValue = { valueOf: 'valueOf string' };
expect(() => GraphQLString.serialize(badValueOfObjValue)).to.throw(
'String cannot represent value: { valueOf: "valueOf string" }',
);
});

it('serializes output as Boolean', () => {
Expand Down
Loading

0 comments on commit 8eeee77

Please sign in to comment.