github.com/fortexxx/gqlgen@v0.10.3-0.20191216030626-ca5ea8b21ead/example/fileupload/fileupload_test.go (about) 1 //go:generate go run ../../testdata/gqlgen.go -stub stubs.go 2 package fileupload 3 4 import ( 5 "bytes" 6 "context" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "mime/multipart" 11 "net/http" 12 "net/http/httptest" 13 "testing" 14 15 "github.com/99designs/gqlgen/example/fileupload/model" 16 "github.com/99designs/gqlgen/graphql" 17 "github.com/99designs/gqlgen/graphql/handler" 18 "github.com/99designs/gqlgen/graphql/handler/transport" 19 "github.com/stretchr/testify/require" 20 ) 21 22 func TestFileUpload(t *testing.T) { 23 client := http.Client{} 24 25 t.Run("valid single file upload", func(t *testing.T) { 26 resolver := &Stub{} 27 resolver.MutationResolver.SingleUpload = func(ctx context.Context, file graphql.Upload) (*model.File, error) { 28 require.NotNil(t, file) 29 require.NotNil(t, file.File) 30 content, err := ioutil.ReadAll(file.File) 31 require.Nil(t, err) 32 require.Equal(t, string(content), "test") 33 34 return &model.File{ 35 ID: 1, 36 Name: file.Filename, 37 Content: string(content), 38 }, nil 39 } 40 srv := httptest.NewServer(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))) 41 defer srv.Close() 42 43 operations := `{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id, name, content } }", "variables": { "file": null } }` 44 mapData := `{ "0": ["variables.file"] }` 45 files := []file{ 46 { 47 mapKey: "0", 48 name: "a.txt", 49 content: "test", 50 }, 51 } 52 req := createUploadRequest(t, srv.URL, operations, mapData, files) 53 54 resp, err := client.Do(req) 55 require.Nil(t, err) 56 require.Equal(t, http.StatusOK, resp.StatusCode) 57 responseBody, err := ioutil.ReadAll(resp.Body) 58 require.Nil(t, err) 59 responseString := string(responseBody) 60 require.Equal(t, `{"data":{"singleUpload":{"id":1,"name":"a.txt","content":"test"}}}`, responseString) 61 err = resp.Body.Close() 62 require.Nil(t, err) 63 }) 64 65 t.Run("valid single file upload with payload", func(t *testing.T) { 66 resolver := &Stub{} 67 resolver.MutationResolver.SingleUploadWithPayload = func(ctx context.Context, req model.UploadFile) (*model.File, error) { 68 require.Equal(t, req.ID, 1) 69 require.NotNil(t, req.File) 70 require.NotNil(t, req.File.File) 71 content, err := ioutil.ReadAll(req.File.File) 72 require.Nil(t, err) 73 require.Equal(t, string(content), "test") 74 75 return &model.File{ 76 ID: 1, 77 Name: req.File.Filename, 78 Content: string(content), 79 }, nil 80 } 81 srv := httptest.NewServer(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))) 82 defer srv.Close() 83 84 operations := `{ "query": "mutation ($req: UploadFile!) { singleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": {"file": null, "id": 1 } } }` 85 mapData := `{ "0": ["variables.req.file"] }` 86 files := []file{ 87 { 88 mapKey: "0", 89 name: "a.txt", 90 content: "test", 91 }, 92 } 93 req := createUploadRequest(t, srv.URL, operations, mapData, files) 94 95 resp, err := client.Do(req) 96 require.Nil(t, err) 97 require.Equal(t, http.StatusOK, resp.StatusCode) 98 responseBody, err := ioutil.ReadAll(resp.Body) 99 require.Nil(t, err) 100 require.Equal(t, `{"data":{"singleUploadWithPayload":{"id":1,"name":"a.txt","content":"test"}}}`, string(responseBody)) 101 err = resp.Body.Close() 102 require.Nil(t, err) 103 }) 104 105 t.Run("valid file list upload", func(t *testing.T) { 106 resolver := &Stub{} 107 resolver.MutationResolver.MultipleUpload = func(ctx context.Context, files []*graphql.Upload) ([]*model.File, error) { 108 require.Len(t, files, 2) 109 var contents []string 110 var resp []*model.File 111 for i := range files { 112 require.NotNil(t, files[i].File) 113 content, err := ioutil.ReadAll(files[i].File) 114 require.Nil(t, err) 115 contents = append(contents, string(content)) 116 resp = append(resp, &model.File{ 117 ID: i + 1, 118 Name: files[i].Filename, 119 Content: string(content), 120 }) 121 } 122 require.ElementsMatch(t, []string{"test1", "test2"}, contents) 123 return resp, nil 124 } 125 srv := httptest.NewServer(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))) 126 defer srv.Close() 127 128 operations := `{ "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id, name, content } }", "variables": { "files": [null, null] } }` 129 mapData := `{ "0": ["variables.files.0"], "1": ["variables.files.1"] }` 130 files := []file{ 131 { 132 mapKey: "0", 133 name: "a.txt", 134 content: "test1", 135 }, 136 { 137 mapKey: "1", 138 name: "b.txt", 139 content: "test2", 140 }, 141 } 142 req := createUploadRequest(t, srv.URL, operations, mapData, files) 143 144 resp, err := client.Do(req) 145 require.Nil(t, err) 146 require.Equal(t, http.StatusOK, resp.StatusCode) 147 responseBody, err := ioutil.ReadAll(resp.Body) 148 require.Nil(t, err) 149 require.Equal(t, `{"data":{"multipleUpload":[{"id":1,"name":"a.txt","content":"test1"},{"id":2,"name":"b.txt","content":"test2"}]}}`, string(responseBody)) 150 err = resp.Body.Close() 151 require.Nil(t, err) 152 }) 153 154 t.Run("valid file list upload with payload", func(t *testing.T) { 155 resolver := &Stub{} 156 resolver.MutationResolver.MultipleUploadWithPayload = func(ctx context.Context, req []*model.UploadFile) ([]*model.File, error) { 157 require.Len(t, req, 2) 158 var ids []int 159 var contents []string 160 var resp []*model.File 161 for i := range req { 162 require.NotNil(t, req[i].File) 163 require.NotNil(t, req[i].File.File) 164 content, err := ioutil.ReadAll(req[i].File.File) 165 require.Nil(t, err) 166 ids = append(ids, req[i].ID) 167 contents = append(contents, string(content)) 168 resp = append(resp, &model.File{ 169 ID: i + 1, 170 Name: req[i].File.Filename, 171 Content: string(content), 172 }) 173 } 174 require.ElementsMatch(t, []int{1, 2}, ids) 175 require.ElementsMatch(t, []string{"test1", "test2"}, contents) 176 return resp, nil 177 } 178 srv := httptest.NewServer(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))) 179 defer srv.Close() 180 181 operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }` 182 mapData := `{ "0": ["variables.req.0.file"], "1": ["variables.req.1.file"] }` 183 files := []file{ 184 { 185 mapKey: "0", 186 name: "a.txt", 187 content: "test1", 188 }, 189 { 190 mapKey: "1", 191 name: "b.txt", 192 content: "test2", 193 }, 194 } 195 req := createUploadRequest(t, srv.URL, operations, mapData, files) 196 197 resp, err := client.Do(req) 198 require.Nil(t, err) 199 require.Equal(t, http.StatusOK, resp.StatusCode) 200 responseBody, err := ioutil.ReadAll(resp.Body) 201 require.Nil(t, err) 202 require.Equal(t, `{"data":{"multipleUploadWithPayload":[{"id":1,"name":"a.txt","content":"test1"},{"id":2,"name":"b.txt","content":"test2"}]}}`, string(responseBody)) 203 err = resp.Body.Close() 204 require.Nil(t, err) 205 }) 206 207 t.Run("valid file list upload with payload and file reuse", func(t *testing.T) { 208 resolver := &Stub{} 209 resolver.MutationResolver.MultipleUploadWithPayload = func(ctx context.Context, req []*model.UploadFile) ([]*model.File, error) { 210 require.Len(t, req, 2) 211 var ids []int 212 var contents []string 213 var resp []*model.File 214 for i := range req { 215 require.NotNil(t, req[i].File) 216 require.NotNil(t, req[i].File.File) 217 ids = append(ids, req[i].ID) 218 219 var got []byte 220 buf := make([]byte, 2) 221 for { 222 n, err := req[i].File.File.Read(buf) 223 got = append(got, buf[:n]...) 224 if err != nil { 225 if err == io.EOF { 226 break 227 } 228 require.Fail(t, "unexpected error while reading", err.Error()) 229 } 230 } 231 contents = append(contents, string(got)) 232 resp = append(resp, &model.File{ 233 ID: i + 1, 234 Name: req[i].File.Filename, 235 Content: string(got), 236 }) 237 } 238 require.ElementsMatch(t, []int{1, 2}, ids) 239 require.ElementsMatch(t, []string{"test1", "test1"}, contents) 240 return resp, nil 241 } 242 243 operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }` 244 mapData := `{ "0": ["variables.req.0.file", "variables.req.1.file"] }` 245 files := []file{ 246 { 247 mapKey: "0", 248 name: "a.txt", 249 content: "test1", 250 }, 251 } 252 253 test := func(uploadMaxMemory int64) { 254 hndlr := handler.New(NewExecutableSchema(Config{Resolvers: resolver})) 255 hndlr.AddTransport(transport.MultipartForm{MaxMemory: uploadMaxMemory}) 256 257 srv := httptest.NewServer(hndlr) 258 defer srv.Close() 259 req := createUploadRequest(t, srv.URL, operations, mapData, files) 260 resp, err := client.Do(req) 261 require.Nil(t, err) 262 require.Equal(t, http.StatusOK, resp.StatusCode) 263 responseBody, err := ioutil.ReadAll(resp.Body) 264 require.Nil(t, err) 265 require.Equal(t, `{"data":{"multipleUploadWithPayload":[{"id":1,"name":"a.txt","content":"test1"},{"id":2,"name":"a.txt","content":"test1"}]}}`, string(responseBody)) 266 err = resp.Body.Close() 267 require.Nil(t, err) 268 } 269 270 t.Run("payload smaller than UploadMaxMemory, stored in memory", func(t *testing.T) { 271 test(5000) 272 }) 273 274 t.Run("payload bigger than UploadMaxMemory, persisted to disk", func(t *testing.T) { 275 test(2) 276 }) 277 }) 278 } 279 280 type file struct { 281 mapKey string 282 name string 283 content string 284 } 285 286 func createUploadRequest(t *testing.T, url, operations, mapData string, files []file) *http.Request { 287 bodyBuf := &bytes.Buffer{} 288 bodyWriter := multipart.NewWriter(bodyBuf) 289 290 err := bodyWriter.WriteField("operations", operations) 291 require.NoError(t, err) 292 293 err = bodyWriter.WriteField("map", mapData) 294 require.NoError(t, err) 295 296 for i := range files { 297 ff, err := bodyWriter.CreateFormFile(files[i].mapKey, files[i].name) 298 require.NoError(t, err) 299 _, err = ff.Write([]byte(files[i].content)) 300 require.NoError(t, err) 301 } 302 err = bodyWriter.Close() 303 require.NoError(t, err) 304 305 req, err := http.NewRequest("POST", fmt.Sprintf("%s/graphql", url), bodyBuf) 306 require.NoError(t, err) 307 308 req.Header.Set("Content-Type", bodyWriter.FormDataContentType()) 309 return req 310 }