2022-06-14 13:27:09 +01:00
|
|
|
package carddav
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/emersion/go-vcard"
|
|
|
|
"github.com/emersion/go-webdav"
|
|
|
|
)
|
|
|
|
|
|
|
|
type testBackend struct{}
|
|
|
|
|
|
|
|
type contextKey string
|
|
|
|
|
|
|
|
var (
|
|
|
|
aliceData = `BEGIN:VCARD
|
|
|
|
VERSION:4.0
|
|
|
|
UID:urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1
|
|
|
|
FN;PID=1.1:Alice Gopher
|
|
|
|
N:Gopher;Alice;;;
|
|
|
|
EMAIL;PID=1.1:alice@example.com
|
|
|
|
CLIENTPIDMAP:1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0551
|
|
|
|
END:VCARD`
|
|
|
|
alicePath = "urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1.vcf"
|
|
|
|
|
2022-11-15 20:25:37 +00:00
|
|
|
currentUserPrincipalKey = contextKey("test:currentUserPrincipal")
|
|
|
|
homeSetPathKey = contextKey("test:homeSetPath")
|
|
|
|
addressBookPathKey = contextKey("test:addressBookPath")
|
2022-06-14 13:27:09 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func (*testBackend) CurrentUserPrincipal(ctx context.Context) (string, error) {
|
|
|
|
r := ctx.Value(currentUserPrincipalKey).(string)
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*testBackend) AddressbookHomeSetPath(ctx context.Context) (string, error) {
|
|
|
|
r := ctx.Value(homeSetPathKey).(string)
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*testBackend) AddressBook(ctx context.Context) (*AddressBook, error) {
|
|
|
|
p := ctx.Value(addressBookPathKey).(string)
|
|
|
|
return &AddressBook{
|
|
|
|
Path: p,
|
|
|
|
Name: "My contacts",
|
|
|
|
Description: "Default address book",
|
|
|
|
MaxResourceSize: 1024,
|
|
|
|
SupportedAddressData: nil,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*testBackend) GetAddressObject(ctx context.Context, path string, req *AddressDataRequest) (*AddressObject, error) {
|
|
|
|
if path == alicePath {
|
|
|
|
card, err := vcard.NewDecoder(strings.NewReader(aliceData)).Decode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &AddressObject{
|
|
|
|
Path: path,
|
|
|
|
Card: card,
|
|
|
|
}, nil
|
|
|
|
} else {
|
|
|
|
return nil, webdav.NewHTTPError(404, fmt.Errorf("Not found"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *testBackend) ListAddressObjects(ctx context.Context, req *AddressDataRequest) ([]AddressObject, error) {
|
|
|
|
alice, err := b.GetAddressObject(ctx, alicePath, req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return []AddressObject{*alice}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*testBackend) QueryAddressObjects(ctx context.Context, query *AddressBookQuery) ([]AddressObject, error) {
|
|
|
|
panic("TODO: implement")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*testBackend) PutAddressObject(ctx context.Context, path string, card vcard.Card, opts *PutAddressObjectOptions) (loc string, err error) {
|
|
|
|
panic("TODO: implement")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*testBackend) DeleteAddressObject(ctx context.Context, path string) error {
|
|
|
|
panic("TODO: implement")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAddressBookDiscovery(t *testing.T) {
|
|
|
|
for _, tc := range []struct {
|
|
|
|
name string
|
2022-08-24 10:52:11 +01:00
|
|
|
prefix string
|
2022-06-14 13:27:09 +01:00
|
|
|
currentUserPrincipal string
|
|
|
|
homeSetPath string
|
|
|
|
addressBookPath string
|
|
|
|
}{
|
|
|
|
{
|
2022-08-24 10:52:11 +01:00
|
|
|
name: "simple",
|
|
|
|
prefix: "",
|
2022-06-14 13:27:09 +01:00
|
|
|
currentUserPrincipal: "/test/",
|
|
|
|
homeSetPath: "/test/contacts/",
|
|
|
|
addressBookPath: "/test/contacts/private",
|
|
|
|
},
|
2022-08-24 10:52:11 +01:00
|
|
|
{
|
|
|
|
name: "prefix",
|
|
|
|
prefix: "/dav",
|
|
|
|
currentUserPrincipal: "/dav/test/",
|
|
|
|
homeSetPath: "/dav/test/contacts/",
|
|
|
|
addressBookPath: "/dav/test/contacts/private",
|
|
|
|
},
|
2022-06-14 13:27:09 +01:00
|
|
|
} {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
2023-12-13 13:37:38 +00:00
|
|
|
ctx := context.Background()
|
2022-06-14 13:27:09 +01:00
|
|
|
|
2022-08-24 10:52:11 +01:00
|
|
|
h := Handler{&testBackend{}, tc.prefix}
|
2022-06-14 13:27:09 +01:00
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
ctx = context.WithValue(ctx, currentUserPrincipalKey, tc.currentUserPrincipal)
|
|
|
|
ctx = context.WithValue(ctx, homeSetPathKey, tc.homeSetPath)
|
|
|
|
ctx = context.WithValue(ctx, addressBookPathKey, tc.addressBookPath)
|
|
|
|
r = r.WithContext(ctx)
|
|
|
|
(&h).ServeHTTP(w, r)
|
|
|
|
}))
|
|
|
|
defer ts.Close()
|
|
|
|
|
|
|
|
// client supports .well-known discovery if explicitly pointed to it
|
|
|
|
startURL := ts.URL
|
|
|
|
if tc.currentUserPrincipal != "/" {
|
|
|
|
startURL = ts.URL + "/.well-known/carddav"
|
|
|
|
}
|
|
|
|
|
|
|
|
client, err := NewClient(nil, startURL)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error creating client: %s", err)
|
|
|
|
}
|
2023-12-13 13:37:38 +00:00
|
|
|
cup, err := client.FindCurrentUserPrincipal(ctx)
|
2022-06-14 13:27:09 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error finding user principal url: %s", err)
|
|
|
|
}
|
|
|
|
if cup != tc.currentUserPrincipal {
|
|
|
|
t.Fatalf("Found current user principal URL '%s', expected '%s'", cup, tc.currentUserPrincipal)
|
|
|
|
}
|
2023-12-13 13:37:38 +00:00
|
|
|
hsp, err := client.FindAddressBookHomeSet(ctx, cup)
|
2022-06-14 13:27:09 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error finding home set path: %s", err)
|
|
|
|
}
|
|
|
|
if hsp != tc.homeSetPath {
|
|
|
|
t.Fatalf("Found home set path '%s', expected '%s'", hsp, tc.homeSetPath)
|
|
|
|
}
|
2023-12-13 13:37:38 +00:00
|
|
|
abs, err := client.FindAddressBooks(ctx, hsp)
|
2022-06-14 13:27:09 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error finding address books: %s", err)
|
|
|
|
}
|
|
|
|
if len(abs) != 1 {
|
|
|
|
t.Fatalf("Found %d address books, expected 1", len(abs))
|
|
|
|
}
|
|
|
|
if abs[0].Path != tc.addressBookPath {
|
|
|
|
t.Fatalf("Found address book at %s, expected %s", abs[0].Path, tc.addressBookPath)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|