Files
QSfera/Server/services/proxy/pkg/staticroutes/internal/backchannellogout/backchannellogout_test.go
T
Курнат Андрей 2315f25754 Initial QSfera import
2026-06-07 10:20:04 +03:00

307 lines
8.0 KiB
Go

package backchannellogout
import (
"slices"
"strings"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go-micro.dev/v4/store"
"github.com/qsfera/server/services/proxy/pkg/staticroutes/internal/backchannellogout/mocks"
)
func mustNewKey(t *testing.T, subject, session string) string {
key, err := NewKey(subject, session)
require.NoError(t, err)
return key
}
func mustNewSuSe(t *testing.T, subject, session string) SuSe {
suse, err := NewSuSe(mustNewKey(t, subject, session))
require.NoError(t, err)
return suse
}
func TestNewKey(t *testing.T) {
tests := []struct {
name string
subject string
session string
wantKey string
wantErr error
}{
{
name: "key variation: 'subject.session'",
subject: "subject",
session: "session",
wantKey: "c3ViamVjdA==.c2Vzc2lvbg==",
},
{
name: "key variation: 'subject.'",
subject: "subject",
wantKey: "c3ViamVjdA==.",
},
{
name: "key variation: '.session'",
session: "session",
wantKey: ".c2Vzc2lvbg==",
},
{
name: "key variation: '.'",
wantErr: ErrInvalidKey,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
key, err := NewKey(tt.subject, tt.session)
require.ErrorIs(t, err, tt.wantErr)
require.Equal(t, tt.wantKey, key)
})
}
}
func TestNewSuSe(t *testing.T) {
tests := []struct {
name string
key string
wantSubject string
wantSession string
wantMode LogoutMode
wantErr error
}{
{
name: "key variation: '.session'",
key: mustNewKey(t, "", "session"),
wantSession: "session",
wantMode: LogoutModeSession,
},
{
name: "key variation: 'session'",
key: mustNewKey(t, "", "session"),
wantSession: "session",
wantMode: LogoutModeSession,
},
{
name: "key variation: 'subject.'",
key: mustNewKey(t, "subject", ""),
wantSubject: "subject",
wantMode: LogoutModeSubject,
},
{
name: "key variation: 'subject.session'",
key: mustNewKey(t, "subject", "session"),
wantSubject: "subject",
wantSession: "session",
wantMode: LogoutModeSession,
},
{
name: "key variation: 'dot'",
key: ".",
wantErr: ErrInvalidSubjectOrSession,
},
{
name: "key variation: 'empty'",
key: "",
wantErr: ErrInvalidSubjectOrSession,
},
{
name: "key variation: string('subject.session')",
key: "subject.session",
wantErr: ErrInvalidSubjectOrSession,
wantMode: LogoutModeSession,
},
{
name: "key variation: string('subject.')",
key: "subject.",
wantErr: ErrInvalidSubjectOrSession,
wantMode: LogoutModeSubject,
},
{
name: "key variation: string('.session')",
key: ".session",
wantErr: ErrInvalidSubjectOrSession,
wantMode: LogoutModeSession,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
suSe, err := NewSuSe(tt.key)
require.ErrorIs(t, err, tt.wantErr)
mode := suSe.Mode()
require.Equal(t, tt.wantMode, mode)
subject, _ := suSe.Subject()
require.Equal(t, tt.wantSubject, subject)
session, _ := suSe.Session()
require.Equal(t, tt.wantSession, session)
})
}
}
func TestGetLogoutRecords(t *testing.T) {
sessionStore := store.NewMemoryStore()
recordClaimA := &store.Record{Key: "claim-a", Value: []byte("claim-a-data")}
recordClaimB := &store.Record{Key: "claim-b", Value: []byte("claim-b-data")}
recordClaimC := &store.Record{Key: "claim-c", Value: []byte("claim-c-data")}
recordClaimD := &store.Record{Key: "claim-d", Value: []byte("claim-d-data")}
recordSessionA := &store.Record{Key: mustNewKey(t, "", "session-a"), Value: []byte(recordClaimA.Key)}
recordSessionB := &store.Record{Key: mustNewKey(t, "", "session-b"), Value: []byte(recordClaimB.Key)}
recordSubjectASessionC := &store.Record{Key: mustNewKey(t, "subject-a", "session-c"), Value: []byte(recordSessionA.Key)}
recordSubjectASessionD := &store.Record{Key: mustNewKey(t, "subject-a", "session-d"), Value: []byte(recordSessionA.Key)}
for _, r := range []*store.Record{
recordClaimA,
recordClaimB,
recordClaimC,
recordClaimD,
recordSessionA,
recordSessionB,
recordSubjectASessionC,
recordSubjectASessionD,
} {
require.NoError(t, sessionStore.Write(r))
}
tests := []struct {
name string
suSe SuSe
store func(t *testing.T) store.Store
wantRecords []*store.Record
wantErrs []error
}{
{
name: "fails if multiple session records are found",
suSe: mustNewSuSe(t, "", "session-a"),
store: func(t *testing.T) store.Store {
s := mocks.NewStore(t)
s.EXPECT().Read(mock.Anything, mock.Anything).Return([]*store.Record{
recordSessionA,
recordSessionB,
}, nil)
return s
},
wantRecords: []*store.Record{},
wantErrs: []error{ErrSuspiciousCacheResult}},
{
name: "fails if the record key is not ok",
suSe: mustNewSuSe(t, "", "session-a"),
store: func(t *testing.T) store.Store {
s := mocks.NewStore(t)
s.EXPECT().Read(mock.Anything, mock.Anything).Return([]*store.Record{
{Key: "invalid.record.key"},
}, nil)
return s
},
wantRecords: []*store.Record{},
wantErrs: []error{ErrInvalidSubjectOrSession, ErrSuspiciousCacheResult},
},
{
name: "fails if the session does not match the retrieved record",
suSe: mustNewSuSe(t, "", "session-a"),
store: func(t *testing.T) store.Store {
s := mocks.NewStore(t)
s.EXPECT().Read(mock.Anything, mock.Anything).Return([]*store.Record{
recordSessionB,
}, nil)
return s
},
wantRecords: []*store.Record{},
wantErrs: []error{ErrSuspiciousCacheResult}},
{
name: "fails if the subject does not match the retrieved record",
suSe: mustNewSuSe(t, "subject-a", ""),
store: func(t *testing.T) store.Store {
s := mocks.NewStore(t)
s.EXPECT().Read(mock.Anything, mock.Anything).Return([]*store.Record{
recordSessionB,
}, nil)
return s
},
wantRecords: []*store.Record{},
wantErrs: []error{ErrSuspiciousCacheResult}},
// key variation tests
{
name: "key variation: 'session-a'",
suSe: mustNewSuSe(t, "", "session-a"),
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{recordSessionA},
},
{
name: "key variation: 'session-b'",
suSe: mustNewSuSe(t, "", "session-b"),
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{recordSessionB},
},
{
name: "key variation: 'session-c'",
suSe: mustNewSuSe(t, "", "session-c"),
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{recordSubjectASessionC},
},
{
name: "key variation: 'ession-c'",
suSe: mustNewSuSe(t, "", "ession-c"),
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{},
wantErrs: []error{store.ErrNotFound},
},
{
name: "key variation: 'subject-a'",
suSe: mustNewSuSe(t, "subject-a", ""),
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{recordSubjectASessionC, recordSubjectASessionD},
},
{
name: "key variation: 'subject-'",
suSe: mustNewSuSe(t, "subject-", ""),
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{},
wantErrs: []error{store.ErrNotFound},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
records, err := GetLogoutRecords(tt.suSe, tt.store(t))
for _, wantErr := range tt.wantErrs {
require.ErrorIs(t, err, wantErr)
}
require.Len(t, records, len(tt.wantRecords))
sortRecords := func(r []*store.Record) []*store.Record {
slices.SortFunc(r, func(a, b *store.Record) int {
return strings.Compare(a.Key, b.Key)
})
return r
}
records = sortRecords(records)
for i, wantRecords := range sortRecords(tt.wantRecords) {
require.True(t, len(records) >= i+1)
require.Equal(t, wantRecords.Key, records[i].Key)
require.Equal(t, wantRecords.Value, records[i].Value)
}
})
}
}