1package cos
2
3import (
4	"context"
5	"fmt"
6	"net/http"
7	"net/url"
8	"reflect"
9	"testing"
10	"time"
11)
12
13func TestNewAuthorization(t *testing.T) {
14	expectAuthorization := `q-sign-algorithm=sha1&q-ak=QmFzZTY0IGlzIGEgZ2VuZXJp&q-sign-time=1480932292;1481012292&q-key-time=1480932292;1481012292&q-header-list=host;x-cos-content-sha1;x-cos-stroage-class&q-url-param-list=&q-signature=91f7814df035319aa08d47e5a7a66ea989d57301`
15	secretID := "QmFzZTY0IGlzIGEgZ2VuZXJp"
16	secretKey := "AKIDZfbOA78asKUYBcXFrJD0a1ICvR98JM"
17	host := "testbucket-125000000.cos.ap-beijing-1.myqcloud.com"
18	uri := "https://testbucket-125000000.cos.ap-beijing-1.myqcloud.com/testfile2"
19	startTime := time.Unix(int64(1480932292), 0)
20	endTime := time.Unix(int64(1481012292), 0)
21
22	req, _ := http.NewRequest("PUT", uri, nil)
23	req.Header.Add("Host", host)
24	req.Header.Add("x-cos-content-sha1", "db8ac1c259eb89d4a131b253bacfca5f319d54f2")
25	req.Header.Add("x-cos-stroage-class", "nearline")
26
27	authTime := &AuthTime{
28		SignStartTime: startTime,
29		SignEndTime:   endTime,
30		KeyStartTime:  startTime,
31		KeyEndTime:    endTime,
32	}
33	auth := newAuthorization(Auth{
34		SecretID:  secretID,
35		SecretKey: secretKey,
36	}, req, *authTime)
37
38	if auth != expectAuthorization {
39		t.Errorf("NewAuthorization returned \n%#v, want \n%#v", auth, expectAuthorization)
40	}
41}
42
43func TestAuthorizationTransport(t *testing.T) {
44	setup()
45	defer teardown()
46
47	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
48		auth := r.Header.Get("Authorization")
49		if auth == "" {
50			t.Error("AuthorizationTransport didn't add Authorization header")
51		}
52	})
53
54	(client.Sender).(*DefaultSender).Transport = &AuthorizationTransport{}
55	req, _ := http.NewRequest("GET", client.BaseURL.BucketURL.String(), nil)
56	req.Header.Set("X-Testing", "0")
57	client.doAPI(context.Background(), Caller{}, req, nil, true)
58}
59
60func TestAuthorizationTransportWithSessionToken(t *testing.T) {
61	setup()
62	defer teardown()
63
64	sessionToken := "CxQQbwSzzX5obZm23yEcyQtpROuDB0Q60d322a47737c8241991d12dc4b8387c7J6NL50eH1BYN6VnFYB_Ml6oPZzUxz5wxDGVvvgxZXr1m-4HvmkvmMH4YB02XdVPapKp7oGnrMous2jsSTALo4iU2fuRclbVw-czYwggSxuNxXAwmqcT1HpD3h3zc3e24sryIhJKqzSOczQZjtGrxSSQ4K23o9Mx8VHgrosliU0aIiI2KFhxJhij03SzDDOQcBAwpFZyM0NvpOdN6b14yJbrt9bAzYGNjX-PeU3MXfi0"
65
66	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
67		auth := r.Header.Get("Authorization")
68		if auth == "" {
69			t.Error("AuthorizationTransport didn't add Authorization header")
70		}
71		token := r.Header.Get("x-cos-security-token")
72		if token == "" {
73			t.Error("AuthorizationTransport didn't add x-cos-security-token header")
74		}
75		if token != sessionToken {
76			t.Errorf("AuthorizationTransport didn't add expected x-cos-security-token header, expected: %s, got: %s", sessionToken, token)
77		}
78	})
79
80	(client.Sender).(*DefaultSender).Transport = &AuthorizationTransport{
81		SecretID:     "233",
82		SecretKey:    "666",
83		SessionToken: sessionToken,
84	}
85	req, _ := http.NewRequest("GET", client.BaseURL.BucketURL.String(), nil)
86	req.Header.Set("X-Testing", "0")
87	client.doAPI(context.Background(), Caller{}, req, nil, true)
88}
89
90func TestAuthorizationTransport_skip_PresignedURL(t *testing.T) {
91	setup()
92	defer teardown()
93
94	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
95		_, exist := r.Header["Authorization"]
96		if exist {
97			t.Error("AuthorizationTransport add Authorization header when use PresignedURL")
98		}
99	})
100
101	(client.Sender).(*DefaultSender).Transport = &AuthorizationTransport{}
102	sign := "q-sign-algorithm=sha1&q-ak=QmFzZTY0IGlzIGEgZ2VuZXJp&q-sign-time=1480932292;1481012292&q-key-time=1480932292;1481012292&q-header-list=&q-url-param-list=&q-signature=a5de76b0734f084a7ea24413f7168b4bdbe5676c"
103	u := fmt.Sprintf("%s?sign=%s", client.BaseURL.BucketURL.String(), sign)
104	req, _ := http.NewRequest("GET", u, nil)
105	client.doAPI(context.Background(), Caller{}, req, nil, true)
106}
107
108func TestAuthorizationTransport_with_another_transport(t *testing.T) {
109	setup()
110	defer teardown()
111
112	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
113		auth := r.Header.Get("Authorization")
114		if auth == "" {
115			t.Error("AuthorizationTransport didn't add Authorization header")
116		}
117	})
118
119	tr := &testingTransport{}
120	(client.Sender).(*DefaultSender).Transport = &AuthorizationTransport{
121		Transport: tr,
122	}
123	req, _ := http.NewRequest("GET", client.BaseURL.BucketURL.String(), nil)
124	req.Header.Set("X-Testing", "0")
125	client.doAPI(context.Background(), Caller{}, req, nil, true)
126	if tr.called != 1 {
127		t.Error("AuthorizationTransport not call another Transport")
128	}
129}
130
131type testingTransport struct {
132	called int
133}
134
135func (t *testingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
136	t.called++
137	return http.DefaultTransport.RoundTrip(req)
138}
139
140func Test_camSafeURLEncode(t *testing.T) {
141	type args struct {
142		s string
143	}
144	tests := []struct {
145		name string
146		args args
147		want string
148	}{
149		{
150			name: "no replace",
151			args: args{"1234 +abc0AB#@"},
152			want: "1234%20%2Babc0AB%23%40",
153		},
154		{
155			name: "replace",
156			args: args{"1234 +abc0AB#@,!'()*"},
157			want: "1234%20%2Babc0AB%23%40%2C%21%27%28%29%2A",
158		},
159	}
160	for _, tt := range tests {
161		t.Run(tt.name, func(t *testing.T) {
162			if got := camSafeURLEncode(tt.args.s); got != tt.want {
163				t.Errorf("camSafeURLEncode() = %v, want %v", got, tt.want)
164			}
165		})
166	}
167}
168
169func Test_valuesForSign_Encode(t *testing.T) {
170	tests := []struct {
171		name string
172		vs   valuesForSign
173		want string
174	}{
175		{
176			name: "test escape",
177			vs: valuesForSign{
178				"test+233": {"value 666"},
179				"test+234": {"value 667"},
180			},
181			want: "test%2B233=value%20666&test%2B234=value%20667",
182		},
183		{
184			name: "test order",
185			vs: valuesForSign{
186				"test_233": {"value_666"},
187				"233":      {"value_2"},
188				"test_666": {"value_123"},
189			},
190			want: "233=value_2&test_233=value_666&test_666=value_123",
191		},
192	}
193	for _, tt := range tests {
194		t.Run(tt.name, func(t *testing.T) {
195			if got := tt.vs.Encode(); got != tt.want {
196				t.Errorf("valuesForSign.Encode() = %v, want %v", got, tt.want)
197			}
198		})
199	}
200}
201
202func Test_valuesForSign_Add(t *testing.T) {
203	type args struct {
204		key   string
205		value string
206	}
207	tests := []struct {
208		name string
209		vs   valuesForSign
210		args args
211		want valuesForSign
212	}{
213		{
214			name: "add new key",
215			vs:   valuesForSign{},
216			args: args{"test_key", "value_233"},
217			want: valuesForSign{"test_key": {"value_233"}},
218		},
219		{
220			name: "extend key",
221			vs:   valuesForSign{"test_key": {"value_233"}},
222			args: args{"test_key", "value_666"},
223			want: valuesForSign{"test_key": {"value_233", "value_666"}},
224		},
225		{
226			name: "key to lower(add)",
227			vs:   valuesForSign{},
228			args: args{"TEST_KEY", "value_233"},
229			want: valuesForSign{"test_key": {"value_233"}},
230		},
231		{
232			name: "key to lower(extend)",
233			vs:   valuesForSign{"test_key": {"value_233"}},
234			args: args{"TEST_KEY", "value_666"},
235			want: valuesForSign{"test_key": {"value_233", "value_666"}},
236		},
237	}
238	for _, tt := range tests {
239		t.Run(tt.name, func(t *testing.T) {
240			tt.vs.Add(tt.args.key, tt.args.value)
241			if !reflect.DeepEqual(tt.vs, tt.want) {
242				t.Errorf("%v, want %v", tt.vs, tt.want)
243			}
244		})
245	}
246}
247
248func Test_genFormatParameters(t *testing.T) {
249	type args struct {
250		parameters url.Values
251	}
252	tests := []struct {
253		name                    string
254		args                    args
255		wantFormatParameters    string
256		wantSignedParameterList []string
257	}{
258		{
259			name: "test order",
260			args: args{url.Values{
261				"test_key_233": {"666"},
262				"233":          {"222"},
263				"test_key_2":   {"value"},
264			}},
265			wantFormatParameters:    "233=222&test_key_2=value&test_key_233=666",
266			wantSignedParameterList: []string{"233", "test_key_2", "test_key_233"},
267		},
268		{
269			name: "test escape",
270			args: args{url.Values{
271				"Test+key": {"666 value"},
272				"233 666":  {"22+2"},
273			}},
274			wantFormatParameters:    "233%20666=22%2B2&test%2Bkey=666%20value",
275			wantSignedParameterList: []string{"233 666", "test+key"},
276		},
277	}
278	for _, tt := range tests {
279		t.Run(tt.name, func(t *testing.T) {
280			gotFormatParameters, gotSignedParameterList := genFormatParameters(tt.args.parameters)
281			if gotFormatParameters != tt.wantFormatParameters {
282				t.Errorf("genFormatParameters() gotFormatParameters = %v, want %v", gotFormatParameters, tt.wantFormatParameters)
283			}
284			if !reflect.DeepEqual(gotSignedParameterList, tt.wantSignedParameterList) {
285				t.Errorf("genFormatParameters() gotSignedParameterList = %v, want %v", gotSignedParameterList, tt.wantSignedParameterList)
286			}
287		})
288	}
289}
290
291func Test_genFormatHeaders(t *testing.T) {
292	type args struct {
293		headers http.Header
294	}
295	tests := []struct {
296		name                 string
297		args                 args
298		wantFormatHeaders    string
299		wantSignedHeaderList []string
300	}{
301		{
302			name: "test order",
303			args: args{http.Header{
304				"host":           {"example.com"},
305				"content-length": {"22"},
306				"content-md5":    {"xxx222"},
307			}},
308			wantFormatHeaders:    "content-length=22&content-md5=xxx222&host=example.com",
309			wantSignedHeaderList: []string{"content-length", "content-md5", "host"},
310		},
311		{
312			name: "test escape",
313			args: args{http.Header{
314				"host":                {"example.com"},
315				"content-length":      {"22"},
316				"Content-Disposition": {"attachment; filename=hello - world!(+).go"},
317			}},
318			wantFormatHeaders:    "content-disposition=attachment%3B%20filename%3Dhello%20-%20world%21%28%2B%29.go&content-length=22&host=example.com",
319			wantSignedHeaderList: []string{"content-disposition", "content-length", "host"},
320		},
321		{
322			name: "test skip key",
323			args: args{http.Header{
324				"Host":           {"example.com"},
325				"content-length": {"22"},
326				"x-cos-xyz":      {"lala"},
327				"Content-Type":   {"text/html"},
328			}},
329			wantFormatHeaders:    "content-length=22&host=example.com&x-cos-xyz=lala",
330			wantSignedHeaderList: []string{"content-length", "host", "x-cos-xyz"},
331		},
332	}
333	for _, tt := range tests {
334		t.Run(tt.name, func(t *testing.T) {
335			gotFormatHeaders, gotSignedHeaderList := genFormatHeaders(tt.args.headers)
336			if gotFormatHeaders != tt.wantFormatHeaders {
337				t.Errorf("genFormatHeaders() gotFormatHeaders = %v, want %v", gotFormatHeaders, tt.wantFormatHeaders)
338			}
339			if !reflect.DeepEqual(gotSignedHeaderList, tt.wantSignedHeaderList) {
340				t.Errorf("genFormatHeaders() gotSignedHeaderList = %v, want %v", gotSignedHeaderList, tt.wantSignedHeaderList)
341			}
342		})
343	}
344}
345