1 package extension_test
2
3 import (
4 "context"
5 "net/http"
6 "testing"
7
8 "github.com/stretchr/testify/require"
9
10 "github.com/99designs/gqlgen/graphql"
11 "github.com/99designs/gqlgen/graphql/handler/extension"
12 "github.com/99designs/gqlgen/graphql/handler/testserver"
13 "github.com/99designs/gqlgen/graphql/handler/transport"
14 )
15
16 func TestAPQIntegration(t *testing.T) {
17 h := testserver.New()
18 h.Use(&extension.AutomaticPersistedQuery{Cache: graphql.MapCache{}})
19 h.AddTransport(&transport.POST{})
20
21 var stats *extension.ApqStats
22 h.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
23 stats = extension.GetApqStats(ctx)
24 return next(ctx)
25 })
26
27 resp := doRequest(h, "POST", "/graphql", `{"query":"{ name }","extensions":{"persistedQuery":{"version":1,"sha256Hash":"30166fc3298853f22709fce1e4a00e98f1b6a3160eaaaf9cb3b7db6a16073b07"}}}`)
28 require.Equal(t, http.StatusOK, resp.Code, resp.Body.String())
29 require.Equal(t, `{"data":{"name":"test"}}`, resp.Body.String())
30
31 require.NotNil(t, stats)
32 require.True(t, stats.SentQuery)
33 require.Equal(t, "30166fc3298853f22709fce1e4a00e98f1b6a3160eaaaf9cb3b7db6a16073b07", stats.Hash)
34 }
35
36 func TestAPQ(t *testing.T) {
37 const query = "{ me { name } }"
38 const hash = "b8d9506e34c83b0e53c2aa463624fcea354713bc38f95276e6f0bd893ffb5b88"
39
40 t.Run("with query and no hash", func(t *testing.T) {
41 ctx := newOC()
42 params := &graphql.RawParams{
43 Query: "original query",
44 }
45 err := extension.AutomaticPersistedQuery{graphql.MapCache{}}.MutateOperationParameters(ctx, params)
46 require.Nil(t, err)
47
48 require.Equal(t, "original query", params.Query)
49 })
50
51 t.Run("with hash miss and no query", func(t *testing.T) {
52 ctx := newOC()
53 params := &graphql.RawParams{
54 Extensions: map[string]interface{}{
55 "persistedQuery": map[string]interface{}{
56 "sha256Hash": hash,
57 "version": 1,
58 },
59 },
60 }
61
62 err := extension.AutomaticPersistedQuery{graphql.MapCache{}}.MutateOperationParameters(ctx, params)
63 require.Equal(t, err.Message, "PersistedQueryNotFound")
64 })
65
66 t.Run("with hash miss and query", func(t *testing.T) {
67 ctx := newOC()
68 params := &graphql.RawParams{
69 Query: query,
70 Extensions: map[string]interface{}{
71 "persistedQuery": map[string]interface{}{
72 "sha256Hash": hash,
73 "version": 1,
74 },
75 },
76 }
77 cache := graphql.MapCache{}
78 err := extension.AutomaticPersistedQuery{cache}.MutateOperationParameters(ctx, params)
79 require.Nil(t, err)
80
81 require.Equal(t, "{ me { name } }", params.Query)
82 require.Equal(t, "{ me { name } }", cache[hash])
83 })
84
85 t.Run("with hash miss and query", func(t *testing.T) {
86 ctx := newOC()
87 params := &graphql.RawParams{
88 Query: query,
89 Extensions: map[string]interface{}{
90 "persistedQuery": map[string]interface{}{
91 "sha256Hash": hash,
92 "version": 1,
93 },
94 },
95 }
96 cache := graphql.MapCache{}
97 err := extension.AutomaticPersistedQuery{cache}.MutateOperationParameters(ctx, params)
98 require.Nil(t, err)
99
100 require.Equal(t, "{ me { name } }", params.Query)
101 require.Equal(t, "{ me { name } }", cache[hash])
102 })
103
104 t.Run("with hash hit and no query", func(t *testing.T) {
105 ctx := newOC()
106 params := &graphql.RawParams{
107 Extensions: map[string]interface{}{
108 "persistedQuery": map[string]interface{}{
109 "sha256Hash": hash,
110 "version": 1,
111 },
112 },
113 }
114 cache := graphql.MapCache{
115 hash: query,
116 }
117 err := extension.AutomaticPersistedQuery{cache}.MutateOperationParameters(ctx, params)
118 require.Nil(t, err)
119
120 require.Equal(t, "{ me { name } }", params.Query)
121 })
122
123 t.Run("with malformed extension payload", func(t *testing.T) {
124 ctx := newOC()
125 params := &graphql.RawParams{
126 Extensions: map[string]interface{}{
127 "persistedQuery": "asdf",
128 },
129 }
130
131 err := extension.AutomaticPersistedQuery{graphql.MapCache{}}.MutateOperationParameters(ctx, params)
132 require.Equal(t, err.Message, "invalid APQ extension data")
133 })
134
135 t.Run("with invalid extension version", func(t *testing.T) {
136 ctx := newOC()
137 params := &graphql.RawParams{
138 Extensions: map[string]interface{}{
139 "persistedQuery": map[string]interface{}{
140 "version": 2,
141 },
142 },
143 }
144 err := extension.AutomaticPersistedQuery{graphql.MapCache{}}.MutateOperationParameters(ctx, params)
145 require.Equal(t, err.Message, "unsupported APQ version")
146 })
147
148 t.Run("with hash mismatch", func(t *testing.T) {
149 ctx := newOC()
150 params := &graphql.RawParams{
151 Query: query,
152 Extensions: map[string]interface{}{
153 "persistedQuery": map[string]interface{}{
154 "sha256Hash": "badhash",
155 "version": 1,
156 },
157 },
158 }
159
160 err := extension.AutomaticPersistedQuery{graphql.MapCache{}}.MutateOperationParameters(ctx, params)
161 require.Equal(t, err.Message, "provided APQ hash does not match query")
162 })
163 }
164
165 func newOC() context.Context {
166 oc := &graphql.OperationContext{}
167 return graphql.WithOperationContext(context.Background(), oc)
168 }
169
View as plain text