From 9f9556eab062ab7ffe7d59eeb8ee1114b26dfc69 Mon Sep 17 00:00:00 2001 From: Dmitry Verkhoturov Date: Sun, 2 Oct 2022 17:37:48 +0200 Subject: [PATCH] replace mockery with moq Telegram interfaces change after migrating from the v4 to the v5 version of the Telegram library, so they are not changed here. --- app/bot/anecdot_test.go | 49 +++++---- app/bot/banhammer_test.go | 12 ++- app/bot/bot.go | 6 +- app/bot/bot_test.go | 41 ++++---- app/bot/duckduckgo_test.go | 11 +-- app/bot/mock_interface.go | 181 ++++++++++++++++++++++++---------- app/bot/mocks/http_client.go | 93 ++++++++++------- app/bot/mocks/super_user.go | 85 +++++++++++----- app/bot/news_test.go | 26 +++-- app/bot/preppost_test.go | 39 ++++---- app/bot/wtf_test.go | 10 +- app/events/mock_msg_logger.go | 82 +++++++++++---- app/events/telegram.go | 2 +- app/events/telegram_test.go | 159 +++++++++++++---------------- 14 files changed, 476 insertions(+), 320 deletions(-) diff --git a/app/bot/anecdot_test.go b/app/bot/anecdot_test.go index 4e073127..2fbc5643 100644 --- a/app/bot/anecdot_test.go +++ b/app/bot/anecdot_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/radio-t/super-bot/app/bot/mocks" @@ -22,67 +21,67 @@ func TestAnecdot_Help(t *testing.T) { } func TestAnecdot_ReactsOnJokeRequest(t *testing.T) { - mockHTTP := &mocks.HTTPClient{} + mockHTTP := &mocks.HTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) { + return &http.Response{ + Body: io.NopCloser(strings.NewReader(`{"content": "Добраться до вершины не так сложно, как пробраться через толпу у её основания."}`)), + }, nil + }} b := NewAnecdote(mockHTTP) - mockHTTP.On("Do", mock.Anything).Return(&http.Response{ - Body: io.NopCloser(strings.NewReader(`{"content": "Добраться до вершины не так сложно, как пробраться через толпу у её основания."}`)), - }, nil) - response := b.OnMessage(Message{Text: "joke!"}) require.True(t, response.Send) require.Equal(t, "Добраться до вершины не так сложно, как пробраться через толпу у её основания", response.Text) } func TestAnecdot_ujokesrvRetursnNothingOnUnableToDoReq(t *testing.T) { - mockHTTP := &mocks.HTTPClient{} + mockHTTP := &mocks.HTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) { + return nil, fmt.Errorf("err") + }} b := NewAnecdote(mockHTTP) - mockHTTP.On("Do", mock.Anything).Return(nil, fmt.Errorf("err")) - response := b.jokesrv("oneliners") require.False(t, response.Send) require.Empty(t, response.Text) } func TestAnecdotReactsOnUnexpectedMessage(t *testing.T) { - mockHTTP := &mocks.HTTPClient{} + mockHTTP := &mocks.HTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) { + return nil, fmt.Errorf("err") + }} b := NewAnecdote(mockHTTP) - mockHTTP.On("Do", mock.Anything).Return(nil, fmt.Errorf("err")) - result := b.OnMessage(Message{Text: "unexpected msg"}) require.False(t, result.Send) assert.Empty(t, result.Text) } func TestAnecdotReactsOnBadChuckMessage(t *testing.T) { - mockHTTP := &mocks.HTTPClient{} + mockHTTP := &mocks.HTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) { + return &http.Response{ + Body: io.NopCloser(bytes.NewReader([]byte(`not a json`))), + }, nil + }} b := NewAnecdote(mockHTTP) - mockHTTP.On("Do", mock.Anything).Return(&http.Response{ - Body: io.NopCloser(bytes.NewReader([]byte(`not a json`))), - }, nil) - require.Equal(t, Response{}, b.OnMessage(Message{Text: "chuck!"})) } func TestAnecdotReactsOnChuckMessageUnableToDoReq(t *testing.T) { - mockHTTP := &mocks.HTTPClient{} + mockHTTP := &mocks.HTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) { + return nil, fmt.Errorf("err") + }} b := NewAnecdote(mockHTTP) - mockHTTP.On("Do", mock.Anything).Return(nil, fmt.Errorf("err")) - require.Equal(t, Response{}, b.OnMessage(Message{Text: "chuck!"})) } func TestAnecdotReactsOnChuckMessage(t *testing.T) { - mockHTTP := &mocks.HTTPClient{} + mockHTTP := &mocks.HTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) { + return &http.Response{ + Body: io.NopCloser(bytes.NewReader([]byte(`{"Value" : {"Joke" : ""joke""}}`))), + }, nil + }} b := NewAnecdote(mockHTTP) - mockHTTP.On("Do", mock.Anything).Return(&http.Response{ - Body: io.NopCloser(bytes.NewReader([]byte(`{"Value" : {"Joke" : ""joke""}}`))), - }, nil) - require.Equal(t, Response{Text: "- \"joke\"", Send: true}, b.OnMessage(Message{Text: "chuck!"})) } diff --git a/app/bot/banhammer_test.go b/app/bot/banhammer_test.go index 1a41779f..c1fa677f 100644 --- a/app/bot/banhammer_test.go +++ b/app/bot/banhammer_test.go @@ -45,13 +45,15 @@ func TestBanhammer_parse(t *testing.T) { } func TestBanhammer_OnMessage(t *testing.T) { - su := &mocks.SuperUser{} + su := &mocks.SuperUser{IsSuperFunc: func(userName string) bool { + if userName == "admin" { + return true + } + return false + }} tg := &mocks.TgBanClient{} b := NewBanhammer(tg, su, 10) - su.On("IsSuper", "admin").Return(true).Times(2) - su.On("IsSuper", "user1").Return(false).Times(3) - tg.On("KickChatMember", mock.MatchedBy(func(u tbapi.KickChatMemberConfig) bool { return u.UserID == 1 && u.ChatID == 123 })).Return(tbapi.APIResponse{}, nil) @@ -72,6 +74,6 @@ func TestBanhammer_OnMessage(t *testing.T) { resp = b.OnMessage(Message{Text: "unban! user1", From: User{Username: "admin"}, ChatID: 123}) assert.Equal(t, Response{Text: "амнистия для user1", Send: true}, resp) - su.AssertExpectations(t) + assert.Equal(t, 5, len(su.IsSuperCalls())) tg.AssertExpectations(t) } diff --git a/app/bot/bot.go b/app/bot/bot.go index 130775b9..cbc4ffc5 100644 --- a/app/bot/bot.go +++ b/app/bot/bot.go @@ -14,9 +14,9 @@ import ( "github.com/go-pkgz/syncs" ) -//go:generate mockery --name HTTPClient --case snake -//go:generate mockery --inpackage --name Interface --case snake -//go:generate mockery --name SuperUser --case snake +//go:generate moq --out mocks/http_client.go --pkg mocks --skip-ensure . HTTPClient:HTTPClient +//go:generate moq --out mock_interface.go . Interface +//go:generate moq --out mocks/super_user.go --pkg mocks --skip-ensure . SuperUser:SuperUser // genHelpMsg construct help message from bot's ReactOn func genHelpMsg(com []string, msg string) string { diff --git a/app/bot/bot_test.go b/app/bot/bot_test.go index 7dc5bce7..cc25d549 100644 --- a/app/bot/bot_test.go +++ b/app/bot/bot_test.go @@ -12,10 +12,12 @@ func TestGenHelpMsg(t *testing.T) { } func TestMultiBotHelp(t *testing.T) { - b1 := &MockInterface{} - b1.On("Help").Return("b1 help") - b2 := &MockInterface{} - b2.On("Help").Return("b2 help") + b1 := &InterfaceMock{HelpFunc: func() string { + return "b1 help" + }} + b2 := &InterfaceMock{HelpFunc: func() string { + return "b2 help" + }} // Must return concatenated b1 and b2 without space // Line formatting only in genHelpMsg() @@ -23,9 +25,14 @@ func TestMultiBotHelp(t *testing.T) { } func TestMultiBotReactsOnHelp(t *testing.T) { - b := &MockInterface{} - b.On("ReactOn").Return([]string{"help"}) - b.On("Help").Return("help") + b := &InterfaceMock{ + ReactOnFunc: func() []string { + return []string{"help"} + }, + HelpFunc: func() string { + return "help" + }, + } mb := MultiBot{b} resp := mb.OnMessage(Message{Text: "help"}) @@ -37,18 +44,14 @@ func TestMultiBotReactsOnHelp(t *testing.T) { func TestMultiBotCombinesAllBotResponses(t *testing.T) { msg := Message{Text: "cmd"} - b1 := &MockInterface{} - b1.On("ReactOn").Return([]string{"cmd"}) - b1.On("OnMessage", msg).Return(Response{ - Text: "b1 resp", - Send: true, - }) - b2 := &MockInterface{} - b2.On("ReactOn").Return([]string{"cmd"}) - b2.On("OnMessage", msg).Return(Response{ - Text: "b2 resp", - Send: true, - }) + b1 := &InterfaceMock{ + ReactOnFunc: func() []string { return []string{"cmd"} }, + OnMessageFunc: func(m Message) Response { return Response{Send: true, Text: "b1 resp"} }, + } + b2 := &InterfaceMock{ + ReactOnFunc: func() []string { return []string{"cmd"} }, + OnMessageFunc: func(m Message) Response { return Response{Send: true, Text: "b2 resp"} }, + } mb := MultiBot{b1, b2} resp := mb.OnMessage(msg) diff --git a/app/bot/duckduckgo_test.go b/app/bot/duckduckgo_test.go index 13bdd311..0c8a7d29 100644 --- a/app/bot/duckduckgo_test.go +++ b/app/bot/duckduckgo_test.go @@ -9,7 +9,6 @@ import ( "github.com/radio-t/super-bot/app/bot/mocks" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -18,13 +17,13 @@ func TestDuck_Help(t *testing.T) { } func TestDuck_OnMessage(t *testing.T) { - mockHTTP := &mocks.HTTPClient{} + mockHTTP := &mocks.HTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) { + return &http.Response{ + Body: io.NopCloser(bytes.NewReader([]byte(`{"AbstractText":"the answer", "AbstractSource":"test", "AbstractURL":"http://example.com"}`))), + }, nil + }} d := NewDuck("key", mockHTTP) - mockHTTP.On("Do", mock.Anything).Return(&http.Response{ - Body: io.NopCloser(bytes.NewReader([]byte(`{"AbstractText":"the answer", "AbstractSource":"test", "AbstractURL":"http://example.com"}`))), - }, nil) - assert.Equal(t, Response{Text: "- the answer\n[test](http://example.com)", Send: true}, d.OnMessage(Message{Text: "?? search"})) } diff --git a/app/bot/mock_interface.go b/app/bot/mock_interface.go index 94ca33c0..cbe73ac4 100644 --- a/app/bot/mock_interface.go +++ b/app/bot/mock_interface.go @@ -1,69 +1,148 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by moq; DO NOT EDIT. +// github.com/matryer/moq package bot -import mock "github.com/stretchr/testify/mock" - -// MockInterface is an autogenerated mock type for the Interface type -type MockInterface struct { - mock.Mock +import ( + "sync" +) + +// Ensure, that InterfaceMock does implement Interface. +// If this is not the case, regenerate this file with moq. +var _ Interface = &InterfaceMock{} + +// InterfaceMock is a mock implementation of Interface. +// +// func TestSomethingThatUsesInterface(t *testing.T) { +// +// // make and configure a mocked Interface +// mockedInterface := &InterfaceMock{ +// HelpFunc: func() string { +// panic("mock out the Help method") +// }, +// OnMessageFunc: func(msg Message) Response { +// panic("mock out the OnMessage method") +// }, +// ReactOnFunc: func() []string { +// panic("mock out the ReactOn method") +// }, +// } +// +// // use mockedInterface in code that requires Interface +// // and then make assertions. +// +// } +type InterfaceMock struct { + // HelpFunc mocks the Help method. + HelpFunc func() string + + // OnMessageFunc mocks the OnMessage method. + OnMessageFunc func(msg Message) Response + + // ReactOnFunc mocks the ReactOn method. + ReactOnFunc func() []string + + // calls tracks calls to the methods. + calls struct { + // Help holds details about calls to the Help method. + Help []struct { + } + // OnMessage holds details about calls to the OnMessage method. + OnMessage []struct { + // Msg is the msg argument value. + Msg Message + } + // ReactOn holds details about calls to the ReactOn method. + ReactOn []struct { + } + } + lockHelp sync.RWMutex + lockOnMessage sync.RWMutex + lockReactOn sync.RWMutex } -// Help provides a mock function with given fields: -func (_m *MockInterface) Help() string { - ret := _m.Called() - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) +// Help calls HelpFunc. +func (mock *InterfaceMock) Help() string { + if mock.HelpFunc == nil { + panic("InterfaceMock.HelpFunc: method is nil but Interface.Help was just called") } - - return r0 + callInfo := struct { + }{} + mock.lockHelp.Lock() + mock.calls.Help = append(mock.calls.Help, callInfo) + mock.lockHelp.Unlock() + return mock.HelpFunc() } -// OnMessage provides a mock function with given fields: msg -func (_m *MockInterface) OnMessage(msg Message) Response { - ret := _m.Called(msg) - - var r0 Response - if rf, ok := ret.Get(0).(func(Message) Response); ok { - r0 = rf(msg) - } else { - r0 = ret.Get(0).(Response) +// HelpCalls gets all the calls that were made to Help. +// Check the length with: +// +// len(mockedInterface.HelpCalls()) +func (mock *InterfaceMock) HelpCalls() []struct { +} { + var calls []struct { } - - return r0 + mock.lockHelp.RLock() + calls = mock.calls.Help + mock.lockHelp.RUnlock() + return calls } -// ReactOn provides a mock function with given fields: -func (_m *MockInterface) ReactOn() []string { - ret := _m.Called() - - var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } +// OnMessage calls OnMessageFunc. +func (mock *InterfaceMock) OnMessage(msg Message) Response { + if mock.OnMessageFunc == nil { + panic("InterfaceMock.OnMessageFunc: method is nil but Interface.OnMessage was just called") } - - return r0 + callInfo := struct { + Msg Message + }{ + Msg: msg, + } + mock.lockOnMessage.Lock() + mock.calls.OnMessage = append(mock.calls.OnMessage, callInfo) + mock.lockOnMessage.Unlock() + return mock.OnMessageFunc(msg) } -type mockConstructorTestingTNewMockInterface interface { - mock.TestingT - Cleanup(func()) +// OnMessageCalls gets all the calls that were made to OnMessage. +// Check the length with: +// +// len(mockedInterface.OnMessageCalls()) +func (mock *InterfaceMock) OnMessageCalls() []struct { + Msg Message +} { + var calls []struct { + Msg Message + } + mock.lockOnMessage.RLock() + calls = mock.calls.OnMessage + mock.lockOnMessage.RUnlock() + return calls } -// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMockInterface(t mockConstructorTestingTNewMockInterface) *MockInterface { - mock := &MockInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) +// ReactOn calls ReactOnFunc. +func (mock *InterfaceMock) ReactOn() []string { + if mock.ReactOnFunc == nil { + panic("InterfaceMock.ReactOnFunc: method is nil but Interface.ReactOn was just called") + } + callInfo := struct { + }{} + mock.lockReactOn.Lock() + mock.calls.ReactOn = append(mock.calls.ReactOn, callInfo) + mock.lockReactOn.Unlock() + return mock.ReactOnFunc() +} - return mock +// ReactOnCalls gets all the calls that were made to ReactOn. +// Check the length with: +// +// len(mockedInterface.ReactOnCalls()) +func (mock *InterfaceMock) ReactOnCalls() []struct { +} { + var calls []struct { + } + mock.lockReactOn.RLock() + calls = mock.calls.ReactOn + mock.lockReactOn.RUnlock() + return calls } diff --git a/app/bot/mocks/http_client.go b/app/bot/mocks/http_client.go index 646d5a0b..6bbefa58 100644 --- a/app/bot/mocks/http_client.go +++ b/app/bot/mocks/http_client.go @@ -1,52 +1,71 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by moq; DO NOT EDIT. +// github.com/matryer/moq package mocks import ( - http "net/http" - - mock "github.com/stretchr/testify/mock" + "net/http" + "sync" ) -// HTTPClient is an autogenerated mock type for the HTTPClient type +// HTTPClient is a mock implementation of bot.HTTPClient. +// +// func TestSomethingThatUsesHTTPClient(t *testing.T) { +// +// // make and configure a mocked bot.HTTPClient +// mockedHTTPClient := &HTTPClient{ +// DoFunc: func(req *http.Request) (*http.Response, error) { +// panic("mock out the Do method") +// }, +// } +// +// // use mockedHTTPClient in code that requires bot.HTTPClient +// // and then make assertions. +// +// } type HTTPClient struct { - mock.Mock -} - -// Do provides a mock function with given fields: req -func (_m *HTTPClient) Do(req *http.Request) (*http.Response, error) { - ret := _m.Called(req) + // DoFunc mocks the Do method. + DoFunc func(req *http.Request) (*http.Response, error) - var r0 *http.Response - if rf, ok := ret.Get(0).(func(*http.Request) *http.Response); ok { - r0 = rf(req) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*http.Response) + // calls tracks calls to the methods. + calls struct { + // Do holds details about calls to the Do method. + Do []struct { + // Req is the req argument value. + Req *http.Request } } - - var r1 error - if rf, ok := ret.Get(1).(func(*http.Request) error); ok { - r1 = rf(req) - } else { - r1 = ret.Error(1) - } - - return r0, r1 + lockDo sync.RWMutex } -type mockConstructorTestingTNewHTTPClient interface { - mock.TestingT - Cleanup(func()) +// Do calls DoFunc. +func (mock *HTTPClient) Do(req *http.Request) (*http.Response, error) { + if mock.DoFunc == nil { + panic("HTTPClient.DoFunc: method is nil but HTTPClient.Do was just called") + } + callInfo := struct { + Req *http.Request + }{ + Req: req, + } + mock.lockDo.Lock() + mock.calls.Do = append(mock.calls.Do, callInfo) + mock.lockDo.Unlock() + return mock.DoFunc(req) } -// NewHTTPClient creates a new instance of HTTPClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewHTTPClient(t mockConstructorTestingTNewHTTPClient) *HTTPClient { - mock := &HTTPClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock +// DoCalls gets all the calls that were made to Do. +// Check the length with: +// +// len(mockedHTTPClient.DoCalls()) +func (mock *HTTPClient) DoCalls() []struct { + Req *http.Request +} { + var calls []struct { + Req *http.Request + } + mock.lockDo.RLock() + calls = mock.calls.Do + mock.lockDo.RUnlock() + return calls } diff --git a/app/bot/mocks/super_user.go b/app/bot/mocks/super_user.go index 3600f32d..e87123b9 100644 --- a/app/bot/mocks/super_user.go +++ b/app/bot/mocks/super_user.go @@ -1,39 +1,70 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by moq; DO NOT EDIT. +// github.com/matryer/moq package mocks -import mock "github.com/stretchr/testify/mock" +import ( + "sync" +) -// SuperUser is an autogenerated mock type for the SuperUser type +// SuperUser is a mock implementation of bot.SuperUser. +// +// func TestSomethingThatUsesSuperUser(t *testing.T) { +// +// // make and configure a mocked bot.SuperUser +// mockedSuperUser := &SuperUser{ +// IsSuperFunc: func(userName string) bool { +// panic("mock out the IsSuper method") +// }, +// } +// +// // use mockedSuperUser in code that requires bot.SuperUser +// // and then make assertions. +// +// } type SuperUser struct { - mock.Mock -} - -// IsSuper provides a mock function with given fields: userName -func (_m *SuperUser) IsSuper(userName string) bool { - ret := _m.Called(userName) + // IsSuperFunc mocks the IsSuper method. + IsSuperFunc func(userName string) bool - var r0 bool - if rf, ok := ret.Get(0).(func(string) bool); ok { - r0 = rf(userName) - } else { - r0 = ret.Get(0).(bool) + // calls tracks calls to the methods. + calls struct { + // IsSuper holds details about calls to the IsSuper method. + IsSuper []struct { + // UserName is the userName argument value. + UserName string + } } - - return r0 + lockIsSuper sync.RWMutex } -type mockConstructorTestingTNewSuperUser interface { - mock.TestingT - Cleanup(func()) +// IsSuper calls IsSuperFunc. +func (mock *SuperUser) IsSuper(userName string) bool { + if mock.IsSuperFunc == nil { + panic("SuperUser.IsSuperFunc: method is nil but SuperUser.IsSuper was just called") + } + callInfo := struct { + UserName string + }{ + UserName: userName, + } + mock.lockIsSuper.Lock() + mock.calls.IsSuper = append(mock.calls.IsSuper, callInfo) + mock.lockIsSuper.Unlock() + return mock.IsSuperFunc(userName) } -// NewSuperUser creates a new instance of SuperUser. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSuperUser(t mockConstructorTestingTNewSuperUser) *SuperUser { - mock := &SuperUser{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock +// IsSuperCalls gets all the calls that were made to IsSuper. +// Check the length with: +// +// len(mockedSuperUser.IsSuperCalls()) +func (mock *SuperUser) IsSuperCalls() []struct { + UserName string +} { + var calls []struct { + UserName string + } + mock.lockIsSuper.RLock() + calls = mock.calls.IsSuper + mock.lockIsSuper.RUnlock() + return calls } diff --git a/app/bot/news_test.go b/app/bot/news_test.go index cd811ae2..91c7081f 100644 --- a/app/bot/news_test.go +++ b/app/bot/news_test.go @@ -9,14 +9,10 @@ import ( "time" "github.com/radio-t/super-bot/app/bot/mocks" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) func TestNewsBot_ReactionOnNewsRequest(t *testing.T) { - mockHTTP := &mocks.HTTPClient{} - b := NewNews(mockHTTP, "", 5) - articles := []newsArticle{ { Title: "title1", @@ -32,9 +28,12 @@ func TestNewsBot_ReactionOnNewsRequest(t *testing.T) { articleJSON, err := json.Marshal(articles) require.NoError(t, err) - mockHTTP.On("Do", mock.Anything).Return(&http.Response{ - Body: io.NopCloser(bytes.NewReader(articleJSON)), - }, nil) + mockHTTP := &mocks.HTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) { + return &http.Response{ + Body: io.NopCloser(bytes.NewReader(articleJSON)), + }, nil + }} + b := NewNews(mockHTTP, "", 5) require.Equal( t, @@ -45,19 +44,18 @@ func TestNewsBot_ReactionOnNewsRequest(t *testing.T) { } func TestNewsBot_ReactionOnNewsRequestAlt(t *testing.T) { - mockHTTP := &mocks.HTTPClient{} - b := NewNews(mockHTTP, "", 5) - article := newsArticle{ Title: "title", Link: "link", } articleJSON, err := json.Marshal([]newsArticle{article}) require.NoError(t, err) - - mockHTTP.On("Do", mock.Anything).Return(&http.Response{ - Body: io.NopCloser(bytes.NewReader(articleJSON)), - }, nil) + mockHTTP := &mocks.HTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) { + return &http.Response{ + Body: io.NopCloser(bytes.NewReader(articleJSON)), + }, nil + }} + b := NewNews(mockHTTP, "", 5) require.Equal( t, diff --git a/app/bot/preppost_test.go b/app/bot/preppost_test.go index 59e341ba..3bdd0f62 100644 --- a/app/bot/preppost_test.go +++ b/app/bot/preppost_test.go @@ -11,7 +11,6 @@ import ( "github.com/radio-t/super-bot/app/bot/mocks" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" ) func TestPrepPost_OnMessage(t *testing.T) { @@ -54,10 +53,12 @@ func TestPrepPost_OnMessage(t *testing.T) { for i, tt := range tbl { t.Run(strconv.Itoa(i), func(t *testing.T) { - mockHTTP.On("Do", mock.Anything).Return(&http.Response{ - Body: io.NopCloser(strings.NewReader(tt.body)), - StatusCode: tt.status, - }, tt.err).Times(1) + mockHTTP.DoFunc = func(req *http.Request) (*http.Response, error) { + return &http.Response{ + Body: io.NopCloser(strings.NewReader(tt.body)), + StatusCode: tt.status, + }, tt.err + } resp := pp.OnMessage(Message{}) assert.Equal(t, tt.resp, resp) time.Sleep(time.Millisecond * 11) @@ -67,24 +68,26 @@ func TestPrepPost_OnMessage(t *testing.T) { } func TestPrepPost_checkDuration(t *testing.T) { - mockHTTP := &mocks.HTTPClient{} + hit := false + mockHTTP := &mocks.HTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) { + if !hit { + hit = true + return &http.Response{ + Body: io.NopCloser(strings.NewReader(`[{"url":"blah1","title":"Темы для 689","categories":["prep"]}]`)), + StatusCode: 200, + }, nil + } + return &http.Response{ + Body: io.NopCloser(strings.NewReader(`[{"url":"blah2","title":"Темы для 689","categories":["prep"]}]`)), + StatusCode: 200, + }, nil + }} pp := NewPrepPost(mockHTTP, "http://example.com", time.Millisecond*50) - mockHTTP.On("Do", mock.Anything).Return(&http.Response{ - Body: io.NopCloser(strings.NewReader(`[{"url":"blah1","title":"Темы для 689","categories":["prep"]}]`)), - StatusCode: 200, - }, nil).Times(1) - - mockHTTP.On("Do", mock.Anything).Return(&http.Response{ - Body: io.NopCloser(strings.NewReader(`[{"url":"blah2","title":"Темы для 689","categories":["prep"]}]`)), - StatusCode: 200, - }, nil).Times(1) - for i := 0; i < 10; i++ { pp.OnMessage(Message{}) time.Sleep(6 * time.Millisecond) } - mockHTTP.AssertNumberOfCalls(t, "Do", 2) - mockHTTP.AssertExpectations(t) + assert.Equal(t, 2, len(mockHTTP.DoCalls())) } diff --git a/app/bot/wtf_test.go b/app/bot/wtf_test.go index 7cc09d66..ef37d6ba 100644 --- a/app/bot/wtf_test.go +++ b/app/bot/wtf_test.go @@ -11,9 +11,12 @@ import ( ) func TestWTF_OnMessage(t *testing.T) { - su := &mocks.SuperUser{} - su.On("IsSuper", "user").Return(false) - su.On("IsSuper", "super").Return(true) + su := &mocks.SuperUser{IsSuperFunc: func(userName string) bool { + if userName == "super" { + return true + } + return false + }} min := time.Hour * 24 max := 7 * time.Hour * 24 b := NewWTF(min, max, su) @@ -59,6 +62,7 @@ func TestWTF_OnMessage(t *testing.T) { assert.True(t, resp.Send) assert.Equal(t, min+10*time.Second+5*59*time.Hour, resp.BanInterval) } + assert.Equal(t, 9, len(su.IsSuperCalls())) } func TestWTF_Help(t *testing.T) { diff --git a/app/events/mock_msg_logger.go b/app/events/mock_msg_logger.go index 3eb2a3f2..5142a493 100644 --- a/app/events/mock_msg_logger.go +++ b/app/events/mock_msg_logger.go @@ -1,33 +1,75 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by moq; DO NOT EDIT. +// github.com/matryer/moq package events import ( bot "github.com/radio-t/super-bot/app/bot" - mock "github.com/stretchr/testify/mock" + "sync" ) -// mockMsgLogger is an autogenerated mock type for the msgLogger type -type mockMsgLogger struct { - mock.Mock -} +// Ensure, that msgLoggerMock does implement msgLogger. +// If this is not the case, regenerate this file with moq. +var _ msgLogger = &msgLoggerMock{} -// Save provides a mock function with given fields: msg -func (_m *mockMsgLogger) Save(msg *bot.Message) { - _m.Called(msg) -} +// msgLoggerMock is a mock implementation of msgLogger. +// +// func TestSomethingThatUsesmsgLogger(t *testing.T) { +// +// // make and configure a mocked msgLogger +// mockedmsgLogger := &msgLoggerMock{ +// SaveFunc: func(msg *bot.Message) { +// panic("mock out the Save method") +// }, +// } +// +// // use mockedmsgLogger in code that requires msgLogger +// // and then make assertions. +// +// } +type msgLoggerMock struct { + // SaveFunc mocks the Save method. + SaveFunc func(msg *bot.Message) -type mockConstructorTestingTnewMockMsgLogger interface { - mock.TestingT - Cleanup(func()) + // calls tracks calls to the methods. + calls struct { + // Save holds details about calls to the Save method. + Save []struct { + // Msg is the msg argument value. + Msg *bot.Message + } + } + lockSave sync.RWMutex } -// newMockMsgLogger creates a new instance of mockMsgLogger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func newMockMsgLogger(t mockConstructorTestingTnewMockMsgLogger) *mockMsgLogger { - mock := &mockMsgLogger{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) +// Save calls SaveFunc. +func (mock *msgLoggerMock) Save(msg *bot.Message) { + if mock.SaveFunc == nil { + panic("msgLoggerMock.SaveFunc: method is nil but msgLogger.Save was just called") + } + callInfo := struct { + Msg *bot.Message + }{ + Msg: msg, + } + mock.lockSave.Lock() + mock.calls.Save = append(mock.calls.Save, callInfo) + mock.lockSave.Unlock() + mock.SaveFunc(msg) +} - return mock +// SaveCalls gets all the calls that were made to Save. +// Check the length with: +// +// len(mockedmsgLogger.SaveCalls()) +func (mock *msgLoggerMock) SaveCalls() []struct { + Msg *bot.Message +} { + var calls []struct { + Msg *bot.Message + } + mock.lockSave.RLock() + calls = mock.calls.Save + mock.lockSave.RUnlock() + return calls } diff --git a/app/events/telegram.go b/app/events/telegram.go index 9df5bba3..f910b620 100644 --- a/app/events/telegram.go +++ b/app/events/telegram.go @@ -15,7 +15,7 @@ import ( ) //go:generate mockery --inpackage --name tbAPI --case snake -//go:generate mockery --inpackage --name msgLogger --case snake +//go:generate moq --out mock_msg_logger.go . msgLogger // TelegramListener listens to tg update, forward to bots and send back responses // Not thread safe diff --git a/app/events/telegram_test.go b/app/events/telegram_test.go index 377fb2fe..fcf123b3 100644 --- a/app/events/telegram_test.go +++ b/app/events/telegram_test.go @@ -13,9 +13,11 @@ import ( ) func TestTelegramListener_DoNoBots(t *testing.T) { - msgLogger := &mockMsgLogger{} + msgLogger := &msgLoggerMock{SaveFunc: func(msg *bot.Message) { return }} tbAPI := &mockTbAPI{} - bots := &bot.MockInterface{} + bots := &bot.InterfaceMock{OnMessageFunc: func(msg bot.Message) bot.Response { + return bot.Response{Send: false} + }} l := TelegramListener{ MsgLogger: msgLogger, @@ -42,22 +44,24 @@ func TestTelegramListener_DoNoBots(t *testing.T) { close(updChan) tbAPI.On("GetUpdatesChan", mock.Anything).Return(tbapi.UpdatesChannel(updChan), nil) - bots.On("OnMessage", mock.Anything).Return(bot.Response{Send: false}) - msgLogger.On("Save", mock.MatchedBy(func(msg *bot.Message) bool { - t.Logf("%v", msg) - return msg.Text == "text 123" && msg.From.Username == "user" - })) err := l.Do(ctx) assert.EqualError(t, err, "telegram update chan closed") - msgLogger.AssertExpectations(t) - msgLogger.AssertNumberOfCalls(t, "Save", 1) + assert.Equal(t, 1, len(msgLogger.SaveCalls())) + assert.Equal(t, "text 123", msgLogger.SaveCalls()[0].Msg.Text) + assert.Equal(t, "user", msgLogger.SaveCalls()[0].Msg.From.Username) } func TestTelegramListener_DoWithBots(t *testing.T) { - msgLogger := &mockMsgLogger{} + msgLogger := &msgLoggerMock{SaveFunc: func(msg *bot.Message) { return }} tbAPI := &mockTbAPI{} - bots := &bot.MockInterface{} + bots := &bot.InterfaceMock{OnMessageFunc: func(msg bot.Message) bot.Response { + t.Logf("on-message: %+v", msg) + if msg.Text == "text 123" && msg.From.Username == "user" { + return bot.Response{Send: true, Text: "bot's answer"} + } + return bot.Response{} + }} l := TelegramListener{ MsgLogger: msgLogger, @@ -85,20 +89,6 @@ func TestTelegramListener_DoWithBots(t *testing.T) { close(updChan) tbAPI.On("GetUpdatesChan", mock.Anything).Return(tbapi.UpdatesChannel(updChan), nil) - bots.On("OnMessage", mock.MatchedBy(func(msg bot.Message) bool { - t.Logf("on-message: %+v", msg) - return msg.Text == "text 123" && msg.From.Username == "user" - })).Return(bot.Response{Send: true, Text: "bot's answer"}) - - msgLogger.On("Save", mock.MatchedBy(func(msg *bot.Message) bool { - t.Logf("save: %+v", msg) - return msg.Text == "text 123" && msg.From.Username == "user" - })) - msgLogger.On("Save", mock.MatchedBy(func(msg *bot.Message) bool { - t.Logf("save: %+v", msg) - return msg.Text == "bot's answer" - })) - tbAPI.On("Send", mock.MatchedBy(func(c tbapi.MessageConfig) bool { t.Logf("send: %+v", c) return c.Text == "bot's answer" @@ -106,15 +96,17 @@ func TestTelegramListener_DoWithBots(t *testing.T) { err := l.Do(ctx) assert.EqualError(t, err, "telegram update chan closed") - msgLogger.AssertExpectations(t) - msgLogger.AssertNumberOfCalls(t, "Save", 2) + assert.Equal(t, 2, len(msgLogger.SaveCalls())) + assert.Equal(t, "text 123", msgLogger.SaveCalls()[0].Msg.Text) + assert.Equal(t, "user", msgLogger.SaveCalls()[0].Msg.From.Username) + assert.Equal(t, "bot's answer", msgLogger.SaveCalls()[1].Msg.Text) tbAPI.AssertNumberOfCalls(t, "Send", 1) } func TestTelegramListener_DoWithRtjc(t *testing.T) { - msgLogger := &mockMsgLogger{} + msgLogger := &msgLoggerMock{SaveFunc: func(msg *bot.Message) { return }} tbAPI := &mockTbAPI{} - bots := &bot.MockInterface{} + bots := &bot.InterfaceMock{} l := TelegramListener{ MsgLogger: msgLogger, @@ -132,11 +124,6 @@ func TestTelegramListener_DoWithRtjc(t *testing.T) { tbAPI.On("GetUpdatesChan", mock.Anything).Return(tbapi.UpdatesChannel(updChan), nil) - msgLogger.On("Save", mock.MatchedBy(func(msg *bot.Message) bool { - t.Logf("save: %+v", msg) - return msg.Text == "rtjc message" - })) - tbAPI.On("Send", mock.MatchedBy(func(c tbapi.MessageConfig) bool { t.Logf("send: %+v", c) return c.Text == "rtjc message" @@ -149,16 +136,18 @@ func TestTelegramListener_DoWithRtjc(t *testing.T) { err := l.Do(ctx) assert.EqualError(t, err, "context deadline exceeded") - msgLogger.AssertExpectations(t) - msgLogger.AssertNumberOfCalls(t, "Save", 1) + assert.Equal(t, 1, len(msgLogger.SaveCalls())) + assert.Equal(t, "rtjc message", msgLogger.SaveCalls()[0].Msg.Text) tbAPI.AssertNumberOfCalls(t, "Send", 1) tbAPI.AssertNumberOfCalls(t, "PinChatMessage", 1) } func TestTelegramListener_DoWithAutoBan(t *testing.T) { - msgLogger := &mockMsgLogger{} + msgLogger := &msgLoggerMock{SaveFunc: func(msg *bot.Message) { return }} tbAPI := &mockTbAPI{} - bots := &bot.MockInterface{} + bots := &bot.InterfaceMock{OnMessageFunc: func(msg bot.Message) bot.Response { + return bot.Response{Send: false} + }} l := TelegramListener{ MsgLogger: msgLogger, @@ -197,16 +186,6 @@ func TestTelegramListener_DoWithAutoBan(t *testing.T) { tbAPI.On("GetUpdatesChan", mock.Anything).Return(tbapi.UpdatesChannel(updChan), nil) - bots.On("OnMessage", mock.Anything).Return(bot.Response{Send: false}) - msgLogger.On("Save", mock.MatchedBy(func(msg *bot.Message) bool { - t.Logf("%v", msg) - return msg.Text == "text 123" && msg.From.Username == "user_name" - })) - msgLogger.On("Save", mock.MatchedBy(func(msg *bot.Message) bool { - t.Logf("%v", msg) - return msg.Text == "[@user_name](tg://user?id=1) _тебя слишком много, отдохни..._" && msg.From.Username == "user_name" - })).Once() - tbAPI.On("Send", mock.MatchedBy(func(c tbapi.MessageConfig) bool { t.Logf("send: %+v", c) return c.Text == "[@user_name](tg://user?id=1) _тебя слишком много, отдохни..._" @@ -217,14 +196,23 @@ func TestTelegramListener_DoWithAutoBan(t *testing.T) { assert.EqualError(t, err, "telegram update chan closed") tbAPI.AssertNumberOfCalls(t, "Send", 1) - msgLogger.AssertExpectations(t) - msgLogger.AssertNumberOfCalls(t, "Save", 6) + assert.Equal(t, 6, len(msgLogger.SaveCalls())) + assert.Equal(t, "text 123", msgLogger.SaveCalls()[0].Msg.Text) + assert.Equal(t, "user_name", msgLogger.SaveCalls()[0].Msg.From.Username) + assert.Equal(t, "user_name", msgLogger.SaveCalls()[5].Msg.From.Username) + assert.Equal(t, "[@user_name](tg://user?id=1) _тебя слишком много, отдохни..._", msgLogger.SaveCalls()[4].Msg.Text) } func TestTelegramListener_DoWithBotBan(t *testing.T) { - msgLogger := &mockMsgLogger{} + msgLogger := &msgLoggerMock{SaveFunc: func(msg *bot.Message) { return }} tbAPI := &mockTbAPI{} - bots := &bot.MockInterface{} + bots := &bot.InterfaceMock{OnMessageFunc: func(msg bot.Message) bot.Response { + t.Logf("on-message: %+v", msg) + if msg.Text == "text 123" && msg.From.Username == "user" { + return bot.Response{Send: true, Text: "bot's answer", BanInterval: 2 * time.Minute, User: bot.User{Username: "user", ID: 1}} + } + return bot.Response{} + }} l := TelegramListener{ MsgLogger: msgLogger, @@ -252,20 +240,6 @@ func TestTelegramListener_DoWithBotBan(t *testing.T) { close(updChan) tbAPI.On("GetUpdatesChan", mock.Anything).Return(tbapi.UpdatesChannel(updChan), nil) - bots.On("OnMessage", mock.MatchedBy(func(msg bot.Message) bool { - t.Logf("on-message: %+v", msg) - return msg.Text == "text 123" && msg.From.Username == "user" - })).Return(bot.Response{Send: true, Text: "bot's answer", BanInterval: 2 * time.Minute, User: bot.User{Username: "user", ID: 1}}) - - msgLogger.On("Save", mock.MatchedBy(func(msg *bot.Message) bool { - t.Logf("save: %+v", msg) - return msg.Text == "text 123" && msg.From.Username == "user" - })) - msgLogger.On("Save", mock.MatchedBy(func(msg *bot.Message) bool { - t.Logf("save: %+v", msg) - return msg.Text == "bot's answer" - })) - tbAPI.On("Send", mock.MatchedBy(func(c tbapi.MessageConfig) bool { t.Logf("send: %+v", c) return c.Text == "bot's answer" @@ -275,17 +249,24 @@ func TestTelegramListener_DoWithBotBan(t *testing.T) { err := l.Do(ctx) assert.EqualError(t, err, "telegram update chan closed") - msgLogger.AssertExpectations(t) - msgLogger.AssertNumberOfCalls(t, "Save", 2) + assert.Equal(t, 2, len(msgLogger.SaveCalls())) + assert.Equal(t, "text 123", msgLogger.SaveCalls()[0].Msg.Text) + assert.Equal(t, "user", msgLogger.SaveCalls()[0].Msg.From.Username) + assert.Equal(t, "bot's answer", msgLogger.SaveCalls()[1].Msg.Text) tbAPI.AssertNumberOfCalls(t, "Send", 1) } func TestTelegramListener_DoPinMessages(t *testing.T) { - msgLogger := &mockMsgLogger{} - msgLogger.On("Save", mock.Anything).Return() + msgLogger := &msgLoggerMock{SaveFunc: func(msg *bot.Message) { return }} tbAPI := &mockTbAPI{} - bots := &bot.MockInterface{} + bots := &bot.InterfaceMock{OnMessageFunc: func(msg bot.Message) bot.Response { + t.Logf("on-message: %+v", msg) + if msg.Text == "text 123" && msg.From.Username == "user" { + return bot.Response{Send: true, Text: "bot's answer", Pin: true} + } + return bot.Response{} + }} l := TelegramListener{ MsgLogger: msgLogger, @@ -313,11 +294,6 @@ func TestTelegramListener_DoPinMessages(t *testing.T) { close(updChan) tbAPI.On("GetUpdatesChan", mock.Anything).Return(tbapi.UpdatesChannel(updChan), nil) - bots.On("OnMessage", mock.MatchedBy(func(msg bot.Message) bool { - t.Logf("on-message: %+v", msg) - return msg.Text == "text 123" && msg.From.Username == "user" - })).Return(bot.Response{Send: true, Text: "bot's answer", Pin: true}) - tbAPI.On("Send", mock.MatchedBy(func(c tbapi.MessageConfig) bool { t.Logf("send: %+v", c) return c.Text == "bot's answer" @@ -330,16 +306,21 @@ func TestTelegramListener_DoPinMessages(t *testing.T) { err := l.Do(ctx) assert.EqualError(t, err, "telegram update chan closed") - bots.AssertExpectations(t) + assert.Equal(t, 1, len(bots.OnMessageCalls())) tbAPI.AssertExpectations(t) } func TestTelegramListener_DoUnpinMessages(t *testing.T) { - msgLogger := &mockMsgLogger{} - msgLogger.On("Save", mock.Anything).Return() + msgLogger := &msgLoggerMock{SaveFunc: func(msg *bot.Message) { return }} tbAPI := &mockTbAPI{} - bots := &bot.MockInterface{} + bots := &bot.InterfaceMock{OnMessageFunc: func(msg bot.Message) bot.Response { + t.Logf("on-message: %+v", msg) + if msg.Text == "text 123" && msg.From.Username == "user" { + return bot.Response{Send: true, Text: "bot's answer", Unpin: true} + } + return bot.Response{} + }} l := TelegramListener{ MsgLogger: msgLogger, @@ -367,11 +348,6 @@ func TestTelegramListener_DoUnpinMessages(t *testing.T) { close(updChan) tbAPI.On("GetUpdatesChan", mock.Anything).Return(tbapi.UpdatesChannel(updChan), nil) - bots.On("OnMessage", mock.MatchedBy(func(msg bot.Message) bool { - t.Logf("on-message: %+v", msg) - return msg.Text == "text 123" && msg.From.Username == "user" - })).Return(bot.Response{Send: true, Text: "bot's answer", Unpin: true}) - tbAPI.On("Send", mock.MatchedBy(func(c tbapi.MessageConfig) bool { t.Logf("send: %+v", c) return c.Text == "bot's answer" @@ -384,14 +360,16 @@ func TestTelegramListener_DoUnpinMessages(t *testing.T) { err := l.Do(ctx) assert.EqualError(t, err, "telegram update chan closed") - bots.AssertExpectations(t) + assert.Equal(t, 1, len(bots.OnMessageCalls())) tbAPI.AssertExpectations(t) } func TestTelegramListener_DoNotSaveMessagesFromOtherChats(t *testing.T) { - msgLogger := &mockMsgLogger{} + msgLogger := &msgLoggerMock{SaveFunc: func(msg *bot.Message) { return }} tbAPI := &mockTbAPI{} - bots := &bot.MockInterface{} + bots := &bot.InterfaceMock{OnMessageFunc: func(msg bot.Message) bot.Response { + return bot.Response{Send: true, Text: "bot's answer"} + }} l := TelegramListener{ MsgLogger: msgLogger, @@ -419,14 +397,13 @@ func TestTelegramListener_DoNotSaveMessagesFromOtherChats(t *testing.T) { close(updChan) tbAPI.On("GetUpdatesChan", mock.Anything).Return(tbapi.UpdatesChannel(updChan), nil) - bots.On("OnMessage", mock.Anything).Return(bot.Response{Send: true, Text: "bot's answer"}) tbAPI.On("Send", mock.Anything).Return(tbapi.Message{Text: "bot's answer", From: &tbapi.User{UserName: "user"}}, nil) err := l.Do(ctx) assert.EqualError(t, err, "telegram update chan closed") - msgLogger.AssertNotCalled(t, "Save", mock.Anything) - bots.AssertExpectations(t) + assert.Equal(t, 0, len(msgLogger.SaveCalls())) + assert.Equal(t, 1, len(bots.OnMessageCalls())) tbAPI.AssertExpectations(t) }