1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package httptest
6
7import (
8	"context"
9	"crypto/tls"
10	"io"
11	"net/http"
12	"net/url"
13	"reflect"
14	"strings"
15	"testing"
16)
17
18func TestNewRequest(t *testing.T) {
19	got := NewRequest("GET", "/", nil)
20	want := &http.Request{
21		Method:     "GET",
22		Host:       "example.com",
23		URL:        &url.URL{Path: "/"},
24		Header:     http.Header{},
25		Proto:      "HTTP/1.1",
26		ProtoMajor: 1,
27		ProtoMinor: 1,
28		RemoteAddr: "192.0.2.1:1234",
29		RequestURI: "/",
30	}
31	got.Body = nil // before DeepEqual
32	want = want.WithContext(context.Background())
33	if !reflect.DeepEqual(got, want) {
34		t.Errorf("Request mismatch:\n got: %#v\nwant: %#v", got, want)
35	}
36}
37
38func TestNewRequestWithContext(t *testing.T) {
39	for _, tt := range [...]struct {
40		name string
41
42		method, uri string
43		body        io.Reader
44
45		want     *http.Request
46		wantBody string
47	}{
48		{
49			name:   "Empty method means GET",
50			method: "",
51			uri:    "/",
52			body:   nil,
53			want: &http.Request{
54				Method:     "GET",
55				Host:       "example.com",
56				URL:        &url.URL{Path: "/"},
57				Header:     http.Header{},
58				Proto:      "HTTP/1.1",
59				ProtoMajor: 1,
60				ProtoMinor: 1,
61				RemoteAddr: "192.0.2.1:1234",
62				RequestURI: "/",
63			},
64			wantBody: "",
65		},
66
67		{
68			name:   "GET with full URL",
69			method: "GET",
70			uri:    "http://foo.com/path/%2f/bar/",
71			body:   nil,
72			want: &http.Request{
73				Method: "GET",
74				Host:   "foo.com",
75				URL: &url.URL{
76					Scheme:  "http",
77					Path:    "/path///bar/",
78					RawPath: "/path/%2f/bar/",
79					Host:    "foo.com",
80				},
81				Header:     http.Header{},
82				Proto:      "HTTP/1.1",
83				ProtoMajor: 1,
84				ProtoMinor: 1,
85				RemoteAddr: "192.0.2.1:1234",
86				RequestURI: "http://foo.com/path/%2f/bar/",
87			},
88			wantBody: "",
89		},
90
91		{
92			name:   "GET with full https URL",
93			method: "GET",
94			uri:    "https://foo.com/path/",
95			body:   nil,
96			want: &http.Request{
97				Method: "GET",
98				Host:   "foo.com",
99				URL: &url.URL{
100					Scheme: "https",
101					Path:   "/path/",
102					Host:   "foo.com",
103				},
104				Header:     http.Header{},
105				Proto:      "HTTP/1.1",
106				ProtoMajor: 1,
107				ProtoMinor: 1,
108				RemoteAddr: "192.0.2.1:1234",
109				RequestURI: "https://foo.com/path/",
110				TLS: &tls.ConnectionState{
111					Version:           tls.VersionTLS12,
112					HandshakeComplete: true,
113					ServerName:        "foo.com",
114				},
115			},
116			wantBody: "",
117		},
118
119		{
120			name:   "Post with known length",
121			method: "POST",
122			uri:    "/",
123			body:   strings.NewReader("foo"),
124			want: &http.Request{
125				Method:        "POST",
126				Host:          "example.com",
127				URL:           &url.URL{Path: "/"},
128				Header:        http.Header{},
129				Proto:         "HTTP/1.1",
130				ContentLength: 3,
131				ProtoMajor:    1,
132				ProtoMinor:    1,
133				RemoteAddr:    "192.0.2.1:1234",
134				RequestURI:    "/",
135			},
136			wantBody: "foo",
137		},
138
139		{
140			name:   "Post with unknown length",
141			method: "POST",
142			uri:    "/",
143			body:   struct{ io.Reader }{strings.NewReader("foo")},
144			want: &http.Request{
145				Method:        "POST",
146				Host:          "example.com",
147				URL:           &url.URL{Path: "/"},
148				Header:        http.Header{},
149				Proto:         "HTTP/1.1",
150				ContentLength: -1,
151				ProtoMajor:    1,
152				ProtoMinor:    1,
153				RemoteAddr:    "192.0.2.1:1234",
154				RequestURI:    "/",
155			},
156			wantBody: "foo",
157		},
158
159		{
160			name:   "OPTIONS *",
161			method: "OPTIONS",
162			uri:    "*",
163			want: &http.Request{
164				Method:     "OPTIONS",
165				Host:       "example.com",
166				URL:        &url.URL{Path: "*"},
167				Header:     http.Header{},
168				Proto:      "HTTP/1.1",
169				ProtoMajor: 1,
170				ProtoMinor: 1,
171				RemoteAddr: "192.0.2.1:1234",
172				RequestURI: "*",
173			},
174		},
175	} {
176		t.Run(tt.name, func(t *testing.T) {
177			got := NewRequestWithContext(context.Background(), tt.method, tt.uri, tt.body)
178			slurp, err := io.ReadAll(got.Body)
179			if err != nil {
180				t.Errorf("ReadAll: %v", err)
181			}
182			if string(slurp) != tt.wantBody {
183				t.Errorf("Body = %q; want %q", slurp, tt.wantBody)
184			}
185			tt.want = tt.want.WithContext(context.Background())
186			got.Body = nil // before DeepEqual
187			if !reflect.DeepEqual(got.URL, tt.want.URL) {
188				t.Errorf("Request.URL mismatch:\n got: %#v\nwant: %#v", got.URL, tt.want.URL)
189			}
190			if !reflect.DeepEqual(got.Header, tt.want.Header) {
191				t.Errorf("Request.Header mismatch:\n got: %#v\nwant: %#v", got.Header, tt.want.Header)
192			}
193			if !reflect.DeepEqual(got.TLS, tt.want.TLS) {
194				t.Errorf("Request.TLS mismatch:\n got: %#v\nwant: %#v", got.TLS, tt.want.TLS)
195			}
196			if !reflect.DeepEqual(got, tt.want) {
197				t.Errorf("Request mismatch:\n got: %#v\nwant: %#v", got, tt.want)
198			}
199		})
200	}
201}
202