git.sr.ht/~sircmpwn/gqlgen@v0.0.0-20200522192042-c84d29a1c940/complexity/complexity_test.go (about) 1 package complexity 2 3 import ( 4 "math" 5 "testing" 6 7 "git.sr.ht/~sircmpwn/gqlgen/graphql" 8 "github.com/stretchr/testify/require" 9 "github.com/vektah/gqlparser/v2" 10 "github.com/vektah/gqlparser/v2/ast" 11 ) 12 13 var schema = gqlparser.MustLoadSchema( 14 &ast.Source{ 15 Name: "test.graphql", 16 Input: ` 17 interface NameInterface { 18 name: String 19 } 20 21 type Item implements NameInterface { 22 scalar: String 23 name: String 24 list(size: Int = 10): [Item] 25 } 26 27 type ExpensiveItem implements NameInterface { 28 name: String 29 } 30 31 type Named { 32 name: String 33 } 34 35 union NameUnion = Item | Named 36 37 type Query { 38 scalar: String 39 object: Item 40 interface: NameInterface 41 union: NameUnion 42 customObject: Item 43 list(size: Int = 10): [Item] 44 } 45 `, 46 }, 47 ) 48 49 func requireComplexity(t *testing.T, source string, complexity int) { 50 t.Helper() 51 query := gqlparser.MustLoadQuery(schema, source) 52 53 es := &graphql.ExecutableSchemaMock{ 54 ComplexityFunc: func(typeName, field string, childComplexity int, args map[string]interface{}) (int, bool) { 55 switch typeName + "." + field { 56 case "ExpensiveItem.name": 57 return 5, true 58 case "Query.list", "Item.list": 59 return int(args["size"].(int64)) * childComplexity, true 60 case "Query.customObject": 61 return 1, true 62 } 63 return 0, false 64 }, 65 SchemaFunc: func() *ast.Schema { 66 return schema 67 }, 68 } 69 70 actualComplexity := Calculate(es, query.Operations[0], nil) 71 require.Equal(t, complexity, actualComplexity) 72 } 73 74 func TestCalculate(t *testing.T) { 75 t.Run("uses default cost", func(t *testing.T) { 76 const query = ` 77 { 78 scalar 79 } 80 ` 81 requireComplexity(t, query, 1) 82 }) 83 84 t.Run("adds together fields", func(t *testing.T) { 85 const query = ` 86 { 87 scalar1: scalar 88 scalar2: scalar 89 } 90 ` 91 requireComplexity(t, query, 2) 92 }) 93 94 t.Run("a level of nesting adds complexity", func(t *testing.T) { 95 const query = ` 96 { 97 object { 98 scalar 99 } 100 } 101 ` 102 requireComplexity(t, query, 2) 103 }) 104 105 t.Run("adds together children", func(t *testing.T) { 106 const query = ` 107 { 108 scalar 109 object { 110 scalar 111 } 112 } 113 ` 114 requireComplexity(t, query, 3) 115 }) 116 117 t.Run("adds inline fragments", func(t *testing.T) { 118 const query = ` 119 { 120 ... { 121 scalar 122 } 123 } 124 ` 125 requireComplexity(t, query, 1) 126 }) 127 128 t.Run("adds fragments", func(t *testing.T) { 129 const query = ` 130 { 131 ... Fragment 132 } 133 134 fragment Fragment on Query { 135 scalar 136 } 137 ` 138 requireComplexity(t, query, 1) 139 }) 140 141 t.Run("uses custom complexity", func(t *testing.T) { 142 const query = ` 143 { 144 list { 145 scalar 146 } 147 } 148 ` 149 requireComplexity(t, query, 10) 150 }) 151 152 t.Run("ignores negative custom complexity values", func(t *testing.T) { 153 const query = ` 154 { 155 list(size: -100) { 156 scalar 157 } 158 } 159 ` 160 requireComplexity(t, query, 2) 161 }) 162 163 t.Run("custom complexity must be >= child complexity", func(t *testing.T) { 164 const query = ` 165 { 166 customObject { 167 list(size: 100) { 168 scalar 169 } 170 } 171 } 172 ` 173 requireComplexity(t, query, 101) 174 }) 175 176 t.Run("interfaces take max concrete cost", func(t *testing.T) { 177 const query = ` 178 { 179 interface { 180 name 181 } 182 } 183 ` 184 requireComplexity(t, query, 6) 185 }) 186 187 t.Run("guards against integer overflow", func(t *testing.T) { 188 if maxInt == math.MaxInt32 { 189 // this test is written assuming 64-bit ints 190 t.Skip() 191 } 192 const query = ` 193 { 194 list1: list(size: 2147483647) { 195 list(size: 2147483647) { 196 list(size: 2) { 197 scalar 198 } 199 } 200 } 201 # total cost so far: 2*0x7fffffff*0x7fffffff 202 # = 0x7ffffffe00000002 203 # Adding the same again should cause overflow 204 list2: list(size: 2147483647) { 205 list(size: 2147483647) { 206 list(size: 2) { 207 scalar 208 } 209 } 210 } 211 } 212 ` 213 requireComplexity(t, query, math.MaxInt64) 214 }) 215 }