|
|
@@ -1,4 +1,4 @@
|
|
|
-package state
|
|
|
+package webapi
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
@@ -11,37 +11,37 @@ import (
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
- "github.com/mk6i/open-oscar-server/server/webapi/types"
|
|
|
+ "github.com/mk6i/open-oscar-server/state"
|
|
|
"github.com/mk6i/open-oscar-server/wire"
|
|
|
)
|
|
|
|
|
|
-func TestWebAPISession_TempBuddies(t *testing.T) {
|
|
|
+func TestSession_TempBuddies(t *testing.T) {
|
|
|
tests := []struct {
|
|
|
name string
|
|
|
- setupSession func() *WebAPISession
|
|
|
- operations func(*WebAPISession)
|
|
|
- expectedChecks func(*testing.T, *WebAPISession)
|
|
|
+ setupSession func() *Session
|
|
|
+ operations func(*Session)
|
|
|
+ expectedChecks func(*testing.T, *Session)
|
|
|
}{
|
|
|
{
|
|
|
name: "Initialize_NilTempBuddies",
|
|
|
- setupSession: func() *WebAPISession {
|
|
|
- return &WebAPISession{
|
|
|
+ setupSession: func() *Session {
|
|
|
+ return &Session{
|
|
|
AimSID: "test-session",
|
|
|
- ScreenName: DisplayScreenName("testuser"),
|
|
|
- EventQueue: types.NewEventQueue(100),
|
|
|
+ ScreenName: state.DisplayScreenName("testuser"),
|
|
|
+ EventQueue: NewEventQueue(100),
|
|
|
CreatedAt: time.Now(),
|
|
|
LastAccessed: time.Now(),
|
|
|
ExpiresAt: time.Now().Add(time.Hour),
|
|
|
}
|
|
|
},
|
|
|
- operations: func(s *WebAPISession) {
|
|
|
+ operations: func(s *Session) {
|
|
|
// Initialize TempBuddies if nil
|
|
|
if s.TempBuddies == nil {
|
|
|
s.TempBuddies = make(map[string]bool)
|
|
|
}
|
|
|
s.TempBuddies["buddy1"] = true
|
|
|
},
|
|
|
- expectedChecks: func(t *testing.T, s *WebAPISession) {
|
|
|
+ expectedChecks: func(t *testing.T, s *Session) {
|
|
|
assert.NotNil(t, s.TempBuddies)
|
|
|
assert.True(t, s.TempBuddies["buddy1"])
|
|
|
assert.Equal(t, 1, len(s.TempBuddies))
|
|
|
@@ -49,23 +49,23 @@ func TestWebAPISession_TempBuddies(t *testing.T) {
|
|
|
},
|
|
|
{
|
|
|
name: "Add_MultipleTempBuddies",
|
|
|
- setupSession: func() *WebAPISession {
|
|
|
- return &WebAPISession{
|
|
|
+ setupSession: func() *Session {
|
|
|
+ return &Session{
|
|
|
AimSID: "test-session",
|
|
|
- ScreenName: DisplayScreenName("testuser"),
|
|
|
+ ScreenName: state.DisplayScreenName("testuser"),
|
|
|
TempBuddies: make(map[string]bool),
|
|
|
- EventQueue: types.NewEventQueue(100),
|
|
|
+ EventQueue: NewEventQueue(100),
|
|
|
CreatedAt: time.Now(),
|
|
|
LastAccessed: time.Now(),
|
|
|
ExpiresAt: time.Now().Add(time.Hour),
|
|
|
}
|
|
|
},
|
|
|
- operations: func(s *WebAPISession) {
|
|
|
+ operations: func(s *Session) {
|
|
|
s.TempBuddies["buddy1"] = true
|
|
|
s.TempBuddies["buddy2"] = true
|
|
|
s.TempBuddies["buddy3"] = true
|
|
|
},
|
|
|
- expectedChecks: func(t *testing.T, s *WebAPISession) {
|
|
|
+ expectedChecks: func(t *testing.T, s *Session) {
|
|
|
assert.Equal(t, 3, len(s.TempBuddies))
|
|
|
assert.True(t, s.TempBuddies["buddy1"])
|
|
|
assert.True(t, s.TempBuddies["buddy2"])
|
|
|
@@ -74,22 +74,22 @@ func TestWebAPISession_TempBuddies(t *testing.T) {
|
|
|
},
|
|
|
{
|
|
|
name: "Add_DuplicateTempBuddy",
|
|
|
- setupSession: func() *WebAPISession {
|
|
|
- return &WebAPISession{
|
|
|
+ setupSession: func() *Session {
|
|
|
+ return &Session{
|
|
|
AimSID: "test-session",
|
|
|
- ScreenName: DisplayScreenName("testuser"),
|
|
|
+ ScreenName: state.DisplayScreenName("testuser"),
|
|
|
TempBuddies: map[string]bool{"buddy1": true},
|
|
|
- EventQueue: types.NewEventQueue(100),
|
|
|
+ EventQueue: NewEventQueue(100),
|
|
|
CreatedAt: time.Now(),
|
|
|
LastAccessed: time.Now(),
|
|
|
ExpiresAt: time.Now().Add(time.Hour),
|
|
|
}
|
|
|
},
|
|
|
- operations: func(s *WebAPISession) {
|
|
|
+ operations: func(s *Session) {
|
|
|
// Add the same buddy again
|
|
|
s.TempBuddies["buddy1"] = true
|
|
|
},
|
|
|
- expectedChecks: func(t *testing.T, s *WebAPISession) {
|
|
|
+ expectedChecks: func(t *testing.T, s *Session) {
|
|
|
// Should still only have one entry
|
|
|
assert.Equal(t, 1, len(s.TempBuddies))
|
|
|
assert.True(t, s.TempBuddies["buddy1"])
|
|
|
@@ -97,24 +97,24 @@ func TestWebAPISession_TempBuddies(t *testing.T) {
|
|
|
},
|
|
|
{
|
|
|
name: "Remove_TempBuddy",
|
|
|
- setupSession: func() *WebAPISession {
|
|
|
- return &WebAPISession{
|
|
|
+ setupSession: func() *Session {
|
|
|
+ return &Session{
|
|
|
AimSID: "test-session",
|
|
|
- ScreenName: DisplayScreenName("testuser"),
|
|
|
+ ScreenName: state.DisplayScreenName("testuser"),
|
|
|
TempBuddies: map[string]bool{
|
|
|
"buddy1": true,
|
|
|
"buddy2": true,
|
|
|
},
|
|
|
- EventQueue: types.NewEventQueue(100),
|
|
|
+ EventQueue: NewEventQueue(100),
|
|
|
CreatedAt: time.Now(),
|
|
|
LastAccessed: time.Now(),
|
|
|
ExpiresAt: time.Now().Add(time.Hour),
|
|
|
}
|
|
|
},
|
|
|
- operations: func(s *WebAPISession) {
|
|
|
+ operations: func(s *Session) {
|
|
|
delete(s.TempBuddies, "buddy1")
|
|
|
},
|
|
|
- expectedChecks: func(t *testing.T, s *WebAPISession) {
|
|
|
+ expectedChecks: func(t *testing.T, s *Session) {
|
|
|
assert.Equal(t, 1, len(s.TempBuddies))
|
|
|
assert.False(t, s.TempBuddies["buddy1"])
|
|
|
assert.True(t, s.TempBuddies["buddy2"])
|
|
|
@@ -122,21 +122,21 @@ func TestWebAPISession_TempBuddies(t *testing.T) {
|
|
|
},
|
|
|
{
|
|
|
name: "Check_NonExistentBuddy",
|
|
|
- setupSession: func() *WebAPISession {
|
|
|
- return &WebAPISession{
|
|
|
+ setupSession: func() *Session {
|
|
|
+ return &Session{
|
|
|
AimSID: "test-session",
|
|
|
- ScreenName: DisplayScreenName("testuser"),
|
|
|
+ ScreenName: state.DisplayScreenName("testuser"),
|
|
|
TempBuddies: map[string]bool{"buddy1": true},
|
|
|
- EventQueue: types.NewEventQueue(100),
|
|
|
+ EventQueue: NewEventQueue(100),
|
|
|
CreatedAt: time.Now(),
|
|
|
LastAccessed: time.Now(),
|
|
|
ExpiresAt: time.Now().Add(time.Hour),
|
|
|
}
|
|
|
},
|
|
|
- operations: func(s *WebAPISession) {
|
|
|
+ operations: func(s *Session) {
|
|
|
// No operations, just checking
|
|
|
},
|
|
|
- expectedChecks: func(t *testing.T, s *WebAPISession) {
|
|
|
+ expectedChecks: func(t *testing.T, s *Session) {
|
|
|
assert.False(t, s.TempBuddies["nonexistent"])
|
|
|
assert.True(t, s.TempBuddies["buddy1"])
|
|
|
},
|
|
|
@@ -157,7 +157,7 @@ func TestWebAPISession_TempBuddies(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func TestWebAPISession_IsExpired(t *testing.T) {
|
|
|
+func TestSession_IsExpired(t *testing.T) {
|
|
|
tests := []struct {
|
|
|
name string
|
|
|
expiresAt time.Time
|
|
|
@@ -182,9 +182,9 @@ func TestWebAPISession_IsExpired(t *testing.T) {
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
- session := &WebAPISession{
|
|
|
+ session := &Session{
|
|
|
AimSID: "test-session",
|
|
|
- ScreenName: DisplayScreenName("testuser"),
|
|
|
+ ScreenName: state.DisplayScreenName("testuser"),
|
|
|
ExpiresAt: tt.expiresAt,
|
|
|
}
|
|
|
|
|
|
@@ -193,12 +193,12 @@ func TestWebAPISession_IsExpired(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func TestWebAPISession_WithTempBuddiesIntegration(t *testing.T) {
|
|
|
+func TestSession_WithTempBuddiesIntegration(t *testing.T) {
|
|
|
// Test that temp buddies work correctly with a full session
|
|
|
- session := &WebAPISession{
|
|
|
+ session := &Session{
|
|
|
AimSID: "integration-test",
|
|
|
- ScreenName: DisplayScreenName("testuser"),
|
|
|
- EventQueue: types.NewEventQueue(100),
|
|
|
+ ScreenName: state.DisplayScreenName("testuser"),
|
|
|
+ EventQueue: NewEventQueue(100),
|
|
|
TempBuddies: nil,
|
|
|
CreatedAt: time.Now(),
|
|
|
LastAccessed: time.Now(),
|
|
|
@@ -234,18 +234,18 @@ func TestWebAPISession_WithTempBuddiesIntegration(t *testing.T) {
|
|
|
assert.True(t, session.TempBuddies["charlie"])
|
|
|
}
|
|
|
|
|
|
-func TestWebAPISession_TempBuddiesIndependence(t *testing.T) {
|
|
|
+func TestSession_TempBuddiesIndependence(t *testing.T) {
|
|
|
// Test that temp buddies are independent across sessions
|
|
|
- session1 := &WebAPISession{
|
|
|
+ session1 := &Session{
|
|
|
AimSID: "session1",
|
|
|
- ScreenName: DisplayScreenName("user1"),
|
|
|
+ ScreenName: state.DisplayScreenName("user1"),
|
|
|
TempBuddies: map[string]bool{"buddy1": true},
|
|
|
ExpiresAt: time.Now().Add(time.Hour),
|
|
|
}
|
|
|
|
|
|
- session2 := &WebAPISession{
|
|
|
+ session2 := &Session{
|
|
|
AimSID: "session2",
|
|
|
- ScreenName: DisplayScreenName("user2"),
|
|
|
+ ScreenName: state.DisplayScreenName("user2"),
|
|
|
TempBuddies: map[string]bool{"buddy2": true},
|
|
|
ExpiresAt: time.Now().Add(time.Hour),
|
|
|
}
|
|
|
@@ -265,11 +265,11 @@ func TestWebAPISession_TempBuddiesIndependence(t *testing.T) {
|
|
|
assert.False(t, session2.TempBuddies["buddy3"])
|
|
|
}
|
|
|
|
|
|
-// TestWebAPISessionManager_ShutdownIdempotent verifies Shutdown is safe to call
|
|
|
+// TestSessionManager_ShutdownIdempotent verifies Shutdown is safe to call
|
|
|
// more than once (e.g. from overlapping shutdown paths): the closed flag makes
|
|
|
// the second call a no-op instead of re-draining.
|
|
|
-func TestWebAPISessionManager_ShutdownIdempotent(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
+func TestSessionManager_ShutdownIdempotent(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
|
|
|
_ = mgr.Shutdown(context.Background())
|
|
|
|
|
|
@@ -278,15 +278,15 @@ func TestWebAPISessionManager_ShutdownIdempotent(t *testing.T) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-// TestWebAPISessionManager_CreateAfterShutdown verifies that a session cannot be
|
|
|
+// TestSessionManager_CreateAfterShutdown verifies that a session cannot be
|
|
|
// created once the manager is shut down. Otherwise the reaper is stopped and the
|
|
|
// session would never be closed or reaped, leaking its OSCAR session.
|
|
|
-func TestWebAPISessionManager_CreateAfterShutdown(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
+func TestSessionManager_CreateAfterShutdown(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
|
|
|
_ = mgr.Shutdown(context.Background())
|
|
|
|
|
|
- sess, err := mgr.CreateSession(DisplayScreenName("testuser"), "dev", []string{"presence"}, nil, "", nil)
|
|
|
+ sess, err := mgr.CreateSession(state.DisplayScreenName("testuser"), "dev", []string{"presence"}, nil, "", nil)
|
|
|
assert.Nil(t, sess)
|
|
|
assert.ErrorIs(t, err, ErrWebAPISessionManagerClosed)
|
|
|
}
|
|
|
@@ -294,13 +294,13 @@ func TestWebAPISessionManager_CreateAfterShutdown(t *testing.T) {
|
|
|
// A broadcast rate limit SNAC surfaces to the client only for the IM class: the
|
|
|
// web client renders any rateLimit event as the conversation-window alert. Code 1
|
|
|
// (a class-params change) is not a status transition and is dropped.
|
|
|
-func TestWebAPISession_handleRateLimitUpdate(t *testing.T) {
|
|
|
+func TestSession_handleRateLimitUpdate(t *testing.T) {
|
|
|
const imClass = wire.RateLimitClassID(3)
|
|
|
|
|
|
- newSession := func() *WebAPISession {
|
|
|
- return &WebAPISession{
|
|
|
+ newSession := func() *Session {
|
|
|
+ return &Session{
|
|
|
IMRateClassID: imClass,
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
}
|
|
|
}
|
|
|
@@ -319,8 +319,8 @@ func TestWebAPISession_handleRateLimitUpdate(t *testing.T) {
|
|
|
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 2)
|
|
|
- assert.Equal(t, "limit", events[0].Data.(types.RateLimitEvent).Classes[0].Status)
|
|
|
- assert.Equal(t, "clear", events[1].Data.(types.RateLimitEvent).Classes[0].Status)
|
|
|
+ assert.Equal(t, "limit", events[0].Data.(RateLimitEvent).Classes[0].Status)
|
|
|
+ assert.Equal(t, "clear", events[1].Data.(RateLimitEvent).Classes[0].Status)
|
|
|
})
|
|
|
|
|
|
t.Run("other classes and non-status codes are ignored", func(t *testing.T) {
|
|
|
@@ -346,8 +346,8 @@ func TestWebAPISession_handleRateLimitUpdate(t *testing.T) {
|
|
|
// requests against a dead session (and, downstream, spam clear events on every
|
|
|
// one of them). Once the aimsid is turned away at RequireSession, neither is
|
|
|
// possible.
|
|
|
-func TestWebAPISessionManager_GetSession_rejectsAfterRateLimitDisconnect(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
+func TestSessionManager_GetSession_rejectsAfterRateLimitDisconnect(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
|
|
|
// A rate class that escalates to disconnect after a short back-to-back burst.
|
|
|
var classes [5]wire.RateClass
|
|
|
@@ -362,10 +362,10 @@ func TestWebAPISessionManager_GetSession_rejectsAfterRateLimitDisconnect(t *test
|
|
|
MaxLevel: 200,
|
|
|
}
|
|
|
}
|
|
|
- inst := NewSession().AddInstance()
|
|
|
+ inst := state.NewSession().AddInstance()
|
|
|
inst.Session().SetRateClasses(time.Now(), wire.NewRateLimitClasses(classes))
|
|
|
|
|
|
- sess, err := mgr.CreateSession(DisplayScreenName("advbot"), "dev", []string{"presence"}, inst, "", slog.Default())
|
|
|
+ sess, err := mgr.CreateSession(state.DisplayScreenName("advbot"), "dev", []string{"presence"}, inst, "", slog.Default())
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
// Healthy session resolves.
|
|
|
@@ -395,19 +395,19 @@ func TestWebAPISessionManager_GetSession_rejectsAfterRateLimitDisconnect(t *test
|
|
|
assert.NotContains(t, mgr.sessions, sess.AimSID)
|
|
|
}
|
|
|
|
|
|
-// TestWebAPISessionManager_ShutdownDrainsAndClosesSessions verifies that Shutdown
|
|
|
+// TestSessionManager_ShutdownDrainsAndClosesSessions verifies that Shutdown
|
|
|
// collects every live session and tears it down: it drains the maps and closes
|
|
|
// each session's event queue and OSCAR instance.
|
|
|
-func TestWebAPISessionManager_ShutdownDrainsAndClosesSessions(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
+func TestSessionManager_ShutdownDrainsAndClosesSessions(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
ctx := context.Background()
|
|
|
|
|
|
- inst1 := NewSession().AddInstance()
|
|
|
- inst2 := NewSession().AddInstance()
|
|
|
+ inst1 := state.NewSession().AddInstance()
|
|
|
+ inst2 := state.NewSession().AddInstance()
|
|
|
|
|
|
- s1, err := mgr.CreateSession(DisplayScreenName("alice"), "dev", []string{"presence"}, inst1, "", slog.Default())
|
|
|
+ s1, err := mgr.CreateSession(state.DisplayScreenName("alice"), "dev", []string{"presence"}, inst1, "", slog.Default())
|
|
|
assert.NoError(t, err)
|
|
|
- s2, err := mgr.CreateSession(DisplayScreenName("bob"), "dev", []string{"presence"}, inst2, "", slog.Default())
|
|
|
+ s2, err := mgr.CreateSession(state.DisplayScreenName("bob"), "dev", []string{"presence"}, inst2, "", slog.Default())
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
assert.NoError(t, mgr.Shutdown(context.Background()))
|
|
|
@@ -417,10 +417,10 @@ func TestWebAPISessionManager_ShutdownDrainsAndClosesSessions(t *testing.T) {
|
|
|
|
|
|
// Each session's event queue and OSCAR instance were closed: the teardown
|
|
|
// loop ran for every collected session.
|
|
|
- for _, s := range []*WebAPISession{s1, s2} {
|
|
|
+ for _, s := range []*Session{s1, s2} {
|
|
|
assertQueueClosed(t, ctx, s)
|
|
|
}
|
|
|
- for _, inst := range []*SessionInstance{inst1, inst2} {
|
|
|
+ for _, inst := range []*state.SessionInstance{inst1, inst2} {
|
|
|
select {
|
|
|
case <-inst.Closed():
|
|
|
default:
|
|
|
@@ -429,14 +429,14 @@ func TestWebAPISessionManager_ShutdownDrainsAndClosesSessions(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// TestWebAPISessionManager_ReapExpired verifies reapExpired removes and tears
|
|
|
+// TestSessionManager_ReapExpired verifies reapExpired removes and tears
|
|
|
// down only expired sessions, leaving live ones untouched.
|
|
|
-func TestWebAPISessionManager_ReapExpired(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
+func TestSessionManager_ReapExpired(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
ctx := context.Background()
|
|
|
|
|
|
- expiredInst := NewSession().AddInstance()
|
|
|
- liveInst := NewSession().AddInstance()
|
|
|
+ expiredInst := state.NewSession().AddInstance()
|
|
|
+ liveInst := state.NewSession().AddInstance()
|
|
|
|
|
|
expired, err := mgr.CreateSession("alice", "dev", []string{"presence"}, expiredInst, "", slog.Default())
|
|
|
assert.NoError(t, err)
|
|
|
@@ -470,7 +470,7 @@ func TestWebAPISessionManager_ReapExpired(t *testing.T) {
|
|
|
|
|
|
// assertQueueClosed asserts the session's event queue is closed: a fetch returns
|
|
|
// straight away with no events and no error, rather than parking for the timeout.
|
|
|
-func assertQueueClosed(t *testing.T, ctx context.Context, sess *WebAPISession) {
|
|
|
+func assertQueueClosed(t *testing.T, ctx context.Context, sess *Session) {
|
|
|
t.Helper()
|
|
|
|
|
|
const timeout = 5 * time.Second
|
|
|
@@ -482,11 +482,11 @@ func assertQueueClosed(t *testing.T, ctx context.Context, sess *WebAPISession) {
|
|
|
assert.Less(t, time.Since(start), timeout/2, "fetch parked instead of returning on a closed queue")
|
|
|
}
|
|
|
|
|
|
-// TestWebAPISessionManager_ShutdownWithoutReaper verifies Shutdown returns when no
|
|
|
+// TestSessionManager_ShutdownWithoutReaper verifies Shutdown returns when no
|
|
|
// reaper was ever started. Shutdown must not depend on the caller cancelling the
|
|
|
// context passed to Run.
|
|
|
-func TestWebAPISessionManager_ShutdownWithoutReaper(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
+func TestSessionManager_ShutdownWithoutReaper(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
|
|
|
done := make(chan struct{})
|
|
|
go func() {
|
|
|
@@ -502,10 +502,10 @@ func TestWebAPISessionManager_ShutdownWithoutReaper(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// TestWebAPISessionManager_ShutdownJoinsReaper verifies Shutdown stops a running
|
|
|
+// TestSessionManager_ShutdownJoinsReaper verifies Shutdown stops a running
|
|
|
// reaper on its own and does not return until that reaper has exited.
|
|
|
-func TestWebAPISessionManager_ShutdownJoinsReaper(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
+func TestSessionManager_ShutdownJoinsReaper(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
|
|
|
reaperExited := make(chan struct{})
|
|
|
go func() {
|
|
|
@@ -537,10 +537,10 @@ func TestWebAPISessionManager_ShutdownJoinsReaper(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// TestWebAPISessionManager_RunAfterShutdown verifies a reaper that loses the race
|
|
|
+// TestSessionManager_RunAfterShutdown verifies a reaper that loses the race
|
|
|
// with Shutdown never starts, so it cannot reap an already-drained manager.
|
|
|
-func TestWebAPISessionManager_RunAfterShutdown(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
+func TestSessionManager_RunAfterShutdown(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
assert.NoError(t, mgr.Shutdown(context.Background()))
|
|
|
|
|
|
done := make(chan struct{})
|
|
|
@@ -559,12 +559,12 @@ func TestWebAPISessionManager_RunAfterShutdown(t *testing.T) {
|
|
|
// The client deletes the alias it holds each time it merges a user map, so every
|
|
|
// event naming a buddy has to repeat it. An incoming IM and a presence change both
|
|
|
// carry a user map, and both would otherwise rename an aliased buddy.
|
|
|
-func TestWebAPISession_RepeatsBuddyAliasOnOSCAREvents(t *testing.T) {
|
|
|
- newSession := func() *WebAPISession {
|
|
|
- return &WebAPISession{
|
|
|
- ScreenName: DisplayScreenName("me"),
|
|
|
+func TestSession_RepeatsBuddyAliasOnOSCAREvents(t *testing.T) {
|
|
|
+ newSession := func() *Session {
|
|
|
+ return &Session{
|
|
|
+ ScreenName: state.DisplayScreenName("me"),
|
|
|
Events: []string{"im", "conversation", "presence"},
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
BuddyAliasLoader: func(_ context.Context) (map[string]string, error) {
|
|
|
return map[string]string{"mikekelly": "MICHAELKELLY"}, nil
|
|
|
@@ -586,7 +586,7 @@ func TestWebAPISession_RepeatsBuddyAliasOnOSCAREvents(t *testing.T) {
|
|
|
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.NotEmpty(t, events)
|
|
|
- imEvent := events[0].Data.(types.IMEvent)
|
|
|
+ imEvent := events[0].Data.(IMEvent)
|
|
|
assert.Equal(t, "mikekelly", imEvent.Source.AimID)
|
|
|
assert.Equal(t, "Mike Kelly", imEvent.Source.DisplayID)
|
|
|
assert.Equal(t, "MICHAELKELLY", imEvent.Source.Friendly)
|
|
|
@@ -600,7 +600,7 @@ func TestWebAPISession_RepeatsBuddyAliasOnOSCAREvents(t *testing.T) {
|
|
|
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 1)
|
|
|
- presence := events[0].Data.(types.PresenceEvent)
|
|
|
+ presence := events[0].Data.(PresenceEvent)
|
|
|
assert.Equal(t, "mikekelly", presence.AimID)
|
|
|
assert.Equal(t, "MICHAELKELLY", presence.Friendly)
|
|
|
})
|
|
|
@@ -613,7 +613,7 @@ func TestWebAPISession_RepeatsBuddyAliasOnOSCAREvents(t *testing.T) {
|
|
|
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 1)
|
|
|
- presence := events[0].Data.(types.PresenceEvent)
|
|
|
+ presence := events[0].Data.(PresenceEvent)
|
|
|
assert.Equal(t, "mikekelly", presence.AimID)
|
|
|
assert.Equal(t, "MICHAELKELLY", presence.Friendly)
|
|
|
})
|
|
|
@@ -626,18 +626,18 @@ func TestWebAPISession_RepeatsBuddyAliasOnOSCAREvents(t *testing.T) {
|
|
|
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 1)
|
|
|
- assert.Empty(t, events[0].Data.(types.PresenceEvent).Friendly)
|
|
|
+ assert.Empty(t, events[0].Data.(PresenceEvent).Friendly)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// Aliases all come from one feedbag query, so a signon that brings a whole buddy
|
|
|
// list online must not re-query the feedbag per buddy.
|
|
|
-func TestWebAPISession_CachesBuddyAliases(t *testing.T) {
|
|
|
+func TestSession_CachesBuddyAliases(t *testing.T) {
|
|
|
var loads int
|
|
|
- sess := &WebAPISession{
|
|
|
- ScreenName: DisplayScreenName("me"),
|
|
|
+ sess := &Session{
|
|
|
+ ScreenName: state.DisplayScreenName("me"),
|
|
|
Events: []string{"presence"},
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
BuddyAliasLoader: func(_ context.Context) (map[string]string, error) {
|
|
|
loads++
|
|
|
@@ -654,32 +654,32 @@ func TestWebAPISession_CachesBuddyAliases(t *testing.T) {
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 5)
|
|
|
for _, event := range events {
|
|
|
- assert.Equal(t, "MICHAELKELLY", event.Data.(types.PresenceEvent).Friendly)
|
|
|
+ assert.Equal(t, "MICHAELKELLY", event.Data.(PresenceEvent).Friendly)
|
|
|
}
|
|
|
assert.Equal(t, 1, loads, "aliases should be loaded once, not once per event")
|
|
|
}
|
|
|
|
|
|
// A feedbag change from another of the owner's clients arrives as a SNAC, which is
|
|
|
// the session's only signal that its cached aliases are stale.
|
|
|
-func TestWebAPISession_FeedbagSNACInvalidatesAliasCache(t *testing.T) {
|
|
|
+func TestSession_FeedbagSNACInvalidatesAliasCache(t *testing.T) {
|
|
|
alias := "MICHAELKELLY"
|
|
|
- sess := &WebAPISession{
|
|
|
- ScreenName: DisplayScreenName("me"),
|
|
|
+ sess := &Session{
|
|
|
+ ScreenName: state.DisplayScreenName("me"),
|
|
|
Events: []string{"presence"},
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
BuddyAliasLoader: func(_ context.Context) (map[string]string, error) {
|
|
|
return map[string]string{"mikekelly": alias}, nil
|
|
|
},
|
|
|
}
|
|
|
|
|
|
- arrive := func() types.PresenceEvent {
|
|
|
+ arrive := func() PresenceEvent {
|
|
|
sess.handleBuddyArrived(wire.SNACMessage{Body: wire.SNAC_0x03_0x0B_BuddyArrived{
|
|
|
TLVUserInfo: wire.TLVUserInfo{ScreenName: "Mike Kelly"},
|
|
|
}})
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.NotEmpty(t, events)
|
|
|
- return events[len(events)-1].Data.(types.PresenceEvent)
|
|
|
+ return events[len(events)-1].Data.(PresenceEvent)
|
|
|
}
|
|
|
|
|
|
assert.Equal(t, "MICHAELKELLY", arrive().Friendly)
|
|
|
@@ -697,7 +697,7 @@ func TestWebAPISession_FeedbagSNACInvalidatesAliasCache(t *testing.T) {
|
|
|
// Permit/deny changes from another of the owner's clients arrive as an insert,
|
|
|
// an update, or a delete, and all three have to refresh the client's privacy
|
|
|
// state.
|
|
|
-func TestWebAPISession_FeedbagSNACRefreshesPermitDeny(t *testing.T) {
|
|
|
+func TestSession_FeedbagSNACRefreshesPermitDeny(t *testing.T) {
|
|
|
denyItem := wire.FeedbagItem{ClassID: wire.FeedbagClassIDDeny, Name: "blockeduser"}
|
|
|
buddyItem := wire.FeedbagItem{ClassID: wire.FeedbagClassIdBuddy, Name: "friend"}
|
|
|
|
|
|
@@ -735,9 +735,9 @@ func TestWebAPISession_FeedbagSNACRefreshesPermitDeny(t *testing.T) {
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
- sess := &WebAPISession{
|
|
|
- ScreenName: DisplayScreenName("me"),
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ sess := &Session{
|
|
|
+ ScreenName: state.DisplayScreenName("me"),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
PermitDenyRefresher: func(_ context.Context) (interface{}, error) {
|
|
|
return map[string]any{"pdMode": "denySome"}, nil
|
|
|
@@ -751,7 +751,7 @@ func TestWebAPISession_FeedbagSNACRefreshesPermitDeny(t *testing.T) {
|
|
|
|
|
|
var got int
|
|
|
for _, event := range sess.EventQueue.GetAllEvents() {
|
|
|
- if event.Type == types.EventTypePermitDeny {
|
|
|
+ if event.Type == EventTypePermitDeny {
|
|
|
got++
|
|
|
}
|
|
|
}
|
|
|
@@ -766,9 +766,9 @@ func TestWebAPISession_FeedbagSNACRefreshesPermitDeny(t *testing.T) {
|
|
|
|
|
|
// A session sees no SNAC for feedbag writes it makes itself, so the handlers that
|
|
|
// perform those writes invalidate the cache directly.
|
|
|
-func TestWebAPISession_InvalidateAliases(t *testing.T) {
|
|
|
+func TestSession_InvalidateAliases(t *testing.T) {
|
|
|
alias := "MICHAELKELLY"
|
|
|
- sess := &WebAPISession{
|
|
|
+ sess := &Session{
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
BuddyAliasLoader: func(_ context.Context) (map[string]string, error) {
|
|
|
return map[string]string{"mikekelly": alias}, nil
|
|
|
@@ -786,9 +786,9 @@ func TestWebAPISession_InvalidateAliases(t *testing.T) {
|
|
|
|
|
|
// A failed load must not be cached as an empty map: aliases would stay missing for
|
|
|
// the life of the session.
|
|
|
-func TestWebAPISession_AliasLoadErrorIsNotCached(t *testing.T) {
|
|
|
+func TestSession_AliasLoadErrorIsNotCached(t *testing.T) {
|
|
|
var loads int
|
|
|
- sess := &WebAPISession{
|
|
|
+ sess := &Session{
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
BuddyAliasLoader: func(_ context.Context) (map[string]string, error) {
|
|
|
loads++
|
|
|
@@ -803,11 +803,11 @@ func TestWebAPISession_AliasLoadErrorIsNotCached(t *testing.T) {
|
|
|
assert.Equal(t, "MICHAELKELLY", sess.Aliases(context.Background())["mikekelly"])
|
|
|
}
|
|
|
|
|
|
-func TestWebAPISession_HandleIncomingIM_NormalizesAimID(t *testing.T) {
|
|
|
- sess := &WebAPISession{
|
|
|
- ScreenName: DisplayScreenName("me"),
|
|
|
+func TestSession_HandleIncomingIM_NormalizesAimID(t *testing.T) {
|
|
|
+ sess := &Session{
|
|
|
+ ScreenName: state.DisplayScreenName("me"),
|
|
|
Events: []string{"im", "conversation"},
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
}
|
|
|
|
|
|
@@ -825,11 +825,11 @@ func TestWebAPISession_HandleIncomingIM_NormalizesAimID(t *testing.T) {
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 2)
|
|
|
|
|
|
- imEvent := events[0].Data.(types.IMEvent)
|
|
|
+ imEvent := events[0].Data.(IMEvent)
|
|
|
assert.Equal(t, "mikekelly", imEvent.Source.AimID)
|
|
|
assert.Equal(t, "Mike Kelly", imEvent.Source.DisplayID)
|
|
|
|
|
|
- convData := events[1].Data.(*types.ConversationData)
|
|
|
+ convData := events[1].Data.(*ConversationData)
|
|
|
require.Len(t, convData.Conversations, 1)
|
|
|
entry := convData.Conversations[0]
|
|
|
assert.Equal(t, "mikekelly", entry.AimID)
|
|
|
@@ -844,10 +844,10 @@ func TestWebAPISession_HandleIncomingIM_NormalizesAimID(t *testing.T) {
|
|
|
assert.Equal(t, "hello", msgs[0].Message)
|
|
|
}
|
|
|
|
|
|
-func TestWebAPISession_HandleTypingNotification_NormalizesAimID(t *testing.T) {
|
|
|
- sess := &WebAPISession{
|
|
|
+func TestSession_HandleTypingNotification_NormalizesAimID(t *testing.T) {
|
|
|
+ sess := &Session{
|
|
|
Events: []string{"typing"},
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
}
|
|
|
|
|
|
sess.handleTypingNotification(wire.SNACMessage{
|
|
|
@@ -859,15 +859,15 @@ func TestWebAPISession_HandleTypingNotification_NormalizesAimID(t *testing.T) {
|
|
|
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 1)
|
|
|
- typing := events[0].Data.(types.TypingEvent)
|
|
|
+ typing := events[0].Data.(TypingEvent)
|
|
|
assert.Equal(t, "mikekelly", typing.AimID)
|
|
|
assert.Equal(t, "typing", typing.TypingStatus)
|
|
|
}
|
|
|
|
|
|
-func TestWebAPISession_HandleBuddyArrivedDeparted_NormalizesAimID(t *testing.T) {
|
|
|
- sess := &WebAPISession{
|
|
|
+func TestSession_HandleBuddyArrivedDeparted_NormalizesAimID(t *testing.T) {
|
|
|
+ sess := &Session{
|
|
|
Events: []string{"presence"},
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
}
|
|
|
|
|
|
sess.handleBuddyArrived(wire.SNACMessage{
|
|
|
@@ -884,11 +884,11 @@ func TestWebAPISession_HandleBuddyArrivedDeparted_NormalizesAimID(t *testing.T)
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 2)
|
|
|
|
|
|
- arrived := events[0].Data.(types.PresenceEvent)
|
|
|
+ arrived := events[0].Data.(PresenceEvent)
|
|
|
assert.Equal(t, "mikekelly", arrived.AimID)
|
|
|
assert.Equal(t, "online", arrived.State)
|
|
|
|
|
|
- departed := events[1].Data.(types.PresenceEvent)
|
|
|
+ departed := events[1].Data.(PresenceEvent)
|
|
|
assert.Equal(t, "mikekelly", departed.AimID)
|
|
|
assert.Equal(t, "offline", departed.State)
|
|
|
}
|
|
|
@@ -897,14 +897,14 @@ func TestWebAPISession_HandleBuddyArrivedDeparted_NormalizesAimID(t *testing.T)
|
|
|
// rides along on the presence broadcast and must reach the presence event. The
|
|
|
// stub BuddyIconURL stands in for the handlers-side URL formatter, which state
|
|
|
// cannot import.
|
|
|
-func TestWebAPISession_PublishesBuddyIconOnPresence(t *testing.T) {
|
|
|
- newSession := func() *WebAPISession {
|
|
|
- return &WebAPISession{
|
|
|
- ScreenName: DisplayScreenName("me"),
|
|
|
+func TestSession_PublishesBuddyIconOnPresence(t *testing.T) {
|
|
|
+ newSession := func() *Session {
|
|
|
+ return &Session{
|
|
|
+ ScreenName: state.DisplayScreenName("me"),
|
|
|
Events: []string{"presence"},
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
- BuddyIconURL: func(sn IdentScreenName, hash []byte) string {
|
|
|
+ BuddyIconURL: func(sn state.IdentScreenName, hash []byte) string {
|
|
|
if len(hash) == 0 {
|
|
|
return "placeholder:" + sn.String()
|
|
|
}
|
|
|
@@ -913,7 +913,7 @@ func TestWebAPISession_PublishesBuddyIconOnPresence(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- arrived := func(sess *WebAPISession, screenName string, hash []byte) {
|
|
|
+ arrived := func(sess *Session, screenName string, hash []byte) {
|
|
|
info := wire.TLVUserInfo{ScreenName: screenName}
|
|
|
if hash != nil {
|
|
|
info.Append(wire.NewTLVBE(wire.OServiceUserInfoBARTInfo, wire.BARTID{
|
|
|
@@ -924,10 +924,10 @@ func TestWebAPISession_PublishesBuddyIconOnPresence(t *testing.T) {
|
|
|
sess.handleBuddyArrived(wire.SNACMessage{Body: wire.SNAC_0x03_0x0B_BuddyArrived{TLVUserInfo: info}})
|
|
|
}
|
|
|
|
|
|
- lastPresence := func(sess *WebAPISession) types.PresenceEvent {
|
|
|
+ lastPresence := func(sess *Session) PresenceEvent {
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 1)
|
|
|
- return events[0].Data.(types.PresenceEvent)
|
|
|
+ return events[0].Data.(PresenceEvent)
|
|
|
}
|
|
|
|
|
|
t.Run("icon hash yields the content-addressed URL", func(t *testing.T) {
|
|
|
@@ -966,13 +966,13 @@ func TestWebAPISession_PublishesBuddyIconOnPresence(t *testing.T) {
|
|
|
|
|
|
// A user's own icon change is relayed to their session as OServiceUserInfoUpdate,
|
|
|
// which the pump turns into a myInfo event so the identity badge re-renders.
|
|
|
-func TestWebAPISession_PushesMyInfoOnUserInfoUpdate(t *testing.T) {
|
|
|
- newSession := func(events ...string) (*WebAPISession, *int) {
|
|
|
+func TestSession_PushesMyInfoOnUserInfoUpdate(t *testing.T) {
|
|
|
+ newSession := func(events ...string) (*Session, *int) {
|
|
|
var refreshes int
|
|
|
- return &WebAPISession{
|
|
|
- ScreenName: DisplayScreenName("me"),
|
|
|
+ return &Session{
|
|
|
+ ScreenName: state.DisplayScreenName("me"),
|
|
|
Events: events,
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
MyInfoRefresher: func(_ context.Context) (interface{}, error) {
|
|
|
refreshes++
|
|
|
@@ -1021,15 +1021,15 @@ func TestWebAPISession_PushesMyInfoOnUserInfoUpdate(t *testing.T) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-// TestWebAPISessionManager_ShutdownBoundedByContext verifies that Shutdown
|
|
|
+// TestSessionManager_ShutdownBoundedByContext verifies that Shutdown
|
|
|
// honors its context instead of blocking indefinitely. A listener goroutine that
|
|
|
// ignores cancellation must not be able to hold the whole server open: main
|
|
|
// budgets a few seconds for every server's shutdown combined, so an unbounded
|
|
|
// wait here means the process never exits.
|
|
|
-func TestWebAPISessionManager_ShutdownBoundedByContext(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
+func TestSessionManager_ShutdownBoundedByContext(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
|
|
|
- inst := NewSession().AddInstance()
|
|
|
+ inst := state.NewSession().AddInstance()
|
|
|
sess, err := mgr.CreateSession("alice", "dev", []string{"presence"}, inst, "", slog.Default())
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
@@ -1053,13 +1053,13 @@ func TestWebAPISessionManager_ShutdownBoundedByContext(t *testing.T) {
|
|
|
assert.Less(t, elapsed, 2*time.Second, "Shutdown must give up at its deadline, not wait on the stuck listener")
|
|
|
}
|
|
|
|
|
|
-// TestWebAPISession_CloseCancelsSessionContext verifies that Close cancels the
|
|
|
+// TestSession_CloseCancelsSessionContext verifies that Close cancels the
|
|
|
// context handed to the refresher callbacks. The listener runs feedbag queries
|
|
|
// through it, and without cancellation Close's wait lasts as long as the query.
|
|
|
-func TestWebAPISession_CloseCancelsSessionContext(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
+func TestSession_CloseCancelsSessionContext(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
|
|
|
- inst := NewSession().AddInstance()
|
|
|
+ inst := state.NewSession().AddInstance()
|
|
|
sess, err := mgr.CreateSession("alice", "dev", []string{"presence"}, inst, "", slog.Default())
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
@@ -1074,14 +1074,14 @@ func TestWebAPISession_CloseCancelsSessionContext(t *testing.T) {
|
|
|
// ICBMChannelMsgToClient stamped with a send time. The client models that as its
|
|
|
// own offlineIM event, keyed by a bare aimId and timestamped when the sender sent
|
|
|
// it rather than when it was delivered.
|
|
|
-func TestWebAPISession_OfflineIM(t *testing.T) {
|
|
|
+func TestSession_OfflineIM(t *testing.T) {
|
|
|
sentAt := time.Now().Add(-2 * time.Hour).Unix()
|
|
|
|
|
|
- newSession := func(events ...string) *WebAPISession {
|
|
|
- return &WebAPISession{
|
|
|
- ScreenName: DisplayScreenName("me"),
|
|
|
+ newSession := func(events ...string) *Session {
|
|
|
+ return &Session{
|
|
|
+ ScreenName: state.DisplayScreenName("me"),
|
|
|
Events: events,
|
|
|
- EventQueue: types.NewEventQueue(10),
|
|
|
+ EventQueue: NewEventQueue(10),
|
|
|
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
|
}
|
|
|
}
|
|
|
@@ -1107,8 +1107,8 @@ func TestWebAPISession_OfflineIM(t *testing.T) {
|
|
|
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 1)
|
|
|
- assert.Equal(t, types.EventTypeOfflineIM, events[0].Type)
|
|
|
- offline := events[0].Data.(types.OfflineIMEvent)
|
|
|
+ assert.Equal(t, EventTypeOfflineIM, events[0].Type)
|
|
|
+ offline := events[0].Data.(OfflineIMEvent)
|
|
|
assert.Equal(t, "mikekelly", offline.AimID)
|
|
|
assert.Equal(t, "sent while you were out", offline.Message)
|
|
|
assert.NotEmpty(t, offline.MsgID)
|
|
|
@@ -1121,7 +1121,7 @@ func TestWebAPISession_OfflineIM(t *testing.T) {
|
|
|
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 1)
|
|
|
- assert.Equal(t, types.EventTypeIM, events[0].Type)
|
|
|
+ assert.Equal(t, EventTypeIM, events[0].Type)
|
|
|
})
|
|
|
|
|
|
t.Run("offlineIM subscriber gets a conversation update", func(t *testing.T) {
|
|
|
@@ -1130,8 +1130,8 @@ func TestWebAPISession_OfflineIM(t *testing.T) {
|
|
|
|
|
|
events := sess.EventQueue.GetAllEvents()
|
|
|
require.Len(t, events, 2)
|
|
|
- assert.Equal(t, types.EventTypeOfflineIM, events[0].Type)
|
|
|
- assert.Equal(t, types.EventTypeConversation, events[1].Type)
|
|
|
+ assert.Equal(t, EventTypeOfflineIM, events[0].Type)
|
|
|
+ assert.Equal(t, EventTypeConversation, events[1].Type)
|
|
|
})
|
|
|
|
|
|
// The history the client pulls with fetchStoredIMs has to order the message by
|
|
|
@@ -1161,17 +1161,17 @@ func TestWebAPISession_OfflineIM(t *testing.T) {
|
|
|
// client is parked on a long poll at that moment, so it must be released with a
|
|
|
// sessionEnded event rather than left to hang until the reaper's next sweep —
|
|
|
// which measured 26-28s against a running server.
|
|
|
-func TestWebAPISession_BootReleasesParkedFetcherWithSessionEnded(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
- inst := NewSession().AddInstance()
|
|
|
+func TestSession_BootReleasesParkedFetcherWithSessionEnded(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
+ inst := state.NewSession().AddInstance()
|
|
|
|
|
|
- sess, err := mgr.CreateSession(DisplayScreenName("mike"), "dev", []string{"presence"}, inst, "", slog.Default())
|
|
|
+ sess, err := mgr.CreateSession(state.DisplayScreenName("mike"), "dev", []string{"presence"}, inst, "", slog.Default())
|
|
|
require.NoError(t, err)
|
|
|
sess.StartListeningToOSCARSession()
|
|
|
|
|
|
// Park a fetcher the way fetchEvents does, with nothing pending.
|
|
|
type result struct {
|
|
|
- events []types.Event
|
|
|
+ events []Event
|
|
|
err error
|
|
|
}
|
|
|
done := make(chan result, 1)
|
|
|
@@ -1189,7 +1189,7 @@ func TestWebAPISession_BootReleasesParkedFetcherWithSessionEnded(t *testing.T) {
|
|
|
case got := <-done:
|
|
|
require.NoError(t, got.err)
|
|
|
require.Len(t, got.events, 1)
|
|
|
- assert.Equal(t, types.EventTypeSessionEnded, got.events[0].Type)
|
|
|
+ assert.Equal(t, EventTypeSessionEnded, got.events[0].Type)
|
|
|
case <-time.After(5 * time.Second):
|
|
|
t.Fatal("parked fetcher was not released by the boot")
|
|
|
}
|
|
|
@@ -1198,11 +1198,11 @@ func TestWebAPISession_BootReleasesParkedFetcherWithSessionEnded(t *testing.T) {
|
|
|
// A session tearing itself down — endSession, or the idle reaper — needs no
|
|
|
// sessionEnded event: the client already knows it is leaving. Close closes the
|
|
|
// queue before the instance, so the listener's push lands on a closed queue.
|
|
|
-func TestWebAPISession_SelfCloseEmitsNoSessionEndedEvent(t *testing.T) {
|
|
|
- mgr := NewWebAPISessionManager()
|
|
|
- inst := NewSession().AddInstance()
|
|
|
+func TestSession_SelfCloseEmitsNoSessionEndedEvent(t *testing.T) {
|
|
|
+ mgr := NewSessionManager()
|
|
|
+ inst := state.NewSession().AddInstance()
|
|
|
|
|
|
- sess, err := mgr.CreateSession(DisplayScreenName("mike"), "dev", []string{"presence"}, inst, "", slog.Default())
|
|
|
+ sess, err := mgr.CreateSession(state.DisplayScreenName("mike"), "dev", []string{"presence"}, inst, "", slog.Default())
|
|
|
require.NoError(t, err)
|
|
|
sess.StartListeningToOSCARSession()
|
|
|
|
|
|
@@ -1212,3 +1212,43 @@ func TestWebAPISession_SelfCloseEmitsNoSessionEndedEvent(t *testing.T) {
|
|
|
require.NoError(t, err)
|
|
|
assert.Empty(t, events)
|
|
|
}
|
|
|
+
|
|
|
+func TestSession_GetStoredIMs(t *testing.T) {
|
|
|
+ sess := &Session{}
|
|
|
+ sess.AddStoredIM("buddy1", "me", "hello", "msg-1", 100)
|
|
|
+ sess.AddStoredIM("buddy1", "buddy1", "hi back", "msg-2", 200)
|
|
|
+ sess.AddStoredIM("buddy2", "buddy2", "other chat", "msg-3", 150)
|
|
|
+
|
|
|
+ msgs := sess.GetStoredIMs(StoredIMQuery{
|
|
|
+ PartnerAimID: "buddy1",
|
|
|
+ SortOrder: "descendingDate",
|
|
|
+ NToGet: 10,
|
|
|
+ })
|
|
|
+ assert.Len(t, msgs, 2)
|
|
|
+ assert.Equal(t, "msg-2", msgs[0].MsgID)
|
|
|
+ assert.Equal(t, float64(200), msgs[0].Date)
|
|
|
+ assert.Equal(t, "hello", msgs[1].Message)
|
|
|
+
|
|
|
+ msgs = sess.GetStoredIMs(StoredIMQuery{
|
|
|
+ PartnerAimID: "buddy1",
|
|
|
+ SortOrder: "ascendingDate",
|
|
|
+ StartTime: 150,
|
|
|
+ EndTime: 250,
|
|
|
+ })
|
|
|
+ assert.Len(t, msgs, 1)
|
|
|
+ assert.Equal(t, "msg-2", msgs[0].MsgID)
|
|
|
+}
|
|
|
+
|
|
|
+func TestSession_GetStoredIMs_NormalizesPartner(t *testing.T) {
|
|
|
+ sess := &Session{}
|
|
|
+ sess.AddStoredIM("Mike Kelly", "mikekelly", "hello", "msg-1", 100)
|
|
|
+
|
|
|
+ // The web client queries history by the normalized aimId, never by the
|
|
|
+ // display screen name it was stored under.
|
|
|
+ msgs := sess.GetStoredIMs(StoredIMQuery{
|
|
|
+ PartnerAimID: "mikekelly",
|
|
|
+ NToGet: 10,
|
|
|
+ })
|
|
|
+ require.Len(t, msgs, 1)
|
|
|
+ assert.Equal(t, "msg-1", msgs[0].MsgID)
|
|
|
+}
|