2022-10-21 11:53:04 +01:00
|
|
|
package tables_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"database/sql"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2023-04-19 15:50:33 +01:00
|
|
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
2022-10-21 11:53:04 +01:00
|
|
|
|
|
|
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
2023-04-27 12:54:20 +01:00
|
|
|
rstypes "github.com/matrix-org/dendrite/roomserver/types"
|
2022-10-21 11:53:04 +01:00
|
|
|
"github.com/matrix-org/dendrite/setup/config"
|
|
|
|
"github.com/matrix-org/dendrite/syncapi/storage/postgres"
|
|
|
|
"github.com/matrix-org/dendrite/syncapi/storage/sqlite3"
|
|
|
|
"github.com/matrix-org/dendrite/syncapi/storage/tables"
|
|
|
|
"github.com/matrix-org/dendrite/syncapi/types"
|
|
|
|
"github.com/matrix-org/dendrite/test"
|
|
|
|
)
|
|
|
|
|
|
|
|
func newMembershipsTable(t *testing.T, dbType test.DBType) (tables.Memberships, *sql.DB, func()) {
|
|
|
|
t.Helper()
|
|
|
|
connStr, close := test.PrepareDBConnectionString(t, dbType)
|
|
|
|
db, err := sqlutil.Open(&config.DatabaseOptions{
|
|
|
|
ConnectionString: config.DataSource(connStr),
|
|
|
|
}, sqlutil.NewExclusiveWriter())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to open db: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var tab tables.Memberships
|
|
|
|
switch dbType {
|
|
|
|
case test.DBTypePostgres:
|
|
|
|
tab, err = postgres.NewPostgresMembershipsTable(db)
|
|
|
|
case test.DBTypeSQLite:
|
|
|
|
tab, err = sqlite3.NewSqliteMembershipsTable(db)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to make new table: %s", err)
|
|
|
|
}
|
|
|
|
return tab, db, close
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMembershipsTable(t *testing.T) {
|
|
|
|
|
|
|
|
alice := test.NewUser(t)
|
|
|
|
room := test.NewRoom(t, alice)
|
|
|
|
|
|
|
|
// Create users
|
2023-04-27 12:54:20 +01:00
|
|
|
var userEvents []*rstypes.HeaderedEvent
|
2022-10-21 11:53:04 +01:00
|
|
|
users := []string{alice.ID}
|
|
|
|
for _, x := range room.CurrentState() {
|
|
|
|
if x.StateKeyEquals(alice.ID) {
|
|
|
|
if _, err := x.Membership(); err == nil {
|
|
|
|
userEvents = append(userEvents, x)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(userEvents) == 0 {
|
|
|
|
t.Fatalf("didn't find creator membership event")
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
u := test.NewUser(t)
|
|
|
|
users = append(users, u.ID)
|
|
|
|
|
2023-04-19 15:50:33 +01:00
|
|
|
ev := room.CreateAndInsert(t, u, spec.MRoomMember, map[string]interface{}{
|
2022-10-21 11:53:04 +01:00
|
|
|
"membership": "join",
|
|
|
|
}, test.WithStateKey(u.ID))
|
|
|
|
userEvents = append(userEvents, ev)
|
|
|
|
}
|
|
|
|
|
|
|
|
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
|
|
|
table, _, close := newMembershipsTable(t, dbType)
|
|
|
|
defer close()
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
for _, ev := range userEvents {
|
2023-06-28 19:29:49 +01:00
|
|
|
ev.StateKeyResolved = ev.StateKey()
|
2022-10-21 11:53:04 +01:00
|
|
|
if err := table.UpsertMembership(ctx, nil, ev, types.StreamPosition(ev.Depth()), 1); err != nil {
|
|
|
|
t.Fatalf("failed to upsert membership: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
testUpsert(t, ctx, table, userEvents[0], alice, room)
|
|
|
|
testMembershipCount(t, ctx, table, room)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func testMembershipCount(t *testing.T, ctx context.Context, table tables.Memberships, room *test.Room) {
|
|
|
|
t.Run("membership counts are correct", func(t *testing.T) {
|
|
|
|
// After 10 events, we should have 6 users (5 create related [incl. one member event], 5 member events = 6 users)
|
2023-04-19 15:50:33 +01:00
|
|
|
count, err := table.SelectMembershipCount(ctx, nil, room.ID, spec.Join, 10)
|
2022-10-21 11:53:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to get membership count: %s", err)
|
|
|
|
}
|
|
|
|
expectedCount := 6
|
|
|
|
if expectedCount != count {
|
|
|
|
t.Fatalf("expected member count to be %d, got %d", expectedCount, count)
|
|
|
|
}
|
|
|
|
|
|
|
|
// After 100 events, we should have all 11 users
|
2023-04-19 15:50:33 +01:00
|
|
|
count, err = table.SelectMembershipCount(ctx, nil, room.ID, spec.Join, 100)
|
2022-10-21 11:53:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to get membership count: %s", err)
|
|
|
|
}
|
|
|
|
expectedCount = 11
|
|
|
|
if expectedCount != count {
|
|
|
|
t.Fatalf("expected member count to be %d, got %d", expectedCount, count)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-04-27 12:54:20 +01:00
|
|
|
func testUpsert(t *testing.T, ctx context.Context, table tables.Memberships, membershipEvent *rstypes.HeaderedEvent, user *test.User, room *test.Room) {
|
2022-10-21 11:53:04 +01:00
|
|
|
t.Run("upserting works as expected", func(t *testing.T) {
|
|
|
|
if err := table.UpsertMembership(ctx, nil, membershipEvent, 1, 1); err != nil {
|
|
|
|
t.Fatalf("failed to upsert membership: %s", err)
|
|
|
|
}
|
|
|
|
membership, pos, err := table.SelectMembershipForUser(ctx, nil, room.ID, user.ID, 1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to select membership: %s", err)
|
|
|
|
}
|
|
|
|
expectedPos := 1
|
|
|
|
if pos != expectedPos {
|
|
|
|
t.Fatalf("expected pos to be %d, got %d", expectedPos, pos)
|
|
|
|
}
|
2023-04-19 15:50:33 +01:00
|
|
|
if membership != spec.Join {
|
2022-10-21 11:53:04 +01:00
|
|
|
t.Fatalf("expected membership to be join, got %s", membership)
|
|
|
|
}
|
|
|
|
// Create a new event which gets upserted and should not cause issues
|
2023-04-19 15:50:33 +01:00
|
|
|
ev := room.CreateAndInsert(t, user, spec.MRoomMember, map[string]interface{}{
|
|
|
|
"membership": spec.Join,
|
2022-10-21 11:53:04 +01:00
|
|
|
}, test.WithStateKey(user.ID))
|
2023-06-28 19:29:49 +01:00
|
|
|
ev.StateKeyResolved = ev.StateKey()
|
2022-10-21 11:53:04 +01:00
|
|
|
// Insert the same event again, but with different positions, which should get updated
|
|
|
|
if err = table.UpsertMembership(ctx, nil, ev, 2, 2); err != nil {
|
|
|
|
t.Fatalf("failed to upsert membership: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the position got updated
|
|
|
|
membership, pos, err = table.SelectMembershipForUser(ctx, nil, room.ID, user.ID, 10)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to select membership: %s", err)
|
|
|
|
}
|
|
|
|
expectedPos = 2
|
|
|
|
if pos != expectedPos {
|
|
|
|
t.Fatalf("expected pos to be %d, got %d", expectedPos, pos)
|
|
|
|
}
|
2023-04-19 15:50:33 +01:00
|
|
|
if membership != spec.Join {
|
2022-10-21 11:53:04 +01:00
|
|
|
t.Fatalf("expected membership to be join, got %s", membership)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we can't find a membership, it should default to leave
|
|
|
|
if membership, _, err = table.SelectMembershipForUser(ctx, nil, room.ID, user.ID, 1); err != nil {
|
|
|
|
t.Fatalf("failed to select membership: %s", err)
|
|
|
|
}
|
2023-04-19 15:50:33 +01:00
|
|
|
if membership != spec.Leave {
|
2022-10-21 11:53:04 +01:00
|
|
|
t.Fatalf("expected membership to be leave, got %s", membership)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|