snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
729
server/channels/store/storetest/session_store.go
Normal file
729
server/channels/store/storetest/session_store.go
Normal file
@@ -0,0 +1,729 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package storetest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||
)
|
||||
|
||||
const (
|
||||
TenMinutes = 600000
|
||||
)
|
||||
|
||||
func TestSessionStore(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
// Run serially to prevent interfering with other tests
|
||||
testSessionCleanup(t, rctx, ss)
|
||||
|
||||
t.Run("Save", func(t *testing.T) { testSessionStoreSave(t, rctx, ss) })
|
||||
t.Run("SessionGet", func(t *testing.T) { testSessionGet(t, rctx, ss) })
|
||||
t.Run("SessionGetWithDeviceId", func(t *testing.T) { testSessionGetWithDeviceId(t, rctx, ss) })
|
||||
t.Run("SessionRemove", func(t *testing.T) { testSessionRemove(t, rctx, ss) })
|
||||
t.Run("SessionRemoveAll", func(t *testing.T) { testSessionRemoveAll(t, rctx, ss) })
|
||||
t.Run("SessionRemoveByUser", func(t *testing.T) { testSessionRemoveByUser(t, rctx, ss) })
|
||||
t.Run("SessionRemoveToken", func(t *testing.T) { testSessionRemoveToken(t, rctx, ss) })
|
||||
t.Run("SessionUpdateDeviceId", func(t *testing.T) { testSessionUpdateDeviceId(t, rctx, ss) })
|
||||
t.Run("SessionUpdateDeviceId2", func(t *testing.T) { testSessionUpdateDeviceId2(t, rctx, ss) })
|
||||
t.Run("SessionUpdateDeviceIdClearsOnEmpty", func(t *testing.T) { testSessionUpdateDeviceIdClearsOnEmpty(t, rctx, ss) })
|
||||
t.Run("UpdateExpiresAt", func(t *testing.T) { testSessionStoreUpdateExpiresAt(t, rctx, ss) })
|
||||
t.Run("UpdateLastActivityAt", func(t *testing.T) { testSessionStoreUpdateLastActivityAt(t, rctx, ss) })
|
||||
t.Run("SessionCount", func(t *testing.T) { testSessionCount(t, rctx, ss) })
|
||||
t.Run("GetSessionsExpired", func(t *testing.T) { testGetSessionsExpired(t, rctx, ss) })
|
||||
t.Run("UpdateExpiredNotify", func(t *testing.T) { testUpdateExpiredNotify(t, rctx, ss) })
|
||||
t.Run("GetLRUSessions", func(t *testing.T) { testGetLRUSessions(t, rctx, ss) })
|
||||
t.Run("GetSessionsWithActiveDeviceIds", func(t *testing.T) { testGetSessionsWithActiveDeviceIds(t, rctx, ss) })
|
||||
t.Run("GetAllSessionsWithActiveDeviceIds", func(t *testing.T) { testGetAllSessionsWithActiveDeviceIds(t, rctx, ss) })
|
||||
t.Run("GetMobileSessionMetadata", func(t *testing.T) { testGetMobileSessionMetadata(t, rctx, ss) })
|
||||
}
|
||||
|
||||
func testSessionStoreSave(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
|
||||
_, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func testSessionGet(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
s2 := &model.Session{}
|
||||
s2.UserId = s1.UserId
|
||||
|
||||
_, err = ss.Session().Save(rctx, s2)
|
||||
require.NoError(t, err)
|
||||
|
||||
s3 := &model.Session{}
|
||||
s3.UserId = s1.UserId
|
||||
s3.ExpiresAt = 1
|
||||
|
||||
_, err = ss.Session().Save(rctx, s3)
|
||||
require.NoError(t, err)
|
||||
|
||||
session, err := ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, session.Id, s1.Id, "should match")
|
||||
|
||||
session.Props[model.SessionPropOs] = "linux"
|
||||
session.Props[model.SessionPropBrowser] = "Chrome"
|
||||
err = ss.Session().UpdateProps(session)
|
||||
require.NoError(t, err)
|
||||
|
||||
session2, err := ss.Session().Get(rctx, session.Id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, session.Props, session2.Props, "should match")
|
||||
|
||||
data, err := ss.Session().GetSessions(rctx, s1.UserId)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, data, 3, "should match len")
|
||||
}
|
||||
|
||||
func testSessionGetWithDeviceId(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
s1.ExpiresAt = model.GetMillis() + 10000
|
||||
s1.Props = model.StringMap{}
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
s2 := &model.Session{}
|
||||
s2.UserId = s1.UserId
|
||||
s2.DeviceId = model.NewId()
|
||||
s2.ExpiresAt = model.GetMillis() + 10000
|
||||
s2.Props = model.StringMap{}
|
||||
|
||||
_, err = ss.Session().Save(rctx, s2)
|
||||
require.NoError(t, err)
|
||||
|
||||
s3 := &model.Session{}
|
||||
s3.UserId = s1.UserId
|
||||
s3.ExpiresAt = 1
|
||||
s3.DeviceId = model.NewId()
|
||||
s3.Props = model.StringMap{}
|
||||
|
||||
_, err = ss.Session().Save(rctx, s3)
|
||||
require.NoError(t, err)
|
||||
|
||||
s4 := &model.Session{}
|
||||
s4.UserId = s1.UserId
|
||||
s4.DeviceId = model.NewId()
|
||||
s4.ExpiresAt = model.GetMillis() + 10000
|
||||
s4.Props = model.StringMap{
|
||||
model.SessionPropLastRemovedDeviceId: s4.DeviceId,
|
||||
}
|
||||
|
||||
_, err = ss.Session().Save(rctx, s4)
|
||||
require.NoError(t, err)
|
||||
|
||||
data, err := ss.Session().GetSessionsWithActiveDeviceIds(s1.UserId)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, data, 1, "should match len")
|
||||
}
|
||||
|
||||
func testSessionRemove(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
session, err := ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, session.Id, s1.Id, "should match")
|
||||
|
||||
removeErr := ss.Session().Remove(s1.Id)
|
||||
require.NoError(t, removeErr)
|
||||
|
||||
_, err = ss.Session().Get(rctx, s1.Id)
|
||||
require.Error(t, err, "should have been removed")
|
||||
}
|
||||
|
||||
func testSessionRemoveAll(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
session, err := ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, session.Id, s1.Id, "should match")
|
||||
|
||||
removeErr := ss.Session().RemoveAllSessions()
|
||||
require.NoError(t, removeErr)
|
||||
|
||||
_, err = ss.Session().Get(rctx, s1.Id)
|
||||
require.Error(t, err, "should have been removed")
|
||||
}
|
||||
|
||||
func testSessionRemoveByUser(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
session, err := ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, session.Id, s1.Id, "should match")
|
||||
|
||||
deleteErr := ss.Session().PermanentDeleteSessionsByUser(s1.UserId)
|
||||
require.NoError(t, deleteErr)
|
||||
|
||||
_, err = ss.Session().Get(rctx, s1.Id)
|
||||
require.Error(t, err, "should have been removed")
|
||||
}
|
||||
|
||||
func testSessionRemoveToken(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
session, err := ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, session.Id, s1.Id, "should match")
|
||||
|
||||
removeErr := ss.Session().Remove(s1.Token)
|
||||
require.NoError(t, removeErr)
|
||||
|
||||
_, err = ss.Session().Get(rctx, s1.Id)
|
||||
require.Error(t, err, "should have been removed")
|
||||
|
||||
data, err := ss.Session().GetSessions(rctx, s1.UserId)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, data, "should match len")
|
||||
}
|
||||
|
||||
func testSessionUpdateDeviceId(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ss.Session().UpdateDeviceId(s1.Id, model.PushNotifyAppleReactNative+":1234567890", "", s1.ExpiresAt)
|
||||
require.NoError(t, err)
|
||||
|
||||
s2 := &model.Session{}
|
||||
s2.UserId = model.NewId()
|
||||
|
||||
s2, err = ss.Session().Save(rctx, s2)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ss.Session().UpdateDeviceId(s2.Id, model.PushNotifyAppleReactNative+":1234567890", "", s1.ExpiresAt)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func testSessionUpdateDeviceId2(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ss.Session().UpdateDeviceId(s1.Id, model.PushNotifyAppleReactNative+":1234567890", model.PushNotifyAppleReactNative+":voiptoken", s1.ExpiresAt)
|
||||
require.NoError(t, err)
|
||||
|
||||
s2 := &model.Session{}
|
||||
s2.UserId = model.NewId()
|
||||
|
||||
s2, err = ss.Session().Save(rctx, s2)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ss.Session().UpdateDeviceId(s2.Id, model.PushNotifyAppleReactNative+":1234567890", "", s1.ExpiresAt)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify both columns round-tripped via the regular Get path.
|
||||
got, err := ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, model.PushNotifyAppleReactNative+":1234567890", got.DeviceId)
|
||||
require.Equal(t, model.PushNotifyAppleReactNative+":voiptoken", got.VoIPDeviceId)
|
||||
}
|
||||
|
||||
func testSessionUpdateDeviceIdClearsOnEmpty(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s := &model.Session{UserId: model.NewId()}
|
||||
s, err := ss.Session().Save(rctx, s)
|
||||
require.NoError(t, err)
|
||||
|
||||
standardToken := model.PushNotifyAppleReactNative + ":standard"
|
||||
voIPToken := model.PushNotifyAppleReactNative + ":voip"
|
||||
err = ss.Session().UpdateDeviceId(s.Id, standardToken, voIPToken, s.ExpiresAt)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ss.Session().UpdateDeviceId(s.Id, "", "", s.ExpiresAt)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := ss.Session().Get(rctx, s.Id)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, got.DeviceId)
|
||||
require.Empty(t, got.VoIPDeviceId)
|
||||
}
|
||||
|
||||
func testSessionStoreUpdateExpiresAt(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ss.Session().UpdateExpiresAt(s1.Id, 1234567890)
|
||||
require.NoError(t, err)
|
||||
|
||||
session, err := ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, session.ExpiresAt, 1234567890, "ExpiresAt not updated correctly")
|
||||
}
|
||||
|
||||
func testSessionStoreUpdateLastActivityAt(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ss.Session().UpdateLastActivityAt(s1.Id, 1234567890)
|
||||
require.NoError(t, err)
|
||||
|
||||
session, err := ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, session.LastActivityAt, 1234567890, "LastActivityAt not updated correctly")
|
||||
}
|
||||
|
||||
func testSessionCount(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
s1.ExpiresAt = model.GetMillis() + 100000
|
||||
|
||||
_, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
count, err := ss.Session().AnalyticsSessionCount()
|
||||
require.NoError(t, err)
|
||||
require.NotZero(t, count, "should have at least 1 session")
|
||||
}
|
||||
|
||||
func testSessionCleanup(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
now := model.GetMillis()
|
||||
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
s1.ExpiresAt = 0 // never expires
|
||||
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
s2 := &model.Session{}
|
||||
s2.UserId = s1.UserId
|
||||
s2.ExpiresAt = now + 1000000 // expires in the future
|
||||
|
||||
s2, err = ss.Session().Save(rctx, s2)
|
||||
require.NoError(t, err)
|
||||
|
||||
s3 := &model.Session{}
|
||||
s3.UserId = model.NewId()
|
||||
s3.ExpiresAt = 1 // expired
|
||||
|
||||
s3, err = ss.Session().Save(rctx, s3)
|
||||
require.NoError(t, err)
|
||||
|
||||
s4 := &model.Session{}
|
||||
s4.UserId = model.NewId()
|
||||
s4.ExpiresAt = 2 // expired
|
||||
|
||||
s4, err = ss.Session().Save(rctx, s4)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ss.Session().Cleanup(now, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = ss.Session().Get(rctx, s1.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = ss.Session().Get(rctx, s2.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = ss.Session().Get(rctx, s3.Id)
|
||||
assert.Error(t, err)
|
||||
|
||||
_, err = ss.Session().Get(rctx, s4.Id)
|
||||
assert.Error(t, err)
|
||||
|
||||
removeErr := ss.Session().Remove(s1.Id)
|
||||
require.NoError(t, removeErr)
|
||||
|
||||
removeErr = ss.Session().Remove(s2.Id)
|
||||
require.NoError(t, removeErr)
|
||||
}
|
||||
|
||||
func testGetSessionsExpired(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
now := model.GetMillis()
|
||||
|
||||
// Clear existing sessions.
|
||||
err := ss.Session().RemoveAllSessions()
|
||||
require.NoError(t, err)
|
||||
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
s1.DeviceId = model.NewId()
|
||||
s1.ExpiresAt = 0 // never expires
|
||||
_, err = ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
s2 := &model.Session{}
|
||||
s2.UserId = model.NewId()
|
||||
s2.DeviceId = model.NewId()
|
||||
s2.ExpiresAt = now - TenMinutes // expired within threshold
|
||||
s2, err = ss.Session().Save(rctx, s2)
|
||||
require.NoError(t, err)
|
||||
|
||||
s3 := &model.Session{}
|
||||
s3.UserId = model.NewId()
|
||||
s3.DeviceId = model.NewId()
|
||||
s3.ExpiresAt = now - (TenMinutes * 100) // expired outside threshold
|
||||
_, err = ss.Session().Save(rctx, s3)
|
||||
require.NoError(t, err)
|
||||
|
||||
s4 := &model.Session{}
|
||||
s4.UserId = model.NewId()
|
||||
s4.ExpiresAt = now - TenMinutes // expired within threshold, but not mobile
|
||||
s4, err = ss.Session().Save(rctx, s4)
|
||||
require.NoError(t, err)
|
||||
|
||||
s5 := &model.Session{}
|
||||
s5.UserId = model.NewId()
|
||||
s5.DeviceId = model.NewId()
|
||||
s5.ExpiresAt = now + (TenMinutes * 100000) // not expired
|
||||
_, err = ss.Session().Save(rctx, s5)
|
||||
require.NoError(t, err)
|
||||
|
||||
sessions, err := ss.Session().GetSessionsExpired(TenMinutes*2, true, true) // mobile only
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sessions, 1)
|
||||
require.Equal(t, s2.Id, sessions[0].Id)
|
||||
|
||||
sessions, err = ss.Session().GetSessionsExpired(TenMinutes*2, false, true) // all client types
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sessions, 2)
|
||||
expected := []string{s2.Id, s4.Id}
|
||||
for _, sess := range sessions {
|
||||
require.Contains(t, expected, sess.Id)
|
||||
}
|
||||
}
|
||||
|
||||
func testGetSessionsWithActiveDeviceIds(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
userId := model.NewId()
|
||||
|
||||
// Create session 1 with a device ID
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = userId
|
||||
s1.ExpiresAt = model.GetMillis() + 100000
|
||||
s1.DeviceId = model.NewId()
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create session 2 with a device ID and a prop for last_removed_device_id that doesn't match the device ID
|
||||
s2 := &model.Session{}
|
||||
s2.UserId = userId
|
||||
s2.ExpiresAt = model.GetMillis() + 100000
|
||||
s2.DeviceId = model.NewId()
|
||||
s2.AddProp(model.SessionPropLastRemovedDeviceId, model.NewId())
|
||||
s2, err = ss.Session().Save(rctx, s2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create session 3 with a device ID and a prop for last_removed_device_id that matches the device ID - this should be filtered out
|
||||
s3 := &model.Session{}
|
||||
s3.UserId = userId
|
||||
s3.ExpiresAt = model.GetMillis() + 100000
|
||||
s3.DeviceId = model.NewId()
|
||||
s3.AddProp(model.SessionPropLastRemovedDeviceId, s3.DeviceId)
|
||||
s3, err = ss.Session().Save(rctx, s3)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create session 4 with no device ID - this should be filtered out
|
||||
s4 := &model.Session{}
|
||||
s4.UserId = userId
|
||||
s4.ExpiresAt = model.GetMillis() + 100000
|
||||
s4, err = ss.Session().Save(rctx, s4)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create session 5 with a device ID but expired - this should be filtered out
|
||||
s5 := &model.Session{}
|
||||
s5.UserId = userId
|
||||
s5.ExpiresAt = model.GetMillis() - 100000
|
||||
s5.DeviceId = model.NewId()
|
||||
s5, err = ss.Session().Save(rctx, s5)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create session 6 with dead standard token but live VoIP token - should be INCLUDED
|
||||
// (the "silence chat, keep ringing" case: alert topic dead, PushKit still alive).
|
||||
s6 := &model.Session{}
|
||||
s6.UserId = userId
|
||||
s6.ExpiresAt = model.GetMillis() + 100000
|
||||
s6.DeviceId = model.NewId()
|
||||
s6.VoIPDeviceId = model.NewId()
|
||||
s6.AddProp(model.SessionPropLastRemovedDeviceId, s6.DeviceId)
|
||||
s6, err = ss.Session().Save(rctx, s6)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create session 7 with live standard + dead VoIP - should be INCLUDED (via standard).
|
||||
s7 := &model.Session{}
|
||||
s7.UserId = userId
|
||||
s7.ExpiresAt = model.GetMillis() + 100000
|
||||
s7.DeviceId = model.NewId()
|
||||
s7.VoIPDeviceId = model.NewId()
|
||||
s7.AddProp(model.SessionPropLastRemovedVoIPDeviceId, s7.VoIPDeviceId)
|
||||
s7, err = ss.Session().Save(rctx, s7)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create session 8 with both tokens dead - should be EXCLUDED.
|
||||
s8 := &model.Session{}
|
||||
s8.UserId = userId
|
||||
s8.ExpiresAt = model.GetMillis() + 100000
|
||||
s8.DeviceId = model.NewId()
|
||||
s8.VoIPDeviceId = model.NewId()
|
||||
s8.AddProp(model.SessionPropLastRemovedDeviceId, s8.DeviceId)
|
||||
s8.AddProp(model.SessionPropLastRemovedVoIPDeviceId, s8.VoIPDeviceId)
|
||||
s8, err = ss.Session().Save(rctx, s8)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create session 9 with VoIP token only (no standard) - should be INCLUDED.
|
||||
s9 := &model.Session{}
|
||||
s9.UserId = userId
|
||||
s9.ExpiresAt = model.GetMillis() + 100000
|
||||
s9.VoIPDeviceId = model.NewId()
|
||||
s9, err = ss.Session().Save(rctx, s9)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get sessions with active device IDs
|
||||
sessions, err := ss.Session().GetSessionsWithActiveDeviceIds(userId)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Expected included: s1, s2, s6, s7, s9.
|
||||
require.Len(t, sessions, 5)
|
||||
|
||||
sessionIds := make(map[string]bool)
|
||||
for _, session := range sessions {
|
||||
sessionIds[session.Id] = true
|
||||
}
|
||||
require.True(t, sessionIds[s1.Id])
|
||||
require.True(t, sessionIds[s2.Id])
|
||||
require.True(t, sessionIds[s6.Id])
|
||||
require.True(t, sessionIds[s7.Id])
|
||||
require.True(t, sessionIds[s9.Id])
|
||||
|
||||
// Excluded: s3 (dead standard, no VoIP), s4 (no device IDs), s5 (expired),
|
||||
// s8 (both dead).
|
||||
require.False(t, sessionIds[s3.Id])
|
||||
require.False(t, sessionIds[s4.Id])
|
||||
require.False(t, sessionIds[s5.Id])
|
||||
require.False(t, sessionIds[s8.Id])
|
||||
}
|
||||
|
||||
func testUpdateExpiredNotify(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
s1.DeviceId = model.NewId()
|
||||
s1.ExpiresAt = model.GetMillis() + TenMinutes
|
||||
s1, err := ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
session, err := ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.False(t, session.ExpiredNotify)
|
||||
|
||||
err = ss.Session().UpdateExpiredNotify(session.Id, true)
|
||||
require.NoError(t, err)
|
||||
session, err = ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.True(t, session.ExpiredNotify)
|
||||
|
||||
err = ss.Session().UpdateExpiredNotify(session.Id, false)
|
||||
require.NoError(t, err)
|
||||
session, err = ss.Session().Get(rctx, s1.Id)
|
||||
require.NoError(t, err)
|
||||
require.False(t, session.ExpiredNotify)
|
||||
}
|
||||
|
||||
func testGetLRUSessions(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
userId := model.NewId()
|
||||
|
||||
// Clear existing sessions.
|
||||
err := ss.Session().RemoveAllSessions()
|
||||
require.NoError(t, err)
|
||||
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = userId
|
||||
s1.DeviceId = model.NewId()
|
||||
_, err = ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
|
||||
s2 := &model.Session{}
|
||||
s2.UserId = userId
|
||||
s2.DeviceId = model.NewId()
|
||||
s2, err = ss.Session().Save(rctx, s2)
|
||||
require.NoError(t, err)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
|
||||
s3 := &model.Session{}
|
||||
s3.UserId = userId
|
||||
s3.DeviceId = model.NewId()
|
||||
s3, err = ss.Session().Save(rctx, s3)
|
||||
require.NoError(t, err)
|
||||
|
||||
sessions, err := ss.Session().GetLRUSessions(rctx, userId, 3, 3)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sessions, 0)
|
||||
|
||||
sessions, err = ss.Session().GetLRUSessions(rctx, userId, 3, 2)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sessions, 1)
|
||||
require.Equal(t, s1.Id, sessions[0].Id)
|
||||
|
||||
sessions, err = ss.Session().GetLRUSessions(rctx, userId, 3, 1)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sessions, 2)
|
||||
require.Equal(t, s2.Id, sessions[0].Id)
|
||||
require.Equal(t, s1.Id, sessions[1].Id)
|
||||
|
||||
sessions, err = ss.Session().GetLRUSessions(rctx, userId, 3, 0)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sessions, 3)
|
||||
require.Equal(t, s3.Id, sessions[0].Id)
|
||||
require.Equal(t, s2.Id, sessions[1].Id)
|
||||
require.Equal(t, s1.Id, sessions[2].Id)
|
||||
}
|
||||
|
||||
func testGetAllSessionsWithActiveDeviceIds(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
err := ss.Session().RemoveAllSessions()
|
||||
require.NoError(t, err)
|
||||
|
||||
userId1 := model.NewId()
|
||||
userId2 := model.NewId()
|
||||
|
||||
s1 := &model.Session{UserId: userId1, ExpiresAt: model.GetMillis() + 100000, DeviceId: model.NewId()}
|
||||
s1, err = ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
s2 := &model.Session{UserId: userId2, ExpiresAt: model.GetMillis() + 100000, DeviceId: model.NewId()}
|
||||
s2, err = ss.Session().Save(rctx, s2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// no device ID — excluded
|
||||
s3 := &model.Session{UserId: userId1, ExpiresAt: model.GetMillis() + 100000}
|
||||
s3, err = ss.Session().Save(rctx, s3)
|
||||
require.NoError(t, err)
|
||||
|
||||
// expired — still included so the wipe reaches devices with stale local data
|
||||
s4 := &model.Session{UserId: userId2, ExpiresAt: model.GetMillis() - 100000, DeviceId: model.NewId()}
|
||||
s4, err = ss.Session().Save(rctx, s4)
|
||||
require.NoError(t, err)
|
||||
|
||||
sessions, err := ss.Session().GetAllSessionsWithActiveDeviceIds()
|
||||
require.NoError(t, err)
|
||||
|
||||
sessionIds := make(map[string]bool, len(sessions))
|
||||
for _, s := range sessions {
|
||||
sessionIds[s.Id] = true
|
||||
}
|
||||
require.True(t, sessionIds[s1.Id], "expected session from user1")
|
||||
require.True(t, sessionIds[s2.Id], "expected session from user2")
|
||||
require.False(t, sessionIds[s3.Id], "expected session without device ID excluded")
|
||||
require.True(t, sessionIds[s4.Id], "expected expired session included for wipe")
|
||||
}
|
||||
|
||||
func testGetMobileSessionMetadata(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
userId1 := model.NewId()
|
||||
userId2 := model.NewId()
|
||||
userId3 := model.NewId()
|
||||
userId4 := model.NewId()
|
||||
userId5 := model.NewId()
|
||||
|
||||
// Clear existing sessions.
|
||||
err := ss.Session().RemoveAllSessions()
|
||||
require.NoError(t, err)
|
||||
|
||||
s1 := &model.Session{}
|
||||
s1.UserId = userId1
|
||||
s1.ExpiresAt = model.GetMillis() + 10000
|
||||
|
||||
_, err = ss.Session().Save(rctx, s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
s2 := &model.Session{}
|
||||
s2.UserId = userId2
|
||||
s2.DeviceId = "android:" + model.NewId()
|
||||
s2.ExpiresAt = model.GetMillis() + 10000
|
||||
s2.Props = model.StringMap{
|
||||
model.SessionPropDeviceNotificationDisabled: "false",
|
||||
model.SessionPropMobileVersion: "1.2.3",
|
||||
}
|
||||
|
||||
_, err = ss.Session().Save(rctx, s2)
|
||||
require.NoError(t, err)
|
||||
|
||||
s3 := &model.Session{}
|
||||
s3.UserId = userId3
|
||||
s3.DeviceId = "ios:" + model.NewId()
|
||||
s3.ExpiresAt = model.GetMillis() + 10000
|
||||
s3.Props = model.StringMap{
|
||||
model.SessionPropDeviceNotificationDisabled: "true",
|
||||
model.SessionPropMobileVersion: "1.2.3",
|
||||
}
|
||||
|
||||
_, err = ss.Session().Save(rctx, s3)
|
||||
require.NoError(t, err)
|
||||
|
||||
s4 := &model.Session{}
|
||||
s4.UserId = userId4
|
||||
s4.DeviceId = "android:" + model.NewId()
|
||||
s4.ExpiresAt = model.GetMillis() + 10000
|
||||
s4.Props = model.StringMap{
|
||||
model.SessionPropDeviceNotificationDisabled: "true",
|
||||
model.SessionPropMobileVersion: "3.2.1",
|
||||
}
|
||||
|
||||
_, err = ss.Session().Save(rctx, s4)
|
||||
require.NoError(t, err)
|
||||
|
||||
s5 := &model.Session{}
|
||||
s5.UserId = userId5
|
||||
s5.DeviceId = "android:" + model.NewId()
|
||||
s5.ExpiresAt = model.GetMillis() + 10000
|
||||
s5.Props = model.StringMap{
|
||||
model.SessionPropDeviceNotificationDisabled: "true",
|
||||
model.SessionPropMobileVersion: "3.2.1",
|
||||
}
|
||||
|
||||
_, err = ss.Session().Save(rctx, s5)
|
||||
require.NoError(t, err)
|
||||
|
||||
metadata, err := ss.Session().GetMobileSessionMetadata()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, metadata, 4)
|
||||
found := false
|
||||
for _, d := range metadata {
|
||||
if d.NotificationDisabled == "true" &&
|
||||
d.Platform == "android" &&
|
||||
d.Version == "3.2.1" {
|
||||
found = true
|
||||
require.Equal(t, float64(2), d.Count)
|
||||
}
|
||||
}
|
||||
require.True(t, found)
|
||||
}
|
||||
Reference in New Issue
Block a user