github.com/go-playground/pkg/v5@v5.29.1/net/http/helpers_test.go (about)

     1  package httpext
     2  
     3  import (
     4  	"bytes"
     5  	"compress/gzip"
     6  	"encoding/json"
     7  	"encoding/xml"
     8  	"mime/multipart"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"net/url"
    12  	"os"
    13  	"strings"
    14  	"testing"
    15  
    16  	. "github.com/go-playground/assert/v2"
    17  )
    18  
    19  func TestAcceptedLanguages(t *testing.T) {
    20  	req, _ := http.NewRequest("POST", "/", nil)
    21  	req.Header.Set(AcceptedLanguage, "da, en-GB;q=0.8, en;q=0.7")
    22  
    23  	languages := AcceptedLanguages(req)
    24  
    25  	Equal(t, languages[0], "da")
    26  	Equal(t, languages[1], "en-GB")
    27  	Equal(t, languages[2], "en")
    28  
    29  	req.Header.Del(AcceptedLanguage)
    30  
    31  	languages = AcceptedLanguages(req)
    32  	Equal(t, len(languages), 0)
    33  
    34  	req.Header.Set(AcceptedLanguage, "")
    35  	languages = AcceptedLanguages(req)
    36  	Equal(t, len(languages), 0)
    37  }
    38  
    39  func TestAttachment(t *testing.T) {
    40  	mux := http.NewServeMux()
    41  	mux.HandleFunc("/dl", func(w http.ResponseWriter, r *http.Request) {
    42  		f, _ := os.Open("../../README.md")
    43  		if err := Attachment(w, f, "README.md"); err != nil {
    44  			panic(err)
    45  		}
    46  	})
    47  	mux.HandleFunc("/dl-unknown-type", func(w http.ResponseWriter, r *http.Request) {
    48  		f, _ := os.Open("../../README.md")
    49  		if err := Attachment(w, f, "readme"); err != nil {
    50  			panic(err)
    51  		}
    52  	})
    53  	mux.HandleFunc("/dl-fake-png", func(w http.ResponseWriter, r *http.Request) {
    54  		f, _ := os.Open("../../README.md")
    55  		if err := Attachment(w, f, "logo.png"); err != nil {
    56  			panic(err)
    57  		}
    58  	})
    59  
    60  	tests := []struct {
    61  		name        string
    62  		code        int
    63  		disposition string
    64  		typ         string
    65  		url         string
    66  	}{
    67  		{
    68  			code:        http.StatusOK,
    69  			disposition: "attachment;filename=README.md",
    70  			typ:         TextMarkdown,
    71  			url:         "/dl",
    72  		},
    73  		{
    74  			code:        http.StatusOK,
    75  			disposition: "attachment;filename=readme",
    76  			typ:         ApplicationOctetStream,
    77  			url:         "/dl-unknown-type",
    78  		},
    79  		{
    80  			code:        http.StatusOK,
    81  			disposition: "attachment;filename=logo.png",
    82  			typ:         ImagePNG,
    83  			url:         "/dl-fake-png",
    84  		},
    85  	}
    86  
    87  	for _, tt := range tests {
    88  		t.Run(tt.name, func(t *testing.T) {
    89  			req, err := http.NewRequest(http.MethodGet, tt.url, nil)
    90  			Equal(t, err, nil)
    91  
    92  			w := httptest.NewRecorder()
    93  			mux.ServeHTTP(w, req)
    94  
    95  			if tt.code != w.Code {
    96  				t.Errorf("Status Code = %d, want %d", w.Code, tt.code)
    97  			}
    98  			if tt.disposition != w.Header().Get(ContentDisposition) {
    99  				t.Errorf("Content Disaposition = %s, want %s", w.Header().Get(ContentDisposition), tt.disposition)
   100  			}
   101  			if tt.typ != w.Header().Get(ContentType) {
   102  				t.Errorf("Content Type = %s, want %s", w.Header().Get(ContentType), tt.typ)
   103  			}
   104  		})
   105  	}
   106  }
   107  
   108  func TestInline(t *testing.T) {
   109  	mux := http.NewServeMux()
   110  	mux.HandleFunc("/dl-inline", func(w http.ResponseWriter, r *http.Request) {
   111  		f, _ := os.Open("../../README.md")
   112  		if err := Inline(w, f, "README.md"); err != nil {
   113  			panic(err)
   114  		}
   115  	})
   116  	mux.HandleFunc("/dl-unknown-type-inline", func(w http.ResponseWriter, r *http.Request) {
   117  		f, _ := os.Open("../../README.md")
   118  		if err := Inline(w, f, "readme"); err != nil {
   119  			panic(err)
   120  		}
   121  	})
   122  
   123  	tests := []struct {
   124  		name        string
   125  		code        int
   126  		disposition string
   127  		typ         string
   128  		url         string
   129  	}{
   130  		{
   131  			code:        http.StatusOK,
   132  			disposition: "inline;filename=README.md",
   133  			typ:         TextMarkdown,
   134  			url:         "/dl-inline",
   135  		},
   136  		{
   137  			code:        http.StatusOK,
   138  			disposition: "inline;filename=readme",
   139  			typ:         ApplicationOctetStream,
   140  			url:         "/dl-unknown-type-inline",
   141  		},
   142  	}
   143  
   144  	for _, tt := range tests {
   145  		t.Run(tt.name, func(t *testing.T) {
   146  			req, err := http.NewRequest(http.MethodGet, tt.url, nil)
   147  			Equal(t, err, nil)
   148  
   149  			w := httptest.NewRecorder()
   150  			mux.ServeHTTP(w, req)
   151  
   152  			if tt.code != w.Code {
   153  				t.Errorf("Status Code = %d, want %d", w.Code, tt.code)
   154  			}
   155  			if tt.disposition != w.Header().Get(ContentDisposition) {
   156  				t.Errorf("Content Disaposition = %s, want %s", w.Header().Get(ContentDisposition), tt.disposition)
   157  			}
   158  			if tt.typ != w.Header().Get(ContentType) {
   159  				t.Errorf("Content Type = %s, want %s", w.Header().Get(ContentType), tt.typ)
   160  			}
   161  		})
   162  	}
   163  }
   164  
   165  func TestClientIP(t *testing.T) {
   166  	req, _ := http.NewRequest("POST", "/", nil)
   167  	req.Header.Set("X-Real-IP", " 10.10.10.10  ")
   168  	req.Header.Set("X-Forwarded-For", "  20.20.20.20, 30.30.30.30")
   169  	req.RemoteAddr = "  40.40.40.40:42123 "
   170  
   171  	Equal(t, ClientIP(req), "10.10.10.10")
   172  
   173  	req.Header.Del("X-Real-IP")
   174  	Equal(t, ClientIP(req), "20.20.20.20")
   175  
   176  	req.Header.Set("X-Forwarded-For", "30.30.30.30  ")
   177  	Equal(t, ClientIP(req), "30.30.30.30")
   178  
   179  	req.Header.Del("X-Forwarded-For")
   180  	Equal(t, ClientIP(req), "40.40.40.40")
   181  }
   182  
   183  func TestJSON(t *testing.T) {
   184  	w := httptest.NewRecorder()
   185  	type test struct {
   186  		Field string `json:"field"`
   187  	}
   188  	tst := test{Field: "myfield"}
   189  	b, err := json.Marshal(tst)
   190  	Equal(t, err, nil)
   191  
   192  	err = JSON(w, http.StatusOK, tst)
   193  	Equal(t, err, nil)
   194  	Equal(t, w.Header().Get(ContentType), ApplicationJSON)
   195  	Equal(t, w.Body.Bytes(), b)
   196  
   197  	err = JSON(w, http.StatusOK, func() {})
   198  	NotEqual(t, err, nil)
   199  }
   200  
   201  func TestJSONBytes(t *testing.T) {
   202  	w := httptest.NewRecorder()
   203  	type test struct {
   204  		Field string `json:"field"`
   205  	}
   206  	tst := test{Field: "myfield"}
   207  	b, err := json.Marshal(tst)
   208  	Equal(t, err, nil)
   209  
   210  	err = JSONBytes(w, http.StatusOK, b)
   211  	Equal(t, err, nil)
   212  	Equal(t, w.Header().Get(ContentType), ApplicationJSON)
   213  	Equal(t, w.Body.Bytes(), b)
   214  }
   215  
   216  func TestJSONP(t *testing.T) {
   217  	callbackFunc := "CallbackFunc"
   218  	w := httptest.NewRecorder()
   219  	type test struct {
   220  		Field string `json:"field"`
   221  	}
   222  	tst := test{Field: "myfield"}
   223  	err := JSONP(w, http.StatusOK, tst, callbackFunc)
   224  	Equal(t, err, nil)
   225  	Equal(t, w.Header().Get(ContentType), ApplicationJSON)
   226  
   227  	err = JSON(w, http.StatusOK, func() {})
   228  	NotEqual(t, err, nil)
   229  }
   230  
   231  func TestXML(t *testing.T) {
   232  	w := httptest.NewRecorder()
   233  	type zombie struct {
   234  		ID   int    `json:"id"   xml:"id"`
   235  		Name string `json:"name" xml:"name"`
   236  	}
   237  	tst := zombie{1, "Patient Zero"}
   238  	xmlData := `<zombie><id>1</id><name>Patient Zero</name></zombie>`
   239  	err := XML(w, http.StatusOK, tst)
   240  	Equal(t, err, nil)
   241  	Equal(t, w.Header().Get(ContentType), ApplicationXML)
   242  	Equal(t, w.Body.Bytes(), []byte(xml.Header+xmlData))
   243  
   244  	err = JSON(w, http.StatusOK, func() {})
   245  	NotEqual(t, err, nil)
   246  }
   247  
   248  func TestXMLBytes(t *testing.T) {
   249  	xmlData := `<zombie><id>1</id><name>Patient Zero</name></zombie>`
   250  	w := httptest.NewRecorder()
   251  	err := XMLBytes(w, http.StatusOK, []byte(xmlData))
   252  	Equal(t, err, nil)
   253  	Equal(t, w.Header().Get(ContentType), ApplicationXML)
   254  	Equal(t, w.Body.Bytes(), []byte(xml.Header+xmlData))
   255  }
   256  
   257  func TestDecode(t *testing.T) {
   258  	type TestStruct struct {
   259  		ID              int `form:"id"`
   260  		Posted          string
   261  		MultiPartPosted string
   262  	}
   263  
   264  	test := new(TestStruct)
   265  
   266  	mux := http.NewServeMux()
   267  	mux.HandleFunc("/decode-noquery", func(w http.ResponseWriter, r *http.Request) {
   268  		err := Decode(r, NoQueryParams, 16<<10, test)
   269  		Equal(t, err, nil)
   270  	})
   271  	mux.HandleFunc("/decode-query", func(w http.ResponseWriter, r *http.Request) {
   272  		err := Decode(r, QueryParams, 16<<10, test)
   273  		Equal(t, err, nil)
   274  	})
   275  
   276  	// test query params
   277  	r, _ := http.NewRequest(http.MethodGet, "/decode-query?id=5", nil)
   278  	w := httptest.NewRecorder()
   279  	mux.ServeHTTP(w, r)
   280  	Equal(t, w.Code, http.StatusOK)
   281  	Equal(t, test.ID, 5)
   282  	Equal(t, test.Posted, "")
   283  	Equal(t, test.MultiPartPosted, "")
   284  
   285  	// test Form decode
   286  	form := url.Values{}
   287  	form.Add("Posted", "values")
   288  
   289  	test = new(TestStruct)
   290  	r, _ = http.NewRequest(http.MethodPost, "/decode-query?id=13", strings.NewReader(form.Encode()))
   291  	r.Header.Set(ContentType, ApplicationForm)
   292  	w = httptest.NewRecorder()
   293  	mux.ServeHTTP(w, r)
   294  	Equal(t, w.Code, http.StatusOK)
   295  	Equal(t, test.ID, 13)
   296  	Equal(t, test.Posted, "values")
   297  	Equal(t, test.MultiPartPosted, "")
   298  
   299  	test = new(TestStruct)
   300  	r, _ = http.NewRequest(http.MethodPost, "/decode-noquery?id=14", strings.NewReader(form.Encode()))
   301  	r.Header.Set(ContentType, ApplicationForm)
   302  	w = httptest.NewRecorder()
   303  	mux.ServeHTTP(w, r)
   304  	Equal(t, w.Code, http.StatusOK)
   305  	Equal(t, test.ID, 0)
   306  	Equal(t, test.Posted, "values")
   307  	Equal(t, test.MultiPartPosted, "")
   308  
   309  	// test MultipartForm
   310  	body := &bytes.Buffer{}
   311  	writer := multipart.NewWriter(body)
   312  
   313  	err := writer.WriteField("MultiPartPosted", "values")
   314  	Equal(t, err, nil)
   315  
   316  	// Don't forget to close the multipart writer.
   317  	// If you don't close it, your request will be missing the terminating boundary.
   318  	err = writer.Close()
   319  	Equal(t, err, nil)
   320  
   321  	test = new(TestStruct)
   322  	r, _ = http.NewRequest(http.MethodPost, "/decode-query?id=12", body)
   323  	r.Header.Set(ContentType, writer.FormDataContentType())
   324  	w = httptest.NewRecorder()
   325  	mux.ServeHTTP(w, r)
   326  	Equal(t, w.Code, http.StatusOK)
   327  	Equal(t, test.ID, 12)
   328  	Equal(t, test.Posted, "")
   329  	Equal(t, test.MultiPartPosted, "values")
   330  
   331  	body = &bytes.Buffer{}
   332  	writer = multipart.NewWriter(body)
   333  
   334  	err = writer.WriteField("MultiPartPosted", "values")
   335  	Equal(t, err, nil)
   336  
   337  	// Don't forget to close the multipart writer.
   338  	// If you don't close it, your request will be missing the terminating boundary.
   339  	err = writer.Close()
   340  	Equal(t, err, nil)
   341  
   342  	test = new(TestStruct)
   343  	r, _ = http.NewRequest(http.MethodPost, "/decode-noquery?id=13", body)
   344  	r.Header.Set(ContentType, writer.FormDataContentType())
   345  	w = httptest.NewRecorder()
   346  	mux.ServeHTTP(w, r)
   347  	Equal(t, w.Code, http.StatusOK)
   348  	Equal(t, test.ID, 0)
   349  	Equal(t, test.Posted, "")
   350  	Equal(t, test.MultiPartPosted, "values")
   351  
   352  	// test JSON
   353  	jsonBody := `{"ID":13,"Posted":"values","MultiPartPosted":"values"}`
   354  	test = new(TestStruct)
   355  	r, _ = http.NewRequest(http.MethodPost, "/decode-query?id=13", strings.NewReader(jsonBody))
   356  	r.Header.Set(ContentType, ApplicationJSON)
   357  	w = httptest.NewRecorder()
   358  	mux.ServeHTTP(w, r)
   359  	Equal(t, w.Code, http.StatusOK)
   360  	Equal(t, test.ID, 13)
   361  	Equal(t, test.Posted, "values")
   362  	Equal(t, test.MultiPartPosted, "values")
   363  
   364  	var buff bytes.Buffer
   365  	gzw := gzip.NewWriter(&buff)
   366  	defer func() {
   367  		_ = gzw.Close()
   368  	}()
   369  	_, err = gzw.Write([]byte(jsonBody))
   370  	Equal(t, err, nil)
   371  
   372  	err = gzw.Close()
   373  	Equal(t, err, nil)
   374  
   375  	test = new(TestStruct)
   376  	r, _ = http.NewRequest(http.MethodPost, "/decode-query?id=14", &buff)
   377  	r.Header.Set(ContentType, ApplicationJSON)
   378  	r.Header.Set(ContentEncoding, Gzip)
   379  	w = httptest.NewRecorder()
   380  	mux.ServeHTTP(w, r)
   381  	Equal(t, w.Code, http.StatusOK)
   382  	Equal(t, test.ID, 14)
   383  	Equal(t, test.Posted, "values")
   384  	Equal(t, test.MultiPartPosted, "values")
   385  
   386  	test = new(TestStruct)
   387  	r, _ = http.NewRequest(http.MethodPost, "/decode-noquery?id=14", strings.NewReader(jsonBody))
   388  	r.Header.Set(ContentType, ApplicationJSON)
   389  	w = httptest.NewRecorder()
   390  	mux.ServeHTTP(w, r)
   391  	Equal(t, w.Code, http.StatusOK)
   392  	Equal(t, test.ID, 13)
   393  	Equal(t, test.Posted, "values")
   394  	Equal(t, test.MultiPartPosted, "values")
   395  
   396  	// test XML
   397  	xmlBody := `<TestStruct><ID>13</ID><Posted>values</Posted><MultiPartPosted>values</MultiPartPosted></TestStruct>`
   398  	test = new(TestStruct)
   399  	r, _ = http.NewRequest(http.MethodPost, "/decode-noquery?id=14", strings.NewReader(xmlBody))
   400  	r.Header.Set(ContentType, ApplicationXML)
   401  	w = httptest.NewRecorder()
   402  	mux.ServeHTTP(w, r)
   403  	Equal(t, w.Code, http.StatusOK)
   404  	Equal(t, test.ID, 13)
   405  	Equal(t, test.Posted, "values")
   406  	Equal(t, test.MultiPartPosted, "values")
   407  
   408  	test = new(TestStruct)
   409  	r, _ = http.NewRequest(http.MethodPost, "/decode-query?id=14", strings.NewReader(xmlBody))
   410  	r.Header.Set(ContentType, ApplicationXML)
   411  	w = httptest.NewRecorder()
   412  	mux.ServeHTTP(w, r)
   413  	Equal(t, w.Code, http.StatusOK)
   414  	Equal(t, test.ID, 14)
   415  	Equal(t, test.Posted, "values")
   416  	Equal(t, test.MultiPartPosted, "values")
   417  
   418  	buff.Reset()
   419  	gzw = gzip.NewWriter(&buff)
   420  	defer func() {
   421  		_ = gzw.Close()
   422  	}()
   423  	_, err = gzw.Write([]byte(xmlBody))
   424  	Equal(t, err, nil)
   425  
   426  	err = gzw.Close()
   427  	Equal(t, err, nil)
   428  
   429  	test = new(TestStruct)
   430  	r, _ = http.NewRequest(http.MethodPost, "/decode-noquery?id=14", &buff)
   431  	r.Header.Set(ContentType, ApplicationXML)
   432  	r.Header.Set(ContentEncoding, Gzip)
   433  	w = httptest.NewRecorder()
   434  	mux.ServeHTTP(w, r)
   435  	Equal(t, w.Code, http.StatusOK)
   436  	Equal(t, test.ID, 13)
   437  	Equal(t, test.Posted, "values")
   438  	Equal(t, test.MultiPartPosted, "values")
   439  }