blob: d5f8e787bb0d17f3f4db7b23d8cc8b7d0e08422b [file] [log] [blame]
// Copyright 2015 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"
"fmt"
"net/http"
"reflect"
"sort"
"testing"
)
func TestMemPS(t *testing.T) {
// calcProps calculates the getlastmodified and getetag DAV: property
// values in pstats for resource name in file-system fs.
calcProps := func(name string, fs FileSystem, pstats []Propstat) error {
fi, err := fs.Stat(name)
if err != nil {
return err
}
for _, pst := range pstats {
for i, p := range pst.Props {
switch p.XMLName {
case xml.Name{Space: "DAV:", Local: "getlastmodified"}:
p.InnerXML = []byte(fi.ModTime().Format(http.TimeFormat))
pst.Props[i] = p
case xml.Name{Space: "DAV:", Local: "getetag"}:
// TODO(rost) ETag will be defined in the next CL.
panic("Not implemented")
}
}
}
return nil
}
type propOp struct {
op string
name string
propnames []xml.Name
wantNames []xml.Name
wantPropstats []Propstat
}
testCases := []struct {
desc string
buildfs []string
propOp []propOp
}{{
"propname",
[]string{"mkdir /dir", "touch /file"},
[]propOp{{
op: "propname",
name: "/dir",
wantNames: []xml.Name{
xml.Name{Space: "DAV:", Local: "resourcetype"},
xml.Name{Space: "DAV:", Local: "displayname"},
xml.Name{Space: "DAV:", Local: "getcontentlength"},
xml.Name{Space: "DAV:", Local: "getlastmodified"},
},
}, {
op: "propname",
name: "/file",
wantNames: []xml.Name{
xml.Name{Space: "DAV:", Local: "resourcetype"},
xml.Name{Space: "DAV:", Local: "displayname"},
xml.Name{Space: "DAV:", Local: "getcontentlength"},
xml.Name{Space: "DAV:", Local: "getlastmodified"},
},
}},
}, {
"allprop dir and file",
[]string{"mkdir /dir", "write /file foobarbaz"},
[]propOp{{
op: "allprop",
name: "/dir",
wantPropstats: []Propstat{{
Status: http.StatusOK,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
InnerXML: []byte(`<collection xmlns="DAV:"/>`),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
InnerXML: []byte("dir"),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
InnerXML: []byte("0"),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
InnerXML: nil, // Calculated during test.
}},
}},
}, {
op: "allprop",
name: "/file",
wantPropstats: []Propstat{{
Status: http.StatusOK,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
InnerXML: []byte(""),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
InnerXML: []byte("file"),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
InnerXML: []byte("9"),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
InnerXML: nil, // Calculated during test.
}},
}},
}, {
op: "allprop",
name: "/file",
propnames: []xml.Name{
{"DAV:", "resourcetype"},
{"foo", "bar"},
},
wantPropstats: []Propstat{{
Status: http.StatusOK,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
InnerXML: []byte(""),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
InnerXML: []byte("file"),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
InnerXML: []byte("9"),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
InnerXML: nil, // Calculated during test.
}}}, {
Status: http.StatusNotFound,
Props: []Property{{
XMLName: xml.Name{Space: "foo", Local: "bar"},
}}},
},
}},
}, {
"propfind DAV:resourcetype",
[]string{"mkdir /dir", "touch /file"},
[]propOp{{
op: "propfind",
name: "/dir",
propnames: []xml.Name{{"DAV:", "resourcetype"}},
wantPropstats: []Propstat{{
Status: http.StatusOK,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
InnerXML: []byte(`<collection xmlns="DAV:"/>`),
}},
}},
}, {
op: "propfind",
name: "/file",
propnames: []xml.Name{{"DAV:", "resourcetype"}},
wantPropstats: []Propstat{{
Status: http.StatusOK,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
InnerXML: []byte(""),
}},
}},
}},
}, {
"propfind unsupported DAV properties",
[]string{"mkdir /dir"},
[]propOp{{
op: "propfind",
name: "/dir",
propnames: []xml.Name{{"DAV:", "getcontentlanguage"}},
wantPropstats: []Propstat{{
Status: http.StatusNotFound,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "getcontentlanguage"},
}},
}},
}, {
op: "propfind",
name: "/dir",
propnames: []xml.Name{{"DAV:", "creationdate"}},
wantPropstats: []Propstat{{
Status: http.StatusNotFound,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "creationdate"},
}},
}},
}},
}, {
"bad: propfind unknown property",
[]string{"mkdir /dir"},
[]propOp{{
op: "propfind",
name: "/dir",
propnames: []xml.Name{{"foo:", "bar"}},
wantPropstats: []Propstat{{
Status: http.StatusNotFound,
Props: []Property{{
XMLName: xml.Name{Space: "foo:", Local: "bar"},
}},
}},
}},
}}
for _, tc := range testCases {
fs, err := buildTestFS(tc.buildfs)
if err != nil {
t.Fatalf("%s: cannot create test filesystem: %v", tc.desc, err)
}
ls := NewMemLS()
ps := NewMemPS(fs, ls)
for _, op := range tc.propOp {
desc := fmt.Sprintf("%s: %s %s", tc.desc, op.op, op.name)
if err = calcProps(op.name, fs, op.wantPropstats); err != nil {
t.Fatalf("%s: calcProps: %v", desc, err)
}
// Call property system.
var propstats []Propstat
switch op.op {
case "propname":
names, err := ps.Propnames(op.name)
if err != nil {
t.Errorf("%s: got error %v, want nil", desc, err)
continue
}
sort.Sort(byXMLName(names))
sort.Sort(byXMLName(op.wantNames))
if !reflect.DeepEqual(names, op.wantNames) {
t.Errorf("%s: names\ngot %q\nwant %q", desc, names, op.wantNames)
}
continue
case "allprop":
propstats, err = ps.Allprop(op.name, op.propnames)
case "propfind":
propstats, err = ps.Find(op.name, op.propnames)
default:
t.Fatalf("%s: %s not implemented", desc, op.op)
}
if err != nil {
t.Errorf("%s: got error %v, want nil", desc, err)
continue
}
// Compare return values from allprop or propfind.
for _, pst := range propstats {
sort.Sort(byPropname(pst.Props))
}
for _, pst := range op.wantPropstats {
sort.Sort(byPropname(pst.Props))
}
sort.Sort(byStatus(propstats))
sort.Sort(byStatus(op.wantPropstats))
if !reflect.DeepEqual(propstats, op.wantPropstats) {
t.Errorf("%s: propstat\ngot %q\nwant %q", desc, propstats, op.wantPropstats)
}
}
}
}
func cmpXMLName(a, b xml.Name) bool {
if a.Space != b.Space {
return a.Space < b.Space
}
return a.Local < b.Local
}
type byXMLName []xml.Name
func (b byXMLName) Len() int {
return len(b)
}
func (b byXMLName) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
func (b byXMLName) Less(i, j int) bool {
return cmpXMLName(b[i], b[j])
}
type byPropname []Property
func (b byPropname) Len() int {
return len(b)
}
func (b byPropname) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
func (b byPropname) Less(i, j int) bool {
return cmpXMLName(b[i].XMLName, b[j].XMLName)
}
type byStatus []Propstat
func (b byStatus) Len() int {
return len(b)
}
func (b byStatus) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
func (b byStatus) Less(i, j int) bool {
return b[i].Status < b[j].Status
}