diff --git a/pkg/jmap/jmap_integration_contact_test.go b/pkg/jmap/jmap_integration_contact_test.go index 6ed729b793..2daa684ecb 100644 --- a/pkg/jmap/jmap_integration_contact_test.go +++ b/pkg/jmap/jmap_integration_contact_test.go @@ -1,7 +1,6 @@ package jmap import ( - "fmt" "math/rand" "reflect" "slices" @@ -10,7 +9,6 @@ import ( "github.com/stretchr/testify/require" "github.com/opencloud-eu/opencloud/pkg/jscontact" - "github.com/opencloud-eu/opencloud/pkg/structs" ) func TestContacts(t *testing.T) { @@ -26,7 +24,7 @@ func TestContacts(t *testing.T) { require.NoError(err) defer s.Close() - accountId, addressbookId, cardsById, sentById, boxes, err := s.fillContacts(t, count) + accountId, addressbookId, expectedContactCardsById, boxes, err := s.fillContacts(t, count) require.NoError(err) require.NotEmpty(accountId) require.NotEmpty(addressbookId) @@ -49,17 +47,9 @@ func TestContacts(t *testing.T) { require.Len(contacts, int(count)) for _, actual := range contacts { - expected, ok := cardsById[actual.Id] + expected, ok := expectedContactCardsById[actual.Id] require.True(ok, "failed to find created contact by its id") - 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 - }) + matchContact(t, actual, expected) } } @@ -76,23 +66,6 @@ func allTrue[S any](t *testing.T, s S, exceptions ...string) { } } -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 matchContact(t *testing.T, actual jscontact.ContactCard, expected jscontact.ContactCard) { + require.Equal(t, expected, actual) } diff --git a/pkg/jmap/jmap_integration_test.go b/pkg/jmap/jmap_integration_test.go index 922674d23e..85ac294792 100644 --- a/pkg/jmap/jmap_integration_test.go +++ b/pkg/jmap/jmap_integration_test.go @@ -18,6 +18,7 @@ import ( "net/mail" "net/url" "os" + "reflect" "regexp" "slices" "strconv" @@ -792,7 +793,7 @@ type ContactsBoxes struct { func (s *StalwartTest) fillContacts( t *testing.T, count uint, -) (string, string, map[string]jscontact.ContactCard, map[string]map[string]any, ContactsBoxes, error) { +) (string, string, map[string]jscontact.ContactCard, ContactsBoxes, error) { require := require.New(t) c, err := NewTestJmapClient(s.session, s.username, s.password, true, true) require.NoError(err) @@ -821,8 +822,9 @@ func (s *StalwartTest) fillContacts( } require.NotEmpty(addressbookId) + u := true + filled := map[string]jscontact.ContactCard{} - sent := map[string]map[string]any{} for i := range count { person := gofakeit.Person() nameMap, nameObj := createName(person) @@ -896,14 +898,14 @@ func (s *StalwartTest) fillContacts( "number": tel, "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, + }, untype(jscontact.Phone{ + Type: jscontact.PhoneType, Number: tel, Features: features, Contexts: contexts, - }, nil + }, u), nil }); err != nil { - return "", "", nil, nil, boxes, err + return "", "", nil, boxes, err } 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 @@ -936,15 +938,15 @@ func (s *StalwartTest) fillContacts( "defaultSeparator": ", ", "isOrdered": true, "timeZone": tz, - }, jscontact.Address{ - //Type: jscontact.AddressType, + }, untype(jscontact.Address{ + Type: jscontact.AddressType, Components: components, DefaultSeparator: ", ", IsOrdered: true, TimeZone: tz, - }, nil + }, u), nil }); err != nil { - return "", "", nil, nil, boxes, err + return "", "", nil, boxes, err } 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 @@ -955,35 +957,35 @@ func (s *StalwartTest) fillContacts( "service": "Mastodon", "user": "@" + person.Contact.Email, "uri": "https://mastodon.example.com/@" + strings.ToLower(person.FirstName), - }, jscontact.OnlineService{ - //Type: jscontact.OnlineServiceType, + }, untype(jscontact.OnlineService{ + Type: jscontact.OnlineServiceType, Service: "Mastodon", User: "@" + person.Contact.Email, Uri: "https://mastodon.example.com/@" + strings.ToLower(person.FirstName), - }, nil + }, u), nil case 1: return map[string]any{ "@type": "OnlineService", "uri": "xmpp:" + person.Contact.Email, - }, jscontact.OnlineService{ - //Type: jscontact.OnlineServiceType, - Uri: "xmpp:" + person.Contact.Email, - }, nil + }, untype(jscontact.OnlineService{ + Type: jscontact.OnlineServiceType, + Uri: "xmpp:" + person.Contact.Email, + }, u), nil default: return map[string]any{ "@type": "OnlineService", "service": "Discord", "user": person.Contact.Email, "uri": "https://discord.example.com/user/" + person.Contact.Email, - }, jscontact.OnlineService{ - //Type: jscontact.OnlineServiceType, + }, untype(jscontact.OnlineService{ + Type: jscontact.OnlineServiceType, Service: "Discord", User: person.Contact.Email, Uri: "https://discord.example.com/user/" + person.Contact.Email, - }, nil + }, u), nil } }); err != nil { - return "", "", nil, nil, boxes, err + return "", "", nil, boxes, err } if err := propmap(i%3 == 0, 1, 2, contact, "preferredLanguages", &card.PreferredLanguages, func(i int, id string) (map[string]any, jscontact.LanguagePref, error) { @@ -995,14 +997,14 @@ func (s *StalwartTest) fillContacts( "language": lang, "contexts": toBoolMap(contexts), "pref": i + 1, - }, jscontact.LanguagePref{ - // Type: jscontact.LanguagePrefType, + }, untype(jscontact.LanguagePref{ + Type: jscontact.LanguagePrefType, Language: lang, Contexts: toBoolMap(structs.Map(contexts, func(s string) jscontact.LanguagePrefContext { return jscontact.LanguagePrefContext(s) })), Pref: uint(i + 1), - }, nil + }, u), nil }); err != nil { - return "", "", nil, nil, boxes, err + return "", "", nil, boxes, err } if i%2 == 0 { @@ -1019,23 +1021,23 @@ func (s *StalwartTest) fillContacts( "name": person.Job.Company, "contexts": toBoolMapS("work"), } - organizationObjs[orgId] = jscontact.Organization{ - // Type: jscontact.OrganizationType, + organizationObjs[orgId] = untype(jscontact.Organization{ + Type: jscontact.OrganizationType, Name: person.Job.Company, Contexts: toBoolMapS(jscontact.OrganizationContextWork), - } + }, u) titleMaps[titleId] = map[string]any{ "@type": "Title", "kind": "title", "name": person.Job.Title, "organizationId": orgId, } - titleObjs[titleId] = jscontact.Title{ - // Type: jscontact.TitleType, + titleObjs[titleId] = untype(jscontact.Title{ + Type: jscontact.TitleType, Kind: jscontact.TitleKindTitle, Name: person.Job.Title, OrganizationId: orgId, - } + }, u) } contact["organizations"] = organizationMaps contact["titles"] = titleMaps @@ -1058,12 +1060,12 @@ func (s *StalwartTest) fillContacts( return map[string]any{ "@type": "CryptoKey", "uri": "data:application/pgp-keys;base64," + encoded, - }, jscontact.CryptoKey{ - // Type: jscontact.CryptoKeyType, - Uri: "data:application/pgp-keys;base64," + encoded, - }, nil + }, untype(jscontact.CryptoKey{ + Type: jscontact.CryptoKeyType, + Uri: "data:application/pgp-keys;base64," + encoded, + }, u), nil }); err != nil { - return "", "", nil, nil, boxes, err + return "", "", nil, boxes, err } if err := propmap(i%2 == 0, 1, 2, contact, "media", &card.Media, func(i int, id string) (map[string]any, jscontact.Media, error) { @@ -1085,16 +1087,16 @@ func (s *StalwartTest) fillContacts( "mediaType": mime, "contexts": structs.MapKeys(contexts, func(c jscontact.MediaContext) string { return string(c) }), "label": label, - }, jscontact.Media{ - // Type: jscontact.MediaType, + }, untype(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 + }, u), nil + // currently not supported, reported as https://github.com/stalwartlabs/stalwart/issues/2431 + case -1: // change this to 1 to enable it again boxes.mediaWithBlobId = true size := pickRandom(16, 24, 32, 48, 64) img := gofakeit.ImageJpeg(size, size) @@ -1109,14 +1111,14 @@ func (s *StalwartTest) fillContacts( "blobId": blob.BlobId, "contexts": structs.MapKeys(contexts, func(c jscontact.MediaContext) string { return string(c) }), "label": label, - }, jscontact.Media{ - // Type: jscontact.MediaType, + }, untype(jscontact.Media{ + Type: jscontact.MediaType, Kind: jscontact.MediaKindPhoto, BlobId: blob.BlobId, MediaType: blob.Type, Contexts: contexts, Label: label, - }, nil + }, u), nil default: boxes.mediaWithExternalUri = true @@ -1129,16 +1131,16 @@ func (s *StalwartTest) fillContacts( "uri": uri, "contexts": structs.MapKeys(contexts, func(c jscontact.MediaContext) string { return string(c) }), "label": label, - }, jscontact.Media{ - // Type: jscontact.MediaType, + }, untype(jscontact.Media{ + Type: jscontact.MediaType, Kind: jscontact.MediaKindPhoto, Uri: uri, Contexts: contexts, Label: label, - }, nil + }, u), nil } }); err != nil { - return "", "", nil, nil, boxes, err + return "", "", nil, boxes, err } 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 @@ -1147,26 +1149,32 @@ func (s *StalwartTest) fillContacts( "kind": "contact", "uri": "mailto:" + person.Contact.Email, "pref": (i + 1) * 10, - }, jscontact.Link{ - // Type: jscontact.LinkType, + }, untype(jscontact.Link{ + Type: jscontact.LinkType, Kind: jscontact.LinkKindContact, Uri: "mailto:" + person.Contact.Email, Pref: uint((i + 1) * 10), - }, nil + }, u), nil }); err != nil { - return "", "", nil, nil, boxes, err + return "", "", nil, boxes, err } id, err := s.CreateContact(c, accountId, contact) if err != nil { - return "", "", nil, nil, boxes, err + return "", "", nil, boxes, err } 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, sent, boxes, nil + return accountId, addressbookId, filled, boxes, nil +} + +func untype[S any](s S, t bool) S { + if t { + reflect.ValueOf(&s).Elem().FieldByName("Type").SetString("") + } + return s } func (s *StalwartTest) CreateContact(j *TestJmapClient, accountId string, contact map[string]any) (string, error) { diff --git a/pkg/structs/structs_test.go b/pkg/structs/structs_test.go index b6a8ac1213..f56485c7c4 100644 --- a/pkg/structs/structs_test.go +++ b/pkg/structs/structs_test.go @@ -149,9 +149,9 @@ func TestAnyValue(t *testing.T) { 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[string](nil, always)) assert.False(t, AnyValue(map[string]bool{"a": true, "b": false}, never)) - assert.False(t, AnyValue[string, bool](nil, never)) + assert.False(t, AnyValue[string](nil, never)) } func TestAnyItem(t *testing.T) { @@ -160,7 +160,7 @@ func TestAnyItem(t *testing.T) { 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(nil, always)) assert.False(t, AnyItem(map[string]bool{"a": true, "b": false}, never)) - assert.False(t, AnyItem[string, bool](nil, never)) + assert.False(t, AnyItem(nil, never)) }