test: add more backchannellogout tests

This commit is contained in:
Florian Schade
2026-02-16 23:20:39 +01:00
committed by Christian Richter
parent f7a86d681a
commit 6af2c44f7f
5 changed files with 693 additions and 74 deletions

View File

@@ -12,3 +12,8 @@ packages:
github.com/opencloud-eu/opencloud/services/proxy/pkg/userroles:
interfaces:
UserRoleAssigner: {}
go-micro.dev/v4/store:
config:
dir: pkg/staticroutes/internal/backchannellogout/mocks
interfaces:
Store: {}

View File

@@ -37,6 +37,9 @@ import (
// all sessions besides the one that triggered the backchannel logout continue to exist in the identity provider,
// so the user will not be fully logged out until all sessions are logged out or expired.
// this leads to the situation that web renders the logout view even if the instance is not fully logged out yet.
//
// toDo:
// - check logs and errors to not contain any sensitive information like session ids or user ids (keys too)
func (s *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Request) {
logger := s.Logger.SubloggerWithRequestID(r.Context())
if err := r.ParseForm(); err != nil {
@@ -85,14 +88,14 @@ func (s *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Re
// the record value is the key of the record that contains the claim in its value
key, value := record.Key, string(record.Value)
subjectSession, ok := bcl.NewSuSe(key)
if !ok {
logger.Warn().Msgf("invalid logout record key: %s", key)
subjectSession, err := bcl.NewSuSe(key)
if err != nil {
// never leak any key-related information
logger.Warn().Err(err).Msgf("invalid logout record key: %s", "XXX")
continue
}
err := s.publishBackchannelLogoutEvent(r.Context(), subjectSession.Session, value)
if err != nil {
if err := s.publishBackchannelLogoutEvent(r.Context(), subjectSession.Session, value); err != nil {
s.Logger.Warn().Err(err).Msg("could not publish backchannel logout event")
}
@@ -109,7 +112,7 @@ func (s *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Re
// we can ignore errors when deleting the lookup record
err = s.UserInfoCache.Delete(key)
if err != nil {
logger.Debug().Err(err).Msg("Failed to cleanup sessionid lookup entry")
logger.Debug().Err(err).Msg("Failed to cleanup sessionId lookup entry")
}
}

View File

@@ -19,8 +19,11 @@ type SuSe struct {
Session string
}
// ErrInvalidSessionOrSubject is returned when the provided key does not match the expected key format
var ErrInvalidSessionOrSubject = errors.New("invalid session or subject")
// NewSuSe parses the subject and session id from the given key and returns a SuSe struct
func NewSuSe(key string) (SuSe, bool) {
func NewSuSe(key string) (SuSe, error) {
var subject, session string
switch keys := strings.Split(strings.Join(strings.Fields(key), ""), "."); {
case len(keys) == 2 && keys[0] == "" && keys[1] != "":
@@ -33,10 +36,10 @@ func NewSuSe(key string) (SuSe, bool) {
case len(keys) == 1 && keys[0] != "":
session = keys[0]
default:
return SuSe{}, false
return SuSe{}, ErrInvalidSessionOrSubject
}
return SuSe{Session: session, Subject: subject}, true
return SuSe{Session: session, Subject: subject}, nil
}
// LogoutMode defines the mode of backchannel logout, either by session or by subject
@@ -104,9 +107,10 @@ func GetLogoutRecords(suse SuSe, mode LogoutMode, store microstore.Store) ([]*mi
// double-check if the found records match the requested subject and or session id as well,
// to prevent false positives.
for _, record := range records {
recordSuSe, ok := NewSuSe(record.Key)
if !ok {
return nil, microstore.ErrNotFound
recordSuSe, err := NewSuSe(record.Key)
if err != nil {
// never leak any key-related information
return nil, fmt.Errorf("%w %w: failed to parse logout record key: %s", err, ErrSuspiciousCacheResult, "XXX")
}
switch {

View File

@@ -5,84 +5,80 @@ import (
"strings"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go-micro.dev/v4/store"
"github.com/opencloud-eu/opencloud/services/proxy/pkg/staticroutes/internal/backchannellogout/mocks"
)
func TestNewSuSe(t *testing.T) {
tests := []struct {
name string
key string
wantsuSe SuSe
wantOk bool
wantSuSe SuSe
wantErr error
}{
{
name: ".session",
name: "key variation: '.session'",
key: ".session",
wantsuSe: SuSe{Session: "session", Subject: ""},
wantOk: true,
wantSuSe: SuSe{Session: "session", Subject: ""},
},
{
name: ".session",
name: "key variation: '.session'",
key: ".session",
wantsuSe: SuSe{Session: "session", Subject: ""},
wantOk: true,
wantSuSe: SuSe{Session: "session", Subject: ""},
},
{
name: "session",
name: "key variation: 'session'",
key: "session",
wantsuSe: SuSe{Session: "session", Subject: ""},
wantOk: true,
wantSuSe: SuSe{Session: "session", Subject: ""},
},
{
name: "subject.",
name: "key variation: 'subject.'",
key: "subject.",
wantsuSe: SuSe{Session: "", Subject: "subject"},
wantOk: true,
wantSuSe: SuSe{Session: "", Subject: "subject"},
},
{
name: "subject.session",
name: "key variation: 'subject.session'",
key: "subject.session",
wantsuSe: SuSe{Session: "session", Subject: "subject"},
wantOk: true,
wantSuSe: SuSe{Session: "session", Subject: "subject"},
},
{
name: "dot",
name: "key variation: 'dot'",
key: ".",
wantsuSe: SuSe{Session: "", Subject: ""},
wantOk: false,
wantSuSe: SuSe{Session: "", Subject: ""},
wantErr: ErrInvalidSessionOrSubject,
},
{
name: "empty",
name: "key variation: 'empty'",
key: "",
wantsuSe: SuSe{Session: "", Subject: ""},
wantOk: false,
wantSuSe: SuSe{Session: "", Subject: ""},
wantErr: ErrInvalidSessionOrSubject,
},
{
name: "whitespace . whitespace",
name: "key variation: 'whitespace . whitespace'",
key: " . ",
wantsuSe: SuSe{Session: "", Subject: ""},
wantOk: false,
wantSuSe: SuSe{Session: "", Subject: ""},
wantErr: ErrInvalidSessionOrSubject,
},
{
name: "whitespace subject whitespace . whitespace",
name: "key variation: 'whitespace subject whitespace . whitespace'",
key: " subject . ",
wantsuSe: SuSe{Session: "", Subject: "subject"},
wantOk: true,
wantSuSe: SuSe{Session: "", Subject: "subject"},
},
{
name: "whitespace . whitespace session whitespace",
name: "key variation: 'whitespace . whitespace session whitespace'",
key: " . session ",
wantsuSe: SuSe{Session: "session", Subject: ""},
wantOk: true,
wantSuSe: SuSe{Session: "session", Subject: ""},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
suSe, ok := NewSuSe(tt.key)
require.Equal(t, tt.wantOk, ok)
require.Equal(t, tt.wantsuSe, suSe)
require.ErrorIs(t, tt.wantErr, ok)
require.Equal(t, tt.wantSuSe, suSe)
})
}
}
@@ -94,22 +90,22 @@ func TestGetLogoutMode(t *testing.T) {
want LogoutMode
}{
{
name: ".session",
name: "key variation: '.session'",
suSe: SuSe{Session: "session", Subject: ""},
want: LogoutModeSession,
},
{
name: "subject.session",
name: "key variation: 'subject.session'",
suSe: SuSe{Session: "session", Subject: "subject"},
want: LogoutModeSession,
},
{
name: "subject.",
name: "key variation: 'subject.'",
suSe: SuSe{Session: "", Subject: "subject"},
want: LogoutModeSubject,
},
{
name: "",
name: "key variation: 'empty'",
suSe: SuSe{Session: "", Subject: ""},
want: LogoutModeUnknown,
},
@@ -130,8 +126,8 @@ func TestGetLogoutRecords(t *testing.T) {
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: "session-a", Value: []byte(recordClaimA.Key)}
recordSessionB := &store.Record{Key: "session-b", Value: []byte(recordClaimB.Key)}
recordSessionA := &store.Record{Key: ".session-a", Value: []byte(recordClaimA.Key)}
recordSessionB := &store.Record{Key: ".session-b", Value: []byte(recordClaimB.Key)}
recordSubjectASessionC := &store.Record{Key: "subject-a.session-c", Value: []byte(recordSessionA.Key)}
recordSubjectASessionD := &store.Record{Key: "subject-a.session-d", Value: []byte(recordSessionB.Key)}
@@ -152,46 +148,148 @@ func TestGetLogoutRecords(t *testing.T) {
name string
suSe SuSe
mode LogoutMode
store store.Store
store func(t *testing.T) store.Store
wantRecords []*store.Record
wantError error
wantErrs []error
}{
{
name: "session-c",
suSe: SuSe{Session: "session-c"},
mode: LogoutModeSession,
store: sessionStore,
name: "fails if mode is unknown",
suSe: SuSe{Session: "session-a"},
mode: LogoutModeUnknown,
store: func(t *testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{},
wantErrs: []error{ErrSuspiciousCacheResult},
},
{
name: "fails if mode is any random int",
suSe: SuSe{Session: "session-a"},
mode: 999,
store: func(t *testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{},
wantErrs: []error{ErrSuspiciousCacheResult}},
{
name: "fails if multiple session records are found",
suSe: SuSe{Session: "session-a"},
mode: LogoutModeSession,
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: SuSe{Session: "session-a"},
mode: LogoutModeSession,
store: func(t *testing.T) store.Store {
s := mocks.NewStore(t)
s.EXPECT().Read(mock.Anything, mock.Anything).Return([]*store.Record{
&store.Record{Key: "invalid.record.key"},
}, nil)
return s
},
wantRecords: []*store.Record{},
wantErrs: []error{ErrInvalidSessionOrSubject, ErrSuspiciousCacheResult},
},
{
name: "fails if the session does not match the retrieved record",
suSe: SuSe{Session: "session-a"},
mode: LogoutModeSession,
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: SuSe{Subject: "subject-a"},
mode: LogoutModeSubject,
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: SuSe{Session: "session-a"},
mode: LogoutModeSession,
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{recordSessionA},
},
{
name: "key variation: 'session-b'",
suSe: SuSe{Session: "session-b"},
mode: LogoutModeSession,
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{recordSessionB},
},
{
name: "key variation: 'session-c'",
suSe: SuSe{Session: "session-c"},
mode: LogoutModeSession,
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{recordSubjectASessionC},
},
{
name: "ession-c",
suSe: SuSe{Session: "ession-c"},
mode: LogoutModeSession,
store: sessionStore,
wantError: store.ErrNotFound,
name: "key variation: 'ession-c'",
suSe: SuSe{Session: "ession-c"},
mode: LogoutModeSession,
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{},
wantErrs: []error{store.ErrNotFound},
},
{
name: "subject-a",
suSe: SuSe{Subject: "subject-a"},
mode: LogoutModeSubject,
store: sessionStore,
name: "key variation: 'subject-a'",
suSe: SuSe{Subject: "subject-a"},
mode: LogoutModeSubject,
store: func(*testing.T) store.Store {
return sessionStore
},
wantRecords: []*store.Record{recordSubjectASessionC, recordSubjectASessionD},
},
{
name: "subject-",
suSe: SuSe{Subject: "subject-"},
mode: LogoutModeSubject,
store: sessionStore,
wantError: store.ErrNotFound,
name: "key variation: 'subject-'",
suSe: SuSe{Subject: "subject-"},
mode: LogoutModeSubject,
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.mode, tt.store)
require.ErrorIs(t, err, tt.wantError)
records, err := GetLogoutRecords(tt.suSe, tt.mode, 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 {

View File

@@ -0,0 +1,509 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package mocks
import (
mock "github.com/stretchr/testify/mock"
"go-micro.dev/v4/store"
)
// NewStore creates a new instance of Store. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewStore(t interface {
mock.TestingT
Cleanup(func())
}) *Store {
mock := &Store{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// Store is an autogenerated mock type for the Store type
type Store struct {
mock.Mock
}
type Store_Expecter struct {
mock *mock.Mock
}
func (_m *Store) EXPECT() *Store_Expecter {
return &Store_Expecter{mock: &_m.Mock}
}
// Close provides a mock function for the type Store
func (_mock *Store) Close() error {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for Close")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func() error); ok {
r0 = returnFunc()
} else {
r0 = ret.Error(0)
}
return r0
}
// Store_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
type Store_Close_Call struct {
*mock.Call
}
// Close is a helper method to define mock.On call
func (_e *Store_Expecter) Close() *Store_Close_Call {
return &Store_Close_Call{Call: _e.mock.On("Close")}
}
func (_c *Store_Close_Call) Run(run func()) *Store_Close_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Store_Close_Call) Return(err error) *Store_Close_Call {
_c.Call.Return(err)
return _c
}
func (_c *Store_Close_Call) RunAndReturn(run func() error) *Store_Close_Call {
_c.Call.Return(run)
return _c
}
// Delete provides a mock function for the type Store
func (_mock *Store) Delete(key string, opts ...store.DeleteOption) error {
var tmpRet mock.Arguments
if len(opts) > 0 {
tmpRet = _mock.Called(key, opts)
} else {
tmpRet = _mock.Called(key)
}
ret := tmpRet
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(string, ...store.DeleteOption) error); ok {
r0 = returnFunc(key, opts...)
} else {
r0 = ret.Error(0)
}
return r0
}
// Store_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
type Store_Delete_Call struct {
*mock.Call
}
// Delete is a helper method to define mock.On call
// - key string
// - opts ...store.DeleteOption
func (_e *Store_Expecter) Delete(key interface{}, opts ...interface{}) *Store_Delete_Call {
return &Store_Delete_Call{Call: _e.mock.On("Delete",
append([]interface{}{key}, opts...)...)}
}
func (_c *Store_Delete_Call) Run(run func(key string, opts ...store.DeleteOption)) *Store_Delete_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
var arg1 []store.DeleteOption
var variadicArgs []store.DeleteOption
if len(args) > 1 {
variadicArgs = args[1].([]store.DeleteOption)
}
arg1 = variadicArgs
run(
arg0,
arg1...,
)
})
return _c
}
func (_c *Store_Delete_Call) Return(err error) *Store_Delete_Call {
_c.Call.Return(err)
return _c
}
func (_c *Store_Delete_Call) RunAndReturn(run func(key string, opts ...store.DeleteOption) error) *Store_Delete_Call {
_c.Call.Return(run)
return _c
}
// Init provides a mock function for the type Store
func (_mock *Store) Init(options ...store.Option) error {
var tmpRet mock.Arguments
if len(options) > 0 {
tmpRet = _mock.Called(options)
} else {
tmpRet = _mock.Called()
}
ret := tmpRet
if len(ret) == 0 {
panic("no return value specified for Init")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(...store.Option) error); ok {
r0 = returnFunc(options...)
} else {
r0 = ret.Error(0)
}
return r0
}
// Store_Init_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Init'
type Store_Init_Call struct {
*mock.Call
}
// Init is a helper method to define mock.On call
// - options ...store.Option
func (_e *Store_Expecter) Init(options ...interface{}) *Store_Init_Call {
return &Store_Init_Call{Call: _e.mock.On("Init",
append([]interface{}{}, options...)...)}
}
func (_c *Store_Init_Call) Run(run func(options ...store.Option)) *Store_Init_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 []store.Option
var variadicArgs []store.Option
if len(args) > 0 {
variadicArgs = args[0].([]store.Option)
}
arg0 = variadicArgs
run(
arg0...,
)
})
return _c
}
func (_c *Store_Init_Call) Return(err error) *Store_Init_Call {
_c.Call.Return(err)
return _c
}
func (_c *Store_Init_Call) RunAndReturn(run func(options ...store.Option) error) *Store_Init_Call {
_c.Call.Return(run)
return _c
}
// List provides a mock function for the type Store
func (_mock *Store) List(opts ...store.ListOption) ([]string, error) {
var tmpRet mock.Arguments
if len(opts) > 0 {
tmpRet = _mock.Called(opts)
} else {
tmpRet = _mock.Called()
}
ret := tmpRet
if len(ret) == 0 {
panic("no return value specified for List")
}
var r0 []string
var r1 error
if returnFunc, ok := ret.Get(0).(func(...store.ListOption) ([]string, error)); ok {
return returnFunc(opts...)
}
if returnFunc, ok := ret.Get(0).(func(...store.ListOption) []string); ok {
r0 = returnFunc(opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
if returnFunc, ok := ret.Get(1).(func(...store.ListOption) error); ok {
r1 = returnFunc(opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Store_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List'
type Store_List_Call struct {
*mock.Call
}
// List is a helper method to define mock.On call
// - opts ...store.ListOption
func (_e *Store_Expecter) List(opts ...interface{}) *Store_List_Call {
return &Store_List_Call{Call: _e.mock.On("List",
append([]interface{}{}, opts...)...)}
}
func (_c *Store_List_Call) Run(run func(opts ...store.ListOption)) *Store_List_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 []store.ListOption
var variadicArgs []store.ListOption
if len(args) > 0 {
variadicArgs = args[0].([]store.ListOption)
}
arg0 = variadicArgs
run(
arg0...,
)
})
return _c
}
func (_c *Store_List_Call) Return(strings []string, err error) *Store_List_Call {
_c.Call.Return(strings, err)
return _c
}
func (_c *Store_List_Call) RunAndReturn(run func(opts ...store.ListOption) ([]string, error)) *Store_List_Call {
_c.Call.Return(run)
return _c
}
// Options provides a mock function for the type Store
func (_mock *Store) Options() store.Options {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for Options")
}
var r0 store.Options
if returnFunc, ok := ret.Get(0).(func() store.Options); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(store.Options)
}
return r0
}
// Store_Options_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Options'
type Store_Options_Call struct {
*mock.Call
}
// Options is a helper method to define mock.On call
func (_e *Store_Expecter) Options() *Store_Options_Call {
return &Store_Options_Call{Call: _e.mock.On("Options")}
}
func (_c *Store_Options_Call) Run(run func()) *Store_Options_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Store_Options_Call) Return(options store.Options) *Store_Options_Call {
_c.Call.Return(options)
return _c
}
func (_c *Store_Options_Call) RunAndReturn(run func() store.Options) *Store_Options_Call {
_c.Call.Return(run)
return _c
}
// Read provides a mock function for the type Store
func (_mock *Store) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) {
var tmpRet mock.Arguments
if len(opts) > 0 {
tmpRet = _mock.Called(key, opts)
} else {
tmpRet = _mock.Called(key)
}
ret := tmpRet
if len(ret) == 0 {
panic("no return value specified for Read")
}
var r0 []*store.Record
var r1 error
if returnFunc, ok := ret.Get(0).(func(string, ...store.ReadOption) ([]*store.Record, error)); ok {
return returnFunc(key, opts...)
}
if returnFunc, ok := ret.Get(0).(func(string, ...store.ReadOption) []*store.Record); ok {
r0 = returnFunc(key, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*store.Record)
}
}
if returnFunc, ok := ret.Get(1).(func(string, ...store.ReadOption) error); ok {
r1 = returnFunc(key, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Store_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read'
type Store_Read_Call struct {
*mock.Call
}
// Read is a helper method to define mock.On call
// - key string
// - opts ...store.ReadOption
func (_e *Store_Expecter) Read(key interface{}, opts ...interface{}) *Store_Read_Call {
return &Store_Read_Call{Call: _e.mock.On("Read",
append([]interface{}{key}, opts...)...)}
}
func (_c *Store_Read_Call) Run(run func(key string, opts ...store.ReadOption)) *Store_Read_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
var arg1 []store.ReadOption
var variadicArgs []store.ReadOption
if len(args) > 1 {
variadicArgs = args[1].([]store.ReadOption)
}
arg1 = variadicArgs
run(
arg0,
arg1...,
)
})
return _c
}
func (_c *Store_Read_Call) Return(records []*store.Record, err error) *Store_Read_Call {
_c.Call.Return(records, err)
return _c
}
func (_c *Store_Read_Call) RunAndReturn(run func(key string, opts ...store.ReadOption) ([]*store.Record, error)) *Store_Read_Call {
_c.Call.Return(run)
return _c
}
// String provides a mock function for the type Store
func (_mock *Store) String() string {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for String")
}
var r0 string
if returnFunc, ok := ret.Get(0).(func() string); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// Store_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String'
type Store_String_Call struct {
*mock.Call
}
// String is a helper method to define mock.On call
func (_e *Store_Expecter) String() *Store_String_Call {
return &Store_String_Call{Call: _e.mock.On("String")}
}
func (_c *Store_String_Call) Run(run func()) *Store_String_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Store_String_Call) Return(s string) *Store_String_Call {
_c.Call.Return(s)
return _c
}
func (_c *Store_String_Call) RunAndReturn(run func() string) *Store_String_Call {
_c.Call.Return(run)
return _c
}
// Write provides a mock function for the type Store
func (_mock *Store) Write(r *store.Record, opts ...store.WriteOption) error {
var tmpRet mock.Arguments
if len(opts) > 0 {
tmpRet = _mock.Called(r, opts)
} else {
tmpRet = _mock.Called(r)
}
ret := tmpRet
if len(ret) == 0 {
panic("no return value specified for Write")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(*store.Record, ...store.WriteOption) error); ok {
r0 = returnFunc(r, opts...)
} else {
r0 = ret.Error(0)
}
return r0
}
// Store_Write_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Write'
type Store_Write_Call struct {
*mock.Call
}
// Write is a helper method to define mock.On call
// - r *store.Record
// - opts ...store.WriteOption
func (_e *Store_Expecter) Write(r interface{}, opts ...interface{}) *Store_Write_Call {
return &Store_Write_Call{Call: _e.mock.On("Write",
append([]interface{}{r}, opts...)...)}
}
func (_c *Store_Write_Call) Run(run func(r *store.Record, opts ...store.WriteOption)) *Store_Write_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 *store.Record
if args[0] != nil {
arg0 = args[0].(*store.Record)
}
var arg1 []store.WriteOption
var variadicArgs []store.WriteOption
if len(args) > 1 {
variadicArgs = args[1].([]store.WriteOption)
}
arg1 = variadicArgs
run(
arg0,
arg1...,
)
})
return _c
}
func (_c *Store_Write_Call) Return(err error) *Store_Write_Call {
_c.Call.Return(err)
return _c
}
func (_c *Store_Write_Call) RunAndReturn(run func(r *store.Record, opts ...store.WriteOption) error) *Store_Write_Call {
_c.Call.Return(run)
return _c
}