package storage_test import ( "database/sql" "errors" "os" "testing" "github.com/dariuszjaniak/warp/server/internal/storage" ) // migratedDelayedDB opens a migrated database with empty delayed event and // OpenID tables. func migratedDelayedDB(t *testing.T, driver string) storage.Database { t.Helper() db := openTestDB(t, driver) if err := db.Migrate(t.Context()); err != nil { t.Fatalf("Migrate: %v", err) } if driver == storage.DriverPostgres { pool, err := sql.Open("pgx", os.Getenv("WARP_TEST_POSTGRES_DSN")) if err != nil { t.Fatalf("open postgres: %v", err) } defer func() { _ = pool.Close() }() if _, err := pool.ExecContext(t.Context(), `TRUNCATE delayed_events, openid_tokens`); err != nil { t.Fatalf("reset the tables: %v", err) } } return db } func TestDelayedEvents(t *testing.T) { for _, driver := range testDrivers { t.Run(driver, func(t *testing.T) { ctx := t.Context() store := migratedDelayedDB(t, driver).DelayedEvents() if _, ok, err := store.NextDelayedEventDue(ctx); err != nil || ok { t.Fatalf("NextDelayedEventDue on an empty table = %v, %v", ok, err) } key := "_@alice:warp.example_DEV_m.call" state := storage.DelayedEvent{ DelayID: "d1", UserID: "@alice:warp.example", DeviceID: "DEV", RoomID: "!r:warp.example", Type: "org.matrix.msc3401.call.member", StateKey: &key, Content: []byte(`{}`), DelayMS: 5000, RunningSinceMS: 1000, SendAtMS: 6000, } timeline := storage.DelayedEvent{ DelayID: "d2", UserID: "@alice:warp.example", RoomID: "!r:warp.example", Type: "m.room.message", Content: []byte(`{"body":"hi"}`), DelayMS: 1000, RunningSinceMS: 1000, SendAtMS: 2000, } for _, e := range []storage.DelayedEvent{state, timeline} { if err := store.InsertDelayedEvent(ctx, e); err != nil { t.Fatalf("InsertDelayedEvent: %v", err) } } if n, err := store.CountScheduledDelayedEvents(ctx, "@alice:warp.example"); err != nil || n != 2 { t.Errorf("count = %d, %v", n, err) } if next, ok, err := store.NextDelayedEventDue(ctx); err != nil || !ok || next != 2000 { t.Errorf("next due = %d, %v, %v", next, ok, err) } got, err := store.DelayedEvent(ctx, "@alice:warp.example", "d1") if err != nil || got.StateKey == nil || *got.StateKey != key || string(got.Content) != `{}` { t.Errorf("DelayedEvent = %+v, %v", got, err) } if got, err := store.DelayedEvent(ctx, "@alice:warp.example", "d2"); err != nil || got.StateKey != nil { t.Errorf("a timeline event has a state key: %+v, %v", got, err) } if _, err := store.DelayedEvent(ctx, "@bob:warp.example", "d1"); !errors.Is(err, storage.ErrNoSuchDelayedEvent) { t.Errorf("bob read alice's event: %v", err) } due, err := store.DueDelayedEvents(ctx, 3000, 10) if err != nil || len(due) != 1 || due[0].DelayID != "d2" { t.Errorf("due at 3000 = %+v, %v", due, err) } if err := store.RestartDelayedEvent(ctx, "d1", 4000); err != nil { t.Fatalf("RestartDelayedEvent: %v", err) } got, _ = store.ScheduledDelayedEvent(ctx, "d1") if got.RunningSinceMS != 4000 || got.SendAtMS != 9000 { t.Errorf("after restart = %+v", got) } pending, err := store.ScheduledDelayedStateEvents(ctx, "!r:warp.example", "org.matrix.msc3401.call.member", key) if err != nil || len(pending) != 1 { t.Errorf("ScheduledDelayedStateEvents = %+v, %v", pending, err) } sent := timeline sent.FinalisedAtMS, sent.Outcome, sent.Reason, sent.EventID = 2500, "send", "delay", "$e" if err := store.FinaliseDelayedEvent(ctx, sent); err != nil { t.Fatalf("FinaliseDelayedEvent: %v", err) } if err := store.FinaliseDelayedEvent(ctx, sent); !errors.Is(err, storage.ErrNoSuchDelayedEvent) { t.Errorf("finalising twice = %v", err) } if err := store.RestartDelayedEvent(ctx, "d2", 3000); !errors.Is(err, storage.ErrNoSuchDelayedEvent) { t.Errorf("restarting a finalised event = %v", err) } all, err := store.DelayedEvents(ctx, "@alice:warp.example") if err != nil || len(all) != 2 || all[0].DelayID != "d1" || all[1].EventID != "$e" { t.Errorf("DelayedEvents = %+v, %v", all, err) } if err := store.PurgeFinalisedDelayedEvents(ctx, 3000); err != nil { t.Fatal(err) } if all, _ := store.DelayedEvents(ctx, "@alice:warp.example"); len(all) != 1 { t.Errorf("after purge = %+v", all) } }) } } func TestOpenIDTokens(t *testing.T) { for _, driver := range testDrivers { t.Run(driver, func(t *testing.T) { ctx := t.Context() store := migratedDelayedDB(t, driver).OpenIDTokens() if err := store.InsertOpenIDToken(ctx, "hash", "@alice:warp.example", 5000); err != nil { t.Fatal(err) } if user, err := store.OpenIDTokenUser(ctx, "hash", 4000); err != nil || user != "@alice:warp.example" { t.Errorf("OpenIDTokenUser = %q, %v", user, err) } if _, err := store.OpenIDTokenUser(ctx, "hash", 6000); !errors.Is(err, storage.ErrNoSuchOpenIDToken) { t.Errorf("an expired token = %v", err) } if err := store.PurgeOpenIDTokens(ctx, 6000); err != nil { t.Fatal(err) } if _, err := store.OpenIDTokenUser(ctx, "hash", 0); !errors.Is(err, storage.ErrNoSuchOpenIDToken) { t.Errorf("a purged token = %v", err) } }) } }