From 769a97e2e6903960df001e5953e5369e86d6432e Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Mon, 25 Jun 2018 18:13:47 -0500 Subject: [PATCH] fix race in chat example test - t.Parallel() doesn't guarantee parallel execution - moved goroutine so the test can execute independently --- example/chat/chat_test.go | 69 +++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/example/chat/chat_test.go b/example/chat/chat_test.go index 44734c2e969..4b63fd0526c 100644 --- a/example/chat/chat_test.go +++ b/example/chat/chat_test.go @@ -2,7 +2,6 @@ package chat import ( "net/http/httptest" - "sync" "testing" "github.com/stretchr/testify/require" @@ -13,8 +12,6 @@ import ( func TestChat(t *testing.T) { srv := httptest.NewServer(handler.GraphQL(MakeExecutableSchema(New()))) c := client.New(srv.URL) - var wg sync.WaitGroup - wg.Add(1) t.Run("subscribe to chat events", func(t *testing.T) { t.Parallel() @@ -22,31 +19,63 @@ func TestChat(t *testing.T) { sub := c.Websocket(`subscription { messageAdded(roomName:"#gophers") { text createdBy } }`) defer sub.Close() - wg.Done() - var resp struct { + postErrCh := make(chan error) + go func() { + var resp interface{} + // can't call t.Fatal from separate goroutine, so we return to the err chan for later + err := c.Post(`mutation { + a:post(text:"Hello!", roomName:"#gophers", username:"vektah") { id } + b:post(text:"Whats up?", roomName:"#gophers", username:"vektah") { id } + }`, &resp) + if err != nil { + // only push this error if non-nil + postErrCh <- err + } + }() + + type resp struct { MessageAdded struct { Text string CreatedBy string } } - require.NoError(t, sub.Next(&resp)) - require.Equal(t, "Hello!", resp.MessageAdded.Text) - require.Equal(t, "vektah", resp.MessageAdded.CreatedBy) - require.NoError(t, sub.Next(&resp)) - require.Equal(t, "Whats up?", resp.MessageAdded.Text) - require.Equal(t, "vektah", resp.MessageAdded.CreatedBy) - }) + // Contains the result of a `sub.Next` call + type subMsg struct { + resp + err error + } - t.Run("post two messages", func(t *testing.T) { - t.Parallel() + subCh := make(chan subMsg) + go func() { + var msg subMsg + + msg.err = sub.Next(&msg.resp) + subCh <- msg + + msg.err = sub.Next(&msg.resp) + subCh <- msg + }() + + var m subMsg + // Either can fail, and results in a failed test. + // + // Using a select prevents us from hanging the test + // in the event of a failure and instead reports + // back immediately. + select { + case m = <-subCh: + case err := <-postErrCh: + require.NoError(t, err, "post 2 messages") + } + require.NoError(t, m.err, "sub.Next") + require.Equal(t, "Hello!", m.resp.MessageAdded.Text) + require.Equal(t, "vektah", m.resp.MessageAdded.CreatedBy) - wg.Wait() - var resp interface{} - c.MustPost(`mutation { - a:post(text:"Hello!", roomName:"#gophers", username:"vektah") { id } - b:post(text:"Whats up?", roomName:"#gophers", username:"vektah") { id } - }`, &resp) + m = <-subCh + require.NoError(t, m.err, "sub.Next") + require.Equal(t, "Whats up?", m.resp.MessageAdded.Text) + require.Equal(t, "vektah", m.resp.MessageAdded.CreatedBy) }) }