diff --git a/persistence/sql/persister_session.go b/persistence/sql/persister_session.go index 259b7fcdfb4..8eca79d052b 100644 --- a/persistence/sql/persister_session.go +++ b/persistence/sql/persister_session.go @@ -58,6 +58,7 @@ func (p *Persister) GetSession(ctx context.Context, sid uuid.UUID, expandables s s.Identity = i } + s.Active = s.IsActive() return &s, nil } @@ -77,7 +78,11 @@ func (p *Persister) ListSessions(ctx context.Context, active *bool, paginatorOpt if err := p.Transaction(ctx, func(ctx context.Context, c *pop.Connection) error { q := c.Where("nid = ?", nid) if active != nil { - q = q.Where("active = ?", *active) + if *active { + q.Where("active = ? AND expires_at >= ?", *active, time.Now().UTC()) + } else { + q.Where("(active = ? OR expires_at < ?)", *active, time.Now().UTC()) + } } // if len(expandables) > 0 { @@ -106,6 +111,7 @@ func (p *Persister) ListSessions(ctx context.Context, active *bool, paginatorOpt return err } + sess.Active = sess.IsActive() sess.Identity = i } } @@ -133,7 +139,11 @@ func (p *Persister) ListSessionsByIdentity(ctx context.Context, iID uuid.UUID, a q = q.Where("id != ?", except) } if active != nil { - q = q.Where("active = ?", *active) + if *active { + q.Where("active = ? AND expires_at >= ?", *active, time.Now().UTC()) + } else { + q.Where("(active = ? OR expires_at < ?)", *active, time.Now().UTC()) + } } if len(expandables) > 0 { q = q.Eager(expandables.ToEager()...) @@ -152,12 +162,13 @@ func (p *Persister) ListSessionsByIdentity(ctx context.Context, iID uuid.UUID, a } if expandables.Has(session.ExpandSessionIdentity) { - for _, s := range s { - i, err := p.GetIdentity(ctx, s.IdentityID) - if err != nil { - return err - } + i, err := p.GetIdentity(ctx, iID) + if err != nil { + return sqlcon.HandleError(err) + } + for _, s := range s { + s.Active = s.IsActive() s.Identity = i } } diff --git a/session/handler_test.go b/session/handler_test.go index 9aed7a72887..4e1704adcc3 100644 --- a/session/handler_test.go +++ b/session/handler_test.go @@ -632,6 +632,48 @@ func TestHandlerAdminSessionManagement(t *testing.T) { assert.False(t, session.Active) }) + t.Run("case=session status should be false when session expiry is past", func(t *testing.T) { + client := testhelpers.NewClientWithCookies(t) + + s.ExpiresAt = time.Now().Add(-time.Hour * 1) + require.NoError(t, reg.SessionPersister().UpsertSession(ctx, s)) + + assert.NotEqual(t, uuid.Nil, s.ID) + assert.NotEqual(t, uuid.Nil, s.Identity.ID) + + req, _ := http.NewRequest("GET", ts.URL+"/admin/sessions/"+s.ID.String(), nil) + res, err := client.Do(req) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, res.StatusCode) + + body, err := io.ReadAll(res.Body) + require.NoError(t, err) + assert.Equal(t, "false", gjson.GetBytes(body, "active").String(), "%s", body) + }) + + t.Run("case=session status should be false for inactive identity", func(t *testing.T) { + client := testhelpers.NewClientWithCookies(t) + var s1 *Session + require.NoError(t, faker.FakeData(&s1)) + s1.Active = true + s1.Identity.State = identity.StateInactive + require.NoError(t, reg.Persister().CreateIdentity(ctx, s1.Identity)) + + assert.Equal(t, uuid.Nil, s1.ID) + require.NoError(t, reg.SessionPersister().UpsertSession(ctx, s1)) + assert.NotEqual(t, uuid.Nil, s1.ID) + assert.NotEqual(t, uuid.Nil, s1.Identity.ID) + + req, _ := http.NewRequest("GET", ts.URL+"/admin/sessions/"+s1.ID.String()+"?expand=Identity", nil) + res, err := client.Do(req) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, res.StatusCode) + + body, err := io.ReadAll(res.Body) + require.NoError(t, err) + assert.Equal(t, "false", gjson.GetBytes(body, "active").String(), "%s", body) + }) + req, _ := http.NewRequest("DELETE", ts.URL+"/admin/identities/"+s.Identity.ID.String()+"/sessions", nil) res, err := client.Do(req) require.NoError(t, err) @@ -649,52 +691,6 @@ func TestHandlerAdminSessionManagement(t *testing.T) { }) }) - t.Run("case=session status should be false for inactive identity", func(t *testing.T) { - client := testhelpers.NewClientWithCookies(t) - var s *Session - require.NoError(t, faker.FakeData(&s)) - s.Active = true - s.Identity.State = identity.StateInactive - require.NoError(t, reg.Persister().CreateIdentity(ctx, s.Identity)) - - assert.Equal(t, uuid.Nil, s.ID) - require.NoError(t, reg.SessionPersister().UpsertSession(ctx, s)) - assert.NotEqual(t, uuid.Nil, s.ID) - assert.NotEqual(t, uuid.Nil, s.Identity.ID) - - req, _ := http.NewRequest("GET", ts.URL+"/admin/sessions/"+s.ID.String()+"?expand=Identity", nil) - res, err := client.Do(req) - require.NoError(t, err) - assert.Equal(t, http.StatusOK, res.StatusCode) - - body, err := io.ReadAll(res.Body) - require.NoError(t, err) - assert.Equal(t, "false", gjson.GetBytes(body, "active").String(), "%s", body) - }) - - t.Run("case=session status should be false when session expiry is past", func(t *testing.T) { - client := testhelpers.NewClientWithCookies(t) - var s *Session - require.NoError(t, faker.FakeData(&s)) - s.Active = true - s.ExpiresAt = time.Now().Add(-time.Hour * 1) - require.NoError(t, reg.Persister().CreateIdentity(ctx, s.Identity)) - - assert.Equal(t, uuid.Nil, s.ID) - require.NoError(t, reg.SessionPersister().UpsertSession(ctx, s)) - assert.NotEqual(t, uuid.Nil, s.ID) - assert.NotEqual(t, uuid.Nil, s.Identity.ID) - - req, _ := http.NewRequest("GET", ts.URL+"/admin/sessions/"+s.ID.String(), nil) - res, err := client.Do(req) - require.NoError(t, err) - assert.Equal(t, http.StatusOK, res.StatusCode) - - body, err := io.ReadAll(res.Body) - require.NoError(t, err) - assert.Equal(t, "false", gjson.GetBytes(body, "active").String(), "%s", body) - }) - t.Run("case=should return 400 when bad UUID is sent", func(t *testing.T) { client := testhelpers.NewClientWithCookies(t) @@ -719,8 +715,9 @@ func TestHandlerAdminSessionManagement(t *testing.T) { t.Run("case=should return pagination headers on list response", func(t *testing.T) { client := testhelpers.NewClientWithCookies(t) - i := identity.NewIdentity("") - require.NoError(t, reg.IdentityManager().Create(ctx, i)) + var i *identity.Identity + require.NoError(t, faker.FakeData(&i)) + require.NoError(t, reg.Persister().CreateIdentity(ctx, i)) numSessions := 5 numSessionsActive := 2 @@ -731,78 +728,35 @@ func TestHandlerAdminSessionManagement(t *testing.T) { sess[j].Identity = i if j < numSessionsActive { sess[j].Active = true + sess[j].ExpiresAt = time.Now().Add(time.Hour) } else { sess[j].Active = false + sess[j].ExpiresAt = time.Now().Add(-time.Hour) } require.NoError(t, reg.SessionPersister().UpsertSession(ctx, &sess[j])) } for _, tc := range []struct { activeOnly string - expectedTotalCount int + expectedSessionIds []uuid.UUID }{ { activeOnly: "true", - expectedTotalCount: numSessionsActive, + expectedSessionIds: []uuid.UUID{sess[0].ID, sess[1].ID}, }, { activeOnly: "false", - expectedTotalCount: numSessions - numSessionsActive, + expectedSessionIds: []uuid.UUID{sess[2].ID, sess[3].ID, sess[4].ID}, }, { activeOnly: "", - expectedTotalCount: numSessions, + expectedSessionIds: []uuid.UUID{sess[0].ID, sess[1].ID, sess[2].ID, sess[3].ID, sess[4].ID}, }, } { t.Run(fmt.Sprintf("active=%#v", tc.activeOnly), func(t *testing.T) { - reqURL := ts.URL + "/admin/identities/" + i.ID.String() + "/sessions" - if tc.activeOnly != "" { - reqURL += "?active=" + tc.activeOnly - } - req, _ := http.NewRequest("GET", reqURL, nil) - res, err := client.Do(req) - require.NoError(t, err) - require.Equal(t, http.StatusOK, res.StatusCode) - - totalCount, err := strconv.Atoi(res.Header.Get("X-Total-Count")) - require.NoError(t, err) - require.Equal(t, tc.expectedTotalCount, totalCount) - require.NotEqual(t, "", res.Header.Get("Link")) - }) - } - }) + sessions, _, _ := reg.SessionPersister().ListSessionsByIdentity(ctx, i.ID, nil, 1, 10, uuid.Nil, ExpandEverything) + require.Equal(t, 5, len(sessions)) - t.Run("case=should respect active on list", func(t *testing.T) { - client := testhelpers.NewClientWithCookies(t) - i := identity.NewIdentity("") - require.NoError(t, reg.IdentityManager().Create(ctx, i)) - - sess := make([]Session, 2) - for j := range sess { - require.NoError(t, faker.FakeData(&sess[j])) - sess[j].Identity = i - sess[j].Active = j%2 == 0 - require.NoError(t, reg.SessionPersister().UpsertSession(ctx, &sess[j])) - } - - for _, tc := range []struct { - activeOnly string - expectedIDs []uuid.UUID - }{ - { - activeOnly: "true", - expectedIDs: []uuid.UUID{sess[0].ID}, - }, - { - activeOnly: "false", - expectedIDs: []uuid.UUID{sess[1].ID}, - }, - { - activeOnly: "", - expectedIDs: []uuid.UUID{sess[0].ID, sess[1].ID}, - }, - } { - t.Run(fmt.Sprintf("active=%#v", tc.activeOnly), func(t *testing.T) { reqURL := ts.URL + "/admin/identities/" + i.ID.String() + "/sessions" if tc.activeOnly != "" { reqURL += "?active=" + tc.activeOnly @@ -812,17 +766,18 @@ func TestHandlerAdminSessionManagement(t *testing.T) { require.NoError(t, err) require.Equal(t, http.StatusOK, res.StatusCode) - var sessions []Session - require.NoError(t, json.NewDecoder(res.Body).Decode(&sessions)) - require.Equal(t, len(sessions), len(tc.expectedIDs)) - - for _, id := range tc.expectedIDs { - found := false - for _, s := range sessions { - found = found || s.ID == id - } - assert.True(t, found) + var actualSessions []Session + require.NoError(t, json.NewDecoder(res.Body).Decode(&actualSessions)) + actualSessionIds := make([]uuid.UUID, 0) + for _, s := range actualSessions { + actualSessionIds = append(actualSessionIds, s.ID) } + + totalCount, err := strconv.Atoi(res.Header.Get("X-Total-Count")) + require.NoError(t, err) + assert.Equal(t, len(tc.expectedSessionIds), totalCount) + assert.NotEqual(t, "", res.Header.Get("Link")) + assert.ElementsMatch(t, tc.expectedSessionIds, actualSessionIds) }) } }) diff --git a/session/session.go b/session/session.go index 74edf61a046..fbff6853606 100644 --- a/session/session.go +++ b/session/session.go @@ -153,17 +153,6 @@ func (s Session) TableName(ctx context.Context) string { return "sessions" } -func (s Session) MarshalJSON() ([]byte, error) { - type sl Session - s.Active = s.IsActive() - - result, err := json.Marshal(sl(s)) - if err != nil { - return nil, err - } - return result, nil -} - func (s *Session) CompletedLoginFor(method identity.CredentialsType, aal identity.AuthenticatorAssuranceLevel) { s.AMR = append(s.AMR, AuthenticationMethod{Method: method, AAL: aal, CompletedAt: time.Now().UTC()}) } diff --git a/session/test/persistence.go b/session/test/persistence.go index 22a4e2b9bde..da972b80f75 100644 --- a/session/test/persistence.go +++ b/session/test/persistence.go @@ -113,237 +113,235 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface { }) }) - t.Run("method=listing", func(t *testing.T) { - i := identity.NewIdentity("") - require.NoError(t, p.CreateIdentity(ctx, i)) - sess := make([]session.Session, 4) - for j := range sess { - require.NoError(t, faker.FakeData(&sess[j])) - sess[j].Identity = i - sess[j].Active = j%2 == 0 - - var device session.Device - require.NoError(t, faker.FakeData(&device)) - sess[j].Devices = []session.Device{ - device, - } - require.NoError(t, p.UpsertSession(ctx, &sess[j])) + t.Run("case=update session", func(t *testing.T) { + expected.AuthenticatorAssuranceLevel = identity.AuthenticatorAssuranceLevel3 + require.NoError(t, p.UpsertSession(ctx, &expected)) + + actual, err := p.GetSessionByToken(ctx, expected.Token, session.ExpandDefault) + check(actual, err) + assert.Equal(t, identity.AuthenticatorAssuranceLevel3, actual.AuthenticatorAssuranceLevel) + }) + + t.Run("case=remove amr and update", func(t *testing.T) { + expected.AMR = nil + require.NoError(t, p.UpsertSession(ctx, &expected)) + + actual, err := p.GetSessionByToken(ctx, expected.Token, session.ExpandDefault) + check(actual, err) + assert.Empty(t, actual.AMR) + }) + }) + + t.Run("case=list sessions", func(t *testing.T) { + var identity1 identity.Identity + require.NoError(t, faker.FakeData(&identity1)) + + // Second identity to test listing by identity isolation + var identity2 identity.Identity + var identity2Session session.Session + require.NoError(t, faker.FakeData(&identity2)) + require.NoError(t, faker.FakeData(&identity2Session)) + + // Create seed identities + _, l := testhelpers.NewNetwork(t, ctx, p) + require.NoError(t, l.CreateIdentity(ctx, &identity1)) + require.NoError(t, l.CreateIdentity(ctx, &identity2)) + + seedSessionIDs := make([]uuid.UUID, 5) + seedSessionsList := make([]session.Session, 5) + for j := range seedSessionsList { + require.NoError(t, faker.FakeData(&seedSessionsList[j])) + seedSessionsList[j].Identity = &identity1 + seedSessionsList[j].Active = j%2 == 0 + + if seedSessionsList[j].Active { + seedSessionsList[j].ExpiresAt = time.Now().UTC().Add(time.Hour) + } else { + seedSessionsList[j].ExpiresAt = time.Now().UTC().Add(-time.Hour) } - for _, tc := range []struct { - desc string - except uuid.UUID - expected []session.Session - active *bool - }{ - { - desc: "all", - expected: sess, - }, - { - desc: "except one", - except: sess[0].ID, - expected: []session.Session{ - sess[1], - sess[2], - sess[3], - }, + var device session.Device + require.NoError(t, faker.FakeData(&device)) + seedSessionsList[j].Devices = []session.Device{ + device, + } + require.NoError(t, l.UpsertSession(ctx, &seedSessionsList[j])) + seedSessionIDs[j] = seedSessionsList[j].ID + } + + identity2Session.Identity = &identity2 + identity2Session.Active = true + identity2Session.ExpiresAt = time.Now().UTC().Add(time.Hour) + require.NoError(t, l.UpsertSession(ctx, &identity2Session)) + + for _, tc := range []struct { + desc string + except uuid.UUID + expectedSessionIds []uuid.UUID + active *bool + }{ + { + desc: "all", + expectedSessionIds: seedSessionIDs, + }, + { + desc: "except one", + except: seedSessionsList[0].ID, + expectedSessionIds: []uuid.UUID{ + seedSessionIDs[1], + seedSessionIDs[2], + seedSessionIDs[3], + seedSessionIDs[4], }, - { - desc: "active only", - active: pointerx.Bool(true), - expected: []session.Session{ - sess[0], - sess[2], - }, + }, + { + desc: "active only", + active: pointerx.Bool(true), + expectedSessionIds: []uuid.UUID{ + seedSessionIDs[0], + seedSessionIDs[2], + seedSessionIDs[4], }, - { - desc: "active only and except", - active: pointerx.Bool(true), - except: sess[0].ID, - expected: []session.Session{ - sess[2], - }, + }, + { + desc: "active only and except", + active: pointerx.Bool(true), + except: seedSessionsList[0].ID, + expectedSessionIds: []uuid.UUID{ + seedSessionIDs[2], + seedSessionIDs[4], }, - { - desc: "inactive only", - active: pointerx.Bool(false), - expected: []session.Session{ - sess[1], - sess[3], - }, + }, + { + desc: "inactive only", + active: pointerx.Bool(false), + expectedSessionIds: []uuid.UUID{ + seedSessionIDs[1], + seedSessionIDs[3], }, - { - desc: "inactive only and except", - active: pointerx.Bool(false), - except: sess[3].ID, - expected: []session.Session{ - sess[1], - }, + }, + { + desc: "inactive only and except", + active: pointerx.Bool(false), + except: seedSessionsList[3].ID, + expectedSessionIds: []uuid.UUID{ + seedSessionIDs[1], }, - } { - t.Run("case=ListSessionsByIdentity "+tc.desc, func(t *testing.T) { - actual, total, err := p.ListSessionsByIdentity(ctx, i.ID, tc.active, 1, 10, tc.except, session.ExpandEverything) - require.NoError(t, err) - - require.Equal(t, len(tc.expected), len(actual)) - require.Equal(t, int64(len(tc.expected)), total) - for _, es := range tc.expected { - found := false - for _, as := range actual { - if as.ID == es.ID { - found = true - assert.Equal(t, len(es.Devices), len(as.Devices)) - } - } - assert.True(t, found) - } - }) - } - - t.Run("case=ListSessionsByIdentity - other network", func(t *testing.T) { - _, other := testhelpers.NewNetwork(t, ctx, p) - actual, total, err := other.ListSessionsByIdentity(ctx, i.ID, nil, 1, 10, uuid.Nil, session.ExpandNothing) + }, + } { + t.Run("case=by Identity "+tc.desc, func(t *testing.T) { + actual, total, err := l.ListSessionsByIdentity(ctx, identity1.ID, tc.active, 1, 10, tc.except, session.ExpandEverything) require.NoError(t, err) - require.Equal(t, int64(0), total) - assert.Len(t, actual, 0) + + actualSessionIds := make([]uuid.UUID, 0) + for _, s := range actual { + actualSessionIds = append(actualSessionIds, s.ID) + } + + assert.Equal(t, int64(len(tc.expectedSessionIds)), total) + assert.ElementsMatch(t, tc.expectedSessionIds, actualSessionIds) }) + } - for _, tc := range []struct { - desc string - except uuid.UUID - expected []session.Session - active *bool - }{ - { - desc: "all", - expected: append(sess, expected), - }, - { - desc: "active only", - active: pointerx.Bool(true), - expected: []session.Session{ - expected, - sess[0], - sess[2], - }, + t.Run("case=by Identity on other network", func(t *testing.T) { + _, other := testhelpers.NewNetwork(t, ctx, p) + actual, total, err := other.ListSessionsByIdentity(ctx, identity1.ID, nil, 1, 10, uuid.Nil, session.ExpandNothing) + require.NoError(t, err) + require.Equal(t, int64(0), total) + assert.Len(t, actual, 0) + }) + + for _, tc := range []struct { + desc string + except uuid.UUID + expected []session.Session + active *bool + }{ + { + desc: "all", + expected: append(seedSessionsList, identity2Session), + }, + { + desc: "active only", + active: pointerx.Bool(true), + expected: []session.Session{ + seedSessionsList[0], + seedSessionsList[2], + seedSessionsList[4], + identity2Session, }, - { - desc: "inactive only", - active: pointerx.Bool(false), - expected: []session.Session{ - sess[1], - sess[3], - }, + }, + { + desc: "inactive only", + active: pointerx.Bool(false), + expected: []session.Session{ + seedSessionsList[1], + seedSessionsList[3], }, - } { - t.Run("case=ListSessions "+tc.desc, func(t *testing.T) { - paginatorOpts := make([]keysetpagination.Option, 0) - actual, total, nextPage, err := p.ListSessions(ctx, tc.active, paginatorOpts, session.ExpandEverything) - require.NoError(t, err) - - require.Equal(t, len(tc.expected), len(actual)) - require.Equal(t, int64(len(tc.expected)), total) - assert.Equal(t, true, nextPage.IsLast()) - assert.Equal(t, uuid.Nil.String(), nextPage.Token().Encode()) - assert.Equal(t, 250, nextPage.Size()) - for _, es := range tc.expected { - found := false - for _, as := range actual { - if as.ID == es.ID { - found = true - assert.Equal(t, len(es.Devices), len(as.Devices)) - assert.Equal(t, es.Identity.ID.String(), as.Identity.ID.String()) - } - } - assert.True(t, found) - } - }) - } - - t.Run("case=ListSessions last page", func(t *testing.T) { + }, + } { + t.Run("case=all "+tc.desc, func(t *testing.T) { paginatorOpts := make([]keysetpagination.Option, 0) - actual, total, page, err := p.ListSessions(ctx, nil, paginatorOpts, session.ExpandEverything) + actual, total, nextPage, err := l.ListSessions(ctx, tc.active, paginatorOpts, session.ExpandEverything) require.NoError(t, err) - require.Equal(t, 5, len(actual)) - require.Equal(t, int64(5), total) - assert.Equal(t, true, page.IsLast()) - assert.Equal(t, uuid.Nil.String(), page.Token().Encode()) - assert.Equal(t, 250, page.Size()) - }) - - t.Run("case=ListSessions page iteration", func(t *testing.T) { - - }) - - t.Run("case=ListSessions - other network", func(t *testing.T) { - var identity1 identity.Identity - require.NoError(t, faker.FakeData(&identity1)) - - _, other := testhelpers.NewNetwork(t, ctx, p) - require.NoError(t, other.CreateIdentity(ctx, &identity1)) - - expectedIDs := make([]uuid.UUID, 5) - seedSessionsList := make([]session.Session, 5) - for j := range seedSessionsList { - require.NoError(t, faker.FakeData(&seedSessionsList[j])) - seedSessionsList[j].Identity = &identity1 - seedSessionsList[j].Active = j%2 == 0 - - var device session.Device - require.NoError(t, faker.FakeData(&device)) - seedSessionsList[j].Devices = []session.Device{ - device, + require.Equal(t, len(tc.expected), len(actual)) + require.Equal(t, int64(len(tc.expected)), total) + assert.Equal(t, true, nextPage.IsLast()) + assert.Equal(t, uuid.Nil.String(), nextPage.Token().Encode()) + assert.Equal(t, 250, nextPage.Size()) + for _, es := range tc.expected { + found := false + for _, as := range actual { + if as.ID == es.ID { + found = true + assert.Equal(t, len(es.Devices), len(as.Devices)) + assert.Equal(t, es.Identity.ID.String(), as.Identity.ID.String()) + } } - require.NoError(t, other.UpsertSession(ctx, &seedSessionsList[j])) - expectedIDs[j] = seedSessionsList[j].ID + assert.True(t, found) } + }) + } - paginatorOpts := make([]keysetpagination.Option, 0) - paginatorOpts = append(paginatorOpts, keysetpagination.WithSize(3)) - firstPageItems, total, page1, err := other.ListSessions(ctx, nil, paginatorOpts, session.ExpandEverything) - require.NoError(t, err) - require.Equal(t, int64(5), total) - assert.Len(t, firstPageItems, 3) - - assert.Equal(t, false, page1.IsLast()) - assert.Equal(t, firstPageItems[len(firstPageItems)-1].ID.String(), page1.Token().Encode()) - assert.Equal(t, 3, page1.Size()) - - // Validate secondPageItems page - secondPageItems, total, page2, err := other.ListSessions(ctx, nil, page1.ToOptions(), session.ExpandEverything) - require.NoError(t, err) - - acutalIDs := make([]uuid.UUID, 0) - for _, s := range append(firstPageItems, secondPageItems...) { - acutalIDs = append(acutalIDs, s.ID) - } - assert.ElementsMatch(t, expectedIDs, acutalIDs) + t.Run("case=all sessions pagination only one page", func(t *testing.T) { + paginatorOpts := make([]keysetpagination.Option, 0) + actual, total, page, err := l.ListSessions(ctx, nil, paginatorOpts, session.ExpandEverything) + require.NoError(t, err) - require.Equal(t, int64(5), total) - assert.Len(t, secondPageItems, 2) - assert.True(t, page2.IsLast()) - assert.Equal(t, 3, page2.Size()) - }) + require.Equal(t, 6, len(actual)) + require.Equal(t, int64(6), total) + assert.Equal(t, true, page.IsLast()) + assert.Equal(t, uuid.Nil.String(), page.Token().Encode()) + assert.Equal(t, 250, page.Size()) }) - t.Run("case=update session", func(t *testing.T) { - expected.AuthenticatorAssuranceLevel = identity.AuthenticatorAssuranceLevel3 - require.NoError(t, p.UpsertSession(ctx, &expected)) + t.Run("case=all sessions pagination multiple pages", func(t *testing.T) { + paginatorOpts := make([]keysetpagination.Option, 0) + paginatorOpts = append(paginatorOpts, keysetpagination.WithSize(3)) + firstPageItems, total, page1, err := l.ListSessions(ctx, nil, paginatorOpts, session.ExpandEverything) + require.NoError(t, err) + require.Equal(t, int64(6), total) + assert.Len(t, firstPageItems, 3) - actual, err := p.GetSessionByToken(ctx, expected.Token, session.ExpandDefault) - check(actual, err) - assert.Equal(t, identity.AuthenticatorAssuranceLevel3, actual.AuthenticatorAssuranceLevel) - }) + assert.Equal(t, false, page1.IsLast()) + assert.Equal(t, firstPageItems[len(firstPageItems)-1].ID.String(), page1.Token().Encode()) + assert.Equal(t, 3, page1.Size()) - t.Run("case=remove amr and update", func(t *testing.T) { - expected.AMR = nil - require.NoError(t, p.UpsertSession(ctx, &expected)) + // Validate secondPageItems page + secondPageItems, total, page2, err := l.ListSessions(ctx, nil, page1.ToOptions(), session.ExpandEverything) + require.NoError(t, err) - actual, err := p.GetSessionByToken(ctx, expected.Token, session.ExpandDefault) - check(actual, err) - assert.Empty(t, actual.AMR) + acutalIDs := make([]uuid.UUID, 0) + for _, s := range append(firstPageItems, secondPageItems...) { + acutalIDs = append(acutalIDs, s.ID) + } + assert.ElementsMatch(t, append(seedSessionIDs, identity2Session.ID), acutalIDs) + + require.Equal(t, int64(6), total) + assert.Len(t, secondPageItems, 3) + assert.True(t, page2.IsLast()) + assert.Equal(t, 3, page2.Size()) }) })