mirror of
https://github.com/restic/restic.git
synced 2024-09-07 20:09:40 +02:00
281 lines
9.0 KiB
Go
281 lines
9.0 KiB
Go
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package uritemplates
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/url"
|
|
"testing"
|
|
)
|
|
|
|
func ExampleExpand() {
|
|
values := map[string]string{
|
|
"user": "golang",
|
|
"repo": "go",
|
|
}
|
|
expanded, _, err := Expand("https://api.github.com/repos{/user,repo}", values)
|
|
if err != nil {
|
|
log.Fatalf("Error expanding template: %v", err)
|
|
}
|
|
fmt.Println(expanded)
|
|
// Output:
|
|
// https://api.github.com/repos/golang/go
|
|
}
|
|
|
|
func TestExpand(t *testing.T) {
|
|
testCases := []struct {
|
|
tmpl string
|
|
values map[string]string
|
|
want string
|
|
}{
|
|
// These examples come from the RFC:
|
|
// http://tools.ietf.org/html/rfc6570
|
|
{
|
|
tmpl: "http://www.example.com/foo{?query,number}",
|
|
values: map[string]string{"query": "mycelium", "number": "100"},
|
|
want: "http://www.example.com/foo?query=mycelium&number=100",
|
|
},
|
|
{
|
|
tmpl: "http://www.example.com/foo{?query,number}",
|
|
values: map[string]string{"query": "mycelium"},
|
|
want: "http://www.example.com/foo?query=mycelium",
|
|
},
|
|
{
|
|
tmpl: "http://www.example.com/foo{?query,number}",
|
|
values: map[string]string{},
|
|
want: "http://www.example.com/foo",
|
|
},
|
|
}
|
|
|
|
for _, tt := range testCases {
|
|
exp, _, err := Expand(tt.tmpl, tt.values)
|
|
if err != nil {
|
|
t.Errorf("Expand(%q, %v) error: %v", tt.tmpl, tt.values, err)
|
|
continue
|
|
}
|
|
if exp != tt.want {
|
|
t.Errorf("Expand(%q, %v)\ngot %q\nwant %q", tt.tmpl, tt.values, exp, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExpandRFCLevels(t *testing.T) {
|
|
values := map[string]string{
|
|
"dub": "me/too",
|
|
"hello": "Hello World!",
|
|
"half": "50%",
|
|
"var": "value",
|
|
"who": "fred",
|
|
"base": "http://example.com/home/",
|
|
"path": "/foo/bar",
|
|
"semi": ";",
|
|
"v": "6",
|
|
"x": "1024",
|
|
"y": "768",
|
|
"empty": "",
|
|
// undef not mapped.
|
|
}
|
|
testCases := []struct {
|
|
tmpl, want string
|
|
}{
|
|
// These examples come from the RFC levels specification.
|
|
// http://tools.ietf.org/html/rfc6570
|
|
// Level 1 examples.
|
|
{tmpl: "{var}", want: "value"},
|
|
{tmpl: "{hello}", want: "Hello%20World%21"},
|
|
|
|
// Level 2 examples.
|
|
{tmpl: "{+var}", want: "value"},
|
|
{tmpl: "{+hello}", want: "Hello%20World!"},
|
|
{tmpl: "{+path}/here", want: "/foo/bar/here"},
|
|
{tmpl: "here?ref={+path}", want: "here?ref=/foo/bar"},
|
|
{tmpl: "X{#var}", want: "X#value"},
|
|
{tmpl: "X{#hello}", want: "X#Hello%20World!"},
|
|
|
|
// Level 3 examples.
|
|
{tmpl: "map?{x,y}", want: "map?1024,768"},
|
|
{tmpl: "{x,hello,y}", want: "1024,Hello%20World%21,768"},
|
|
{tmpl: "{+x,hello,y}", want: "1024,Hello%20World!,768"},
|
|
{tmpl: "{+path,x}/here", want: "/foo/bar,1024/here"},
|
|
{tmpl: "{#x,hello,y}", want: "#1024,Hello%20World!,768"},
|
|
{tmpl: "{#path,x}/here", want: "#/foo/bar,1024/here"},
|
|
{tmpl: "X{.var}", want: "X.value"},
|
|
{tmpl: "X{.x,y}", want: "X.1024.768"},
|
|
{tmpl: "{/var}", want: "/value"},
|
|
{tmpl: "{/var,x}/here", want: "/value/1024/here"},
|
|
{tmpl: "{;x,y}", want: ";x=1024;y=768"},
|
|
{tmpl: "{;x,y,empty}", want: ";x=1024;y=768;empty"},
|
|
{tmpl: "{?x,y}", want: "?x=1024&y=768"},
|
|
{tmpl: "{?x,y,empty}", want: "?x=1024&y=768&empty="},
|
|
{tmpl: "?fixed=yes{&x}", want: "?fixed=yes&x=1024"},
|
|
{tmpl: "{&x,y,empty}", want: "&x=1024&y=768&empty="},
|
|
|
|
{tmpl: "{var:3}", want: "val"},
|
|
{tmpl: "{var:30}", want: "value"},
|
|
{tmpl: "{+path:6}/here", want: "/foo/b/here"},
|
|
{tmpl: "{#path:6}/here", want: "#/foo/b/here"},
|
|
{tmpl: "X{.var:3}", want: "X.val"},
|
|
{tmpl: "{/var:1,var}", want: "/v/value"},
|
|
{tmpl: "{;hello:5}", want: ";hello=Hello"},
|
|
{tmpl: "{?var:3}", want: "?var=val"},
|
|
{tmpl: "{&var:3}", want: "&var=val"},
|
|
|
|
// 2.4.1 Prefix values.
|
|
{tmpl: "{var}", want: "value"},
|
|
{tmpl: "{var:20}", want: "value"},
|
|
{tmpl: "{var:3}", want: "val"},
|
|
{tmpl: "{semi}", want: "%3B"},
|
|
{tmpl: "{semi:2}", want: "%3B"},
|
|
// 3.2.2. Simple String Expansion: {var}
|
|
{tmpl: "{var}", want: "value"},
|
|
{tmpl: "{hello}", want: "Hello%20World%21"},
|
|
{tmpl: "{half}", want: "50%25"},
|
|
{tmpl: "O{empty}X", want: "OX"},
|
|
{tmpl: "O{undef}X", want: "OX"},
|
|
{tmpl: "{x,y}", want: "1024,768"},
|
|
{tmpl: "{x,hello,y}", want: "1024,Hello%20World%21,768"},
|
|
{tmpl: "?{x,empty}", want: "?1024,"},
|
|
{tmpl: "?{x,undef}", want: "?1024"},
|
|
{tmpl: "?{undef,y}", want: "?768"},
|
|
{tmpl: "{var:3}", want: "val"},
|
|
{tmpl: "{var:30}", want: "value"},
|
|
// 3.2.3. Reserved Expansion: {+var}
|
|
{tmpl: "{+var}", want: "value"},
|
|
{tmpl: "{+hello}", want: "Hello%20World!"},
|
|
{tmpl: "{+half}", want: "50%25"},
|
|
{tmpl: "{base}index", want: "http%3A%2F%2Fexample.com%2Fhome%2Findex"},
|
|
{tmpl: "{+base}index", want: "http://example.com/home/index"},
|
|
{tmpl: "O{+empty}X", want: "OX"},
|
|
{tmpl: "O{+undef}X", want: "OX"},
|
|
{tmpl: "{+path}/here", want: "/foo/bar/here"},
|
|
{tmpl: "here?ref={+path}", want: "here?ref=/foo/bar"},
|
|
{tmpl: "up{+path}{var}/here", want: "up/foo/barvalue/here"},
|
|
{tmpl: "{+x,hello,y}", want: "1024,Hello%20World!,768"},
|
|
{tmpl: "{+path,x}/here", want: "/foo/bar,1024/here"},
|
|
{tmpl: "{+path:6}/here", want: "/foo/b/here"},
|
|
// 3.2.4. Fragment Expansion: {#var}
|
|
{tmpl: "{#var}", want: "#value"},
|
|
{tmpl: "{#hello}", want: "#Hello%20World!"},
|
|
{tmpl: "{#half}", want: "#50%25"},
|
|
{tmpl: "foo{#empty}", want: "foo#"},
|
|
{tmpl: "foo{#undef}", want: "foo"},
|
|
{tmpl: "{#x,hello,y}", want: "#1024,Hello%20World!,768"},
|
|
{tmpl: "{#path,x}/here", want: "#/foo/bar,1024/here"},
|
|
{tmpl: "{#path:6}/here", want: "#/foo/b/here"},
|
|
// 3.2.5. Label Expansion with Dot-Prefix: {.var}
|
|
{tmpl: "{.who}", want: ".fred"},
|
|
{tmpl: "{.who,who}", want: ".fred.fred"},
|
|
{tmpl: "{.half,who}", want: ".50%25.fred"},
|
|
{tmpl: "X{.var}", want: "X.value"},
|
|
{tmpl: "X{.empty}", want: "X."},
|
|
{tmpl: "X{.undef}", want: "X"},
|
|
{tmpl: "X{.var:3}", want: "X.val"},
|
|
// 3.2.6. Path Segment Expansion: {/var}
|
|
{tmpl: "{/who}", want: "/fred"},
|
|
{tmpl: "{/who,who}", want: "/fred/fred"},
|
|
{tmpl: "{/half,who}", want: "/50%25/fred"},
|
|
{tmpl: "{/who,dub}", want: "/fred/me%2Ftoo"},
|
|
{tmpl: "{/var}", want: "/value"},
|
|
{tmpl: "{/var,empty}", want: "/value/"},
|
|
{tmpl: "{/var,undef}", want: "/value"},
|
|
{tmpl: "{/var,x}/here", want: "/value/1024/here"},
|
|
{tmpl: "{/var:1,var}", want: "/v/value"},
|
|
// 3.2.7. Path-Style Parameter Expansion: {;var}
|
|
{tmpl: "{;who}", want: ";who=fred"},
|
|
{tmpl: "{;half}", want: ";half=50%25"},
|
|
{tmpl: "{;empty}", want: ";empty"},
|
|
{tmpl: "{;v,empty,who}", want: ";v=6;empty;who=fred"},
|
|
{tmpl: "{;v,bar,who}", want: ";v=6;who=fred"},
|
|
{tmpl: "{;x,y}", want: ";x=1024;y=768"},
|
|
{tmpl: "{;x,y,empty}", want: ";x=1024;y=768;empty"},
|
|
{tmpl: "{;x,y,undef}", want: ";x=1024;y=768"},
|
|
{tmpl: "{;hello:5}", want: ";hello=Hello"},
|
|
// 3.2.8. Form-Style Query Expansion: {?var}
|
|
{tmpl: "{?who}", want: "?who=fred"},
|
|
{tmpl: "{?half}", want: "?half=50%25"},
|
|
{tmpl: "{?x,y}", want: "?x=1024&y=768"},
|
|
{tmpl: "{?x,y,empty}", want: "?x=1024&y=768&empty="},
|
|
{tmpl: "{?x,y,undef}", want: "?x=1024&y=768"},
|
|
{tmpl: "{?var:3}", want: "?var=val"},
|
|
// 3.2.9. Form-Style Query Continuation: {&var}
|
|
{tmpl: "{&who}", want: "&who=fred"},
|
|
{tmpl: "{&half}", want: "&half=50%25"},
|
|
{tmpl: "?fixed=yes{&x}", want: "?fixed=yes&x=1024"},
|
|
{tmpl: "{&x,y,empty}", want: "&x=1024&y=768&empty="},
|
|
{tmpl: "{&x,y,undef}", want: "&x=1024&y=768"},
|
|
{tmpl: "{&var:3}", want: "&var=val"},
|
|
}
|
|
for _, tt := range testCases {
|
|
esc, unesc, err := Expand(tt.tmpl, values)
|
|
if err != nil {
|
|
t.Errorf("Expand(%q) error: %v", tt.tmpl, err)
|
|
continue
|
|
}
|
|
if esc != tt.want {
|
|
t.Errorf("Expand(%q)\ngot %q\nwant %q", tt.tmpl, esc, tt.want)
|
|
}
|
|
// Check that the escaped form is equivalent to unescaped.
|
|
urlUnesc, err := url.QueryUnescape(esc)
|
|
if err != nil {
|
|
t.Errorf("Expand(%q) gave invalid escaping %q: %v", tt.tmpl, esc, err)
|
|
continue
|
|
}
|
|
if urlUnesc != unesc {
|
|
t.Errorf("Expand(%q) gave inconsistent escaped/unescaped\nunescaped %q\nescaped %q\nwhich is %q", tt.tmpl, unesc, esc, urlUnesc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExpandUnescaped(t *testing.T) {
|
|
testCases := []struct {
|
|
tmpl, wantEsc, wantUnesc string
|
|
values map[string]string
|
|
}{
|
|
{
|
|
tmpl: "/foo/{bucket}/bar",
|
|
values: map[string]string{
|
|
"bucket": "simple",
|
|
},
|
|
wantEsc: "/foo/simple/bar",
|
|
wantUnesc: "/foo/simple/bar",
|
|
},
|
|
{
|
|
tmpl: "/foo/{bucket}/bar",
|
|
values: map[string]string{
|
|
"bucket": "path/with/slash",
|
|
},
|
|
wantEsc: "/foo/path%2Fwith%2Fslash/bar",
|
|
wantUnesc: "/foo/path/with/slash/bar",
|
|
},
|
|
{
|
|
tmpl: "/foo/{+bucket}/bar",
|
|
values: map[string]string{
|
|
"bucket": "path/with/slash",
|
|
},
|
|
wantEsc: "/foo/path/with/slash/bar",
|
|
wantUnesc: "/foo/path/with/slash/bar",
|
|
},
|
|
{
|
|
tmpl: "/foo/{bucket}/bar",
|
|
values: map[string]string{
|
|
"bucket": "double%2Fescaped",
|
|
},
|
|
wantEsc: "/foo/double%252Fescaped/bar",
|
|
wantUnesc: "/foo/double%2Fescaped/bar",
|
|
},
|
|
}
|
|
for _, tt := range testCases {
|
|
esc, unesc, err := Expand(tt.tmpl, tt.values)
|
|
if err != nil {
|
|
t.Errorf("Expand(%q) error: %v", tt.tmpl, err)
|
|
continue
|
|
}
|
|
if esc != tt.wantEsc || unesc != tt.wantUnesc {
|
|
t.Errorf("Expand(%q)\ngot esc=%q, unesc=%q\nwant esc=%q, unesc=%q", tt.tmpl, esc, unesc, tt.wantEsc, tt.wantUnesc)
|
|
}
|
|
}
|
|
}
|