1package cos 2 3import ( 4 "bytes" 5 "context" 6 "encoding/xml" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net/http" 11 "net/http/httptest" 12 "net/textproto" 13 "net/url" 14 "reflect" 15 "sort" 16 "strings" 17 "testing" 18 "time" 19) 20 21var ( 22 // mux is the HTTP request multiplexer used with the test server. 23 mux *http.ServeMux 24 25 // client is the COS client being tested. 26 client *Client 27 28 // server is a test HTTP server used to provide mock API responses. 29 server *httptest.Server 30) 31 32// setup sets up a test HTTP server along with a cos.Client that is 33// configured to talk to that test server. Tests should register handlers on 34// mux which provide mock responses for the API method being tested. 35func setup() { 36 // test server 37 mux = http.NewServeMux() 38 server = httptest.NewServer(mux) 39 40 u, _ := url.Parse(server.URL) 41 client = NewClient(&BaseURL{u, u}, nil) 42} 43 44// teardown closes the test HTTP server. 45func teardown() { 46 server.Close() 47} 48 49type values map[string]string 50 51func testFormValues(t *testing.T, r *http.Request, values values) { 52 want := url.Values{} 53 for k, v := range values { 54 want.Set(k, v) 55 } 56 57 r.ParseForm() 58 if got := r.Form; !reflect.DeepEqual(got, want) { 59 t.Errorf("Request parameters: %v, want %v", got, want) 60 } 61} 62 63func testMethod(t *testing.T, r *http.Request, want string) { 64 if got := r.Method; got != want { 65 t.Errorf("Request method: %v, want %v", got, want) 66 } 67} 68 69func testHeader(t *testing.T, r *http.Request, header string, want string) { 70 if got := r.Header.Get(header); got != want { 71 t.Errorf("Header.Get(%q) returned %q, want %q", header, got, want) 72 } 73} 74 75func testURLParseError(t *testing.T, err error) { 76 if err == nil { 77 t.Errorf("Expected error to be returned") 78 } 79 if err, ok := err.(*url.Error); !ok || err.Op != "parse" { 80 t.Errorf("Expected URL parse error, got %+v", err) 81 } 82} 83 84func testBody(t *testing.T, r *http.Request, want string) { 85 b, err := ioutil.ReadAll(r.Body) 86 if err != nil { 87 t.Errorf("Error reading request body: %v", err) 88 } 89 if got := string(b); got != want { 90 t.Errorf("request Body is %s, want %s", got, want) 91 } 92} 93 94// Helper function to test that a value is marshalled to XML as expected. 95func testXMLMarshal(t *testing.T, v interface{}, want string) { 96 j, err := xml.Marshal(v) 97 if err != nil { 98 t.Errorf("Unable to marshal JSON for %v", v) 99 } 100 101 w := new(bytes.Buffer) 102 err = xml.NewEncoder(w).Encode([]byte(want)) 103 if err != nil { 104 t.Errorf("String is not valid json: %s", want) 105 } 106 107 if w.String() != string(j) { 108 t.Errorf("xml.Marshal(%q) returned %s, want %s", v, j, w) 109 } 110 111 // now go the other direction and make sure things unmarshal as expected 112 u := reflect.ValueOf(v).Interface() 113 if err := xml.Unmarshal([]byte(want), u); err != nil { 114 t.Errorf("Unable to unmarshal XML for %v", want) 115 } 116 117 if !reflect.DeepEqual(v, u) { 118 t.Errorf("xml.Unmarshal(%q) returned %s, want %s", want, u, v) 119 } 120} 121 122func TestNewClient(t *testing.T) { 123 c := NewClient(nil, nil) 124 125 if got, want := c.BaseURL.ServiceURL.String(), defaultServiceBaseURL; got != want { 126 t.Errorf("NewClient BaseURL is %v, want %v", got, want) 127 } 128 if got, want := c.UserAgent, userAgent; got != want { 129 t.Errorf("NewClient UserAgent is %v, want %v", got, want) 130 } 131} 132 133func TestNewBucketURL_secure_false(t *testing.T) { 134 got := NewBucketURL("bname", "idx", "ap-beijing", false).String() 135 want := "http://bname-idx.cos.ap-beijing.myqcloud.com" 136 if got != want { 137 t.Errorf("NewBucketURL is %v, want %v", got, want) 138 } 139} 140 141func TestNewBucketURL_secure_true(t *testing.T) { 142 got := NewBucketURL("bname", "idx", "ap-beijing", true).String() 143 want := "https://bname-idx.cos.ap-beijing.myqcloud.com" 144 if got != want { 145 t.Errorf("NewBucketURL is %v, want %v", got, want) 146 } 147} 148 149func TestNewBaseURL(t *testing.T) { 150 bu := "https://test-1253846586.cos.ap-beijing.myqcloud.com" 151 got, _ := NewBaseURL(bu) 152 if got.BucketURL.String() != bu { 153 t.Errorf("bucketURL want %s, but got %s", bu, got.BucketURL.String()) 154 } 155 if got.ServiceURL.String() != defaultServiceBaseURL { 156 t.Errorf("serviceURL want %s, but got %s", defaultServiceBaseURL, got.ServiceURL.String()) 157 } 158} 159 160func TestClient_doAPI(t *testing.T) { 161 setup() 162 defer teardown() 163 164} 165 166func TestNewAuthTime(t *testing.T) { 167 a := NewAuthTime(time.Hour) 168 if a.SignStartTime != a.KeyStartTime || 169 a.SignEndTime != a.SignEndTime || 170 a.SignStartTime.Add(time.Hour) != a.SignEndTime { 171 t.Errorf("NewAuthTime request got %+v is not valid", a) 172 } 173} 174 175type traceCloser struct { 176 io.Reader 177 Called bool 178} 179 180func (t traceCloser) Close() error { 181 t.Called = true 182 return nil 183} 184 185func newTraceCloser(r io.Reader) traceCloser { 186 return traceCloser{r, false} 187} 188 189func Test_doAPI_copy_body(t *testing.T) { 190 setup() 191 defer teardown() 192 193 mux.HandleFunc("/test_down", func(w http.ResponseWriter, r *http.Request) { 194 fmt.Fprint(w, `test`) 195 }) 196 197 w := bytes.NewBuffer([]byte{}) 198 resp, err := client.send(context.TODO(), &sendOptions{ 199 baseURL: client.BaseURL.ServiceURL, 200 uri: "/test_down", 201 method: "GET", 202 result: w, 203 }) 204 205 if err != nil { 206 t.Errorf("Expected error == nil, got %+v", err) 207 } 208 b, _ := ioutil.ReadAll(resp.Body) 209 if len(b) != 0 || string(w.Bytes()) != "test" { 210 t.Errorf( 211 "Expected body was copy and close, got %+v, %+v", 212 string(b), string(w.Bytes())) 213 } 214} 215 216func Test_Response_header_method(t *testing.T) { 217 setup() 218 defer teardown() 219 reqID := "NTk0NTRjZjZfNTViMjM1XzlkMV9hZTZh" 220 traceID := "OGVmYzZiMmQzYjA2OWNhODk0NTRkMTBiOWVmMDAxODc0OWRkZjk0ZDM1NmI1M2E2MTRlY2MzZDhmNmI5MWI1OTBjYzE2MjAxN2M1MzJiOTdkZjMxMDVlYTZjN2FiMmI0NTk3NWFiNjAyMzdlM2RlMmVmOGNiNWIxYjYwNDFhYmQ=" 221 objType := "normal" 222 storageCls := "STANDARD" 223 versionID := "xxx-v1" // ? 224 encryption := "AES256" 225 226 mux.HandleFunc("/test_down", func(w http.ResponseWriter, r *http.Request) { 227 w.Header().Set(xCosRequestID, reqID) 228 w.Header().Set(xCosTraceID, traceID) 229 w.Header().Set(xCosObjectType, objType) 230 w.Header().Set(xCosStorageClass, storageCls) 231 w.Header().Set(xCosVersionID, versionID) 232 w.Header().Set(xCosServerSideEncryption, encryption) 233 w.Header().Add("x-cos-meta-1", "1") 234 w.Header().Add("x-cos-meta-1", "11") 235 w.Header().Add("x-cos-meta-2", "2") 236 w.Header().Add("x-cos-meta-2", "22") 237 w.Header().Add("x-cos-meta-3", "33") 238 fmt.Fprint(w, `test`) 239 }) 240 241 w := bytes.NewBuffer([]byte{}) 242 resp, err := client.send(context.TODO(), &sendOptions{ 243 baseURL: client.BaseURL.ServiceURL, 244 uri: "/test_down", 245 method: "GET", 246 result: w, 247 }) 248 249 if err != nil { 250 t.Errorf("Expected error == nil, got %+v", err) 251 } 252 b, _ := ioutil.ReadAll(resp.Body) 253 if len(b) != 0 || string(w.Bytes()) != "test" { 254 t.Errorf( 255 "Expected body was copy and close, got %+v, %+v", 256 string(b), string(w.Bytes())) 257 } 258 h := resp.MetaHeaders() 259 keys := []string{} 260 for k := range h { 261 keys = append(keys, strings.ToLower(k)) 262 } 263 sort.Strings(keys) 264 if resp.RequestID() != reqID || 265 resp.TraceID() != traceID || 266 resp.ObjectType() != objType || 267 resp.StorageClass() != storageCls || 268 resp.VersionID() != versionID || 269 resp.ServerSideEncryption() != encryption || 270 !reflect.DeepEqual(keys, 271 []string{"x-cos-meta-1", "x-cos-meta-2", "x-cos-meta-3"}) { 272 t.Errorf("result of response header method is not expected") 273 } 274 v1 := h[textproto.CanonicalMIMEHeaderKey("x-cos-meta-1")] 275 sort.Strings(v1) 276 v2 := h[textproto.CanonicalMIMEHeaderKey("x-cos-meta-2")] 277 sort.Strings(v2) 278 v3 := h[textproto.CanonicalMIMEHeaderKey("x-cos-meta-3")] 279 sort.Strings(v3) 280 if !reflect.DeepEqual(v1, 281 []string{"1", "11"}) || 282 !reflect.DeepEqual(v2, 283 []string{"2", "22"}) || 284 !reflect.DeepEqual(v3, 285 []string{"33"}) { 286 t.Errorf("result of response meta headers is not expected, %s, %s, %s", 287 v1, v2, v3) 288 } 289} 290