...

Source file src/google.golang.org/api/googleapi/googleapi_test.go

Documentation: google.golang.org/api/googleapi

     1  // Copyright 2011 Google LLC. 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  
     5  package googleapi
     6  
     7  import (
     8  	"encoding/json"
     9  	"io"
    10  	"net/http"
    11  	"net/url"
    12  	"reflect"
    13  	"strings"
    14  	"testing"
    15  )
    16  
    17  type ExpandTest struct {
    18  	in         string
    19  	expansions map[string]string
    20  	want       string
    21  }
    22  
    23  var expandTests = []ExpandTest{
    24  	// no expansions
    25  	{
    26  		"http://www.golang.org/",
    27  		map[string]string{},
    28  		"http://www.golang.org/",
    29  	},
    30  	// one expansion, no escaping
    31  	{
    32  		"http://www.golang.org/{bucket}/delete",
    33  		map[string]string{
    34  			"bucket": "red",
    35  		},
    36  		"http://www.golang.org/red/delete",
    37  	},
    38  	// one expansion, with hex escapes
    39  	{
    40  		"http://www.golang.org/{bucket}/delete",
    41  		map[string]string{
    42  			"bucket": "red/blue",
    43  		},
    44  		"http://www.golang.org/red%2Fblue/delete",
    45  	},
    46  	// one expansion, with space
    47  	{
    48  		"http://www.golang.org/{bucket}/delete",
    49  		map[string]string{
    50  			"bucket": "red or blue",
    51  		},
    52  		"http://www.golang.org/red%20or%20blue/delete",
    53  	},
    54  	// expansion not found
    55  	{
    56  		"http://www.golang.org/{object}/delete",
    57  		map[string]string{
    58  			"bucket": "red or blue",
    59  		},
    60  		"http://www.golang.org//delete",
    61  	},
    62  	// multiple expansions
    63  	{
    64  		"http://www.golang.org/{one}/{two}/{three}/get",
    65  		map[string]string{
    66  			"one":   "ONE",
    67  			"two":   "TWO",
    68  			"three": "THREE",
    69  		},
    70  		"http://www.golang.org/ONE/TWO/THREE/get",
    71  	},
    72  	// utf-8 characters
    73  	{
    74  		"http://www.golang.org/{bucket}/get",
    75  		map[string]string{
    76  			"bucket": "£100",
    77  		},
    78  		"http://www.golang.org/%C2%A3100/get",
    79  	},
    80  	// punctuations
    81  	{
    82  		"http://www.golang.org/{bucket}/get",
    83  		map[string]string{
    84  			"bucket": `/\@:,.`,
    85  		},
    86  		"http://www.golang.org/%2F%5C%40%3A%2C./get",
    87  	},
    88  	// mis-matched brackets
    89  	{
    90  		"http://www.golang.org/{bucket/get",
    91  		map[string]string{
    92  			"bucket": "red",
    93  		},
    94  		"http://www.golang.org/%7Bbucket/get",
    95  	},
    96  	// "+" prefix for suppressing escape
    97  	// See also: http://tools.ietf.org/html/rfc6570#section-3.2.3
    98  	{
    99  		"http://www.golang.org/{+topic}",
   100  		map[string]string{
   101  			"topic": "/topics/myproject/mytopic",
   102  		},
   103  		// The double slashes here look weird, but it's intentional
   104  		"http://www.golang.org//topics/myproject/mytopic",
   105  	},
   106  }
   107  
   108  func TestExpand(t *testing.T) {
   109  	for i, test := range expandTests {
   110  		u := url.URL{
   111  			Path: test.in,
   112  		}
   113  		Expand(&u, test.expansions)
   114  		got := u.EscapedPath()
   115  		if got != test.want {
   116  			t.Errorf("got %q expected %q in test %d", got, test.want, i+1)
   117  		}
   118  	}
   119  }
   120  
   121  func TestResolveRelative(t *testing.T) {
   122  	resolveRelativeTests := []struct {
   123  		basestr   string
   124  		relstr    string
   125  		want      string
   126  		wantPanic bool
   127  	}{
   128  		{
   129  			basestr: "http://www.golang.org/",
   130  			relstr:  "topics/myproject/mytopic",
   131  			want:    "http://www.golang.org/topics/myproject/mytopic",
   132  		},
   133  		{
   134  			basestr: "http://www.golang.org/",
   135  			relstr:  "topics/{+myproject}/{release}:build:test:deploy",
   136  			want:    "http://www.golang.org/topics/{+myproject}/{release}:build:test:deploy",
   137  		},
   138  		{
   139  			basestr: "https://www.googleapis.com/admin/reports/v1/",
   140  			relstr:  "/admin/reports_v1/channels/stop",
   141  			want:    "https://www.googleapis.com/admin/reports_v1/channels/stop",
   142  		},
   143  		{
   144  			basestr: "https://www.googleapis.com/admin/directory/v1/",
   145  			relstr:  "customer/{customerId}/orgunits{/orgUnitPath*}",
   146  			want:    "https://www.googleapis.com/admin/directory/v1/customer/{customerId}/orgunits{/orgUnitPath*}",
   147  		},
   148  		{
   149  			basestr: "https://www.googleapis.com/tagmanager/v2/",
   150  			relstr:  "accounts",
   151  			want:    "https://www.googleapis.com/tagmanager/v2/accounts",
   152  		},
   153  		{
   154  			basestr: "https://www.googleapis.com/tagmanager/v2/",
   155  			relstr:  "{+parent}/workspaces",
   156  			want:    "https://www.googleapis.com/tagmanager/v2/{+parent}/workspaces",
   157  		},
   158  		{
   159  			basestr: "https://www.googleapis.com/tagmanager/v2/",
   160  			relstr:  "{+path}:create_version",
   161  			want:    "https://www.googleapis.com/tagmanager/v2/{+path}:create_version",
   162  		},
   163  		{
   164  			basestr:   "http://localhost",
   165  			relstr:    ":8080foo",
   166  			wantPanic: true,
   167  		},
   168  		{
   169  			basestr: "https://www.googleapis.com/exampleapi/v2/somemethod",
   170  			relstr:  "/upload/exampleapi/v2/somemethod",
   171  			want:    "https://www.googleapis.com/upload/exampleapi/v2/somemethod",
   172  		},
   173  		{
   174  			basestr: "https://otherhost.googleapis.com/exampleapi/v2/somemethod",
   175  			relstr:  "/upload/exampleapi/v2/alternatemethod",
   176  			want:    "https://otherhost.googleapis.com/upload/exampleapi/v2/alternatemethod",
   177  		},
   178  	}
   179  
   180  	for _, test := range resolveRelativeTests {
   181  		t.Run(test.basestr+test.relstr, func(t *testing.T) {
   182  			if test.wantPanic {
   183  				defer func() {
   184  					if r := recover(); r == nil {
   185  						t.Errorf("expected panic, but did not see one")
   186  					}
   187  				}()
   188  			}
   189  
   190  			got := ResolveRelative(test.basestr, test.relstr)
   191  			if got != test.want {
   192  				t.Errorf("got %q expected %q", got, test.want)
   193  			}
   194  		})
   195  	}
   196  }
   197  
   198  type CheckResponseTest struct {
   199  	in       *http.Response
   200  	bodyText string
   201  	want     error
   202  	errText  string
   203  }
   204  
   205  var checkResponseTests = []CheckResponseTest{
   206  	{
   207  		&http.Response{
   208  			StatusCode: http.StatusOK,
   209  		},
   210  		"",
   211  		nil,
   212  		"",
   213  	},
   214  	{
   215  		&http.Response{
   216  			StatusCode: http.StatusInternalServerError,
   217  		},
   218  		`{"error":{}}`,
   219  		&Error{
   220  			Code: http.StatusInternalServerError,
   221  			Body: `{"error":{}}`,
   222  		},
   223  		`googleapi: got HTTP response code 500 with body: {"error":{}}`,
   224  	},
   225  	{
   226  		&http.Response{
   227  			StatusCode: http.StatusNotFound,
   228  		},
   229  		`{"error":{"message":"Error message for StatusNotFound."}}`,
   230  		&Error{
   231  			Code:    http.StatusNotFound,
   232  			Message: "Error message for StatusNotFound.",
   233  			Body:    `{"error":{"message":"Error message for StatusNotFound."}}`,
   234  		},
   235  		"googleapi: Error 404: Error message for StatusNotFound.",
   236  	},
   237  	{
   238  		&http.Response{
   239  			StatusCode: http.StatusBadRequest,
   240  		},
   241  		`{"error":"invalid_token","error_description":"Invalid Value"}`,
   242  		&Error{
   243  			Code: http.StatusBadRequest,
   244  			Body: `{"error":"invalid_token","error_description":"Invalid Value"}`,
   245  		},
   246  		`googleapi: got HTTP response code 400 with body: {"error":"invalid_token","error_description":"Invalid Value"}`,
   247  	},
   248  	{
   249  		&http.Response{
   250  			StatusCode: http.StatusBadRequest,
   251  		},
   252  		`{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`,
   253  		&Error{
   254  			Code: http.StatusBadRequest,
   255  			Errors: []ErrorItem{
   256  				{
   257  					Reason:  "keyInvalid",
   258  					Message: "Bad Request",
   259  				},
   260  			},
   261  			Body:    `{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`,
   262  			Message: "Bad Request",
   263  		},
   264  		"googleapi: Error 400: Bad Request, keyInvalid",
   265  	},
   266  	{
   267  		&http.Response{
   268  			StatusCode: http.StatusBadRequest,
   269  		},
   270  		`{"error": {"code": 400,"message": "The request has errors","status": "INVALID_ARGUMENT","details": [{"@type": "type.googleapis.com/google.rpc.BadRequest","fieldViolations": [{"field": "metadata.name","description": "The revision name must be prefixed by the name of the enclosing Service or Configuration with a trailing -"}]}]}}`,
   271  		&Error{
   272  			Code:    http.StatusBadRequest,
   273  			Message: "The request has errors",
   274  			Details: []interface{}{
   275  				map[string]interface{}{
   276  					"@type": "type.googleapis.com/google.rpc.BadRequest",
   277  					"fieldViolations": []interface{}{
   278  						map[string]interface{}{
   279  							"field":       "metadata.name",
   280  							"description": "The revision name must be prefixed by the name of the enclosing Service or Configuration with a trailing -",
   281  						},
   282  					},
   283  				},
   284  			},
   285  			Body: `{"error": {"code": 400,"message": "The request has errors","status": "INVALID_ARGUMENT","details": [{"@type": "type.googleapis.com/google.rpc.BadRequest","fieldViolations": [{"field": "metadata.name","description": "The revision name must be prefixed by the name of the enclosing Service or Configuration with a trailing -"}]}]}}`,
   286  		},
   287  		`googleapi: Error 400: The request has errors
   288  Details:
   289  [
   290    {
   291      "@type": "type.googleapis.com/google.rpc.BadRequest",
   292      "fieldViolations": [
   293        {
   294          "description": "The revision name must be prefixed by the name of the enclosing Service or Configuration with a trailing -",
   295          "field": "metadata.name"
   296        }
   297      ]
   298    }
   299  ]`,
   300  	},
   301  	{
   302  		// Case: Confirm that response headers are propagated to the error.
   303  		&http.Response{
   304  			StatusCode: http.StatusInternalServerError,
   305  			Header:     map[string][]string{"key1": {"value1"}, "key2": {"value2", "value3"}},
   306  		},
   307  		`{"error":{}}`,
   308  		&Error{
   309  			Code:   http.StatusInternalServerError,
   310  			Body:   `{"error":{}}`,
   311  			Header: map[string][]string{"key1": {"value1"}, "key2": {"value2", "value3"}},
   312  		},
   313  		`googleapi: got HTTP response code 500 with body: {"error":{}}`,
   314  	},
   315  }
   316  
   317  func TestCheckResponse(t *testing.T) {
   318  	for _, test := range checkResponseTests {
   319  		res := test.in
   320  		if test.bodyText != "" {
   321  			res.Body = io.NopCloser(strings.NewReader(test.bodyText))
   322  		}
   323  		g := CheckResponse(res)
   324  		if !reflect.DeepEqual(g, test.want) {
   325  			t.Errorf("CheckResponse: got %v, want %v", g, test.want)
   326  			gotJSON, err := json.Marshal(g)
   327  			if err != nil {
   328  				t.Error(err)
   329  			}
   330  			wantJSON, err := json.Marshal(test.want)
   331  			if err != nil {
   332  				t.Error(err)
   333  			}
   334  			t.Errorf("json(got):  %q\njson(want): %q", string(gotJSON), string(wantJSON))
   335  		}
   336  		if g != nil && g.Error() != test.errText {
   337  			t.Errorf("CheckResponse: unexpected error message.\nGot:  %q\nwant: %q", g, test.errText)
   338  		}
   339  	}
   340  }
   341  
   342  type VariantPoint struct {
   343  	Type        string
   344  	Coordinates []float64
   345  }
   346  
   347  type VariantTest struct {
   348  	in     map[string]interface{}
   349  	result bool
   350  	want   VariantPoint
   351  }
   352  
   353  var coords = []interface{}{1.0, 2.0}
   354  
   355  var variantTests = []VariantTest{
   356  	{
   357  		in: map[string]interface{}{
   358  			"type":        "Point",
   359  			"coordinates": coords,
   360  		},
   361  		result: true,
   362  		want: VariantPoint{
   363  			Type:        "Point",
   364  			Coordinates: []float64{1.0, 2.0},
   365  		},
   366  	},
   367  	{
   368  		in: map[string]interface{}{
   369  			"type":  "Point",
   370  			"bogus": coords,
   371  		},
   372  		result: true,
   373  		want: VariantPoint{
   374  			Type: "Point",
   375  		},
   376  	},
   377  }
   378  
   379  func TestVariantType(t *testing.T) {
   380  	for _, test := range variantTests {
   381  		if g := VariantType(test.in); g != test.want.Type {
   382  			t.Errorf("VariantType(%v): got %v, want %v", test.in, g, test.want.Type)
   383  		}
   384  	}
   385  }
   386  
   387  func TestConvertVariant(t *testing.T) {
   388  	for _, test := range variantTests {
   389  		g := VariantPoint{}
   390  		r := ConvertVariant(test.in, &g)
   391  		if r != test.result {
   392  			t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, r, test.result)
   393  		}
   394  		if !reflect.DeepEqual(g, test.want) {
   395  			t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, g, test.want)
   396  		}
   397  	}
   398  }
   399  
   400  func TestRoundChunkSize(t *testing.T) {
   401  	type testCase struct {
   402  		in   int
   403  		want int
   404  	}
   405  	for _, tc := range []testCase{
   406  		{0, 0},
   407  		{256*1024 - 1, 256 * 1024},
   408  		{256 * 1024, 256 * 1024},
   409  		{256*1024 + 1, 2 * 256 * 1024},
   410  	} {
   411  		mo := &MediaOptions{}
   412  		ChunkSize(tc.in).setOptions(mo)
   413  		if got := mo.ChunkSize; got != tc.want {
   414  			t.Errorf("rounding chunk size: got: %v; want %v", got, tc.want)
   415  		}
   416  	}
   417  }
   418  

View as plain text