diff --git a/in_session.go b/in_session.go index 6fb8043a9..bcb6a464d 100644 --- a/in_session.go +++ b/in_session.go @@ -100,7 +100,7 @@ func (state inSession) Timeout(session *session, event event) (nextState session } session.log.OnEvent("Sent test request TEST") session.peerTimer.Reset(time.Duration(int64(1.2 * float64(session.heartBeatTimeout)))) - return pendingTimeout{} + return pendingTimeout{state} } return state } diff --git a/pending_timeout.go b/pending_timeout.go index f2e8dfa5b..ebdd0cfe3 100644 --- a/pending_timeout.go +++ b/pending_timeout.go @@ -1,15 +1,15 @@ package quickfix type pendingTimeout struct { - inSession + sessionState } -func (currentState pendingTimeout) Timeout(session *session, event event) (nextState sessionState) { +func (s pendingTimeout) Timeout(session *session, event event) (nextState sessionState) { switch event { case peerTimeout: session.log.OnEvent("Session Timeout") return latentState{} } - return currentState + return s } diff --git a/pending_timeout_test.go b/pending_timeout_test.go new file mode 100644 index 000000000..b263fda2a --- /dev/null +++ b/pending_timeout_test.go @@ -0,0 +1,43 @@ +package quickfix + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPendingTimeout_SessionTimeout(t *testing.T) { + session := &session{ + log: nullLog{}, + } + + tests := []pendingTimeout{ + pendingTimeout{inSession{}}, + pendingTimeout{resendState{}}, + } + + for _, state := range tests { + nextState := state.Timeout(session, peerTimeout) + assert.IsType(t, latentState{}, nextState) + } +} + +func TestPendingTimeout_TimeoutUnchangedState(t *testing.T) { + session := &session{ + log: nullLog{}, + } + + tests := []pendingTimeout{ + pendingTimeout{inSession{}}, + pendingTimeout{resendState{}}, + } + + testEvents := []event{needHeartbeat, logonTimeout, logoutTimeout} + + for _, state := range tests { + for _, event := range testEvents { + nextState := state.Timeout(session, event) + assert.Equal(t, state, nextState) + } + } +} diff --git a/resend_state.go b/resend_state.go index d27e855f3..78b602408 100644 --- a/resend_state.go +++ b/resend_state.go @@ -1,20 +1,39 @@ package quickfix -type resendState struct { - inSession +type resendState struct{} + +func (s resendState) String() string { return "Resend" } + +func (s resendState) IsLoggedOn() bool { return true } + +func (s resendState) Timeout(session *session, event event) (nextState sessionState) { + nextState = inSession{}.Timeout(session, event) + switch nextState.(type) { + case inSession: + nextState = s + case pendingTimeout: + //wrap pendingTimeout in resend. prevents us falling back to inSession if recovering + //from pendingTimeout + nextState = pendingTimeout{s} + } + + return } -func (state resendState) String() string { return "Resend" } +func (s resendState) VerifyMsgIn(session *session, msg Message) (err MessageRejectError) { + return inSession{}.VerifyMsgIn(session, msg) +} -func (state resendState) FixMsgIn(session *session, msg Message) (nextState sessionState) { - return state.handleNextState(session, state.inSession.FixMsgIn(session, msg)) +func (s resendState) FixMsgIn(session *session, msg Message) (nextState sessionState) { + session.log.OnEventf("Got FIXMsgIn in resend %s", msg.rawMessage) + return s.handleNextState(session, inSession{}.FixMsgIn(session, msg)) } -func (state resendState) FixMsgInRej(session *session, msg Message, rej MessageRejectError) (nextState sessionState) { - return state.handleNextState(session, state.inSession.FixMsgInRej(session, msg, rej)) +func (s resendState) FixMsgInRej(session *session, msg Message, rej MessageRejectError) (nextState sessionState) { + return s.handleNextState(session, inSession{}.FixMsgInRej(session, msg, rej)) } -func (state resendState) handleNextState(session *session, nextState sessionState) sessionState { +func (s resendState) handleNextState(session *session, nextState sessionState) sessionState { if !nextState.IsLoggedOn() || len(session.messageStash) == 0 { return nextState } @@ -25,5 +44,5 @@ func (state resendState) handleNextState(session *session, nextState sessionStat session.resendIn <- msg } - return resendState{} + return s } diff --git a/resend_state_test.go b/resend_state_test.go new file mode 100644 index 000000000..d8da1cb25 --- /dev/null +++ b/resend_state_test.go @@ -0,0 +1,50 @@ +package quickfix + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestResendState_IsLoggedOn(t *testing.T) { + assert.True(t, resendState{}.IsLoggedOn()) +} + +func TestResendState_TimeoutPeerTimeout(t *testing.T) { + otherEnd := make(chan []byte) + go func() { + <-otherEnd + }() + + session := &session{ + store: new(memoryStore), + application: new(TestClient), + messageOut: otherEnd, + log: nullLog{}, + } + state := resendState{} + nextState := state.Timeout(session, peerTimeout) + assert.Equal(t, pendingTimeout{state}, nextState) +} + +func TestResendState_TimeoutUnchanged(t *testing.T) { + otherEnd := make(chan []byte) + go func() { + <-otherEnd + }() + + session := &session{ + store: new(memoryStore), + application: new(TestClient), + messageOut: otherEnd, + log: nullLog{}, + } + state := resendState{} + + tests := []event{needHeartbeat, logonTimeout, logoutTimeout} + + for _, event := range tests { + nextState := state.Timeout(session, event) + assert.Equal(t, state, nextState) + } +}