...

Source file src/github.com/99designs/gqlgen/client/withfilesoption_test.go

Documentation: github.com/99designs/gqlgen/client

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

View as plain text