...

Source file src/github.com/99designs/gqlgen/complexity/complexity_test.go

Documentation: github.com/99designs/gqlgen/complexity

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

View as plain text