From 0762feaa697fc278e2de726432e9f46b97817855 Mon Sep 17 00:00:00 2001 From: Chris Busbey Date: Tue, 30 Aug 2016 11:06:26 -0500 Subject: [PATCH] fixes issue where initiator ignores toAdmin reset seqnum --- logon_state_test.go | 67 +++++++++++++++++++++++++++++++++++++++++++++ quickfix_test.go | 10 +++++-- session.go | 27 +++++++++++++++++- session_test.go | 29 ++++++++++++++++++++ 4 files changed, 130 insertions(+), 3 deletions(-) diff --git a/logon_state_test.go b/logon_state_test.go index ef02cc5c0..b7576b7bf 100644 --- a/logon_state_test.go +++ b/logon_state_test.go @@ -91,6 +91,32 @@ func (s *LogonStateTestSuite) TestFixMsgInLogon() { s.NextSenderMsgSeqNum(3) } +func (s *LogonStateTestSuite) TestFixMsgInLogonResetSeqNum() { + s.Require().Nil(s.store.IncrNextTargetMsgSeqNum()) + + logon := s.Logon() + logon.Body.SetField(tagHeartBtInt, FIXInt(32)) + logon.Body.SetField(tagResetSeqNumFlag, FIXBoolean(true)) + + s.MockApp.On("FromAdmin").Return(nil) + s.MockApp.On("OnLogon") + s.MockApp.On("ToAdmin") + s.fixMsgIn(s.session, logon) + + s.MockApp.AssertExpectations(s.T()) + + s.State(inSession{}) + s.Equal(32*time.Second, s.session.HeartBtInt) + + s.LastToAdminMessageSent() + s.MessageType(enum.MsgType_LOGON, s.MockApp.lastToAdmin) + s.FieldEquals(tagHeartBtInt, 32, s.MockApp.lastToAdmin.Body) + s.FieldEquals(tagResetSeqNumFlag, true, s.MockApp.lastToAdmin.Body) + + s.NextTargetMsgSeqNum(2) + s.NextSenderMsgSeqNum(2) +} + func (s *LogonStateTestSuite) TestFixMsgInLogonInitiateLogon() { s.session.InitiateLogon = true s.Require().Nil(s.store.IncrNextSenderMsgSeqNum()) @@ -111,6 +137,47 @@ func (s *LogonStateTestSuite) TestFixMsgInLogonInitiateLogon() { s.NextSenderMsgSeqNum(2) } +func (s *LogonStateTestSuite) TestFixMsgInLogonInitiateLogonExpectResetSeqNum() { + s.session.InitiateLogon = true + s.session.sentReset = true + s.Require().Nil(s.store.IncrNextSenderMsgSeqNum()) + + logon := s.Logon() + logon.Body.SetField(tagHeartBtInt, FIXInt(32)) + logon.Body.SetField(tagResetSeqNumFlag, FIXBoolean(true)) + + s.MockApp.On("FromAdmin").Return(nil) + s.MockApp.On("OnLogon") + s.fixMsgIn(s.session, logon) + + s.MockApp.AssertExpectations(s.T()) + s.State(inSession{}) + + s.NextTargetMsgSeqNum(2) + s.NextSenderMsgSeqNum(2) +} + +func (s *LogonStateTestSuite) TestFixMsgInLogonInitiateLogonUnExpectedResetSeqNum() { + s.session.InitiateLogon = true + s.session.sentReset = false + s.Require().Nil(s.store.IncrNextTargetMsgSeqNum()) + s.Require().Nil(s.store.IncrNextSenderMsgSeqNum()) + + logon := s.Logon() + logon.Body.SetField(tagHeartBtInt, FIXInt(32)) + logon.Body.SetField(tagResetSeqNumFlag, FIXBoolean(true)) + + s.MockApp.On("FromAdmin").Return(nil) + s.MockApp.On("OnLogon") + s.fixMsgIn(s.session, logon) + + s.MockApp.AssertExpectations(s.T()) + s.State(inSession{}) + + s.NextTargetMsgSeqNum(2) + s.NextSenderMsgSeqNum(1) +} + func (s *LogonStateTestSuite) TestFixMsgInLogonRefreshOnLogon() { var tests = []bool{true, false} diff --git a/quickfix_test.go b/quickfix_test.go index 7d799a63c..8c7c75700 100644 --- a/quickfix_test.go +++ b/quickfix_test.go @@ -65,8 +65,9 @@ func (s *MockStore) Refresh() error { type MockApp struct { mock.Mock - lastToAdmin Message - lastToApp Message + decorateToAdmin func(Message) + lastToAdmin Message + lastToApp Message } func (e *MockApp) OnCreate(sessionID SessionID) { @@ -90,6 +91,11 @@ func (e *MockApp) FromAdmin(msg Message, sessionID SessionID) (reject MessageRej func (e *MockApp) ToAdmin(msg Message, sessionID SessionID) { e.Called() + + if e.decorateToAdmin != nil { + e.decorateToAdmin(msg) + } + e.lastToAdmin = msg } diff --git a/session.go b/session.go index aba7efa5d..8d004b36d 100644 --- a/session.go +++ b/session.go @@ -34,6 +34,7 @@ type session struct { stateTimer internal.EventTimer peerTimer internal.EventTimer messageStash map[int]Message + sentReset bool targetDefaultApplVerID string @@ -241,6 +242,26 @@ func (s *session) prepMessageForSend(msg *Message) error { if isAdminMessageType(string(msgType)) { s.application.ToAdmin(*msg, s.sessionID) + + if msgType.String() == enum.MsgType_LOGON { + var resetSeqNumFlag FIXBoolean + if msg.Body.Has(tagResetSeqNumFlag) { + if err := msg.Body.GetField(tagResetSeqNumFlag, &resetSeqNumFlag); err != nil { + return err + } + } + + if resetSeqNumFlag.Bool() { + if err := s.store.Reset(); err != nil { + return err + } + + s.sentReset = true + seqNum = s.store.NextSenderMsgSeqNum() + msg.Header.SetField(tagMsgSeqNum, FIXInt(seqNum)) + } + + } } else { if err := s.application.ToApp(*msg, s.sessionID); err != nil { return err @@ -333,7 +354,9 @@ func (s *session) handleLogon(msg Message) error { if err := msg.Body.GetField(tagResetSeqNumFlag, &resetSeqNumFlag); err == nil { if resetSeqNumFlag { s.log.OnEvent("Logon contains ResetSeqNumFlag=Y, resetting sequence numbers to 1") - resetStore = true + if !s.sentReset { + resetStore = true + } } } @@ -358,6 +381,7 @@ func (s *session) handleLogon(msg Message) error { return err } } + s.sentReset = false s.peerTimer.Reset(time.Duration(float64(1.2) * float64(s.HeartBtInt))) s.application.OnLogon(s.sessionID) @@ -596,6 +620,7 @@ func (s *session) onAdmin(msg interface{}) { s.messageIn = msg.messageIn s.messageOut = msg.messageOut s.messageStash = make(map[int]Message) + s.sentReset = false s.Connect(s) diff --git a/session_test.go b/session_test.go index b1cb64c4a..e7fbe2dbb 100644 --- a/session_test.go +++ b/session_test.go @@ -490,6 +490,7 @@ func (s *SessionSuite) TestOnAdminConnectInitiateLogon() { s.MockApp.AssertExpectations(s.T()) s.True(s.session.InitiateLogon) + s.False(s.sentReset) s.State(logonState{}) s.LastToAdminMessageSent() s.MessageType(enum.MsgType_LOGON, s.MockApp.lastToAdmin) @@ -498,6 +499,34 @@ func (s *SessionSuite) TestOnAdminConnectInitiateLogon() { s.NextSenderMsgSeqNum(3) } +func (s *SessionSuite) TestInitiateLogonResetSeqNumFlag() { + adminMsg := connect{ + messageOut: s.Receiver.sendChannel, + } + s.session.State = latentState{} + s.session.HeartBtInt = time.Duration(45) * time.Second + s.session.store.IncrNextTargetMsgSeqNum() + s.session.store.IncrNextSenderMsgSeqNum() + s.session.InitiateLogon = true + + s.MockApp.On("ToAdmin") + s.MockApp.decorateToAdmin = func(msg Message) { + msg.Body.SetField(tagResetSeqNumFlag, FIXBoolean(true)) + } + s.session.onAdmin(adminMsg) + + s.MockApp.AssertExpectations(s.T()) + s.True(s.session.InitiateLogon) + s.True(s.sentReset) + s.State(logonState{}) + s.LastToAdminMessageSent() + s.MessageType(enum.MsgType_LOGON, s.MockApp.lastToAdmin) + s.FieldEquals(tagMsgSeqNum, 1, s.MockApp.lastToAdmin.Header) + s.FieldEquals(tagResetSeqNumFlag, true, s.MockApp.lastToAdmin.Body) + s.NextSenderMsgSeqNum(2) + s.NextTargetMsgSeqNum(1) +} + func (s *SessionSuite) TestOnAdminConnectInitiateLogonFIXT11() { s.session.sessionID.BeginString = enum.BeginStringFIXT11 s.session.DefaultApplVerID = "8"