graphql-php/tests/Utils/FindBreakingChangesTest.php

1875 lines
62 KiB
PHP
Raw Normal View History

2017-11-17 19:21:05 +03:00
<?php
namespace GraphQL\Tests\Utils;
use GraphQL\Language\DirectiveLocation;
use GraphQL\Type\Definition\Directive;
2017-11-18 00:24:04 +03:00
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\FieldArgument;
2017-11-17 23:43:16 +03:00
use GraphQL\Type\Definition\InputObjectType;
2017-11-17 21:04:01 +03:00
use GraphQL\Type\Definition\InterfaceType;
2017-11-17 19:21:05 +03:00
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
2017-11-17 21:04:01 +03:00
use GraphQL\Type\Definition\UnionType;
2017-11-17 19:21:05 +03:00
use GraphQL\Type\Schema;
use GraphQL\Utils\FindBreakingChanges;
2018-07-29 18:43:10 +03:00
use PHPUnit\Framework\TestCase;
2017-11-17 19:21:05 +03:00
2018-07-29 18:43:10 +03:00
class FindBreakingChangesTest extends TestCase
2017-11-17 19:21:05 +03:00
{
2017-11-21 19:33:45 +03:00
private $queryType;
2017-11-17 19:21:05 +03:00
public function setUp()
{
$this->queryType = new ObjectType([
2017-11-21 19:50:11 +03:00
'name' => 'Query',
2017-11-17 19:21:05 +03:00
'fields' => [
'field1' => [
'type' => Type::string()
]
]
]);
}
//DESCRIBE: findBreakingChanges
/**
* @it should detect if a type was removed or not
*/
public function testShouldDetectIfTypeWasRemovedOrNot()
2017-11-17 19:21:05 +03:00
{
$type1 = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => ['type' => Type::string()],
]
]);
$type2 = new ObjectType([
'name' => 'Type2',
'fields' => [
'field1' => ['type' => Type::string()],
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$type1, $type2]
2017-11-17 19:21:05 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$type2]
2017-11-17 19:21:05 +03:00
]);
$expected = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED,
'description' => 'Type1 was removed.'
]
];
$this->assertEquals(
$expected,
FindBreakingChanges::findRemovedTypes($oldSchema, $newSchema)
2017-11-17 19:21:05 +03:00
);
$this->assertEquals([], FindBreakingChanges::findRemovedTypes($oldSchema, $oldSchema));
}
/**
* @it should detect if a type changed its type
*/
public function testShouldDetectIfATypeChangedItsType()
2017-11-17 21:04:01 +03:00
{
$objectType = new ObjectType([
'name' => 'ObjectType',
'fields' => [
'field1' => ['type' => Type::string()],
]
]);
$interfaceType = new InterfaceType([
'name' => 'Type1',
'fields' => [
'field1' => ['type' => Type::string()]
]
]);
$unionType = new UnionType([
'name' => 'Type1',
'types' => [$objectType],
2017-11-17 21:04:01 +03:00
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$interfaceType]
2017-11-17 21:04:01 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$unionType]
2017-11-17 21:04:01 +03:00
]);
$expected = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_CHANGED_KIND,
'description' => 'Type1 changed from an Interface type to a Union type.'
]
];
2017-11-17 21:04:01 +03:00
$this->assertEquals(
$expected,
FindBreakingChanges::findTypesThatChangedKind($oldSchema, $newSchema)
2017-11-17 21:04:01 +03:00
);
}
/**
* @it should detect if a field on a type was deleted or changed type
*/
public function testShouldDetectIfAFieldOnATypeWasDeletedOrChangedType()
{
$typeA = new ObjectType([
'name' => 'TypeA',
'fields' => [
'field1' => ['type' => Type::string()],
]
]);
// logically equivalent to TypeA; findBreakingFieldChanges shouldn't
// treat this as different than TypeA
$typeA2 = new ObjectType([
'name' => 'TypeA',
'fields' => [
'field1' => ['type' => Type::string()],
]
]);
$typeB = new ObjectType([
'name' => 'TypeB',
'fields' => [
'field1' => ['type' => Type::string()],
]
]);
$oldType1 = new InterfaceType([
'name' => 'Type1',
'fields' => [
'field1' => ['type' => $typeA],
'field2' => ['type' => Type::string()],
'field3' => ['type' => Type::string()],
'field4' => ['type' => $typeA],
'field6' => ['type' => Type::string()],
'field7' => ['type' => Type::listOf(Type::string())],
'field8' => ['type' => Type::int()],
'field9' => ['type' => Type::nonNull(Type::int())],
'field10' => ['type' => Type::nonNull(Type::listOf(Type::int()))],
'field11' => ['type' => Type::int()],
'field12' => ['type' => Type::listOf(Type::int())],
'field13' => ['type' => Type::listOf(Type::nonNull(Type::int()))],
'field14' => ['type' => Type::listOf(Type::int())],
'field15' => ['type' => Type::listOf(Type::listOf(Type::int()))],
'field16' => ['type' => Type::nonNull(Type::int())],
'field17' => ['type' => Type::listOf(Type::int())],
'field18' => [
'type' => Type::listOf(Type::nonNull(
Type::listOf(Type::nonNull(Type::int())))),
],
]
]);
$newType1 = new InterfaceType([
'name' => 'Type1',
'fields' => [
'field1' => ['type' => $typeA2],
'field3' => ['type' => Type::boolean()],
'field4' => ['type' => $typeB],
'field5' => ['type' => Type::string()],
'field6' => ['type' => Type::listOf(Type::string())],
'field7' => ['type' => Type::string()],
'field8' => ['type' => Type::nonNull(Type::int())],
'field9' => ['type' => Type::int()],
'field10' => ['type' => Type::listOf(Type::int())],
'field11' => ['type' => Type::nonNull(Type::listOf(Type::int()))],
'field12' => ['type' => Type::listOf(Type::nonNull(Type::int()))],
'field13' => ['type' => Type::listOf(Type::int())],
'field14' => ['type' => Type::listOf(Type::listOf(Type::int()))],
'field15' => ['type' => Type::listOf(Type::int())],
'field16' => ['type' => Type::nonNull(Type::listOf(Type::int()))],
'field17' => ['type' => Type::nonNull(Type::listOf(Type::int()))],
'field18' => [
'type' => Type::listOf(
Type::listOf(Type::nonNull(Type::int()))),
],
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldType1],
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newType1],
]);
$expectedFieldChanges = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_REMOVED,
'description' => 'Type1.field2 was removed.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field3 changed type from String to Boolean.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field4 changed type from TypeA to TypeB.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field6 changed type from String to [String].',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field7 changed type from [String] to String.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field9 changed type from Int! to Int.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field10 changed type from [Int]! to [Int].',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field11 changed type from Int to [Int]!.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field13 changed type from [Int!] to [Int].',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field14 changed type from [Int] to [[Int]].',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field15 changed type from [[Int]] to [Int].',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field16 changed type from Int! to [Int]!.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'Type1.field18 changed type from [[Int!]!] to [[Int!]].',
],
];
$this->assertEquals($expectedFieldChanges, FindBreakingChanges::findFieldsThatChangedTypeOnObjectOrInterfaceTypes($oldSchema, $newSchema));
}
/**
* @it should detect if fields on input types changed kind or were removed
*/
public function testShouldDetectIfFieldsOnInputTypesChangedKindOrWereRemoved()
2017-11-17 23:43:16 +03:00
{
$oldInputType = new InputObjectType([
'name' => 'InputType1',
'fields' => [
'field1' => [
'type' => Type::string(),
],
'field2' => [
'type' => Type::boolean(),
],
'field3' => [
'type' => Type::listOf(Type::string())
],
'field4' => [
'type' => Type::nonNull(Type::string()),
],
'field5' => [
'type' => Type::string(),
],
'field6' => [
'type' => Type::listOf(Type::int())
],
'field7' => [
'type' => Type::nonNull(Type::listOf(Type::int()))
],
'field8' => [
'type' => Type::int(),
],
'field9' => [
'type' => Type::listOf(Type::int())
],
'field10' => [
'type' => Type::listOf(Type::nonNull(Type::int()))
],
'field11' => [
'type' => Type::listOf(Type::int())
],
'field12' => [
'type' => Type::listOf(Type::listOf(Type::int()))
],
'field13' => [
'type' => Type::nonNull(Type::int())
],
'field14' => [
'type' => Type::listOf(Type::nonNull(Type::listOf(Type::int())))
],
'field15' => [
'type' => Type::listOf(Type::nonNull(Type::listOf(Type::int())))
]
]
]);
$newInputType = new InputObjectType([
'name' => 'InputType1',
'fields' => [
'field1' => [
'type' => Type::int(),
],
'field3' => [
'type' => Type::string()
],
'field4' => [
'type' => Type::string()
],
'field5' => [
'type' => Type::nonNull(Type::string())
],
'field6' => [
'type' => Type::nonNull(Type::listOf(Type::int()))
],
'field7' => [
'type' => Type::listOf(Type::int())
],
'field8' => [
'type' => Type::nonNull(Type::listOf(Type::int()))
],
'field9' => [
'type' => Type::listOf(Type::nonNull(Type::int()))
],
'field10' => [
'type' => Type::listOf(Type::int())
],
'field11' => [
'type' => Type::listOf(Type::listOf(Type::int()))
],
'field12' => [
'type' => Type::listOf(Type::int())
],
'field13' => [
'type' => Type::nonNull(Type::listOf(Type::int()))
],
'field14' => [
'type' => Type::listOf(Type::listOf(Type::int()))
],
'field15' => [
'type' => Type::listOf(Type::nonNull(Type::listOf(Type::nonNull(Type::int()))))
]
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldInputType]
2017-11-17 23:43:16 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newInputType]
2017-11-17 23:43:16 +03:00
]);
$expectedFieldChanges = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'InputType1.field1 changed type from String to Int.',
2017-11-17 23:43:16 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_REMOVED,
'description' => 'InputType1.field2 was removed.',
2017-11-17 23:43:16 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'InputType1.field3 changed type from [String] to String.',
2017-11-17 23:43:16 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'InputType1.field5 changed type from String to String!.',
2017-11-17 23:43:16 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'InputType1.field6 changed type from [Int] to [Int]!.',
2017-11-17 23:43:16 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'InputType1.field8 changed type from Int to [Int]!.',
2017-11-17 23:43:16 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'InputType1.field9 changed type from [Int] to [Int!].',
2017-11-17 23:43:16 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'InputType1.field11 changed type from [Int] to [[Int]].',
2017-11-17 23:43:16 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'InputType1.field12 changed type from [[Int]] to [Int].',
2017-11-17 23:43:16 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'InputType1.field13 changed type from Int! to [Int]!.',
2017-11-17 23:43:16 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'InputType1.field15 changed type from [[Int]!] to [[Int!]!].',
2017-11-17 23:43:16 +03:00
],
];
$this->assertEquals($expectedFieldChanges, FindBreakingChanges::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['breakingChanges']);
2017-11-17 23:43:16 +03:00
}
/**
* @it should detect if a non-null field is added to an input type
*/
public function testShouldDetectIfANonNullFieldIsAddedToAnInputType()
{
$oldInputType = new InputObjectType([
'name' => 'InputType1',
'fields' => [
'field1' => Type::string()
]
]);
$newInputType = new InputObjectType([
'name' => 'InputType1',
'fields' => [
'field1' => Type::string(),
'requiredField' => Type::nonNull(Type::int()),
'optionalField' => Type::boolean()
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldInputType],
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newInputType],
]);
$expected = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_NON_NULL_INPUT_FIELD_ADDED,
'description' => 'A non-null field requiredField on input type InputType1 was added.'
],
];
$this->assertEquals(
$expected,
FindBreakingChanges::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['breakingChanges']
);
}
2017-11-18 00:08:44 +03:00
/**
* @it should detect if a type was removed from a union type
*/
public function testShouldRetectIfATypeWasRemovedFromAUnionType()
2017-11-18 00:08:44 +03:00
{
$type1 = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => Type::string()
]
]);
// logially equivalent to type1; findTypesRemovedFromUnions should not
// treat this as different than type1
2017-11-18 00:08:44 +03:00
$type1a = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => Type::string()
]
]);
$type2 = new ObjectType([
'name' => 'Type2',
'fields' => [
'field1' => Type::string()
]
]);
$type3 = new ObjectType([
'name' => 'Type3',
'fields' => [
'field1' => Type::string()
]
]);
$oldUnionType = new UnionType([
'name' => 'UnionType1',
'types' => [$type1, $type2],
]);
$newUnionType = new UnionType([
'name' => 'UnionType1',
'types' => [$type1a, $type3],
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldUnionType],
2017-11-18 00:08:44 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newUnionType],
2017-11-18 00:08:44 +03:00
]);
$expected = [
2017-11-18 00:08:44 +03:00
[
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED_FROM_UNION,
'description' => 'Type2 was removed from union type UnionType1.'
]
];
$this->assertEquals(
$expected,
FindBreakingChanges::findTypesRemovedFromUnions($oldSchema, $newSchema)
2017-11-18 00:08:44 +03:00
);
}
2017-11-18 00:24:04 +03:00
/**
* @it should detect if a value was removed from an enum type
*/
public function testShouldDetectIfAValueWasRemovedFromAnEnumType()
2017-11-18 00:24:04 +03:00
{
$oldEnumType = new EnumType([
'name' => 'EnumType1',
'values' => [
'VALUE0' => 0,
'VALUE1' => 1,
'VALUE2' => 2
]
]);
$newEnumType = new EnumType([
'name' => 'EnumType1',
'values' => [
'VALUE0' => 0,
'VALUE2' => 1,
'VALUE3' => 2
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldEnumType]
2017-11-18 00:24:04 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newEnumType]
2017-11-18 00:24:04 +03:00
]);
$expected = [
2017-11-18 00:24:04 +03:00
[
'type' => FindBreakingChanges::BREAKING_CHANGE_VALUE_REMOVED_FROM_ENUM,
'description' => 'VALUE1 was removed from enum type EnumType1.'
]
];
$this->assertEquals(
$expected,
FindBreakingChanges::findValuesRemovedFromEnums($oldSchema, $newSchema)
2017-11-18 00:24:04 +03:00
);
}
2017-11-18 01:18:26 +03:00
/**
* @it should detect if a field argument was removed
*/
public function testShouldDetectIfAFieldArgumentWasRemoved()
2017-11-18 01:18:26 +03:00
{
$oldType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'name' => Type::string()
]
]
]
]);
$inputType = new InputObjectType([
'name' => 'InputType1',
'fields' => [
'field1' => Type::string()
]
]);
$oldInterfaceType = new InterfaceType([
'name' => 'Interface1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'arg1' => Type::boolean(),
'objectArg' => $inputType
]
]
]
]);
$newType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => []
]
]
]);
$newInterfaceType = new InterfaceType([
'name' => 'Interface1',
'fields' => [
'field1' => Type::string()
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldType, $oldInterfaceType],
2017-11-18 01:18:26 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newType, $newInterfaceType],
2017-11-18 01:18:26 +03:00
]);
$expectedChanges = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_REMOVED,
'description' => 'Type1.field1 arg name was removed',
2017-11-18 01:18:26 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_REMOVED,
'description' => 'Interface1.field1 arg arg1 was removed',
2017-11-18 01:18:26 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_REMOVED,
'description' => 'Interface1.field1 arg objectArg was removed',
2017-11-18 01:18:26 +03:00
]
];
$this->assertEquals($expectedChanges, FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
}
2017-11-18 01:35:33 +03:00
/**
* @it should detect if a field argument has changed type
*/
public function testShouldDetectIfAFieldArgumentHasChangedType()
2017-11-20 20:52:49 +03:00
{
$oldType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'arg1' => Type::string(),
'arg2' => Type::string(),
'arg3' => Type::listOf(Type::string()),
'arg4' => Type::string(),
'arg5' => Type::nonNull(Type::string()),
'arg6' => Type::nonNull(Type::string()),
'arg7' => Type::nonNull(Type::listOf(Type::int())),
'arg8' => Type::int(),
'arg9' => Type::listOf(Type::int()),
'arg10' => Type::listOf(Type::nonNull(Type::int())),
'arg11' => Type::listOf(Type::int()),
'arg12' => Type::listOf(Type::listOf(Type::int())),
'arg13' => Type::nonNull(Type::int()),
'arg14' => Type::listOf(Type::nonNull(Type::listOf(Type::int()))),
'arg15' => Type::listOf(Type::nonNull(Type::listOf(Type::int())))
]
]
]
]);
$newType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'arg1' => Type::int(),
'arg2' => Type::listOf(Type::string()),
'arg3' => Type::string(),
'arg4' => Type::nonNull(Type::string()),
'arg5' => Type::int(),
'arg6' => Type::nonNull(Type::int()),
'arg7' => Type::listOf(Type::int()),
'arg8' => Type::nonNull(Type::listOf(Type::int())),
'arg9' => Type::listOf(Type::nonNull(Type::int())),
'arg10' => Type::listOf(Type::int()),
'arg11' => Type::listOf(Type::listOf(Type::int())),
'arg12' => Type::listOf(Type::int()),
'arg13' => Type::nonNull(Type::listOf(Type::int())),
'arg14' => Type::listOf(Type::listOf(Type::int())),
'arg15' => Type::listOf(Type::nonNull(Type::listOf(Type::nonNull(Type::int()))))
]
]
]
]);
2017-11-18 01:35:33 +03:00
2017-11-20 20:52:49 +03:00
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldType]
2017-11-20 20:52:49 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newType]
2017-11-20 20:52:49 +03:00
]);
$expectedChanges = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg1 has changed type from String to Int',
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg2 has changed type from String to [String]'
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg3 has changed type from [String] to String',
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg4 has changed type from String to String!',
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg5 has changed type from String! to Int',
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg6 has changed type from String! to Int!',
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg8 has changed type from Int to [Int]!',
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg9 has changed type from [Int] to [Int!]',
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg11 has changed type from [Int] to [[Int]]',
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg12 has changed type from [[Int]] to [Int]',
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg13 has changed type from Int! to [Int]!',
2017-11-20 20:52:49 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'Type1.field1 arg arg15 has changed type from [[Int]!] to [[Int!]!]',
2017-11-20 20:52:49 +03:00
],
];
$this->assertEquals($expectedChanges, FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
2017-11-18 01:35:33 +03:00
}
2017-11-20 21:48:21 +03:00
/**
* @it should detect if a non-null field argument was added
*/
public function testShouldDetectIfANonNullFieldArgumentWasAdded()
2017-11-20 21:48:21 +03:00
{
$oldType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'arg1' => Type::string()
]]
]
]);
$newType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'arg1' => Type::string(),
'newRequiredArg' => Type::nonNull(Type::string()),
'newOptionalArg' => Type::int()
]]
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldType]
2017-11-20 21:48:21 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newType]
2017-11-20 21:48:21 +03:00
]);
$expected = [
2017-11-20 21:48:21 +03:00
[
'type' => FindBreakingChanges::BREAKING_CHANGE_NON_NULL_ARG_ADDED,
'description' => 'A non-null arg newRequiredArg on Type1.field1 was added'
]
];
$this->assertEquals(
$expected,
FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
2017-11-20 21:48:21 +03:00
}
/**
* @it should not flag args with the same type signature as breaking
*/
public function testShouldNotFlagArgsWithTheSameTypeSignatureAsBreaking()
2017-11-20 22:39:06 +03:00
{
$inputType1a = new InputObjectType([
'name' => 'InputType1',
'fields' => [
'field1' => Type::string()
]
]);
$inputType1b = new InputObjectType([
'name' => 'InputType1',
'fields' => [
'field1' => Type::string()
]
]);
$oldType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::int(),
'args' => [
'arg1' => Type::nonNull(Type::int()),
'arg2' => $inputType1a
]
]
]
]);
$newType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::int(),
'args' => [
'arg1' => Type::nonNull(Type::int()),
'arg2' => $inputType1b
]
]
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldType],
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newType],
]);
$this->assertEquals([], FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
}
2017-11-20 22:12:26 +03:00
/**
* @it should consider args that move away from NonNull as non-breaking
*/
public function testShouldConsiderArgsThatMoveAwayFromNonNullAsNonBreaking()
2017-11-20 22:39:06 +03:00
{
2017-11-20 22:12:26 +03:00
$oldType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'arg1' => Type::nonNull(Type::string()),
]
]
]
]);
$newType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'arg1' => Type::string()
]
]
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldType],
2017-11-20 22:12:26 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newType],
2017-11-20 22:12:26 +03:00
]);
$this->assertEquals([], FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
}
2017-11-20 22:39:06 +03:00
/**
* @it should detect interfaces removed from types
*/
public function testShouldDetectInterfacesRemovedFromTypes()
2017-11-20 22:39:06 +03:00
{
$interface1 = new InterfaceType([
'name' => 'Interface1',
'fields' => [
'field1' => Type::string()
],
]);
$oldType = new ObjectType([
'name' => 'Type1',
'interfaces' => [$interface1],
'fields' => [
'field1' => Type::string()
]
]);
$newType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => Type::string()
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldType],
2017-11-20 22:39:06 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newType],
2017-11-20 22:39:06 +03:00
]);
$expected = [
2017-11-20 22:39:06 +03:00
[
'type' => FindBreakingChanges::BREAKING_CHANGE_INTERFACE_REMOVED_FROM_OBJECT,
'description' => 'Type1 no longer implements interface Interface1.'
],
];
$this->assertEquals(
$expected,
FindBreakingChanges::findInterfacesRemovedFromObjectTypes($oldSchema, $newSchema));
2017-11-20 22:39:06 +03:00
}
2017-11-21 19:33:45 +03:00
/**
* @it should detect all breaking changes
*/
public function testShouldDetectAllBreakingChanges()
2017-11-21 19:33:45 +03:00
{
$typeThatGetsRemoved = new ObjectType([
'name' => 'TypeThatGetsRemoved',
'fields' => [
'field1' => Type::string()
]
]);
$argThatChanges = new ObjectType([
'name' => 'ArgThatChanges',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'id' => Type::int()
]
]
]
]);
$argChanged = new ObjectType([
'name' => 'ArgThatChanges',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'id' => Type::string()
]
]
]
]);
$typeThatChangesTypeOld = new ObjectType([
'name' => 'TypeThatChangesType',
'fields' => [
'field1' => Type::string()
]
]);
$typeThatChangesTypeNew = new InterfaceType([
'name' => 'TypeThatChangesType',
'fields' => [
'field1' => Type::string()
]
]);
$typeThatHasBreakingFieldChangesOld = new InterfaceType([
'name' => 'TypeThatHasBreakingFieldChanges',
'fields' => [
'field1' => Type::string(),
'field2' => Type::string()
]
]);
$typeThatHasBreakingFieldChangesNew = new InterfaceType([
'name' => 'TypeThatHasBreakingFieldChanges',
'fields' => [
'field2' => Type::boolean()
]
]);
$typeInUnion1 = new ObjectType([
'name' => 'TypeInUnion1',
'fields' => [
'field1' => Type::string()
]
]);
$typeInUnion2 = new ObjectType([
'name' => 'TypeInUnion2',
'fields' => [
'field1' => Type::string()
]
]);
$unionTypeThatLosesATypeOld = new UnionType([
'name' => 'UnionTypeThatLosesAType',
'types' => [$typeInUnion1, $typeInUnion2],
]);
$unionTypeThatLosesATypeNew = new UnionType([
'name' => 'UnionTypeThatLosesAType',
'types' => [$typeInUnion1],
]);
$enumTypeThatLosesAValueOld = new EnumType([
'name' => 'EnumTypeThatLosesAValue',
'values' => [
'VALUE0' => 0,
'VALUE1' => 1,
'VALUE2' => 2
]
]);
$enumTypeThatLosesAValueNew = new EnumType([
'name' => 'EnumTypeThatLosesAValue',
'values' => [
'VALUE1' => 1,
'VALUE2' => 2
]
]);
$interface1 = new InterfaceType([
'name' => 'Interface1',
'fields' => [
'field1' => Type::string()
],
]);
$typeThatLosesInterfaceOld = new ObjectType([
'name' => 'TypeThatLosesInterface1',
'interfaces' => [$interface1],
'fields' => [
'field1' => Type::string()
]
]);
$typeThatLosesInterfaceNew = new ObjectType([
'name' => 'TypeThatLosesInterface1',
'fields' => [
'field1' => Type::string()
]
]);
$directiveThatIsRemoved = Directive::skipDirective();
$directiveThatRemovesArgOld = new Directive([
'name' => 'DirectiveThatRemovesArg',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
'args' => FieldArgument::createMap([
'arg1' => [
'name' => 'arg1',
],
]),
]);
$directiveThatRemovesArgNew = new Directive([
'name' => 'DirectiveThatRemovesArg',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
]);
$nonNullDirectiveAddedOld = new Directive([
'name' => 'NonNullDirectiveAdded',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
]);
$nonNullDirectiveAddedNew = new Directive([
'name' => 'NonNullDirectiveAdded',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
'args' => FieldArgument::createMap([
'arg1' => [
'name' => 'arg1',
'type' => Type::nonNull(Type::boolean()),
],
]),
]);
$directiveRemovedLocationOld = new Directive([
'name' => 'Directive Name',
'locations' => [DirectiveLocation::FIELD_DEFINITION, DirectiveLocation::QUERY],
]);
$directiveRemovedLocationNew = new Directive([
'name' => 'Directive Name',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
]);
2017-11-21 19:33:45 +03:00
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [
$typeThatGetsRemoved,
$typeThatChangesTypeOld,
$typeThatHasBreakingFieldChangesOld,
$unionTypeThatLosesATypeOld,
$enumTypeThatLosesAValueOld,
$argThatChanges,
$typeThatLosesInterfaceOld
],
'directives' => [
$directiveThatIsRemoved,
$directiveThatRemovesArgOld,
$nonNullDirectiveAddedOld,
$directiveRemovedLocationOld,
]
2017-11-21 19:33:45 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [
$typeThatChangesTypeNew,
$typeThatHasBreakingFieldChangesNew,
$unionTypeThatLosesATypeNew,
$enumTypeThatLosesAValueNew,
$argChanged,
$typeThatLosesInterfaceNew,
$interface1
],
'directives' => [
$directiveThatRemovesArgNew,
$nonNullDirectiveAddedNew,
$directiveRemovedLocationNew,
]
2017-11-21 19:33:45 +03:00
]);
$expectedBreakingChanges = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED,
'description' => 'TypeThatGetsRemoved was removed.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED,
'description' => 'TypeInUnion2 was removed.',
],
/* This is reported in the js version because builtin sclar types are added on demand
and not like here always
2017-11-21 19:33:45 +03:00
[
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED,
'description' => 'Int was removed.'
],*/
[
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_CHANGED_KIND,
2017-11-21 19:33:45 +03:00
'description' => 'TypeThatChangesType changed from an Object type to an Interface type.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_REMOVED,
'description' => 'TypeThatHasBreakingFieldChanges.field1 was removed.',
2017-11-21 19:33:45 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
'description' => 'TypeThatHasBreakingFieldChanges.field2 changed type from String to Boolean.',
2017-11-21 19:33:45 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED_FROM_UNION,
'description' => 'TypeInUnion2 was removed from union type UnionTypeThatLosesAType.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_VALUE_REMOVED_FROM_ENUM,
'description' => 'VALUE0 was removed from enum type EnumTypeThatLosesAValue.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
'description' => 'ArgThatChanges.field1 arg id has changed type from Int to String',
2017-11-21 19:33:45 +03:00
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_INTERFACE_REMOVED_FROM_OBJECT,
'description' => 'TypeThatLosesInterface1 no longer implements interface Interface1.',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_REMOVED,
'description' => 'skip was removed',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_ARG_REMOVED,
'description' => 'arg1 was removed from DirectiveThatRemovesArg',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_NON_NULL_DIRECTIVE_ARG_ADDED,
'description' => 'A non-null arg arg1 on directive NonNullDirectiveAdded was added',
],
[
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_LOCATION_REMOVED,
'description' => 'QUERY was removed from Directive Name',
2017-11-21 19:33:45 +03:00
]
];
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findBreakingChanges($oldSchema, $newSchema));
}
2017-11-21 19:50:11 +03:00
/**
* @it should detect if a directive was explicitly removed
*/
public function testShouldDetectIfADirectiveWasExplicitlyRemoved()
{
$oldSchema = new Schema([
'directives' => [Directive::skipDirective(), Directive::includeDirective()],
]);
$newSchema = new Schema([
'directives' => [Directive::skipDirective()],
]);
$includeDirective = Directive::includeDirective();
$expectedBreakingChanges = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_REMOVED,
'description' => "{$includeDirective->name} was removed",
]
];
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findRemovedDirectives($oldSchema, $newSchema));
}
/**
* @it should detect if a directive was implicitly removed
*/
public function testShouldDetectIfADirectiveWasImplicitlyRemoved()
{
$oldSchema = new Schema([]);
$newSchema = new Schema([
'directives' => [Directive::skipDirective(), Directive::includeDirective()],
]);
$deprecatedDirective = Directive::deprecatedDirective();
$expectedBreakingChanges = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_REMOVED,
'description' => "{$deprecatedDirective->name} was removed",
]
];
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findRemovedDirectives($oldSchema, $newSchema));
}
/**
* @it should detect if a directive argument was removed
*/
public function testShouldDetectIfADirectiveArgumentWasRemoved()
{
$oldSchema = new Schema([
'directives' => [
new Directive([
'name' => 'DirectiveWithArg',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
'args' => FieldArgument::createMap([
'arg1' => [
'name' => 'arg1',
],
]),
])
],
]);
$newSchema = new Schema([
'directives' => [
new Directive([
'name' => 'DirectiveWithArg',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
])
],
]);
$expectedBreakingChanges = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_ARG_REMOVED,
'description' => "arg1 was removed from DirectiveWithArg",
]
];
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findRemovedDirectiveArgs($oldSchema, $newSchema));
}
/**
* @it should detect if a non-nullable directive argument was added
*/
public function testShouldDetectIfANonNullableDirectiveArgumentWasAdded()
{
$oldSchema = new Schema([
'directives' => [
new Directive([
'name' => 'DirectiveName',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
])
],
]);
$newSchema = new Schema([
'directives' => [
new Directive([
'name' => 'DirectiveName',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
'args' => FieldArgument::createMap([
'arg1' => [
'name' => 'arg1',
'type' => Type::nonNull(Type::boolean()),
],
]),
])
],
]);
$expectedBreakingChanges = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_NON_NULL_DIRECTIVE_ARG_ADDED,
'description' => "A non-null arg arg1 on directive DirectiveName was added",
]
];
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findAddedNonNullDirectiveArgs($oldSchema, $newSchema));
}
/**
* @it should detect locations removed from a directive
*/
public function testShouldDetectLocationsRemovedFromADirective()
{
$d1 = new Directive([
'name' => 'Directive Name',
'locations' => [DirectiveLocation::FIELD_DEFINITION, DirectiveLocation::QUERY],
]);
$d2 = new Directive([
'name' => 'Directive Name',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
]);
$this->assertEquals([DirectiveLocation::QUERY], FindBreakingChanges::findRemovedLocationsForDirective($d1, $d2));
}
/**
* @it should detect locations removed directives within a schema
*/
public function testShouldDetectLocationsRemovedDirectiveWithinASchema()
{
$oldSchema = new Schema([
'directives' => [
new Directive([
'name' => 'Directive Name',
'locations' => [
DirectiveLocation::FIELD_DEFINITION,
DirectiveLocation::QUERY
],
])
],
]);
$newSchema = new Schema([
'directives' => [
new Directive([
'name' => 'Directive Name',
'locations' => [DirectiveLocation::FIELD_DEFINITION],
])
],
]);
$expectedBreakingChanges = [
[
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_LOCATION_REMOVED,
'description' => "QUERY was removed from Directive Name",
]
];
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findRemovedDirectiveLocations($oldSchema, $newSchema));
}
// DESCRIBE: findDangerousChanges
// DESCRIBE: findArgChanges
2017-11-21 19:50:11 +03:00
/**
* @it should detect if an argument's defaultValue has changed
*/
public function testShouldDetectIfAnArgumentsDefaultValueHasChanged()
2017-11-21 19:50:11 +03:00
{
$oldType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'name' => [
'type' => Type::string(),
'defaultValue' => 'test'
]
]
]
]
]);
$newType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'name' => [
'type' => Type::string(),
'defaultValue' => 'Test'
2017-11-21 19:50:11 +03:00
]
]
]
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldType],
2017-11-21 19:50:11 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newType],
2017-11-21 19:50:11 +03:00
]);
$expected = [
2017-11-21 19:50:11 +03:00
[
'type' => FindBreakingChanges::DANGEROUS_CHANGE_ARG_DEFAULT_VALUE_CHANGED,
'description' => 'Type1.field1 arg name has changed defaultValue'
]
];
$this->assertEquals(
$expected,
FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['dangerousChanges']
2017-11-21 19:50:11 +03:00
);
}
2017-11-21 19:53:14 +03:00
/**
* @it should detect if a value was added to an enum type
*/
public function testShouldDetectIfAValueWasAddedToAnEnumType()
2017-11-21 19:53:14 +03:00
{
$oldEnumType = new EnumType([
'name' => 'EnumType1',
'values' => [
'VALUE0' => 0,
'VALUE1' => 1,
]
]);
$newEnumType = new EnumType([
'name' => 'EnumType1',
'values' => [
'VALUE0' => 0,
'VALUE1' => 1,
'VALUE2' => 2
]
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldEnumType],
2017-11-21 19:53:14 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newEnumType],
2017-11-21 19:53:14 +03:00
]);
$expected = [
2017-11-21 19:53:14 +03:00
[
'type' => FindBreakingChanges::DANGEROUS_CHANGE_VALUE_ADDED_TO_ENUM,
'description' => 'VALUE2 was added to enum type EnumType1.'
]
];
$this->assertEquals(
$expected,
FindBreakingChanges::findValuesAddedToEnums($oldSchema, $newSchema)
);
}
/**
* @it should detect interfaces added to types
*/
public function testShouldDetectInterfacesAddedToTypes()
{
$interface1 = new InterfaceType([
'name' => 'Interface1',
'fields' => [
'field1' => Type::string(),
2017-11-21 19:53:14 +03:00
],
]);
$oldType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => Type::string(),
],
]);
$newType = new ObjectType([
'name' => 'Type1',
'interfaces' => [$interface1],
'fields' => [
'field1' => Type::string(),
],
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldType],
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newType],
]);
$expected = [
[
'type' => FindBreakingChanges::DANGEROUS_CHANGE_INTERFACE_ADDED_TO_OBJECT,
'description' => 'Interface1 added to interfaces implemented by Type1.'
]
];
$this->assertEquals(
$expected,
FindBreakingChanges::findInterfacesAddedToObjectTypes($oldSchema, $newSchema)
2017-11-21 19:53:14 +03:00
);
}
2017-11-21 20:18:28 +03:00
/**
* @it should detect if a type was added to a union type
*/
public function testShouldDetectIfATypeWasAddedToAUnionType()
2017-11-21 20:30:18 +03:00
{
2017-11-21 20:18:28 +03:00
$type1 = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => Type::string()
]
]);
// logially equivalent to type1; findTypesRemovedFromUnions should not
//treat this as different than type1
2017-11-21 20:18:28 +03:00
$type1a = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => Type::string()
]
]);
$type2 = new ObjectType([
'name' => 'Type2',
'fields' => [
'field1' => Type::string()
]
]);
$oldUnionType = new UnionType([
'name' => 'UnionType1',
'types' => [$type1],
]);
$newUnionType = new UnionType([
'name' => 'UnionType1',
'types' => [$type1a, $type2],
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldUnionType],
2017-11-21 20:18:28 +03:00
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newUnionType],
2017-11-21 20:18:28 +03:00
]);
$expected = [
2017-11-21 20:18:28 +03:00
[
2017-11-21 20:30:18 +03:00
'type' => FindBreakingChanges::DANGEROUS_CHANGE_TYPE_ADDED_TO_UNION,
'description' => 'Type2 was added to union type UnionType1.'
]
];
$this->assertEquals(
$expected,
FindBreakingChanges::findTypesAddedToUnions($oldSchema, $newSchema)
2017-11-21 20:18:28 +03:00
);
}
2017-11-21 20:30:18 +03:00
/**
* @it should detect if a nullable field was added to an input
*/
public function testShouldDetectIfANullableFieldWasAddedToAnInput()
{
$oldInputType = new InputObjectType([
'name' => 'InputType1',
'fields' => [
'field1' => [
'type' => Type::string(),
],
],
]);
$newInputType = new InputObjectType([
'name' => 'InputType1',
'fields' => [
'field1' => [
'type' => Type::string(),
],
'field2' => [
'type' => Type::int(),
],
],
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [
$oldInputType,
]
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [
$newInputType,
]
]);
$expectedFieldChanges = [
[
'description' => 'A nullable field field2 on input type InputType1 was added.',
'type' => FindBreakingChanges::DANGEROUS_CHANGE_NULLABLE_INPUT_FIELD_ADDED
],
];
$this->assertEquals($expectedFieldChanges, FindBreakingChanges::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['dangerousChanges']);
}
/**
* @it should find all dangerous changes
*/
public function testShouldFindAllDangerousChanges()
2017-11-21 20:30:18 +03:00
{
$enumThatGainsAValueOld = new EnumType([
'name' => 'EnumType1',
'values' => [
'VALUE0' => 0,
'VALUE1' => 1,
]
]);
$enumThatGainsAValueNew = new EnumType([
'name' => 'EnumType1',
'values' => [
'VALUE0' => 0,
'VALUE1' => 1,
'VALUE2' => 2
]
]);
$oldType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'name' => [
'type' => Type::string(),
'defaultValue' => 'test'
]
]
]
]
]);
$typeInUnion1 = new ObjectType([
'name' => 'TypeInUnion1',
'fields' => [
'field1' => Type::string()
]
]);
$typeInUnion2 = new ObjectType([
'name' => 'TypeInUnion2',
'fields' => [
'field1' => Type::string()
]
]);
$unionTypeThatGainsATypeOld = new UnionType([
'name' => 'UnionTypeThatGainsAType',
'types' => [$typeInUnion1],
]);
$unionTypeThatGainsATypeNew = new UnionType([
'name' => 'UnionTypeThatGainsAType',
'types' => [$typeInUnion1, $typeInUnion2],
]);
2017-11-21 20:30:18 +03:00
$newType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'name' => [
'type' => Type::string(),
'defaultValue' => 'Test'
2017-11-21 20:30:18 +03:00
]
]
]
]
]);
$interface1 = new InterfaceType([
'name' => 'Interface1',
2017-11-21 20:30:18 +03:00
'fields' => [
'field1' => Type::string(),
],
2017-11-21 20:30:18 +03:00
]);
$typeThatGainsInterfaceOld = new ObjectType([
'name' => 'TypeThatGainsInterface1',
2017-11-21 20:30:18 +03:00
'fields' => [
'field1' => Type::string(),
],
2017-11-21 20:30:18 +03:00
]);
$typeThatGainsInterfaceNew = new ObjectType([
'name' => 'TypeThatGainsInterface1',
'interfaces' => [$interface1],
'fields' => [
'field1' => Type::string(),
],
2017-11-21 20:30:18 +03:00
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [
$oldType,
$enumThatGainsAValueOld,
$typeThatGainsInterfaceOld,
2017-11-21 20:30:18 +03:00
$unionTypeThatGainsATypeOld
]
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [
$newType,
$enumThatGainsAValueNew,
$typeThatGainsInterfaceNew,
2017-11-21 20:30:18 +03:00
$unionTypeThatGainsATypeNew
]
]);
$expectedDangerousChanges = [
[
'description' => 'Type1.field1 arg name has changed defaultValue',
'type' => FindBreakingChanges::DANGEROUS_CHANGE_ARG_DEFAULT_VALUE_CHANGED
2017-11-21 20:30:18 +03:00
],
[
'description' => 'VALUE2 was added to enum type EnumType1.',
2017-11-21 20:30:18 +03:00
'type' => FindBreakingChanges::DANGEROUS_CHANGE_VALUE_ADDED_TO_ENUM
],
[
'type' => FindBreakingChanges::DANGEROUS_CHANGE_INTERFACE_ADDED_TO_OBJECT,
'description' => 'Interface1 added to interfaces implemented by TypeThatGainsInterface1.',
],
2017-11-21 20:30:18 +03:00
[
'type' => FindBreakingChanges::DANGEROUS_CHANGE_TYPE_ADDED_TO_UNION,
'description' => 'TypeInUnion2 was added to union type UnionTypeThatGainsAType.',
2017-11-21 20:30:18 +03:00
]
];
$this->assertEquals($expectedDangerousChanges, FindBreakingChanges::findDangerousChanges($oldSchema, $newSchema));
}
/**
* @it should detect if a nullable field argument was added
*/
public function testShouldDetectIfANullableFieldArgumentWasAdded()
{
$oldType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'arg1' => [
'type' => Type::string(),
],
],
],
],
]);
$newType = new ObjectType([
'name' => 'Type1',
'fields' => [
'field1' => [
'type' => Type::string(),
'args' => [
'arg1' => [
'type' => Type::string(),
],
'arg2' => [
'type' => Type::string(),
],
],
],
],
]);
$oldSchema = new Schema([
'query' => $this->queryType,
'types' => [$oldType],
]);
$newSchema = new Schema([
'query' => $this->queryType,
'types' => [$newType],
]);
$expectedFieldChanges = [
[
'description' => 'A nullable arg arg2 on Type1.field1 was added',
'type' => FindBreakingChanges::DANGEROUS_CHANGE_NULLABLE_ARG_ADDED
],
];
$this->assertEquals($expectedFieldChanges, FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['dangerousChanges']);
}
}