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