浏览代码

Validation tests and bugfixes;

bjorn 9 年之前
父节点
当前提交
c31f2f9a21
共有 5 个文件被更改,包括 114 次插入10 次删除
  1. 1 1
      graphql/rules.lua
  2. 3 0
      graphql/validate.lua
  3. 27 4
      tests/data/schema.lua
  4. 1 1
      tests/parse.lua
  5. 82 4
      tests/rules.lua

+ 1 - 1
graphql/rules.lua

@@ -146,7 +146,7 @@ function rules.unambiguousSelections(node, context)
         validateSelectionSet(selection.selectionSet, parentType)
         validateSelectionSet(selection.selectionSet, parentType)
       elseif selection.kind == 'fragmentSpread' then
       elseif selection.kind == 'fragmentSpread' then
         local fragmentDefinition = context.fragmentMap[selection.name.value]
         local fragmentDefinition = context.fragmentMap[selection.name.value]
-        if not seen[fragmentDefinition] then
+        if fragmentDefinition and not seen[fragmentDefinition] then
           seen[fragmentDefinition] = true
           seen[fragmentDefinition] = true
           if fragmentDefinition and fragmentDefinition.typeCondition then
           if fragmentDefinition and fragmentDefinition.typeCondition then
             local parentType = context.schema:getType(fragmentDefinition.typeCondition.name.value)
             local parentType = context.schema:getType(fragmentDefinition.typeCondition.name.value)

+ 3 - 0
graphql/validate.lua

@@ -138,6 +138,9 @@ local visitors = {
       context.usedFragments[node.name.value] = true
       context.usedFragments[node.name.value] = true
 
 
       local fragment = context.fragmentMap[node.name.value]
       local fragment = context.fragmentMap[node.name.value]
+
+      if not fragment then return end
+
       local fragmentType = context.schema:getType(fragment.typeCondition.name.value) or false
       local fragmentType = context.schema:getType(fragment.typeCondition.name.value) or false
 
 
       table.insert(context.objects, fragmentType)
       table.insert(context.objects, fragmentType)

+ 27 - 4
tests/data/schema.lua

@@ -1,5 +1,5 @@
-local types = require 'types'
-local schema = require 'schema'
+local types = require 'graphql.types'
+local schema = require 'graphql.schema'
 
 
 local dogCommand = types.enum({
 local dogCommand = types.enum({
   name = 'DogCommand',
   name = 'DogCommand',
@@ -36,6 +36,25 @@ local dog = types.object({
       arguments = {
       arguments = {
         atOtherHomes = types.boolean
         atOtherHomes = types.boolean
       }
       }
+    },
+    complicatedField = {
+      kind = types.boolean,
+      arguments = {
+        complicatedArgument = types.inputObject({
+          name = 'complicated',
+          fields = {
+            x = types.string,
+            y = types.integer,
+            z = types.inputObject({
+              name = 'alsoComplicated',
+              fields = {
+                x = types.string,
+                y = types.integer
+              }
+            })
+          }
+        })
+      }
     }
     }
   }
   }
 })
 })
@@ -98,11 +117,15 @@ local query = types.object({
         }
         }
       }
       }
     },
     },
+    cat = cat,
     pet = pet,
     pet = pet,
-    catOrDog = catOrDog
+    sentient = sentient,
+    catOrDog = catOrDog,
+    humanOrAlien = humanOrAlien
   }
   }
 })
 })
 
 
 return schema.create({
 return schema.create({
-  query = query
+  query = query,
+  directives = { types.skip, types.include }
 })
 })

+ 1 - 1
tests/parse.lua

@@ -1,5 +1,5 @@
 describe('parse', function()
 describe('parse', function()
-  local parse = require 'parse'
+  local parse = require 'graphql.parse'
 
 
   test('comments', function()
   test('comments', function()
     local document
     local document

+ 82 - 4
tests/rules.lua

@@ -1,5 +1,5 @@
-local parse = require 'parse'
-local validate = require 'validate'
+local parse = require 'graphql.parse'
+local validate = require 'graphql.validate'
 local schema = require 'tests/data/schema'
 local schema = require 'tests/data/schema'
 
 
 local function expectError(message, document)
 local function expectError(message, document)
@@ -248,9 +248,87 @@ describe('rules', function()
     it('fails if a fragment is not used', function()
     it('fails if a fragment is not used', function()
       expectError(message, 'fragment f on Dog {}')
       expectError(message, 'fragment f on Dog {}')
     end)
     end)
+  end)
 
 
-    it('passes if every fragment is used', function()
-      expectError(nil, '{ dog { ...f } } fragment f on Dog {}')
+  describe('fragmentSpreadTargetDefined', function()
+    local message = 'Fragment spread refers to non%-existent'
+
+    it('fails if the fragment does not exist', function()
+      expectError(message, '{ dog { ...f } }')
+    end)
+  end)
+
+  describe('fragmentDefinitionHasNoCycles', function()
+    local message = 'Fragment definition has cycles'
+
+    it('fails if a fragment spread has cycles', function()
+      expectError(message, [[
+        { dog { ...f } }
+        fragment f on Dog { ...g }
+        fragment g on Dog { ...h }
+        fragment h on Dog { ...f }
+      ]])
+    end)
+  end)
+
+  describe('fragmentSpreadIsPossible', function()
+    local message = 'Fragment type condition is not possible'
+
+    it('fails if a fragment type condition refers to a different object than the parent object', function()
+      expectError(message, [[
+        { dog { ...f } }
+        fragment f on Cat { }
+      ]])
+    end)
+
+    it('fails if a fragment type condition refers to an interface that the parent object does not implement', function()
+      expectError(message, [[
+        { dog { ...f } }
+        fragment f on Sentient { }
+      ]])
+    end)
+
+    it('fails if a fragment type condition refers to a union that the parent object does not belong to', function()
+      expectError(message, [[
+        { dog { ...f } }
+        fragment f on HumanOrAlien { }
+      ]])
+    end)
+  end)
+
+  describe('uniqueInputObjectFields', function()
+    local message = 'Multiple input object fields named'
+
+    it('fails if an input object has two fields with the same name', function()
+      expectError(message, [[
+        {
+          dog {
+            complicatedField(complicatedArgument: {x: "hi", x: "hi"})
+          }
+        }
+      ]])
+    end)
+
+    it('passes if an input object has nested fields with the same name', function()
+      expectError(nil, [[
+        {
+          dog {
+            complicatedField(complicatedArgument: {x: "hi", z: {x: "hi"}})
+          }
+        }
+      ]])
+    end)
+  end)
+
+  describe('directivesAreDefined', function()
+    local message = 'Unknown directive'
+
+    it('fails if a directive does not exist', function()
+      expectError(message, 'query @someRandomDirective {}')
+    end)
+
+    it('passes if directives exists', function()
+      expectError(nil, 'query @skip {}')
     end)
     end)
   end)
   end)
 end)
 end)