github.com/senomas/gqlgen@v0.17.11-0.20220626120754-9aee61b0716a/client/withfilesoption_test.go (about) 1 package client_test 2 3 import ( 4 "io" 5 "mime" 6 "mime/multipart" 7 "net/http" 8 "os" 9 "regexp" 10 "strings" 11 "testing" 12 13 "github.com/99designs/gqlgen/client" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestWithFiles(t *testing.T) { 18 tempFile1, _ := os.CreateTemp(os.TempDir(), "tempFile1") 19 tempFile2, _ := os.CreateTemp(os.TempDir(), "tempFile2") 20 tempFile3, _ := os.CreateTemp(os.TempDir(), "tempFile3") 21 defer os.Remove(tempFile1.Name()) 22 defer os.Remove(tempFile2.Name()) 23 defer os.Remove(tempFile3.Name()) 24 tempFile1.WriteString(`The quick brown fox jumps over the lazy dog`) 25 tempFile2.WriteString(`hello world`) 26 tempFile3.WriteString(`La-Li-Lu-Le-Lo`) 27 28 t.Run("with one file", func(t *testing.T) { 29 h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 30 mediaType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) 31 require.NoError(t, err) 32 require.True(t, strings.HasPrefix(mediaType, "multipart/")) 33 34 mr := multipart.NewReader(r.Body, params["boundary"]) 35 for { 36 p, err := mr.NextPart() 37 if err == io.EOF { 38 break 39 } 40 require.NoError(t, err) 41 42 slurp, err := io.ReadAll(p) 43 require.NoError(t, err) 44 45 contentDisposition := p.Header.Get("Content-Disposition") 46 47 if contentDisposition == `form-data; name="operations"` { 48 require.EqualValues(t, `{"query":"{ id }","variables":{"file":{}}}`, slurp) 49 } 50 if contentDisposition == `form-data; name="map"` { 51 require.EqualValues(t, `{"0":["variables.file"]}`, slurp) 52 } 53 if regexp.MustCompile(`form-data; name="0"; filename=.*`).MatchString(contentDisposition) { 54 require.Equal(t, `text/plain; charset=utf-8`, p.Header.Get("Content-Type")) 55 require.EqualValues(t, `The quick brown fox jumps over the lazy dog`, slurp) 56 } 57 } 58 w.Write([]byte(`{}`)) 59 }) 60 61 c := client.New(h) 62 63 var resp struct{} 64 c.MustPost("{ id }", &resp, 65 client.Var("file", tempFile1), 66 client.WithFiles(), 67 ) 68 }) 69 70 t.Run("with multiple files", func(t *testing.T) { 71 h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 72 mediaType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) 73 require.NoError(t, err) 74 require.True(t, strings.HasPrefix(mediaType, "multipart/")) 75 76 mr := multipart.NewReader(r.Body, params["boundary"]) 77 for { 78 p, err := mr.NextPart() 79 if err == io.EOF { 80 break 81 } 82 require.NoError(t, err) 83 84 slurp, err := io.ReadAll(p) 85 require.NoError(t, err) 86 87 contentDisposition := p.Header.Get("Content-Disposition") 88 89 if contentDisposition == `form-data; name="operations"` { 90 require.EqualValues(t, `{"query":"{ id }","variables":{"input":{"files":[{},{}]}}}`, slurp) 91 } 92 if contentDisposition == `form-data; name="map"` { 93 // returns `{"0":["variables.input.files.0"],"1":["variables.input.files.1"]}` 94 // but the order of file inputs is unpredictable between different OS systems 95 require.Contains(t, string(slurp), `{"0":`) 96 require.Contains(t, string(slurp), `["variables.input.files.0"]`) 97 require.Contains(t, string(slurp), `,"1":`) 98 require.Contains(t, string(slurp), `["variables.input.files.1"]`) 99 require.Contains(t, string(slurp), `}`) 100 } 101 if regexp.MustCompile(`form-data; name="[0,1]"; filename=.*`).MatchString(contentDisposition) { 102 require.Equal(t, `text/plain; charset=utf-8`, p.Header.Get("Content-Type")) 103 require.Contains(t, []string{ 104 `The quick brown fox jumps over the lazy dog`, 105 `hello world`, 106 }, string(slurp)) 107 } 108 } 109 w.Write([]byte(`{}`)) 110 }) 111 112 c := client.New(h) 113 114 var resp struct{} 115 c.MustPost("{ id }", &resp, 116 client.Var("input", map[string]interface{}{ 117 "files": []*os.File{tempFile1, tempFile2}, 118 }), 119 client.WithFiles(), 120 ) 121 }) 122 123 t.Run("with multiple files across multiple variables", func(t *testing.T) { 124 h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 125 mediaType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) 126 require.NoError(t, err) 127 require.True(t, strings.HasPrefix(mediaType, "multipart/")) 128 129 mr := multipart.NewReader(r.Body, params["boundary"]) 130 for { 131 p, err := mr.NextPart() 132 if err == io.EOF { 133 break 134 } 135 require.NoError(t, err) 136 137 slurp, err := io.ReadAll(p) 138 require.NoError(t, err) 139 140 contentDisposition := p.Header.Get("Content-Disposition") 141 142 if contentDisposition == `form-data; name="operations"` { 143 require.EqualValues(t, `{"query":"{ id }","variables":{"req":{"files":[{},{}],"foo":{"bar":{}}}}}`, slurp) 144 } 145 if contentDisposition == `form-data; name="map"` { 146 // returns `{"0":["variables.req.files.0"],"1":["variables.req.files.1"],"2":["variables.req.foo.bar"]}` 147 // but the order of file inputs is unpredictable between different OS systems 148 require.Contains(t, string(slurp), `{"0":`) 149 require.Contains(t, string(slurp), `["variables.req.files.0"]`) 150 require.Contains(t, string(slurp), `,"1":`) 151 require.Contains(t, string(slurp), `["variables.req.files.1"]`) 152 require.Contains(t, string(slurp), `,"2":`) 153 require.Contains(t, string(slurp), `["variables.req.foo.bar"]`) 154 require.Contains(t, string(slurp), `}`) 155 } 156 if regexp.MustCompile(`form-data; name="[0,1,2]"; filename=.*`).MatchString(contentDisposition) { 157 require.Equal(t, `text/plain; charset=utf-8`, p.Header.Get("Content-Type")) 158 require.Contains(t, []string{ 159 `The quick brown fox jumps over the lazy dog`, 160 `La-Li-Lu-Le-Lo`, 161 `hello world`, 162 }, string(slurp)) 163 } 164 } 165 w.Write([]byte(`{}`)) 166 }) 167 168 c := client.New(h) 169 170 var resp struct{} 171 c.MustPost("{ id }", &resp, 172 client.Var("req", map[string]interface{}{ 173 "files": []*os.File{tempFile1, tempFile2}, 174 "foo": map[string]interface{}{ 175 "bar": tempFile3, 176 }, 177 }), 178 client.WithFiles(), 179 ) 180 }) 181 182 t.Run("with multiple files and file reuse", func(t *testing.T) { 183 h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 184 mediaType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) 185 require.NoError(t, err) 186 require.True(t, strings.HasPrefix(mediaType, "multipart/")) 187 188 mr := multipart.NewReader(r.Body, params["boundary"]) 189 for { 190 p, err := mr.NextPart() 191 if err == io.EOF { 192 break 193 } 194 require.NoError(t, err) 195 196 slurp, err := io.ReadAll(p) 197 require.NoError(t, err) 198 199 contentDisposition := p.Header.Get("Content-Disposition") 200 201 if contentDisposition == `form-data; name="operations"` { 202 require.EqualValues(t, `{"query":"{ id }","variables":{"files":[{},{},{}]}}`, slurp) 203 } 204 if contentDisposition == `form-data; name="map"` { 205 require.EqualValues(t, `{"0":["variables.files.0","variables.files.2"],"1":["variables.files.1"]}`, slurp) 206 // returns `{"0":["variables.files.0","variables.files.2"],"1":["variables.files.1"]}` 207 // but the order of file inputs is unpredictable between different OS systems 208 require.Contains(t, string(slurp), `{"0":`) 209 require.Contains(t, string(slurp), `["variables.files.0"`) 210 require.Contains(t, string(slurp), `,"1":`) 211 require.Contains(t, string(slurp), `"variables.files.1"]`) 212 require.Contains(t, string(slurp), `"variables.files.2"]`) 213 require.NotContains(t, string(slurp), `,"2":`) 214 require.Contains(t, string(slurp), `}`) 215 } 216 if regexp.MustCompile(`form-data; name="[0,1]"; filename=.*`).MatchString(contentDisposition) { 217 require.Equal(t, `text/plain; charset=utf-8`, p.Header.Get("Content-Type")) 218 require.Contains(t, []string{ 219 `The quick brown fox jumps over the lazy dog`, 220 `hello world`, 221 }, string(slurp)) 222 } 223 require.False(t, regexp.MustCompile(`form-data; name="2"; filename=.*`).MatchString(contentDisposition)) 224 } 225 w.Write([]byte(`{}`)) 226 }) 227 228 c := client.New(h) 229 230 var resp struct{} 231 c.MustPost("{ id }", &resp, 232 client.Var("files", []*os.File{tempFile1, tempFile2, tempFile1}), 233 client.WithFiles(), 234 ) 235 }) 236 }