Skip to content

Commit

Permalink
Reworked integration testing using vitest (#2675)
Browse files Browse the repository at this point in the history
* Reworked integration using vitest
Added SSE client testing
Fixed SSE Transport parse errors not being sent as event-stream

* Added defer testing using urql

* Cleanup unnecessary dependencies
  • Loading branch information
UnAfraid authored Jun 15, 2023
1 parent d16f498 commit d508082
Show file tree
Hide file tree
Showing 50 changed files with 8,115 additions and 30,137 deletions.
12 changes: 5 additions & 7 deletions .github/workflows/check-integration
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set -euo pipefail
cd integration

date
go run ./server/server.go &
go run ./server/cmd/integration/server.go &

sleep 5
curl -s --connect-timeout 5 \
Expand All @@ -15,16 +15,14 @@ curl -s --connect-timeout 5 \
--retry-max-time 40 \
--retry-connrefused \
localhost:8080 > /dev/null
npm install -g typescript
npm link typescript
echo "### running jest integration spec"
./node_modules/.bin/jest --color
echo "### running integration spec"
npm run test


echo "### validating introspected schema"
./node_modules/.bin/graphql-codegen
npm run gen

if ! diff <(tail -n +3 schema-expected.graphql) <(tail -n +3 schema-fetched.graphql) ; then
if ! diff <(tail -n +3 src/generated/schema-expected.graphql) <(tail -n +3 src/generated/schema-fetched.graphql) ; then
echo "The expected schema has changed, you need to update schema-expected.graphql with any expected changes"
exit 1
fi
4 changes: 2 additions & 2 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
go: ["1.18", "1.20"]
node: [14]
node: [18]
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
Expand All @@ -22,7 +22,7 @@ jobs:
with:
node-version: ${{ matrix.node }}
- run: go mod download
- run: cd integration ; npm install
- run: cd integration ; npm ci
- run: .github/workflows/check-integration

federation:
Expand Down
8 changes: 4 additions & 4 deletions api/testdata/default/graph/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions api/testdata/federation2/graph/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 13 additions & 16 deletions graphql/handler/transport/sse.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,29 +73,26 @@ func (t SSE) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecut
return
}

rc, OpErr := exec.CreateOperationContext(ctx, params)
if OpErr != nil {
w.WriteHeader(statusFor(OpErr))
resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), OpErr)
writeJson(w, resp)
return
}

rc, opErr := exec.CreateOperationContext(ctx, params)
ctx = graphql.WithOperationContext(ctx, rc)

w.Header().Set("Content-Type", "text/event-stream")
fmt.Fprint(w, ":\n\n")
flusher.Flush()

responses, ctx := exec.DispatchOperation(ctx, rc)

for {
response := responses(ctx)
if response == nil {
break
if opErr != nil {
resp := exec.DispatchError(ctx, opErr)
writeJsonWithSSE(w, resp)
} else {
responses, ctx := exec.DispatchOperation(ctx, rc)
for {
response := responses(ctx)
if response == nil {
break
}
writeJsonWithSSE(w, response)
flusher.Flush()
}
writeJsonWithSSE(w, response)
flusher.Flush()
}

fmt.Fprint(w, "event: complete\n\n")
Expand Down
62 changes: 39 additions & 23 deletions graphql/handler/transport/sse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ import (
)

func TestSSE(t *testing.T) {
initialize := func(sse transport.SSE) *testserver.TestServer {
initialize := func() *testserver.TestServer {
h := testserver.New()
h.AddTransport(sse)
h.AddTransport(transport.SSE{})
return h
}

initializeWithServer := func(sse transport.SSE) (*testserver.TestServer, *httptest.Server) {
h := initialize(sse)
initializeWithServer := func() (*testserver.TestServer, *httptest.Server) {
h := initialize()
return h, httptest.NewServer(h)
}

Expand All @@ -35,51 +35,65 @@ func TestSSE(t *testing.T) {
return req
}

createHTTPRequest := func(url string, query string) (*http.Request, error) {
createHTTPRequest := func(url string, query string) *http.Request {
req, err := http.NewRequest("POST", url, strings.NewReader(query))
assert.NoError(t, err, "Request threw error -> %s", err)
require.NoError(t, err, "Request threw error -> %s", err)
req.Header.Set("Accept", "text/event-stream")
req.Header.Set("content-type", "application/json; charset=utf-8")
return req, err
return req
}

readLine := func(br *bufio.Reader) string {
bs, err := br.ReadString('\n')
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
return bs
}

t.Run("stream failure", func(t *testing.T) {
h := initialize(transport.SSE{})
h := initialize()
req := httptest.NewRequest(http.MethodPost, "/graphql", strings.NewReader(`{"query":"subscription { name }"}`))
req.Header.Set("content-type", "application/json; charset=utf-8")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
assert.Equal(t, 400, w.Code, "Request return wrong status -> %s", w.Code)
assert.Equal(t, 400, w.Code, "Request return wrong status -> %d", w.Code)
assert.Equal(t, `{"errors":[{"message":"transport not supported"}],"data":null}`, w.Body.String())
})

t.Run("decode failure", func(t *testing.T) {
h := initialize(transport.SSE{})
h := initialize()
req := createHTTPTestRequest("notjson")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
assert.Equal(t, 400, w.Code, "Request return wrong status -> %s", w.Code)
assert.Equal(t, 400, w.Code, "Request return wrong status -> %d", w.Code)
assert.Equal(t, `{"errors":[{"message":"json request body could not be decoded: invalid character 'o' in literal null (expecting 'u') body:notjson"}],"data":null}`, w.Body.String())
})

t.Run("parse failure", func(t *testing.T) {
h := initialize(transport.SSE{})
h := initialize()
req := createHTTPTestRequest(`{"query":"subscription {{ name }"}`)
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
assert.Equal(t, 422, w.Code, "Request return wrong status -> %s", w.Code)

assert.Equal(t, 200, w.Code, "Request return wrong status -> %d", w.Code)
assert.Equal(t, "keep-alive", w.Header().Get("Connection"))
assert.Equal(t, "text/event-stream", w.Header().Get("Content-Type"))

br := bufio.NewReader(w.Body)

assert.Equal(t, ":\n", readLine(br))
assert.Equal(t, "\n", readLine(br))
assert.Equal(t, "event: next\n", readLine(br))
assert.Equal(t, "data: {\"errors\":[{\"message\":\"Expected Name, found {\",\"locations\":[{\"line\":1,\"column\":15}],\"extensions\":{\"code\":\"GRAPHQL_PARSE_FAILED\"}}],\"data\":null}\n", readLine(br))
assert.Equal(t, "\n", readLine(br))
assert.Equal(t, "event: complete\n", readLine(br))
assert.Equal(t, "\n", readLine(br))

_, err := br.ReadByte()
assert.Equal(t, err, io.EOF)
})

t.Run("subscribe", func(t *testing.T) {
handler, srv := initializeWithServer(transport.SSE{})
handler, srv := initializeWithServer()
defer srv.Close()

var wg sync.WaitGroup
Expand All @@ -89,13 +103,15 @@ func TestSSE(t *testing.T) {
handler.SendNextSubscriptionMessage()
}()

Client := &http.Client{}
req, err := createHTTPRequest(srv.URL, `{"query":"subscription { name }"}`)
require.NoError(t, err, "Create request threw error -> %s", err)
res, err := Client.Do(req)
client := &http.Client{}
req := createHTTPRequest(srv.URL, `{"query":"subscription { name }"}`)
res, err := client.Do(req)
require.NoError(t, err, "Request threw error -> %s", err)
defer res.Body.Close()
assert.Equal(t, 200, res.StatusCode, "Request return wrong status -> %s", res.Status)
defer func() {
require.NoError(t, res.Body.Close())
}()

assert.Equal(t, 200, res.StatusCode, "Request return wrong status -> %d", res.Status)
assert.Equal(t, "keep-alive", res.Header.Get("Connection"))
assert.Equal(t, "text/event-stream", res.Header.Get("Content-Type"))

Expand Down
3 changes: 0 additions & 3 deletions integration/.babelrc

This file was deleted.

24 changes: 24 additions & 0 deletions integration/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
14 changes: 0 additions & 14 deletions integration/.graphqlconfig

This file was deleted.

2 changes: 1 addition & 1 deletion integration/readme.md → integration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ These tests run a gqlgen server against the apollo client to test real world con

First start the go server
```bash
go run integration/server/server.go
go run server/cmd/integration/server.go
```

And in another terminal:
Expand Down
17 changes: 17 additions & 0 deletions integration/codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
overwrite: true,
schema: process.env.VITE_SERVER_URL ?? 'http://localhost:8080/query',
documents: 'src/**/*.graphql',
generates: {
'src/generated/': {
preset: 'client-preset'
},
'src/generated/schema-fetched.graphql': {
plugins: ['schema-ast'],
},
},
};

export default config;
6 changes: 0 additions & 6 deletions integration/codegen.yml

This file was deleted.

Empty file removed integration/config.yaml
Empty file.
28 changes: 0 additions & 28 deletions integration/gqlgen.yml

This file was deleted.

7 changes: 7 additions & 0 deletions integration/graphql.config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: Schema
schema: src/generated/schema-expected.graphql
extensions:
endpoints:
dev:
url: http://localhost:8080/query
introspect: true
Loading

0 comments on commit d508082

Please sign in to comment.