From e3adf71edff75df68040e908b84665098954494a Mon Sep 17 00:00:00 2001 From: vyourtchenko Date: Fri, 5 Dec 2025 19:52:21 -0600 Subject: [PATCH 1/2] Fixed broken tests after fixing parsing to validate checksum --- message.go | 45 +++++++++++++++++++++--- message_router_test.go | 24 +++++++++---- message_test.go | 14 ++++---- repeating_group_test.go | 2 +- validation_test.go | 77 +++++++++++++++++++++++------------------ 5 files changed, 111 insertions(+), 51 deletions(-) diff --git a/message.go b/message.go index 35e2ff675..010619b4a 100644 --- a/message.go +++ b/message.go @@ -182,11 +182,17 @@ func ParseMessageWithDataDictionary( // doParsing executes the message parsing process. func doParsing(mp *msgParser) (err error) { mp.msg.Header.rwLock.Lock() - defer mp.msg.Header.rwLock.Unlock() mp.msg.Body.rwLock.Lock() - defer mp.msg.Body.rwLock.Unlock() mp.msg.Trailer.rwLock.Lock() - defer mp.msg.Trailer.rwLock.Unlock() + + locked := true + defer func() { + if locked { + mp.msg.Header.rwLock.Unlock() + mp.msg.Body.rwLock.Unlock() + mp.msg.Trailer.rwLock.Unlock() + } + }() // Initialize for parsing. mp.msg.Header.clearNoLock() @@ -294,13 +300,44 @@ func doParsing(mp *msgParser) (err error) { } } - bodyLength, err := mp.msg.Header.getIntNoLock(tagBodyLength) + var bodyLength int + bodyLength, err = mp.msg.Header.getIntNoLock(tagBodyLength) if err != nil { err = parseError{OrigError: err.Error()} } else if length != bodyLength && !xmlDataMsg { err = parseError{OrigError: fmt.Sprintf("Incorrect Message Length, expected %d, got %d", bodyLength, length)} } + mp.msg.Header.rwLock.Unlock() + mp.msg.Body.rwLock.Unlock() + mp.msg.Trailer.rwLock.Unlock() + locked = false + if err != nil { + return err + } + + calculatedCheckSum := 0 + for i := 0; i <= mp.fieldIndex; i++ { + if mp.msg.fields[i].tag == tagCheckSum { + continue + } + calculatedCheckSum += mp.msg.fields[i].total() + } + + calculatedCheckSum = calculatedCheckSum % 256 + + var expectedCheckSumStr string + expectedCheckSumStr, err = mp.msg.Trailer.GetString(tagCheckSum) + if err != nil { + return parseError{OrigError: "CheckSum tag missing"} + } + + if expectedCheckSumStr != formatCheckSum(calculatedCheckSum) { + return parseError{ + OrigError: fmt.Sprintf("Expected CheckSum=%s, Received CheckSum=%s", formatCheckSum(calculatedCheckSum), expectedCheckSumStr), + } + } + return } diff --git a/message_router_test.go b/message_router_test.go index b2e7b59f5..1b1f8f7e4 100644 --- a/message_router_test.go +++ b/message_router_test.go @@ -115,7 +115,7 @@ func (suite *MessageRouterTestSuite) SetupTest() { } func (suite *MessageRouterTestSuite) TestNoRoute() { - suite.givenTheMessage([]byte("8=FIX.4.39=8735=D49=TW34=356=ISLD52=20160421-14:43:5040=160=20160421-14:43:5054=121=311=id10=235")) + suite.givenTheMessage([]byte("8=FIX.4.39=8735=D49=TW34=356=ISLD52=20160421-14:43:5040=160=20160421-14:43:5054=121=311=id10=236")) rej := suite.Route(suite.msg, suite.sessionID) suite.verifyMessageNotRouted() @@ -123,17 +123,29 @@ func (suite *MessageRouterTestSuite) TestNoRoute() { } func (suite *MessageRouterTestSuite) TestNoRouteWhitelistedMessageTypes() { - var tests = []string{"0", "A", "1", "2", "3", "4", "5", "j"} + var tests = []struct { + msgType string + checksum string + }{ + {"0", "216"}, + {"A", "233"}, + {"1", "217"}, + {"2", "218"}, + {"3", "219"}, + {"4", "220"}, + {"5", "221"}, + {"j", "018"}, + } for _, test := range tests { suite.SetupTest() - msg := fmt.Sprintf("8=FIX.4.39=8735=%v49=TW34=356=ISLD52=20160421-14:43:5040=160=20160421-14:43:5054=121=311=id10=235", test) + msg := fmt.Sprintf("8=FIX.4.39=8735=%s49=TW34=356=ISLD52=20160421-14:43:5040=160=20160421-14:43:5054=121=311=id10=%s", test.msgType, test.checksum) suite.givenTheMessage([]byte(msg)) rej := suite.Route(suite.msg, suite.sessionID) suite.verifyMessageNotRouted() - suite.Nil(rej, "Message type '%v' should not be rejected by the MessageRouter", test) + suite.Nil(rej, "Message type '%s' should not be rejected by the MessageRouter", test.msgType) } } @@ -174,7 +186,7 @@ func (suite *MessageRouterTestSuite) TestRouteFIXT50AppWithApplVerID() { suite.givenTheRoute(ApplVerIDFIX50, "D") suite.givenTheRoute(ApplVerIDFIX50SP1, "D") - suite.givenTheMessage([]byte("8=FIXT.1.19=8935=D49=TW34=356=ISLD52=20160424-16:48:261128=740=160=20160424-16:48:2611=id21=310=120")) + suite.givenTheMessage([]byte("8=FIXT.1.19=8935=D49=TW34=356=ISLD52=20160424-16:48:261128=740=160=20160424-16:48:2611=id21=310=192")) rej := suite.Route(suite.msg, suite.sessionID) suite.verifyMessageRoutedBy(ApplVerIDFIX50, "D") suite.Nil(rej) @@ -185,7 +197,7 @@ func (suite *MessageRouterTestSuite) TestRouteFIXTAppWithApplVerID() { suite.givenTheRoute(ApplVerIDFIX50, "D") suite.givenTheRoute(ApplVerIDFIX50SP1, "D") - suite.givenTheMessage([]byte("8=FIXT.1.19=8935=D49=TW34=356=ISLD52=20160424-16:48:261128=840=160=20160424-16:48:2611=id21=310=120")) + suite.givenTheMessage([]byte("8=FIXT.1.19=8935=D49=TW34=356=ISLD52=20160424-16:48:261128=840=160=20160424-16:48:2611=id21=310=193")) rej := suite.Route(suite.msg, suite.sessionID) suite.verifyMessageRoutedBy(ApplVerIDFIX50SP1, "D") suite.Nil(rej) diff --git a/message_test.go b/message_test.go index b02508cd9..bc8a386a9 100644 --- a/message_test.go +++ b/message_test.go @@ -65,7 +65,7 @@ func (s *MessageSuite) TestParseMessageEmpty() { } func (s *MessageSuite) TestParseMessage() { - rawMsg := bytes.NewBufferString("8=FIX.4.29=10435=D34=249=TW52=20140515-19:49:56.65956=ISLD11=10021=140=154=155=TSLA60=00010101-00:00:00.00010=039") + rawMsg := bytes.NewBufferString("8=FIX.4.29=10435=D34=249=TW52=20140515-19:49:56.65956=ISLD11=10021=140=154=155=TSLA60=00010101-00:00:00.00010=051") err := ParseMessage(s.msg, rawMsg) s.Nil(err) @@ -99,7 +99,7 @@ func (s *MessageSuite) TestParseMessageWithDataDictionary() { 5050: nil, }, } - rawMsg := bytes.NewBufferString("8=FIX.4.29=12635=D34=249=TW52=20140515-19:49:56.65956=ISLD10030=CUST11=10021=140=154=155=TSLA60=00010101-00:00:00.0005050=HELLO10=039") + rawMsg := bytes.NewBufferString("8=FIX.4.29=12635=D34=249=TW52=20140515-19:49:56.65956=ISLD10030=CUST11=10021=140=154=155=TSLA60=00010101-00:00:00.0005050=HELLO10=036") err := ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict) s.Nil(err) @@ -127,7 +127,7 @@ func (s *MessageSuite) TestBuild() { } func (s *MessageSuite) TestReBuild() { - rawMsg := bytes.NewBufferString("8=FIX.4.29=10435=D34=249=TW52=20140515-19:49:56.65956=ISLD11=10021=140=154=155=TSLA60=00010101-00:00:00.00010=039") + rawMsg := bytes.NewBufferString("8=FIX.4.29=10435=D34=249=TW52=20140515-19:49:56.65956=ISLD11=10021=140=154=155=TSLA60=00010101-00:00:00.00010=051") s.Nil(ParseMessage(s.msg, rawMsg)) @@ -419,7 +419,7 @@ func (s *MessageSuite) TestReBuildWithRepeatingGroupForResend() { } func (s *MessageSuite) TestReverseRoute() { - s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123"))) + s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=033"))) builder := s.msg.reverseRoute() @@ -450,7 +450,7 @@ func (s *MessageSuite) TestReverseRoute() { } func (s *MessageSuite) TestReverseRouteIgnoreEmpty() { - s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.09=12835=D34=249=TW52=20060102-15:04:0556=ISLD115=116=CS128=MG129=CB11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123"))) + s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.09=12835=D34=249=TW52=20060102-15:04:0556=ISLD115=116=CS128=MG129=CB11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=041"))) builder := s.msg.reverseRoute() s.False(builder.Header.Has(tagDeliverToCompID), "Should not reverse if empty") @@ -458,7 +458,7 @@ func (s *MessageSuite) TestReverseRouteIgnoreEmpty() { func (s *MessageSuite) TestReverseRouteFIX40() { // The onbehalfof/deliverto location id not supported in fix 4.0. - s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.09=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123"))) + s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.09=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=031"))) builder := s.msg.reverseRoute() @@ -468,7 +468,7 @@ func (s *MessageSuite) TestReverseRouteFIX40() { } func (s *MessageSuite) TestCopyIntoMessage() { - msgString := "8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123" + msgString := "8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=033" msgBuf := bytes.NewBufferString(msgString) s.Nil(ParseMessage(s.msg, msgBuf)) diff --git a/repeating_group_test.go b/repeating_group_test.go index abd316d78..e7d5db481 100644 --- a/repeating_group_test.go +++ b/repeating_group_test.go @@ -230,7 +230,7 @@ func TestRepeatingGroup_ReadRecursive(t *testing.T) { func TestRepeatingGroup_ReadComplete(t *testing.T) { - rawMsg := bytes.NewBufferString("8=FIXT.1.19=26835=W34=711849=TEST52=20151027-18:41:52.69856=TST22=9948=TSTX15262=7268=4269=4270=0.07499272=20151027273=18:41:52.698269=7270=0.07501272=20151027273=18:41:52.698269=8270=0.07494272=20151027273=18:41:52.698269=B271=60272=20151027273=18:41:52.69810=163") + rawMsg := bytes.NewBufferString("8=FIXT.1.19=26835=W34=711849=TEST52=20151027-18:41:52.69856=TST22=9948=TSTX15262=7268=4269=4270=0.07499272=20151027273=18:41:52.698269=7270=0.07501272=20151027273=18:41:52.698269=8270=0.07494272=20151027273=18:41:52.698269=B271=60272=20151027273=18:41:52.69810=094") msg := NewMessage() err := ParseMessage(msg, rawMsg) diff --git a/validation_test.go b/validation_test.go index 7973464d1..67217d4f1 100644 --- a/validation_test.go +++ b/validation_test.go @@ -20,8 +20,6 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/quickfixgo/quickfix/datadictionary" ) @@ -32,6 +30,7 @@ type validateTest struct { ExpectedRejectReason int ExpectedRefTagID *Tag DoNotExpectReject bool + ExpectParseError bool } func TestValidate(t *testing.T) { @@ -90,38 +89,49 @@ func TestValidate(t *testing.T) { msg := NewMessage() for _, test := range tests { - assert.Nil(t, ParseMessage(msg, bytes.NewBuffer(test.MessageBytes))) - reject := test.Validator.Validate(msg) + t.Run(test.TestName, func(t *testing.T) { + err := ParseMessage(msg, bytes.NewBuffer(test.MessageBytes)) + + if test.ExpectParseError { + if err == nil { + t.Errorf("%v: Expected ParseMessage to fail, but it succeeded", test.TestName) + } + return + } - switch { - case reject == nil && test.DoNotExpectReject: - continue + if err != nil { + t.Fatalf("ParseMessage failed: %v", err) + } + reject := test.Validator.Validate(msg) - case reject != nil && test.DoNotExpectReject: - t.Errorf("%v: Unexpected reject: %v", test.TestName, reject) - continue + switch { + case reject == nil && test.DoNotExpectReject: + return - case reject == nil: - t.Errorf("%v: Expected reject", test.TestName) - continue - } + case reject != nil && test.DoNotExpectReject: + t.Errorf("%v: Unexpected reject: %v", test.TestName, reject) + return - if reject.RejectReason() != test.ExpectedRejectReason { - t.Errorf("%v: Expected reason %v got %v", test.TestName, test.ExpectedRejectReason, reject.RejectReason()) - } + case reject == nil: + t.Errorf("%v: Expected reject", test.TestName) + return + } - switch { - case reject.RefTagID() == nil && test.ExpectedRefTagID == nil: - // OK, expected and actual ref tag not set. - case reject.RefTagID() != nil && test.ExpectedRefTagID == nil: - t.Errorf("%v: Unexpected RefTag '%v'", test.TestName, *reject.RefTagID()) - case reject.RefTagID() == nil && test.ExpectedRefTagID != nil: - t.Errorf("%v: Expected RefTag '%v'", test.TestName, *test.ExpectedRefTagID) - case *reject.RefTagID() == *test.ExpectedRefTagID: - // OK, tags equal. - default: - t.Errorf("%v: Expected RefTag '%v' got '%v'", test.TestName, *test.ExpectedRefTagID, *reject.RefTagID()) - } + if reject.RejectReason() != test.ExpectedRejectReason { + t.Errorf("%v: Expected reason %v got %v", test.TestName, test.ExpectedRejectReason, reject.RejectReason()) + } + + switch { + case reject.RefTagID() == nil && test.ExpectedRefTagID == nil: + case reject.RefTagID() != nil && test.ExpectedRefTagID == nil: + t.Errorf("%v: Unexpected RefTag '%v'", test.TestName, *reject.RefTagID()) + case reject.RefTagID() == nil && test.ExpectedRefTagID != nil: + t.Errorf("%v: Expected RefTag '%v'", test.TestName, *test.ExpectedRefTagID) + case *reject.RefTagID() == *test.ExpectedRefTagID: + default: + t.Errorf("%v: Expected RefTag '%v' got '%v'", test.TestName, *test.ExpectedRefTagID, *reject.RefTagID()) + } + }) } } @@ -201,6 +211,7 @@ func tcInvalidTagNumberHeader() validateTest { invalidHeaderFieldMessage := createFIX40NewOrderSingle() tag := Tag(9999) invalidHeaderFieldMessage.Header.SetField(tag, FIXString("hello")) + invalidHeaderFieldMessage.cook() msgBytes := invalidHeaderFieldMessage.build() return validateTest{ @@ -1109,7 +1120,7 @@ func tcTagAppearsMoreThanOnce() validateTest { return validateTest{ TestName: "Tag appears more than once", Validator: validator, - MessageBytes: []byte("8=FIX.4.09=10735=D34=249=TW52=20060102-15:04:0556=ISLD11=ID21=140=140=254=138=20055=INTC60=20060102-15:04:0510=234"), + MessageBytes: []byte("8=FIX.4.09=10735=D34=249=TW52=20060102-15:04:0556=ISLD11=ID21=140=140=254=138=20055=INTC60=20060102-15:04:0510=166"), ExpectedRejectReason: rejectReasonTagAppearsMoreThanOnce, ExpectedRefTagID: &tag, } @@ -1124,7 +1135,7 @@ func tcTagAppearsMoreThanOnceFixT() validateTest { return validateTest{ TestName: "Tag appears more than once FIXT", Validator: validator, - MessageBytes: []byte("8=FIXT.1.19=10735=D34=249=TW52=20060102-15:04:0556=ISLD11=ID21=140=140=254=138=20055=INTC60=20060102-15:04:0510=234"), + MessageBytes: []byte("8=FIXT.1.19=10735=D34=249=TW52=20060102-15:04:0556=ISLD11=ID21=140=140=254=138=20055=INTC60=20060102-15:04:0510=248"), ExpectedRejectReason: rejectReasonTagAppearsMoreThanOnce, ExpectedRefTagID: &tag, } @@ -1151,7 +1162,7 @@ func tcFloatValidationFixT() validateTest { return validateTest{ TestName: "FloatValidation FIXT", Validator: validator, - MessageBytes: []byte("8=FIXT.1.19=10635=D34=249=TW52=20140329-22:38:4556=ISLD11=ID21=140=154=138=+200.0055=INTC60=20140329-22:38:4510=178"), + MessageBytes: []byte("8=FIXT.1.19=10635=D34=249=TW52=20140329-22:38:4556=ISLD11=ID21=140=154=138=+200.0055=INTC60=20140329-22:38:4510=002"), ExpectedRejectReason: rejectReasonIncorrectDataFormatForValue, ExpectedRefTagID: &tag, } @@ -1163,7 +1174,7 @@ func tcMultipleRepeatingGroupFields() validateTest { return validateTest{ TestName: "Multiple repeating group fields in a message", Validator: validator, - MessageBytes: []byte("8=FIX.4.39=17635=D34=249=TW52=20140329-22:38:4556=ISLD11=ID453=2448=PARTYID452=3523=SUBID448=PARTYID2452=378=179=ACCOUNT80=121=140=154=138=20055=INTC60=20140329-22:38:4510=178"), + MessageBytes: []byte("8=FIX.4.39=17635=D34=249=TW52=20140329-22:38:4556=ISLD11=ID453=2448=PARTYID452=3523=SUBID448=PARTYID2452=378=179=ACCOUNT80=121=140=154=138=20055=INTC60=20140329-22:38:4510=012"), DoNotExpectReject: true, } } From a693b59516a41875ae6066b56077fa163d5aa300 Mon Sep 17 00:00:00 2001 From: vyourtchenko Date: Fri, 5 Dec 2025 19:53:49 -0600 Subject: [PATCH 2/2] Added validation test for incorrect checksum --- validation_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/validation_test.go b/validation_test.go index 67217d4f1..e8ec8e4e3 100644 --- a/validation_test.go +++ b/validation_test.go @@ -85,6 +85,7 @@ func TestValidate(t *testing.T) { tcCheckUserDefinedFieldsDisabled(), tcCheckUserDefinedFieldsDisabledFixT(), tcMultipleRepeatingGroupFields(), + tcCheckSumValidation(), } msg := NewMessage() @@ -1179,6 +1180,17 @@ func tcMultipleRepeatingGroupFields() validateTest { } } +func tcCheckSumValidation() validateTest { + dict, _ := datadictionary.Parse("spec/FIX43.xml") + validator := NewValidator(defaultValidatorSettings, dict, nil) + return validateTest{ + TestName: "Checksum is incorrect", + Validator: validator, + MessageBytes: []byte("8=FIX.4.39=17635=D34=249=TW52=20140329-22:38:4556=ISLD11=ID453=2448=PARTYID452=3523=SUBID448=PARTYID2452=378=179=ACCOUNT80=121=140=154=138=20055=INTC60=20140329-22:38:4510=006"), + ExpectParseError: true, + } +} + func TestValidateVisitField(t *testing.T) { fieldType0 := datadictionary.NewFieldType("myfield", 11, "STRING") fieldDef0 := &datadictionary.FieldDef{FieldType: fieldType0}