mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-06 04:11:21 -05:00
groupware: improve JMAP integration tests for ContactCards
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
package jmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/jscontact"
|
||||
"github.com/opencloud-eu/opencloud/pkg/structs"
|
||||
)
|
||||
|
||||
func TestContacts(t *testing.T) {
|
||||
@@ -15,7 +18,7 @@ func TestContacts(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
count := uint(15 + rand.Intn(20))
|
||||
count := uint(20 + rand.Intn(30))
|
||||
|
||||
require := require.New(t)
|
||||
|
||||
@@ -23,11 +26,13 @@ func TestContacts(t *testing.T) {
|
||||
require.NoError(err)
|
||||
defer s.Close()
|
||||
|
||||
accountId, addressbookId, cardsById, err := s.fillContacts(t, count)
|
||||
accountId, addressbookId, cardsById, sentById, boxes, err := s.fillContacts(t, count)
|
||||
require.NoError(err)
|
||||
require.NotEmpty(accountId)
|
||||
require.NotEmpty(addressbookId)
|
||||
|
||||
allTrue(t, boxes, "mediaWithBlobId")
|
||||
|
||||
filter := ContactCardFilterCondition{
|
||||
InAddressBook: addressbookId,
|
||||
}
|
||||
@@ -46,40 +51,48 @@ func TestContacts(t *testing.T) {
|
||||
for _, actual := range contacts {
|
||||
expected, ok := cardsById[actual.Id]
|
||||
require.True(ok, "failed to find created contact by its id")
|
||||
expected.Id = actual.Id
|
||||
matchContact(t, actual, expected)
|
||||
sent := sentById[actual.Id]
|
||||
matchContact(t, actual, expected, sent, func() (jscontact.ContactCard, error) {
|
||||
cards, _, _, _, err := s.client.GetContactCardsById(accountId, s.session, t.Context(), s.logger, "", []string{actual.Id})
|
||||
if err != nil {
|
||||
return jscontact.ContactCard{}, err
|
||||
}
|
||||
require.Contains(cards, actual.Id)
|
||||
return cards[actual.Id], nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func matchContact(t *testing.T, actual jscontact.ContactCard, expected jscontact.ContactCard) {
|
||||
require := require.New(t)
|
||||
func allTrue[S any](t *testing.T, s S, exceptions ...string) {
|
||||
v := reflect.ValueOf(s)
|
||||
typ := v.Type()
|
||||
for i := range v.NumField() {
|
||||
name := typ.Field(i).Name
|
||||
if slices.Contains(exceptions, name) {
|
||||
continue
|
||||
}
|
||||
value := v.Field(i).Bool()
|
||||
require.True(t, value, "should be true: %v", name)
|
||||
}
|
||||
}
|
||||
|
||||
//a := actual
|
||||
//unType(t, &a)
|
||||
func matchContact(t *testing.T, actual jscontact.ContactCard, expected jscontact.ContactCard, sent map[string]any, fetcher func() (jscontact.ContactCard, error)) {
|
||||
require := require.New(t)
|
||||
if structs.AnyValue(expected.Media, func(media jscontact.Media) bool { return media.BlobId != "" }) {
|
||||
fmt.Printf("\x1b[33;1m----------------------------------------------------------\x1b[0m\n")
|
||||
fmt.Printf("\x1b[45;1m expected media: \x1b[0m\n%v\n\n", expected.Media)
|
||||
fmt.Printf("\x1b[46;1m actual media: \x1b[0m\n%v\n\n", actual.Media)
|
||||
fmt.Printf("\x1b[43;1m sent: \x1b[0m\n%v\n\n", sent)
|
||||
fmt.Printf("\x1b[44;1m pulling: \x1b[0m\n")
|
||||
_, err := fetcher()
|
||||
require.NoError(err)
|
||||
fmt.Printf("\x1b[44;1m pulled. \x1b[0m\n")
|
||||
}
|
||||
|
||||
require.Equal(expected.Name, actual.Name)
|
||||
require.Equal(expected.Emails, actual.Emails)
|
||||
require.Equal(expected.Organizations, actual.Organizations)
|
||||
require.Equal(expected.Media, actual.Media)
|
||||
|
||||
require.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func unType(t *testing.T, s any) {
|
||||
ty := reflect.TypeOf(s)
|
||||
|
||||
switch ty.Kind() {
|
||||
case reflect.Map, reflect.Array, reflect.Pointer, reflect.Slice:
|
||||
ty = ty.Elem()
|
||||
}
|
||||
|
||||
switch ty.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := range ty.NumField() {
|
||||
f := ty.Field(i)
|
||||
n := f.Name
|
||||
if n == "Type" {
|
||||
v := reflect.ValueOf(s).Field(i)
|
||||
require.True(t, v.Elem().CanSet(), "cannot set the Type field")
|
||||
v.SetString("")
|
||||
} else {
|
||||
//unType(t, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ func newStalwartTest(t *testing.T) (*StalwartTest, error) {
|
||||
|
||||
tlsConfig := &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
loggerImpl := clog.NewLogger()
|
||||
loggerImpl := clog.NewLogger(clog.Level("trace"))
|
||||
logger := &loggerImpl
|
||||
var j Client
|
||||
var session *Session
|
||||
@@ -774,15 +774,32 @@ func (s *StalwartTest) fillEmailsWithImap(folder string, count int) ([]filledMai
|
||||
|
||||
var productName = "jmaptest"
|
||||
|
||||
type ContactsBoxes struct {
|
||||
nicknames bool
|
||||
secondaryEmails bool
|
||||
secondaryAddress bool
|
||||
phones bool
|
||||
onlineService bool
|
||||
preferredLanguage bool
|
||||
mediaWithBlobId bool
|
||||
mediaWithDataUri bool
|
||||
mediaWithExternalUri bool
|
||||
organization bool
|
||||
cryptoKey bool
|
||||
link bool
|
||||
}
|
||||
|
||||
func (s *StalwartTest) fillContacts(
|
||||
t *testing.T,
|
||||
count uint,
|
||||
) (string, string, map[string]jscontact.ContactCard, error) {
|
||||
) (string, string, map[string]jscontact.ContactCard, map[string]map[string]any, ContactsBoxes, error) {
|
||||
require := require.New(t)
|
||||
c, err := NewTestJmapClient(s.session, s.username, s.password, true, true)
|
||||
require.NoError(err)
|
||||
defer c.Close()
|
||||
|
||||
boxes := ContactsBoxes{}
|
||||
|
||||
printer := func(s string) { log.Println(s) }
|
||||
|
||||
accountId := c.session.PrimaryAccounts.Contacts
|
||||
@@ -805,6 +822,7 @@ func (s *StalwartTest) fillContacts(
|
||||
require.NotEmpty(addressbookId)
|
||||
|
||||
filled := map[string]jscontact.ContactCard{}
|
||||
sent := map[string]map[string]any{}
|
||||
for i := range count {
|
||||
person := gofakeit.Person()
|
||||
nameMap, nameObj := createName(person)
|
||||
@@ -827,11 +845,12 @@ func (s *StalwartTest) fillContacts(
|
||||
Name: &nameObj,
|
||||
}
|
||||
|
||||
if rand.Intn(3) < 1 {
|
||||
if i%3 == 0 {
|
||||
nicknameMap, nicknameObj := createNickName(person)
|
||||
id := id()
|
||||
contact["nicknames"] = map[string]map[string]any{id: nicknameMap}
|
||||
card.Nicknames = map[string]jscontact.Nickname{id: nicknameObj}
|
||||
boxes.nicknames = true
|
||||
}
|
||||
|
||||
{
|
||||
@@ -847,75 +866,62 @@ func (s *StalwartTest) fillContacts(
|
||||
m, o := createSecondaryEmail(gofakeit.Email(), i*100)
|
||||
emailMaps[id] = m
|
||||
emailObjs[id] = o
|
||||
boxes.secondaryEmails = true
|
||||
}
|
||||
if len(emailMaps) > 0 {
|
||||
contact["emails"] = emailMaps
|
||||
card.Emails = emailObjs
|
||||
}
|
||||
}
|
||||
if err := propmap(contact, "phones", &card.Phones, 0, 2, func(i int, id string) (map[string]any, jscontact.Phone, error) {
|
||||
if err := propmap(i%2 == 0, 1, 2, contact, "phones", &card.Phones, func(i int, id string) (map[string]any, jscontact.Phone, error) {
|
||||
boxes.phones = true
|
||||
num := person.Contact.Phone
|
||||
if i > 0 {
|
||||
num = gofakeit.Phone()
|
||||
}
|
||||
var mapFeatures map[string]bool = nil
|
||||
var objFeatures map[jscontact.PhoneFeature]bool = nil
|
||||
var features map[jscontact.PhoneFeature]bool = nil
|
||||
if rand.Intn(3) < 2 {
|
||||
mapFeatures = toBoolMapS("mobile", "voice", "video", "text")
|
||||
objFeatures = toBoolMapS(jscontact.PhoneFeatureMobile, jscontact.PhoneFeatureVoice, jscontact.PhoneFeatureVideo, jscontact.PhoneFeatureText)
|
||||
features = toBoolMapS(jscontact.PhoneFeatureMobile, jscontact.PhoneFeatureVoice, jscontact.PhoneFeatureVideo, jscontact.PhoneFeatureText)
|
||||
} else {
|
||||
mapFeatures = toBoolMapS("voice", "main-number")
|
||||
objFeatures = toBoolMapS(jscontact.PhoneFeatureVoice, jscontact.PhoneFeatureMainNumber)
|
||||
features = toBoolMapS(jscontact.PhoneFeatureVoice, jscontact.PhoneFeatureMainNumber)
|
||||
}
|
||||
mapContexts := map[string]bool{}
|
||||
objContexts := map[jscontact.PhoneContext]bool{}
|
||||
mapContexts["work"] = true
|
||||
objContexts[jscontact.PhoneContextWork] = true
|
||||
|
||||
contexts := map[jscontact.PhoneContext]bool{jscontact.PhoneContextWork: true}
|
||||
if rand.Intn(2) < 1 {
|
||||
mapContexts["private"] = true
|
||||
objContexts[jscontact.PhoneContextPrivate] = true
|
||||
contexts[jscontact.PhoneContextPrivate] = true
|
||||
}
|
||||
tel := "tel:" + "+1" + num
|
||||
return map[string]any{
|
||||
"@type": "Phone",
|
||||
"number": tel,
|
||||
"features": mapFeatures,
|
||||
"contexts": mapContexts,
|
||||
"features": structs.MapKeys(features, func(f jscontact.PhoneFeature) string { return string(f) }),
|
||||
"contexts": structs.MapKeys(contexts, func(c jscontact.PhoneContext) string { return string(c) }),
|
||||
}, jscontact.Phone{
|
||||
//Type: jscontact.PhoneType,
|
||||
Number: tel,
|
||||
Features: objFeatures,
|
||||
Contexts: objContexts,
|
||||
Features: features,
|
||||
Contexts: contexts,
|
||||
}, nil
|
||||
}); err != nil {
|
||||
return "", "", nil, err
|
||||
return "", "", nil, nil, boxes, err
|
||||
}
|
||||
if err := propmap(contact, "addresses", &card.Addresses, 1, 2, func(i int, id string) (map[string]any, jscontact.Address, error) {
|
||||
if err := propmap(i%5 < 4, 1, 2, contact, "addresses", &card.Addresses, func(i int, id string) (map[string]any, jscontact.Address, error) {
|
||||
var source *gofakeit.AddressInfo
|
||||
if i == 0 {
|
||||
source = person.Address
|
||||
} else {
|
||||
source = gofakeit.Address()
|
||||
boxes.secondaryAddress = true
|
||||
}
|
||||
mComps := []map[string]string{}
|
||||
oComps := []jscontact.AddressComponent{}
|
||||
components := []jscontact.AddressComponent{}
|
||||
m := streetNumberRegex.FindAllStringSubmatch(source.Street, -1)
|
||||
if m != nil {
|
||||
mComps = append(mComps, map[string]string{"kind": "name", "value": m[0][2]})
|
||||
mComps = append(mComps, map[string]string{"kind": "number", "value": m[0][1]})
|
||||
oComps = append(oComps, jscontact.AddressComponent{ /*Type: jscontact.AddressComponentType,*/ Kind: jscontact.AddressComponentKindName, Value: m[0][2]})
|
||||
oComps = append(oComps, jscontact.AddressComponent{ /*Type: jscontact.AddressComponentType,*/ Kind: jscontact.AddressComponentKindNumber, Value: m[0][1]})
|
||||
components = append(components, jscontact.AddressComponent{ /*Type: jscontact.AddressComponentType,*/ Kind: jscontact.AddressComponentKindName, Value: m[0][2]})
|
||||
components = append(components, jscontact.AddressComponent{ /*Type: jscontact.AddressComponentType,*/ Kind: jscontact.AddressComponentKindNumber, Value: m[0][1]})
|
||||
} else {
|
||||
mComps = append(mComps, map[string]string{"kind": "name", "value": source.Street})
|
||||
oComps = append(oComps, jscontact.AddressComponent{ /*Type: jscontact.AddressComponentType,*/ Kind: jscontact.AddressComponentKindName, Value: source.Street})
|
||||
components = append(components, jscontact.AddressComponent{ /*Type: jscontact.AddressComponentType,*/ Kind: jscontact.AddressComponentKindName, Value: source.Street})
|
||||
}
|
||||
mComps = append(mComps,
|
||||
map[string]string{"kind": "locality", "value": source.City},
|
||||
map[string]string{"kind": "country", "value": source.Country},
|
||||
map[string]string{"kind": "region", "value": source.State},
|
||||
map[string]string{"kind": "postcode", "value": source.Zip},
|
||||
)
|
||||
oComps = append(oComps,
|
||||
components = append(components,
|
||||
jscontact.AddressComponent{ /*Type: jscontact.AddressComponentType,*/ Kind: jscontact.AddressComponentKindLocality, Value: source.City},
|
||||
jscontact.AddressComponent{ /*Type: jscontact.AddressComponentType,*/ Kind: jscontact.AddressComponentKindCountry, Value: source.Country},
|
||||
jscontact.AddressComponent{ /*Type: jscontact.AddressComponentType,*/ Kind: jscontact.AddressComponentKindRegion, Value: source.State},
|
||||
@@ -923,22 +929,25 @@ func (s *StalwartTest) fillContacts(
|
||||
)
|
||||
tz := pickRandom(timezones...)
|
||||
return map[string]any{
|
||||
"@type": "Address",
|
||||
"components": mComps,
|
||||
"@type": "Address",
|
||||
"components": structs.Map(components, func(c jscontact.AddressComponent) map[string]string {
|
||||
return map[string]string{"kind": string(c.Kind), "value": c.Value}
|
||||
}),
|
||||
"defaultSeparator": ", ",
|
||||
"isOrdered": true,
|
||||
"timeZone": tz,
|
||||
}, jscontact.Address{
|
||||
//Type: jscontact.AddressType,
|
||||
Components: oComps,
|
||||
Components: components,
|
||||
DefaultSeparator: ", ",
|
||||
IsOrdered: true,
|
||||
TimeZone: tz,
|
||||
}, nil
|
||||
}); err != nil {
|
||||
return "", "", nil, err
|
||||
return "", "", nil, nil, boxes, err
|
||||
}
|
||||
if err := propmap(contact, "onlineServices", &card.OnlineServices, 0, 2, func(i int, id string) (map[string]any, jscontact.OnlineService, error) {
|
||||
if err := propmap(i%2 == 0, 1, 2, contact, "onlineServices", &card.OnlineServices, func(i int, id string) (map[string]any, jscontact.OnlineService, error) {
|
||||
boxes.onlineService = true
|
||||
switch rand.Intn(3) {
|
||||
case 0:
|
||||
return map[string]any{
|
||||
@@ -974,10 +983,11 @@ func (s *StalwartTest) fillContacts(
|
||||
}, nil
|
||||
}
|
||||
}); err != nil {
|
||||
return "", "", nil, err
|
||||
return "", "", nil, nil, boxes, err
|
||||
}
|
||||
|
||||
if err := propmap(contact, "preferredLanguages", &card.PreferredLanguages, 0, 2, func(i int, id string) (map[string]any, jscontact.LanguagePref, error) {
|
||||
if err := propmap(i%3 == 0, 1, 2, contact, "preferredLanguages", &card.PreferredLanguages, func(i int, id string) (map[string]any, jscontact.LanguagePref, error) {
|
||||
boxes.preferredLanguage = true
|
||||
lang := pickRandom("en", "fr", "de", "es", "it")
|
||||
contexts := pickRandoms1("work", "private")
|
||||
return map[string]any{
|
||||
@@ -992,15 +1002,16 @@ func (s *StalwartTest) fillContacts(
|
||||
Pref: uint(i + 1),
|
||||
}, nil
|
||||
}); err != nil {
|
||||
return "", "", nil, err
|
||||
return "", "", nil, nil, boxes, err
|
||||
}
|
||||
|
||||
{
|
||||
if i%2 == 0 {
|
||||
organizationMaps := map[string]map[string]any{}
|
||||
organizationObjs := map[string]jscontact.Organization{}
|
||||
titleMaps := map[string]map[string]any{}
|
||||
titleObjs := map[string]jscontact.Title{}
|
||||
for range rand.Intn(2) {
|
||||
for range 1 + rand.Intn(2) {
|
||||
boxes.organization = true
|
||||
orgId := id()
|
||||
titleId := id()
|
||||
organizationMaps[orgId] = map[string]any{
|
||||
@@ -1026,15 +1037,14 @@ func (s *StalwartTest) fillContacts(
|
||||
OrganizationId: orgId,
|
||||
}
|
||||
}
|
||||
if len(organizationMaps) > 0 {
|
||||
contact["organizations"] = organizationMaps
|
||||
contact["titles"] = titleMaps
|
||||
card.Organizations = organizationObjs
|
||||
card.Titles = titleObjs
|
||||
}
|
||||
contact["organizations"] = organizationMaps
|
||||
contact["titles"] = titleMaps
|
||||
card.Organizations = organizationObjs
|
||||
card.Titles = titleObjs
|
||||
}
|
||||
|
||||
if err := propmap(contact, "cryptoKeys", &card.CryptoKeys, 0, 1, func(i int, id string) (map[string]any, jscontact.CryptoKey, error) {
|
||||
if err := propmap(i%2 == 0, 1, 1, contact, "cryptoKeys", &card.CryptoKeys, func(i int, id string) (map[string]any, jscontact.CryptoKey, error) {
|
||||
boxes.cryptoKey = true
|
||||
entity, err := openpgp.NewEntity(person.FirstName+" "+person.LastName, "test", person.Contact.Email, nil)
|
||||
if err != nil {
|
||||
return nil, jscontact.CryptoKey{}, err
|
||||
@@ -1053,47 +1063,85 @@ func (s *StalwartTest) fillContacts(
|
||||
Uri: "data:application/pgp-keys;base64," + encoded,
|
||||
}, nil
|
||||
}); err != nil {
|
||||
return "", "", nil, err
|
||||
return "", "", nil, nil, boxes, err
|
||||
}
|
||||
|
||||
if err := propmap(contact, "media", &card.Media, 0, 1, func(i int, id string) (map[string]any, jscontact.Media, error) {
|
||||
if rand.Intn(2) < 1 {
|
||||
img := gofakeit.ImageJpeg(128, 128)
|
||||
if err := propmap(i%2 == 0, 1, 2, contact, "media", &card.Media, func(i int, id string) (map[string]any, jscontact.Media, error) {
|
||||
label := fmt.Sprintf("photo-%d", 1000+rand.Intn(9000))
|
||||
switch rand.Intn(3) {
|
||||
case 0:
|
||||
boxes.mediaWithDataUri = true
|
||||
// use data uri
|
||||
//size := 16 + rand.Intn(512-16+1) // <- let's not do that right now, makes debugging errors very difficult due to the ASCII wall noise
|
||||
size := pickRandom(16, 24, 32, 48, 64)
|
||||
img := gofakeit.ImagePng(size, size)
|
||||
mime := "image/png"
|
||||
uri := "data:" + mime + ";base64," + base64.StdEncoding.EncodeToString(img)
|
||||
contexts := toBoolMapS(jscontact.MediaContextPrivate)
|
||||
return map[string]any{
|
||||
"@type": "Media",
|
||||
"kind": string(jscontact.MediaKindPhoto),
|
||||
"uri": uri,
|
||||
"mediaType": mime,
|
||||
"contexts": structs.MapKeys(contexts, func(c jscontact.MediaContext) string { return string(c) }),
|
||||
"label": label,
|
||||
}, jscontact.Media{
|
||||
// Type: jscontact.MediaType,
|
||||
Kind: jscontact.MediaKindPhoto,
|
||||
Uri: uri,
|
||||
MediaType: mime,
|
||||
Contexts: contexts,
|
||||
Label: label,
|
||||
}, nil
|
||||
// currently does not work, reported as https://github.com/stalwartlabs/stalwart/issues/2431
|
||||
case 99: // change this to 1 to enable it again
|
||||
boxes.mediaWithBlobId = true
|
||||
size := pickRandom(16, 24, 32, 48, 64)
|
||||
img := gofakeit.ImageJpeg(size, size)
|
||||
blob, err := c.uploadBlob(accountId, img, "image/jpeg")
|
||||
if err != nil {
|
||||
return nil, jscontact.Media{}, err
|
||||
}
|
||||
contexts := toBoolMapS(jscontact.MediaContextPrivate)
|
||||
return map[string]any{
|
||||
"@type": "Media",
|
||||
"kind": "photo",
|
||||
"kind": string(jscontact.MediaKindPhoto),
|
||||
"blobId": blob.BlobId,
|
||||
"contexts": toBoolMapS("private"),
|
||||
"contexts": structs.MapKeys(contexts, func(c jscontact.MediaContext) string { return string(c) }),
|
||||
"label": label,
|
||||
}, jscontact.Media{
|
||||
// Type: jscontact.MediaType,
|
||||
Kind: jscontact.MediaKindPhoto,
|
||||
BlobId: blob.BlobId,
|
||||
MediaType: blob.Type,
|
||||
Contexts: toBoolMapS(jscontact.MediaContextPrivate),
|
||||
Contexts: contexts,
|
||||
Label: label,
|
||||
}, nil
|
||||
|
||||
} else {
|
||||
default:
|
||||
boxes.mediaWithExternalUri = true
|
||||
// use external uri
|
||||
uri := picsum(128, 128)
|
||||
contexts := toBoolMapS(jscontact.MediaContextWork)
|
||||
return map[string]any{
|
||||
"@type": "Media",
|
||||
"kind": "photo",
|
||||
"kind": string(jscontact.MediaKindPhoto),
|
||||
"uri": uri,
|
||||
"contexts": toBoolMapS("work"),
|
||||
"contexts": structs.MapKeys(contexts, func(c jscontact.MediaContext) string { return string(c) }),
|
||||
"label": label,
|
||||
}, jscontact.Media{
|
||||
// Type: jscontact.MediaType,
|
||||
Kind: jscontact.MediaKindPhoto,
|
||||
Uri: uri,
|
||||
Contexts: toBoolMapS(jscontact.MediaContextWork),
|
||||
Contexts: contexts,
|
||||
Label: label,
|
||||
}, nil
|
||||
}
|
||||
}); err != nil {
|
||||
return "", "", nil, err
|
||||
return "", "", nil, nil, boxes, err
|
||||
}
|
||||
if err := propmap(contact, "links", &card.Links, 0, 1, func(i int, id string) (map[string]any, jscontact.Link, error) {
|
||||
if err := propmap(i%2 == 0, 1, 1, contact, "links", &card.Links, func(i int, id string) (map[string]any, jscontact.Link, error) {
|
||||
boxes.link = true
|
||||
return map[string]any{
|
||||
"@type": "Link",
|
||||
"kind": "contact",
|
||||
@@ -1106,17 +1154,19 @@ func (s *StalwartTest) fillContacts(
|
||||
Pref: uint((i + 1) * 10),
|
||||
}, nil
|
||||
}); err != nil {
|
||||
return "", "", nil, err
|
||||
return "", "", nil, nil, boxes, err
|
||||
}
|
||||
|
||||
uid, err := s.CreateContact(c, accountId, contact)
|
||||
id, err := s.CreateContact(c, accountId, contact)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
return "", "", nil, nil, boxes, err
|
||||
}
|
||||
filled[uid] = card
|
||||
printer(fmt.Sprintf("🧑🏻 created %*s/%v uid=%v", int(math.Log10(float64(count))+1), strconv.Itoa(int(i+1)), count, uid))
|
||||
card.Id = id
|
||||
filled[id] = card
|
||||
sent[id] = contact
|
||||
printer(fmt.Sprintf("🧑🏻 created %*s/%v uid=%v", int(math.Log10(float64(count))+1), strconv.Itoa(int(i+1)), count, id))
|
||||
}
|
||||
return accountId, addressbookId, filled, nil
|
||||
return accountId, addressbookId, filled, sent, boxes, nil
|
||||
}
|
||||
|
||||
func (s *StalwartTest) CreateContact(j *TestJmapClient, accountId string, contact map[string]any) (string, error) {
|
||||
@@ -1639,11 +1689,11 @@ func categories() map[string]bool {
|
||||
return toBoolMap(pickRandoms(Categories...))
|
||||
}
|
||||
|
||||
func propmap[T any](container map[string]any, name string, cardProperty *map[string]T, min int, max int, generator func(int, string) (map[string]any, T, error)) error {
|
||||
n := min + rand.Intn(max-min+1)
|
||||
if n < 1 {
|
||||
func propmap[T any](enabled bool, min int, max int, container map[string]any, name string, cardProperty *map[string]T, generator func(int, string) (map[string]any, T, error)) error {
|
||||
if !enabled {
|
||||
return nil
|
||||
}
|
||||
n := min + rand.Intn(max-min+1)
|
||||
|
||||
m := make(map[string]map[string]any, n)
|
||||
o := make(map[string]T, n)
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/opencloud-eu/opencloud/pkg/jscalendar"
|
||||
"github.com/opencloud-eu/opencloud/pkg/jscontact"
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -768,3 +769,48 @@ func TestAlertWithOffsetTriggerInResponse(t *testing.T) {
|
||||
require.Equal(jscalendar.SignedDuration("-PT15M"), offsetTrigger.Offset)
|
||||
require.Equal(jscalendar.RelativeToStart, offsetTrigger.RelativeTo)
|
||||
}
|
||||
|
||||
func TestParseContactCardMedia(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
text := `{
|
||||
"methodResponses":[
|
||||
["ContactCard/get",{
|
||||
"accountId":"b",
|
||||
"state":"ssecq",
|
||||
"list":[{
|
||||
"prodId": "jmaptest",
|
||||
"@type": "Card",
|
||||
"kind": "individual",
|
||||
"media": {
|
||||
"aT8afd59": {
|
||||
"uri": "https://picsum.photos/id/96/128/128",
|
||||
"kind": "photo",
|
||||
"@type": "Media",
|
||||
"contexts": {
|
||||
"work": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
"notFound":[]
|
||||
},"1"]
|
||||
],"sessionState":"7d3cae5b"
|
||||
}`
|
||||
|
||||
var response Response
|
||||
err := json.Unmarshal([]byte(text), &response)
|
||||
require.NoError(err)
|
||||
|
||||
resp := response.MethodResponses[0]
|
||||
require.Equal(CommandContactCardGet, resp.Command)
|
||||
require.IsType(ContactCardGetResponse{}, resp.Parameters)
|
||||
params := resp.Parameters.(ContactCardGetResponse)
|
||||
require.Len(params.List, 1)
|
||||
contact := params.List[0]
|
||||
require.Contains(contact.Media, "aT8afd59")
|
||||
media := contact.Media["aT8afd59"]
|
||||
require.NotNil(media)
|
||||
require.Equal("https://picsum.photos/id/96/128/128", media.Uri)
|
||||
require.Equal(jscontact.MediaKindPhoto, media.Kind)
|
||||
}
|
||||
|
||||
@@ -176,3 +176,51 @@ func FirstKey[K comparable, V any](m map[K]V) *K {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Any[E any](s []E, predicate func(E) bool) bool {
|
||||
if len(s) < 1 {
|
||||
return false
|
||||
}
|
||||
for _, e := range s {
|
||||
if predicate(e) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AnyKey[K comparable, V any](m map[K]V, predicate func(K) bool) bool {
|
||||
if len(m) < 1 {
|
||||
return false
|
||||
}
|
||||
for k := range m {
|
||||
if predicate(k) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AnyValue[K comparable, V any](m map[K]V, predicate func(V) bool) bool {
|
||||
if len(m) < 1 {
|
||||
return false
|
||||
}
|
||||
for _, v := range m {
|
||||
if predicate(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AnyItem[K comparable, V any](m map[K]V, predicate func(K, V) bool) bool {
|
||||
if len(m) < 1 {
|
||||
return false
|
||||
}
|
||||
for k, v := range m {
|
||||
if predicate(k, v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -121,3 +121,46 @@ func TestMissing(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAny(t *testing.T) {
|
||||
always := func(s string) bool { return true }
|
||||
never := func(s string) bool { return false }
|
||||
assert.True(t, Any([]string{"a", "b", "c"}, always))
|
||||
assert.False(t, Any([]string{}, always))
|
||||
assert.False(t, Any(nil, always))
|
||||
assert.False(t, Any([]string{"a", "b", "c"}, never))
|
||||
assert.False(t, Any(nil, never))
|
||||
}
|
||||
|
||||
func TestAnyKey(t *testing.T) {
|
||||
always := func(s string) bool { return true }
|
||||
never := func(s string) bool { return false }
|
||||
|
||||
assert.True(t, AnyKey(map[string]bool{"a": true, "b": false}, always))
|
||||
assert.False(t, AnyKey(map[string]bool{}, always))
|
||||
assert.False(t, AnyKey[string, bool](nil, always))
|
||||
assert.False(t, AnyKey(map[string]bool{"a": true, "b": false}, never))
|
||||
assert.False(t, AnyKey[string, bool](nil, never))
|
||||
}
|
||||
|
||||
func TestAnyValue(t *testing.T) {
|
||||
always := func(b bool) bool { return true }
|
||||
never := func(b bool) bool { return false }
|
||||
|
||||
assert.True(t, AnyValue(map[string]bool{"a": true, "b": false}, always))
|
||||
assert.False(t, AnyValue(map[string]bool{}, always))
|
||||
assert.False(t, AnyValue[string, bool](nil, always))
|
||||
assert.False(t, AnyValue(map[string]bool{"a": true, "b": false}, never))
|
||||
assert.False(t, AnyValue[string, bool](nil, never))
|
||||
}
|
||||
|
||||
func TestAnyItem(t *testing.T) {
|
||||
always := func(s string, b bool) bool { return true }
|
||||
never := func(s string, b bool) bool { return false }
|
||||
|
||||
assert.True(t, AnyItem(map[string]bool{"a": true, "b": false}, always))
|
||||
assert.False(t, AnyItem(map[string]bool{}, always))
|
||||
assert.False(t, AnyItem[string, bool](nil, always))
|
||||
assert.False(t, AnyItem(map[string]bool{"a": true, "b": false}, never))
|
||||
assert.False(t, AnyItem[string, bool](nil, never))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user