- Updated the Contacts API to support contact synchronization with incremental updates using sync tokens. - Added functionality for merging duplicate contacts on the server side. - Introduced new endpoints for enriching contact interactions, including mail, meetings, and files. - Implemented ETag support for contact updates to ensure data integrity. - Enhanced validation for sync tokens and interaction queries. - Updated project checklist to reflect the completion of Contacts API enhancements.
103 lines
3.0 KiB
Go
103 lines
3.0 KiB
Go
package nextcloud
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestBuildSyncCollectionRequest(t *testing.T) {
|
|
initial := buildSyncCollectionRequest("")
|
|
if strings.Contains(initial, "sync-token") {
|
|
t.Fatal("initial sync must omit sync-token element")
|
|
}
|
|
if !strings.Contains(initial, "sync-collection") {
|
|
t.Fatal("expected sync-collection request")
|
|
}
|
|
|
|
incremental := buildSyncCollectionRequest(`http://nc/sync/1`)
|
|
if !strings.Contains(incremental, "<d:sync-token>http://nc/sync/1</d:sync-token>") {
|
|
t.Fatalf("unexpected incremental body: %s", incremental)
|
|
}
|
|
}
|
|
|
|
func TestParseContactSyncResponse(t *testing.T) {
|
|
body := strings.NewReader(`<?xml version="1.0" encoding="utf-8"?>
|
|
<d:multistatus xmlns:d="DAV:" xmlns:card="urn:ietf:params:xml:ns:carddav">
|
|
<d:sync-token>http://nc/sync/2</d:sync-token>
|
|
<d:response>
|
|
<d:href>/remote.php/dav/addressbooks/users/alice/contacts/ada.vcf</d:href>
|
|
<d:propstat>
|
|
<d:prop>
|
|
<d:getetag>"etag-1"</d:getetag>
|
|
<card:address-data>BEGIN:VCARD
|
|
UID:ada@ulti
|
|
FN:Ada Lovelace
|
|
EMAIL:ada@example.com
|
|
END:VCARD</card:address-data>
|
|
</d:prop>
|
|
<d:status>HTTP/1.1 200 OK</d:status>
|
|
</d:propstat>
|
|
</d:response>
|
|
<d:response>
|
|
<d:href>/remote.php/dav/addressbooks/users/alice/contacts/old.vcf</d:href>
|
|
<d:status>HTTP/1.1 404 Not Found</d:status>
|
|
</d:response>
|
|
</d:multistatus>`)
|
|
|
|
result, err := parseContactSyncResponse(body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if result.SyncToken != "http://nc/sync/2" {
|
|
t.Fatalf("sync token: got %q", result.SyncToken)
|
|
}
|
|
if len(result.Contacts) != 1 {
|
|
t.Fatalf("contacts: got %d", len(result.Contacts))
|
|
}
|
|
c := result.Contacts[0]
|
|
if c.FullName != "Ada Lovelace" || c.Path != "/remote.php/dav/addressbooks/users/alice/contacts/ada.vcf" {
|
|
t.Fatalf("unexpected contact: %+v", c)
|
|
}
|
|
if c.ETag != "\"etag-1\"" {
|
|
t.Fatalf("etag: got %q", c.ETag)
|
|
}
|
|
if len(result.Deleted) != 1 || result.Deleted[0] != "/remote.php/dav/addressbooks/users/alice/contacts/old.vcf" {
|
|
t.Fatalf("deleted: %+v", result.Deleted)
|
|
}
|
|
}
|
|
|
|
func TestParseContactListSetsPathAndETag(t *testing.T) {
|
|
body := strings.NewReader(`<?xml version="1.0" encoding="utf-8"?>
|
|
<d:multistatus xmlns:d="DAV:" xmlns:card="urn:ietf:params:xml:ns:carddav">
|
|
<d:response>
|
|
<d:href>/remote.php/dav/addressbooks/users/alice/contacts/</d:href>
|
|
<d:propstat><d:prop/></d:propstat>
|
|
</d:response>
|
|
<d:response>
|
|
<d:href>/remote.php/dav/addressbooks/users/alice/contacts/bob.vcf</d:href>
|
|
<d:propstat>
|
|
<d:prop>
|
|
<d:getetag>"e2"</d:getetag>
|
|
<card:address-data>BEGIN:VCARD
|
|
FN:Bob
|
|
END:VCARD</card:address-data>
|
|
</d:prop>
|
|
</d:propstat>
|
|
</d:response>
|
|
</d:multistatus>`)
|
|
|
|
contacts, err := parseContactList(body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(contacts) != 1 {
|
|
t.Fatalf("contacts: got %d", len(contacts))
|
|
}
|
|
if contacts[0].Path != "/remote.php/dav/addressbooks/users/alice/contacts/bob.vcf" {
|
|
t.Fatalf("path: %q", contacts[0].Path)
|
|
}
|
|
if contacts[0].ETag != "\"e2\"" {
|
|
t.Fatalf("etag: %q", contacts[0].ETag)
|
|
}
|
|
}
|