| // Copyright 2016, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| package gax |
| |
| import "testing" |
| |
| func TestPathTemplateMatchInstantiate(t *testing.T) { |
| testCases := []struct { |
| message string |
| template string |
| path string |
| values map[string]string |
| }{ |
| { |
| "base", |
| "buckets/*/*/objects/*", |
| "buckets/f/o/objects/bar", |
| map[string]string{"$0": "f", "$1": "o", "$2": "bar"}, |
| }, |
| { |
| "custom verb", |
| "buckets/*/objects/*:custom", |
| "buckets/f/objects/o:custom", |
| map[string]string{"$0": "f", "$1": "o"}, |
| }, |
| { |
| "path wildcards", |
| "bar/**/foo/*", |
| "bar/foo/foo/foo/bar", |
| map[string]string{"$0": "foo/foo", "$1": "bar"}, |
| }, |
| { |
| "named binding", |
| "buckets/{foo}/objects/*", |
| "buckets/foo/objects/bar", |
| map[string]string{"$0": "bar", "foo": "foo"}, |
| }, |
| { |
| "named binding with complex patterns", |
| "buckets/{foo=x/*/y/**}/objects/*", |
| "buckets/x/foo/y/bar/baz/objects/quox", |
| map[string]string{"$0": "quox", "foo": "x/foo/y/bar/baz"}, |
| }, |
| { |
| "starts with slash", |
| "/foo/*", |
| "/foo/bar", |
| map[string]string{"$0": "bar"}, |
| }, |
| } |
| for _, testCase := range testCases { |
| pt, err := NewPathTemplate(testCase.template) |
| if err != nil { |
| t.Errorf("[%s] Failed to parse template %s: %v", testCase.message, testCase.template, err) |
| continue |
| } |
| values, err := pt.Match(testCase.path) |
| if err != nil { |
| t.Errorf("[%s] PathTemplate '%s' failed to match with '%s', %v", testCase.message, testCase.template, testCase.path, err) |
| continue |
| } |
| for key, expected := range testCase.values { |
| actual, ok := values[key] |
| if !ok { |
| t.Errorf("[%s] The matched data misses the value for %s", testCase.message, key) |
| continue |
| } |
| delete(values, key) |
| if actual != expected { |
| t.Errorf("[%s] Failed to match: value for '%s' is expected '%s' but is actually '%s'", testCase.message, key, expected, actual) |
| } |
| } |
| if len(values) != 0 { |
| t.Errorf("[%s] The matched data has unexpected keys: %v", testCase.message, values) |
| } |
| built, err := pt.Instantiate(testCase.values) |
| if err != nil || built != testCase.path { |
| t.Errorf("[%s] Built path '%s' is different from the expected '%s', %v", testCase.message, built, testCase.path, err) |
| } |
| } |
| } |
| |
| func TestPathTemplateMatchFailure(t *testing.T) { |
| testCases := []struct { |
| message string |
| template string |
| path string |
| }{ |
| { |
| "too many paths", |
| "buckets/*/*/objects/*", |
| "buckets/f/o/o/objects/bar", |
| }, |
| { |
| "missing last path", |
| "buckets/*/*/objects/*", |
| "buckets/f/o/objects", |
| }, |
| { |
| "too many paths at end", |
| "buckets/*/*/objects/*", |
| "buckets/f/o/objects/too/long", |
| }, |
| { |
| "wrong custom verb", |
| "buckets/*/objects/*:bar", |
| "buckets/foo/objects/bar:bazz", |
| }, |
| } |
| for _, testCase := range testCases { |
| pt, err := NewPathTemplate(testCase.template) |
| if err != nil { |
| t.Errorf("[%s] Failed to parse path %s: %v", testCase.message, testCase.template, err) |
| continue |
| } |
| if values, err := pt.Match(testCase.path); err == nil { |
| t.Errorf("[%s] PathTemplate %s doesn't expect to match %s, but succeeded somehow. Match result: %v", testCase.message, testCase.template, testCase.path, values) |
| |
| } |
| } |
| } |
| |
| func TestPathTemplateInstantiateTooManyValues(t *testing.T) { |
| // Test cases where Instantiate() succeeds but Match() doesn't return the same map. |
| testCases := []struct { |
| message string |
| template string |
| values map[string]string |
| expected string |
| }{ |
| { |
| "too many", |
| "bar/*/foo/*", |
| map[string]string{"$0": "_1", "$1": "_2", "$2": "_3"}, |
| "bar/_1/foo/_2", |
| }, |
| } |
| for _, testCase := range testCases { |
| pt, err := NewPathTemplate(testCase.template) |
| if err != nil { |
| t.Errorf("[%s] Failed to parse template %s (error %v)", testCase.message, testCase.template, err) |
| continue |
| } |
| if result, err := pt.Instantiate(testCase.values); err != nil || result != testCase.expected { |
| t.Errorf("[%s] Failed to build the path (expected '%s' but returned '%s'", testCase.message, testCase.expected, result) |
| } |
| } |
| } |
| |
| func TestPathTemplateParseErrors(t *testing.T) { |
| testCases := []struct { |
| message string |
| template string |
| }{ |
| { |
| "multiple path wildcards", |
| "foo/**/bar/**", |
| }, |
| { |
| "recursive named bindings", |
| "foo/{foo=foo/{bar}/baz/*}/baz/*", |
| }, |
| { |
| "complicated multiple path wildcards patterns", |
| "foo/{foo=foo/**/bar/*}/baz/**", |
| }, |
| { |
| "consective slashes", |
| "foo//bar", |
| }, |
| { |
| "invalid variable pattern", |
| "foo/{foo=foo/*/}bar", |
| }, |
| { |
| "same name multiple times", |
| "foo/{foo}/bar/{foo}", |
| }, |
| } |
| for _, testCase := range testCases { |
| if pt, err := NewPathTemplate(testCase.template); err == nil { |
| t.Errorf("[%s] Template '%s' should fail to be parsed, but succeeded and returned %+v", testCase.message, testCase.template, pt) |
| } |
| } |
| } |