github.com/operandinc/gqlgen@v0.16.1/codegen/testserver/followschema/subscription_test.go (about) 1 package followschema 2 3 import ( 4 "context" 5 "fmt" 6 "runtime" 7 "sort" 8 "testing" 9 "time" 10 11 "github.com/operandinc/gqlgen/graphql/handler/transport" 12 13 "github.com/operandinc/gqlgen/client" 14 "github.com/operandinc/gqlgen/graphql" 15 "github.com/operandinc/gqlgen/graphql/handler" 16 "github.com/stretchr/testify/require" 17 ) 18 19 func TestSubscriptions(t *testing.T) { 20 tick := make(chan string, 1) 21 22 resolvers := &Stub{} 23 24 resolvers.SubscriptionResolver.InitPayload = func(ctx context.Context) (strings <-chan string, e error) { 25 payload := transport.GetInitPayload(ctx) 26 channel := make(chan string, len(payload)+1) 27 28 go func() { 29 <-ctx.Done() 30 close(channel) 31 }() 32 33 // Test the helper function separately 34 auth := payload.Authorization() 35 if auth != "" { 36 channel <- "AUTH:" + auth 37 } else { 38 channel <- "AUTH:NONE" 39 } 40 41 // Send them over the channel in alphabetic order 42 keys := make([]string, 0, len(payload)) 43 for key := range payload { 44 keys = append(keys, key) 45 } 46 sort.Strings(keys) 47 for _, key := range keys { 48 channel <- fmt.Sprintf("%s = %#+v", key, payload[key]) 49 } 50 51 return channel, nil 52 } 53 54 resolvers.SubscriptionResolver.Updated = func(ctx context.Context) (<-chan string, error) { 55 res := make(chan string, 1) 56 57 go func() { 58 for { 59 select { 60 case t := <-tick: 61 res <- t 62 case <-ctx.Done(): 63 close(res) 64 return 65 } 66 } 67 }() 68 return res, nil 69 } 70 71 srv := handler.NewDefaultServer( 72 NewExecutableSchema(Config{Resolvers: resolvers}), 73 ) 74 srv.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { 75 path, _ := ctx.Value(ckey("path")).([]int) 76 return next(context.WithValue(ctx, ckey("path"), append(path, 1))) 77 }) 78 79 srv.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { 80 path, _ := ctx.Value(ckey("path")).([]int) 81 return next(context.WithValue(ctx, ckey("path"), append(path, 2))) 82 }) 83 84 c := client.New(srv) 85 86 t.Run("wont leak goroutines", func(t *testing.T) { 87 runtime.GC() // ensure no go-routines left from preceding tests 88 initialGoroutineCount := runtime.NumGoroutine() 89 90 sub := c.Websocket(`subscription { updated }`) 91 92 tick <- "message" 93 94 var msg struct { 95 resp struct { 96 Updated string 97 } 98 } 99 100 err := sub.Next(&msg.resp) 101 require.NoError(t, err) 102 require.Equal(t, "message", msg.resp.Updated) 103 sub.Close() 104 105 // need a little bit of time for goroutines to settle 106 start := time.Now() 107 for time.Since(start).Seconds() < 2 && initialGoroutineCount != runtime.NumGoroutine() { 108 time.Sleep(5 * time.Millisecond) 109 } 110 111 require.Equal(t, initialGoroutineCount, runtime.NumGoroutine()) 112 }) 113 114 t.Run("will parse init payload", func(t *testing.T) { 115 sub := c.WebsocketWithPayload(`subscription { initPayload }`, map[string]interface{}{ 116 "Authorization": "Bearer of the curse", 117 "number": 32, 118 "strings": []string{"hello", "world"}, 119 }) 120 121 var msg struct { 122 resp struct { 123 InitPayload string 124 } 125 } 126 127 err := sub.Next(&msg.resp) 128 require.NoError(t, err) 129 require.Equal(t, "AUTH:Bearer of the curse", msg.resp.InitPayload) 130 err = sub.Next(&msg.resp) 131 require.NoError(t, err) 132 require.Equal(t, "Authorization = \"Bearer of the curse\"", msg.resp.InitPayload) 133 err = sub.Next(&msg.resp) 134 require.NoError(t, err) 135 require.Equal(t, "number = 32", msg.resp.InitPayload) 136 err = sub.Next(&msg.resp) 137 require.NoError(t, err) 138 require.Equal(t, "strings = []interface {}{\"hello\", \"world\"}", msg.resp.InitPayload) 139 sub.Close() 140 }) 141 }