// Copyright 2014 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 webdav
import (
"encoding/xml"
"net/http"
"reflect"
"strings"
"testing"
)
func TestReadLockInfo(t *testing.T) {
// The "section x.y.z" test cases come from section x.y.z of the spec at
// http://www.webdav.org/specs/rfc4918.html
testCases := []struct {
desc string
input string
wantLI lockInfo
wantStatus int
}{{
"bad: junk",
"xxx",
lockInfo{},
http.StatusBadRequest,
}, {
"bad: invalid owner XML",
"" +
"\n" +
" \n" +
" \n" +
" \n" +
" no end tag \n" +
" \n" +
"",
lockInfo{},
http.StatusBadRequest,
}, {
"bad: invalid UTF-8",
"" +
"\n" +
" \n" +
" \n" +
" \n" +
" \xff \n" +
" \n" +
"",
lockInfo{},
http.StatusBadRequest,
}, {
"bad: unfinished XML #1",
"" +
"\n" +
" \n" +
" \n",
lockInfo{},
http.StatusBadRequest,
}, {
"bad: unfinished XML #2",
"" +
"\n" +
" \n" +
" \n" +
" \n",
lockInfo{},
http.StatusBadRequest,
}, {
"good: empty",
"",
lockInfo{},
0,
}, {
"good: plain-text owner",
"" +
"\n" +
" \n" +
" \n" +
" gopher\n" +
"",
lockInfo{
XMLName: xml.Name{Space: "DAV:", Local: "lockinfo"},
Exclusive: new(struct{}),
Write: new(struct{}),
Owner: owner{
InnerXML: "gopher",
},
},
0,
}, {
"section 9.10.7",
"" +
"\n" +
" \n" +
" \n" +
" \n" +
" http://example.org/~ejw/contact.html\n" +
" \n" +
"",
lockInfo{
XMLName: xml.Name{Space: "DAV:", Local: "lockinfo"},
Exclusive: new(struct{}),
Write: new(struct{}),
Owner: owner{
InnerXML: "\n http://example.org/~ejw/contact.html\n ",
},
},
0,
}}
for _, tc := range testCases {
li, status, err := readLockInfo(strings.NewReader(tc.input))
if tc.wantStatus != 0 {
if err == nil {
t.Errorf("%s: got nil error, want non-nil", tc.desc)
continue
}
} else if err != nil {
t.Errorf("%s: %v", tc.desc, err)
continue
}
if !reflect.DeepEqual(li, tc.wantLI) || status != tc.wantStatus {
t.Errorf("%s:\ngot lockInfo=%v, status=%v\nwant lockInfo=%v, status=%v",
tc.desc, li, status, tc.wantLI, tc.wantStatus)
continue
}
}
}
func TestReadPropfind(t *testing.T) {
testCases := []struct {
desc string
input string
wantPF propfind
wantStatus int
}{{
desc: "propfind: propname",
input: "" +
"\n" +
" \n" +
"",
wantPF: propfind{
XMLName: xml.Name{"DAV:", "propfind"},
Propname: new(struct{}),
},
}, {
desc: "propfind: empty body means allprop",
input: "",
wantPF: propfind{
Allprop: new(struct{}),
},
}, {
desc: "propfind: allprop",
input: "" +
"\n" +
" \n" +
"",
wantPF: propfind{
XMLName: xml.Name{"DAV:", "propfind"},
Allprop: new(struct{}),
},
}, {
desc: "propfind: allprop followed by include",
input: "" +
"\n" +
" \n" +
" \n" +
"",
wantPF: propfind{
XMLName: xml.Name{"DAV:", "propfind"},
Allprop: new(struct{}),
Include: propnames{xml.Name{"DAV:", "displayname"}},
},
}, {
desc: "propfind: include followed by allprop",
input: "" +
"\n" +
" \n" +
" \n" +
"",
wantPF: propfind{
XMLName: xml.Name{"DAV:", "propfind"},
Allprop: new(struct{}),
Include: propnames{xml.Name{"DAV:", "displayname"}},
},
}, {
desc: "propfind: propfind",
input: "" +
"\n" +
" \n" +
"",
wantPF: propfind{
XMLName: xml.Name{"DAV:", "propfind"},
Prop: propnames{xml.Name{"DAV:", "displayname"}},
},
}, {
desc: "propfind: prop with ignored comments",
input: "" +
"\n" +
" \n" +
" \n" +
" \n" +
" \n" +
"",
wantPF: propfind{
XMLName: xml.Name{"DAV:", "propfind"},
Prop: propnames{xml.Name{"DAV:", "displayname"}},
},
}, {
desc: "propfind: propfind with ignored whitespace",
input: "" +
"\n" +
" \n" +
"",
wantPF: propfind{
XMLName: xml.Name{"DAV:", "propfind"},
Prop: propnames{xml.Name{"DAV:", "displayname"}},
},
}, {
desc: "propfind: propfind with ignored mixed-content",
input: "" +
"\n" +
" foobar\n" +
"",
wantPF: propfind{
XMLName: xml.Name{"DAV:", "propfind"},
Prop: propnames{xml.Name{"DAV:", "displayname"}},
},
}, {
desc: "propfind: propname with ignored element (section A.4)",
input: "" +
"\n" +
" \n" +
" *boss*\n" +
"",
wantPF: propfind{
XMLName: xml.Name{"DAV:", "propfind"},
Propname: new(struct{}),
},
}, {
desc: "propfind: bad: junk",
input: "xxx",
wantStatus: http.StatusBadRequest,
}, {
desc: "propfind: bad: propname and allprop (section A.3)",
input: "" +
"\n" +
" " +
" " +
"",
wantStatus: http.StatusBadRequest,
}, {
desc: "propfind: bad: propname and prop",
input: "" +
"\n" +
" \n" +
" \n" +
"",
wantStatus: http.StatusBadRequest,
}, {
desc: "propfind: bad: allprop and prop",
input: "" +
"\n" +
" \n" +
" \n" +
"",
wantStatus: http.StatusBadRequest,
}, {
desc: "propfind: bad: empty propfind with ignored element (section A.4)",
input: "" +
"\n" +
" \n" +
"",
wantStatus: http.StatusBadRequest,
}, {
desc: "propfind: bad: empty prop",
input: "" +
"\n" +
" \n" +
"",
wantStatus: http.StatusBadRequest,
}, {
desc: "propfind: bad: prop with just chardata",
input: "" +
"\n" +
" foo\n" +
"",
wantStatus: http.StatusBadRequest,
}, {
desc: "bad: interrupted prop",
input: "" +
"\n" +
" \n",
wantStatus: http.StatusBadRequest,
}, {
desc: "bad: malformed end element prop",
input: "" +
"\n" +
" \n",
wantStatus: http.StatusBadRequest,
}, {
desc: "propfind: bad: property with chardata value",
input: "" +
"\n" +
" bar\n" +
"",
wantStatus: http.StatusBadRequest,
}, {
desc: "propfind: bad: property with whitespace value",
input: "" +
"\n" +
" \n" +
"",
wantStatus: http.StatusBadRequest,
}, {
desc: "propfind: bad: include without allprop",
input: "" +
"\n" +
" \n" +
"",
wantStatus: http.StatusBadRequest,
}}
for _, tc := range testCases {
pf, status, err := readPropfind(strings.NewReader(tc.input))
if tc.wantStatus != 0 {
if err == nil {
t.Errorf("%s: got nil error, want non-nil", tc.desc)
continue
}
} else if err != nil {
t.Errorf("%s: %v", tc.desc, err)
continue
}
if !reflect.DeepEqual(pf, tc.wantPF) || status != tc.wantStatus {
t.Errorf("%s:\ngot propfind=%v, status=%v\nwant propfind=%v, status=%v",
tc.desc, pf, status, tc.wantPF, tc.wantStatus)
continue
}
}
}