blob: 76dc483c906b77441a832de20b871f124866dbeb [file] [log] [blame]
// Copyright 2011 Google LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package googleapi
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"reflect"
"strings"
"testing"
)
type ExpandTest struct {
in string
expansions map[string]string
want string
}
var expandTests = []ExpandTest{
// no expansions
{
"http://www.golang.org/",
map[string]string{},
"http://www.golang.org/",
},
// one expansion, no escaping
{
"http://www.golang.org/{bucket}/delete",
map[string]string{
"bucket": "red",
},
"http://www.golang.org/red/delete",
},
// one expansion, with hex escapes
{
"http://www.golang.org/{bucket}/delete",
map[string]string{
"bucket": "red/blue",
},
"http://www.golang.org/red%2Fblue/delete",
},
// one expansion, with space
{
"http://www.golang.org/{bucket}/delete",
map[string]string{
"bucket": "red or blue",
},
"http://www.golang.org/red%20or%20blue/delete",
},
// expansion not found
{
"http://www.golang.org/{object}/delete",
map[string]string{
"bucket": "red or blue",
},
"http://www.golang.org//delete",
},
// multiple expansions
{
"http://www.golang.org/{one}/{two}/{three}/get",
map[string]string{
"one": "ONE",
"two": "TWO",
"three": "THREE",
},
"http://www.golang.org/ONE/TWO/THREE/get",
},
// utf-8 characters
{
"http://www.golang.org/{bucket}/get",
map[string]string{
"bucket": "£100",
},
"http://www.golang.org/%C2%A3100/get",
},
// punctuations
{
"http://www.golang.org/{bucket}/get",
map[string]string{
"bucket": `/\@:,.`,
},
"http://www.golang.org/%2F%5C%40%3A%2C./get",
},
// mis-matched brackets
{
"http://www.golang.org/{bucket/get",
map[string]string{
"bucket": "red",
},
"http://www.golang.org/%7Bbucket/get",
},
// "+" prefix for suppressing escape
// See also: http://tools.ietf.org/html/rfc6570#section-3.2.3
{
"http://www.golang.org/{+topic}",
map[string]string{
"topic": "/topics/myproject/mytopic",
},
// The double slashes here look weird, but it's intentional
"http://www.golang.org//topics/myproject/mytopic",
},
}
func TestExpand(t *testing.T) {
for i, test := range expandTests {
u := url.URL{
Path: test.in,
}
Expand(&u, test.expansions)
got := u.EscapedPath()
if got != test.want {
t.Errorf("got %q expected %q in test %d", got, test.want, i+1)
}
}
}
func TestResolveRelative(t *testing.T) {
resolveRelativeTests := []struct {
basestr string
relstr string
want string
wantPanic bool
}{
{
basestr: "http://www.golang.org/",
relstr: "topics/myproject/mytopic",
want: "http://www.golang.org/topics/myproject/mytopic",
},
{
basestr: "http://www.golang.org/",
relstr: "topics/{+myproject}/{release}:build:test:deploy",
want: "http://www.golang.org/topics/{+myproject}/{release}:build:test:deploy",
},
{
basestr: "https://www.googleapis.com/admin/reports/v1/",
relstr: "/admin/reports_v1/channels/stop",
want: "https://www.googleapis.com/admin/reports_v1/channels/stop",
},
{
basestr: "https://www.googleapis.com/admin/directory/v1/",
relstr: "customer/{customerId}/orgunits{/orgUnitPath*}",
want: "https://www.googleapis.com/admin/directory/v1/customer/{customerId}/orgunits{/orgUnitPath*}",
},
{
basestr: "https://www.googleapis.com/tagmanager/v2/",
relstr: "accounts",
want: "https://www.googleapis.com/tagmanager/v2/accounts",
},
{
basestr: "https://www.googleapis.com/tagmanager/v2/",
relstr: "{+parent}/workspaces",
want: "https://www.googleapis.com/tagmanager/v2/{+parent}/workspaces",
},
{
basestr: "https://www.googleapis.com/tagmanager/v2/",
relstr: "{+path}:create_version",
want: "https://www.googleapis.com/tagmanager/v2/{+path}:create_version",
},
{
basestr: "http://localhost",
relstr: ":8080foo",
wantPanic: true,
},
{
basestr: "https://www.googleapis.com/exampleapi/v2/somemethod",
relstr: "/upload/exampleapi/v2/somemethod",
want: "https://www.googleapis.com/upload/exampleapi/v2/somemethod",
},
{
basestr: "https://otherhost.googleapis.com/exampleapi/v2/somemethod",
relstr: "/upload/exampleapi/v2/alternatemethod",
want: "https://otherhost.googleapis.com/upload/exampleapi/v2/alternatemethod",
},
}
for _, test := range resolveRelativeTests {
t.Run(test.basestr+test.relstr, func(t *testing.T) {
if test.wantPanic {
defer func() {
if r := recover(); r == nil {
t.Errorf("expected panic, but did not see one")
}
}()
}
got := ResolveRelative(test.basestr, test.relstr)
if got != test.want {
t.Errorf("got %q expected %q", got, test.want)
}
})
}
}
type CheckResponseTest struct {
in *http.Response
bodyText string
want error
errText string
}
var checkResponseTests = []CheckResponseTest{
{
&http.Response{
StatusCode: http.StatusOK,
},
"",
nil,
"",
},
{
&http.Response{
StatusCode: http.StatusInternalServerError,
},
`{"error":{}}`,
&Error{
Code: http.StatusInternalServerError,
Body: `{"error":{}}`,
},
`googleapi: got HTTP response code 500 with body: {"error":{}}`,
},
{
&http.Response{
StatusCode: http.StatusNotFound,
},
`{"error":{"message":"Error message for StatusNotFound."}}`,
&Error{
Code: http.StatusNotFound,
Message: "Error message for StatusNotFound.",
Body: `{"error":{"message":"Error message for StatusNotFound."}}`,
},
"googleapi: Error 404: Error message for StatusNotFound.",
},
{
&http.Response{
StatusCode: http.StatusBadRequest,
},
`{"error":"invalid_token","error_description":"Invalid Value"}`,
&Error{
Code: http.StatusBadRequest,
Body: `{"error":"invalid_token","error_description":"Invalid Value"}`,
},
`googleapi: got HTTP response code 400 with body: {"error":"invalid_token","error_description":"Invalid Value"}`,
},
{
&http.Response{
StatusCode: http.StatusBadRequest,
},
`{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`,
&Error{
Code: http.StatusBadRequest,
Errors: []ErrorItem{
{
Reason: "keyInvalid",
Message: "Bad Request",
},
},
Body: `{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`,
Message: "Bad Request",
},
"googleapi: Error 400: Bad Request, keyInvalid",
},
}
func TestCheckResponse(t *testing.T) {
for _, test := range checkResponseTests {
res := test.in
if test.bodyText != "" {
res.Body = ioutil.NopCloser(strings.NewReader(test.bodyText))
}
g := CheckResponse(res)
if !reflect.DeepEqual(g, test.want) {
t.Errorf("CheckResponse: got %v, want %v", g, test.want)
gotJSON, err := json.Marshal(g)
if err != nil {
t.Error(err)
}
wantJSON, err := json.Marshal(test.want)
if err != nil {
t.Error(err)
}
t.Errorf("json(got): %q\njson(want): %q", string(gotJSON), string(wantJSON))
}
if g != nil && g.Error() != test.errText {
t.Errorf("CheckResponse: unexpected error message.\nGot: %q\nwant: %q", g, test.errText)
}
}
}
type VariantPoint struct {
Type string
Coordinates []float64
}
type VariantTest struct {
in map[string]interface{}
result bool
want VariantPoint
}
var coords = []interface{}{1.0, 2.0}
var variantTests = []VariantTest{
{
in: map[string]interface{}{
"type": "Point",
"coordinates": coords,
},
result: true,
want: VariantPoint{
Type: "Point",
Coordinates: []float64{1.0, 2.0},
},
},
{
in: map[string]interface{}{
"type": "Point",
"bogus": coords,
},
result: true,
want: VariantPoint{
Type: "Point",
},
},
}
func TestVariantType(t *testing.T) {
for _, test := range variantTests {
if g := VariantType(test.in); g != test.want.Type {
t.Errorf("VariantType(%v): got %v, want %v", test.in, g, test.want.Type)
}
}
}
func TestConvertVariant(t *testing.T) {
for _, test := range variantTests {
g := VariantPoint{}
r := ConvertVariant(test.in, &g)
if r != test.result {
t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, r, test.result)
}
if !reflect.DeepEqual(g, test.want) {
t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, g, test.want)
}
}
}
func TestRoundChunkSize(t *testing.T) {
type testCase struct {
in int
want int
}
for _, tc := range []testCase{
{0, 0},
{256*1024 - 1, 256 * 1024},
{256 * 1024, 256 * 1024},
{256*1024 + 1, 2 * 256 * 1024},
} {
mo := &MediaOptions{}
ChunkSize(tc.in).setOptions(mo)
if got := mo.ChunkSize; got != tc.want {
t.Errorf("rounding chunk size: got: %v; want %v", got, tc.want)
}
}
}