Define avatar field on sync3.Room

This commit is contained in:
David Robertson 2023-07-18 10:42:13 +01:00
parent 64183a3b99
commit 59a6554615
No known key found for this signature in database
GPG Key ID: 903ECE108A39DEDD
4 changed files with 150 additions and 0 deletions

42
sync3/avatar.go Normal file
View File

@ -0,0 +1,42 @@
package sync3
import (
"bytes"
"encoding/json"
)
// An AvatarChange represents a change to a room's avatar. There are three cases:
// - an empty string represents no change, and should be omitted when JSON-serialised;
// - the sentinel `<no-avatar>` represents a room that has never had an avatar,
// or a room whose avatar has been removed. It is JSON-serialised as null.
// - All other strings represent the current avatar of the room and JSON-serialise as
// normal.
type AvatarChange string
const DeletedAvatar = AvatarChange("<no-avatar>")
const UnchangedAvatar AvatarChange = ""
// NewAvatarChange interprets an optional avatar string as an AvatarChange.
func NewAvatarChange(avatar string) AvatarChange {
if avatar == "" {
return DeletedAvatar
}
return AvatarChange(avatar)
}
func (a AvatarChange) MarshalJSON() ([]byte, error) {
if a == DeletedAvatar {
return []byte(`null`), nil
} else {
return json.Marshal(string(a))
}
}
// Note: the unmarshalling is only used in tests.
func (a *AvatarChange) UnmarshalJSON(data []byte) error {
if bytes.Equal(data, []byte("null")) {
*a = DeletedAvatar
return nil
}
return json.Unmarshal(data, (*string)(a))
}

View File

@ -10,6 +10,7 @@ import (
type Room struct {
Name string `json:"name,omitempty"`
AvatarChange AvatarChange `json:"avatar,omitempty"`
RequiredState []json.RawMessage `json:"required_state,omitempty"`
Timeline []json.RawMessage `json:"timeline,omitempty"`
InviteState []json.RawMessage `json:"invite_state,omitempty"`

74
sync3/room_test.go Normal file
View File

@ -0,0 +1,74 @@
package sync3
import (
"encoding/json"
"fmt"
"github.com/tidwall/gjson"
"reflect"
"testing"
)
func TestAvatarChangeMarshalling(t *testing.T) {
var url = "mxc://..."
testCases := []struct {
Name string
AvatarChange AvatarChange
Check func(avatar gjson.Result) error
}{
{
Name: "Avatar exists",
AvatarChange: NewAvatarChange(url),
Check: func(avatar gjson.Result) error {
if !(avatar.Exists() && avatar.Type == gjson.String && avatar.Str == url) {
return fmt.Errorf("unexpected marshalled avatar: got %#v want %s", avatar, url)
}
return nil
},
},
{
Name: "Avatar doesn't exist",
AvatarChange: DeletedAvatar,
Check: func(avatar gjson.Result) error {
if !(avatar.Exists() && avatar.Type == gjson.Null) {
return fmt.Errorf("unexpected marshalled Avatar: got %#v want null", avatar)
}
return nil
},
},
{
Name: "Avatar unchanged",
AvatarChange: UnchangedAvatar,
Check: func(avatar gjson.Result) error {
if avatar.Exists() {
return fmt.Errorf("unexpected marshalled Avatar: got %#v want omitted", avatar)
}
return nil
},
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
room := Room{AvatarChange: tc.AvatarChange}
marshalled, err := json.Marshal(room)
t.Logf("Marshalled to %s", string(marshalled))
if err != nil {
t.Fatal(err)
}
avatar := gjson.GetBytes(marshalled, "avatar")
if err = tc.Check(avatar); err != nil {
t.Fatal(err)
}
var unmarshalled Room
err = json.Unmarshal(marshalled, &unmarshalled)
if err != nil {
t.Fatal(err)
}
t.Logf("Unmarshalled to %#v", unmarshalled.AvatarChange)
if !reflect.DeepEqual(unmarshalled, room) {
t.Fatalf("Unmarshalled struct is different from original")
}
})
}
}

View File

@ -39,6 +39,39 @@ func MatchRoomName(name string) RoomMatcher {
}
}
// MatchRoomAvatar builds a RoomMatcher which checks that the given room response has
// set the room's avatar to the given value.
func MatchRoomAvatar(wantAvatar string) RoomMatcher {
return func(r sync3.Room) error {
if string(r.AvatarChange) != wantAvatar {
return fmt.Errorf("MatchRoomAvatar: got \"%s\" want \"%s\"", r.AvatarChange, wantAvatar)
}
return nil
}
}
// MatchRoomUnsetAvatar builds a RoomMatcher which checks that the given room has no
// avatar, or has had its avatar deleted.
func MatchRoomUnsetAvatar() RoomMatcher {
return func(r sync3.Room) error {
if r.AvatarChange != sync3.DeletedAvatar {
return fmt.Errorf("MatchRoomAvatar: got \"%s\" want \"%s\"", r.AvatarChange, sync3.DeletedAvatar)
}
return nil
}
}
// MatchRoomUnchangedAvatar builds a RoomMatcher which checks that the given room has no
// change to its avatar, or has had its avatar deleted.
func MatchRoomUnchangedAvatar() RoomMatcher {
return func(r sync3.Room) error {
if r.AvatarChange != sync3.UnchangedAvatar {
return fmt.Errorf("MatchRoomAvatar: got \"%s\" want \"%s\"", r.AvatarChange, sync3.UnchangedAvatar)
}
return nil
}
}
func MatchJoinCount(count int) RoomMatcher {
return func(r sync3.Room) error {
if r.JoinedCount != count {