548 lines
24 KiB
Go
548 lines
24 KiB
Go
// Copyright (c) quickfixengine.org All rights reserved.
|
||
//
|
||
// This file may be distributed under the terms of the quickfixengine.org
|
||
// license as defined by quickfixengine.org and appearing in the file
|
||
// LICENSE included in the packaging of this file.
|
||
//
|
||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
|
||
// THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
|
||
// PARTICULAR PURPOSE.
|
||
//
|
||
// See http://www.quickfixengine.org/LICENSE for licensing information.
|
||
//
|
||
// Contact ask@quickfixengine.org if any conditions of this licensing
|
||
// are not clear to you.
|
||
|
||
package quickfix
|
||
|
||
import (
|
||
"bytes"
|
||
"reflect"
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/suite"
|
||
|
||
"quantex.com/qfixdpl/quickfix/datadictionary"
|
||
)
|
||
|
||
func BenchmarkParseMessage(b *testing.B) {
|
||
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")
|
||
|
||
msg := NewMessage()
|
||
for i := 0; i < b.N; i++ {
|
||
_ = ParseMessage(msg, rawMsg)
|
||
}
|
||
}
|
||
|
||
type MessageSuite struct {
|
||
QuickFIXSuite
|
||
msg *Message
|
||
}
|
||
|
||
func TestMessageSuite(t *testing.T) {
|
||
suite.Run(t, new(MessageSuite))
|
||
}
|
||
|
||
func (s *MessageSuite) SetupTest() {
|
||
s.msg = NewMessage()
|
||
}
|
||
|
||
func TestXMLNonFIX(t *testing.T) {
|
||
rawMsg := bytes.NewBufferString("8=FIX.4.29=37235=n34=25512369=148152=20200522-07:05:33.75649=CME50=G56=OAEAAAN57=TRADE_CAPTURE143=US,IL212=261213=<RTRF>8=FIX.4.29=22535=BZ34=6549369=651852=20200522-07:05:33.74649=CME50=G56=9Q5000N57=DUMMY143=US,IL11=ACP159013113373460=20200522-07:05:33.734533=0893=Y1028=Y1300=991369=99612:325081373=31374=91375=15979=159013113373461769710=167</RTRF>10=245\"")
|
||
msg := NewMessage()
|
||
_ = ParseMessage(msg, rawMsg)
|
||
|
||
if !msg.Header.Has(tagXMLData) {
|
||
t.Error("Expected xmldata tag")
|
||
}
|
||
}
|
||
|
||
func (s *MessageSuite) TestParseMessageEmpty() {
|
||
rawMsg := bytes.NewBufferString("")
|
||
|
||
err := ParseMessage(s.msg, rawMsg)
|
||
s.NotNil(err)
|
||
}
|
||
|
||
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")
|
||
|
||
err := ParseMessage(s.msg, rawMsg)
|
||
s.Nil(err)
|
||
|
||
s.True(bytes.Equal(rawMsg.Bytes(), s.msg.rawMessage.Bytes()), "Expected msg bytes to equal raw bytes")
|
||
|
||
expectedBodyBytes := []byte("11=10021=140=154=155=TSLA60=00010101-00:00:00.000")
|
||
|
||
s.True(bytes.Equal(s.msg.bodyBytes, expectedBodyBytes), "Incorrect body bytes, got %s", string(s.msg.bodyBytes))
|
||
|
||
s.Equal(14, len(s.msg.fields))
|
||
|
||
msgType, err := s.msg.MsgType()
|
||
s.Nil(err)
|
||
|
||
s.Equal("D", msgType)
|
||
s.True(s.msg.IsMsgTypeOf("D"))
|
||
|
||
s.False(s.msg.IsMsgTypeOf("A"))
|
||
}
|
||
|
||
func (s *MessageSuite) TestParseMessageWithDataDictionary() {
|
||
dict := new(datadictionary.DataDictionary)
|
||
dict.Header = &datadictionary.MessageDef{
|
||
Fields: map[int]*datadictionary.FieldDef{
|
||
10030: nil,
|
||
},
|
||
}
|
||
dict.Trailer = &datadictionary.MessageDef{
|
||
Fields: map[int]*datadictionary.FieldDef{
|
||
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")
|
||
|
||
err := ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict)
|
||
s.Nil(err)
|
||
s.FieldEquals(Tag(10030), "CUST", s.msg.Header)
|
||
s.FieldEquals(Tag(5050), "HELLO", s.msg.Trailer)
|
||
}
|
||
|
||
func (s *MessageSuite) TestParseOutOfOrder() {
|
||
// Allow fields out of order, save for validation.
|
||
rawMsg := bytes.NewBufferString("8=FIX.4.09=8135=D11=id21=338=10040=154=155=MSFT34=249=TW52=20140521-22:07:0956=ISLD10=250")
|
||
s.Nil(ParseMessage(s.msg, rawMsg))
|
||
}
|
||
|
||
func (s *MessageSuite) TestBuild() {
|
||
s.msg.Header.SetField(tagBeginString, FIXString(BeginStringFIX44))
|
||
s.msg.Header.SetField(tagMsgType, FIXString("A"))
|
||
s.msg.Header.SetField(tagSendingTime, FIXString("20140615-19:49:56"))
|
||
|
||
s.msg.Body.SetField(Tag(553), FIXString("my_user"))
|
||
s.msg.Body.SetField(Tag(554), FIXString("secret"))
|
||
|
||
expectedBytes := []byte("8=FIX.4.49=4935=A52=20140615-19:49:56553=my_user554=secret10=072")
|
||
result := s.msg.build()
|
||
s.True(bytes.Equal(expectedBytes, result), "Unexpected bytes, got %s", string(result))
|
||
}
|
||
|
||
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")
|
||
|
||
s.Nil(ParseMessage(s.msg, rawMsg))
|
||
|
||
s.msg.Header.SetField(tagOrigSendingTime, FIXString("20140515-19:49:56.659"))
|
||
s.msg.Header.SetField(tagSendingTime, FIXString("20140615-19:49:56"))
|
||
s.msg.Header.SetField(tagPossDupFlag, FIXBoolean(true))
|
||
|
||
rebuildBytes := s.msg.build()
|
||
|
||
expectedBytes := []byte("8=FIX.4.29=13135=D34=243=Y49=TW52=20140615-19:49:5656=ISLD122=20140515-19:49:56.65911=10021=140=154=155=TSLA60=00010101-00:00:00.00010=122")
|
||
|
||
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n +%s\n -%s", rebuildBytes, expectedBytes)
|
||
|
||
expectedBodyBytes := []byte("11=10021=140=154=155=TSLA60=00010101-00:00:00.000")
|
||
|
||
s.True(bytes.Equal(s.msg.bodyBytes, expectedBodyBytes), "Incorrect body bytes, got %s", string(s.msg.bodyBytes))
|
||
}
|
||
|
||
func (s *MessageSuite) TestRebuildOneRepeatingGroupWithDictionary() {
|
||
dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
|
||
s.Nil(dictErr)
|
||
|
||
// Given message bytes from a valid string with a 453 repeating group.
|
||
rawMsg := bytes.NewBufferString(
|
||
"8=FIX.4.49=16535=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41453=1448=4501447=D452=28" +
|
||
"10=026")
|
||
|
||
// When we parse it into a message
|
||
s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))
|
||
|
||
// And then rebuild the message bytes
|
||
rebuildBytes := s.msg.build()
|
||
expectedBytes := []byte(
|
||
"8=FIX.4.49=16535=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41453=1448=4501447=D452=28" +
|
||
"10=026")
|
||
|
||
// Then the bytes should have repeating groups properly ordered
|
||
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
|
||
}
|
||
|
||
func (s *MessageSuite) TestRebuildTwoRepeatingGroupsWithDictionary() {
|
||
dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
|
||
s.Nil(dictErr)
|
||
|
||
// Given message bytes from a valid string with a 386 repeating group and a 453 repeating group.
|
||
rawMsg := bytes.NewBufferString("8=FIX.4.49=21035=D34=2347=UTF-852=20231231-20:19:4149=0100150=01001a56=TEST44=1211=139761=1010040021=1386=1336=NOPL55=SYMABC54=160=20231231-20:19:4138=140=259=0453=1448=4501447=D452=28354=6355=Public10=104")
|
||
|
||
// When we parse it into a message
|
||
s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))
|
||
|
||
// And then rebuild the message bytes
|
||
rebuildBytes := s.msg.build()
|
||
expectedBytes := []byte("8=FIX.4.49=21035=D34=249=0100150=01001a52=20231231-20:19:4156=TEST347=UTF-81=1010040011=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41354=6355=Public386=1336=NOPL453=1448=4501447=D452=2810=104")
|
||
|
||
// Then the bytes should have repeating groups properly ordered
|
||
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
|
||
}
|
||
|
||
func (s *MessageSuite) TestRebuildOneRepeatingGroupWithTwoMembersWithDictionary() {
|
||
dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
|
||
s.Nil(dictErr)
|
||
|
||
// Given message bytes from a valid string with a 453 repeating group that has 2 child groups.
|
||
rawMsg := bytes.NewBufferString(
|
||
"8=FIX.4.49=18735=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41453=2448=4501447=D452=28448=4502447=D452=28" +
|
||
"10=044")
|
||
|
||
// When we parse it into a message
|
||
s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))
|
||
|
||
// And then rebuild the message bytes
|
||
rebuildBytes := s.msg.build()
|
||
expectedBytes := []byte(
|
||
"8=FIX.4.49=18735=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41453=2448=4501447=D452=28448=4502447=D452=28" +
|
||
"10=044")
|
||
|
||
// Then the bytes should have repeating groups properly ordered
|
||
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
|
||
}
|
||
|
||
func (s *MessageSuite) TestRebuildTwoSequentialRepeatingGroupWithDictionary() {
|
||
dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
|
||
s.Nil(dictErr)
|
||
|
||
// Given message bytes from a valid string with both a 78 and 453 repeating group that each have 2 child groups.
|
||
rawMsg := bytes.NewBufferString(
|
||
"8=FIX.4.49=21035=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=279=acct179=acct2453=2448=4501447=D452=28448=4502447=D452=28" +
|
||
"10=243")
|
||
|
||
// When we parse it into a message
|
||
s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))
|
||
|
||
// And then rebuild the message bytes
|
||
rebuildBytes := s.msg.build()
|
||
expectedBytes := []byte(
|
||
"8=FIX.4.49=21035=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=279=acct179=acct2453=2448=4501447=D452=28448=4502447=D452=28" +
|
||
"10=243")
|
||
|
||
// Then the bytes should have repeating groups properly ordered
|
||
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
|
||
}
|
||
|
||
func (s *MessageSuite) TestRebuildNestedRepeatingGroupWithDictionary() {
|
||
dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
|
||
s.Nil(dictErr)
|
||
|
||
// Given message bytes from a valid string with a 78 repeating group that has
|
||
// a nested 539 group and then another 80 tag in the 78 group
|
||
rawMsg := bytes.NewBufferString(
|
||
"8=FIX.4.49=17735=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid80=100" +
|
||
"10=206")
|
||
|
||
// When we parse it into a message
|
||
s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))
|
||
|
||
// And then rebuild the message bytes
|
||
rebuildBytes := s.msg.build()
|
||
expectedBytes := []byte(
|
||
"8=FIX.4.49=17735=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid80=100" +
|
||
"10=206")
|
||
|
||
// Then the bytes should have repeating groups properly ordered
|
||
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
|
||
}
|
||
|
||
func (s *MessageSuite) TestRebuildDoubleNestedRepeatingGroupWithDictionary() {
|
||
dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
|
||
s.Nil(dictErr)
|
||
|
||
// Given message bytes from a valid string with a 78 repeating group that has a
|
||
// double nested 539 and then 804 groups and then another 80 tag in the 78 group
|
||
rawMsg := bytes.NewBufferString(
|
||
"8=FIX.4.49=20235=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid80=100" +
|
||
"10=117")
|
||
|
||
// When we parse it into a message
|
||
s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))
|
||
|
||
// And then rebuild the message bytes
|
||
rebuildBytes := s.msg.build()
|
||
expectedBytes := []byte(
|
||
"8=FIX.4.49=20235=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid80=100" +
|
||
"10=117")
|
||
|
||
// Then the bytes should have repeating groups properly ordered
|
||
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
|
||
}
|
||
|
||
func (s *MessageSuite) TestRebuildDoubleNestedThenAnotherRepeatingGroupWithDictionary() {
|
||
dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
|
||
s.Nil(dictErr)
|
||
|
||
// Given message bytes from a valid string with a 78 repeating group that has a double nested 539 and then 804 groups
|
||
// and then another repeating group 453 with two children.
|
||
rawMsg := bytes.NewBufferString(
|
||
"8=FIX.4.49=24535=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid453=2448=4501447=D452=28448=4502447=D452=28" +
|
||
"10=106")
|
||
|
||
// When we parse it into a message
|
||
s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))
|
||
|
||
// And then rebuild the message bytes
|
||
rebuildBytes := s.msg.build()
|
||
expectedBytes := []byte(
|
||
"8=FIX.4.49=24535=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid453=2448=4501447=D452=28448=4502447=D452=28" +
|
||
"10=106")
|
||
|
||
// Then the bytes should have repeating groups properly ordered
|
||
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
|
||
}
|
||
|
||
func (s *MessageSuite) TestRebuildDoubleNestedThenBodyTagThenAnotherRepeatingGroupWithDictionary() {
|
||
dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
|
||
s.Nil(dictErr)
|
||
|
||
// Given message bytes from a valid string with a 78 repeating group that has a double nested 539 and then 804 groups
|
||
// then a 376 body tag and then another repeating group 453 with two children.
|
||
rawMsg := bytes.NewBufferString(
|
||
"8=FIX.4.49=25635=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid376=compid453=2448=4501447=D452=28448=4502447=D452=28" +
|
||
"10=198")
|
||
|
||
// When we parse it into a message
|
||
s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))
|
||
|
||
// And then rebuild the message bytes
|
||
rebuildBytes := s.msg.build()
|
||
expectedBytes := []byte(
|
||
"8=FIX.4.49=25635=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:4178=179=acct1539=1524=nestedid804=1545=doublenestedid376=compid453=2448=4501447=D452=28448=4502447=D452=28" +
|
||
"10=198")
|
||
|
||
// Then the bytes should have repeating groups properly ordered
|
||
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
|
||
}
|
||
|
||
func (s *MessageSuite) TestRebuildDoubleNestedWithTwoMembersRepeatingGroupWithDictionary() {
|
||
dict, dictErr := datadictionary.Parse("spec/FIX44.xml")
|
||
s.Nil(dictErr)
|
||
|
||
// Given message bytes from a valid string with a 78 repeating group that
|
||
// has a double nested 539 and then 804 groups all with two children.
|
||
rawMsg := bytes.NewBufferString(
|
||
"8=FIX.4.49=40635=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41" +
|
||
"78=179=acct1" +
|
||
"539=2" +
|
||
"524=nestedid" +
|
||
"804=2" +
|
||
"545=doublenestedid" +
|
||
"545=doublenestedid2" +
|
||
"524=nestedid2" +
|
||
"804=2" +
|
||
"545=doublenestedid" +
|
||
"545=doublenestedid2" +
|
||
"79=acct2" +
|
||
"539=2" +
|
||
"524=nestedid" +
|
||
"804=2" +
|
||
"545=doublenestedid" +
|
||
"545=doublenestedid2" +
|
||
"524=nestedid2" +
|
||
"804=2" +
|
||
"545=doublenestedid" +
|
||
"545=doublenestedid2" +
|
||
"10=046")
|
||
|
||
// When we parse it into a message
|
||
s.Nil(ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict))
|
||
|
||
// And then rebuild the message bytes
|
||
rebuildBytes := s.msg.build()
|
||
expectedBytes := []byte(
|
||
"8=FIX.4.49=40635=D34=249=0100150=01001a52=20231231-20:19:4156=TEST" +
|
||
"1=acct111=1397621=138=140=244=1254=155=SYMABC59=060=20231231-20:19:41" +
|
||
"78=179=acct1539=2524=nestedid804=2545=doublenestedid545=doublenestedid2524=nestedid2804=2545=doublenestedid545=doublenestedid2" +
|
||
"79=acct2539=2524=nestedid804=2545=doublenestedid545=doublenestedid2524=nestedid2804=2545=doublenestedid545=doublenestedid2" +
|
||
"10=046")
|
||
|
||
// Then the bytes should have repeating groups properly ordered
|
||
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but got: %s", expectedBytes, rebuildBytes)
|
||
}
|
||
|
||
func (s *MessageSuite) TestReBuildWithRepeatingGroupForResend() {
|
||
// Given the following message with a repeating group
|
||
origHeader := "8=FIXT.1.19=16135=834=349=ISLD52=20240415-03:43:17.92356=TW"
|
||
origBody := "6=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=1448=xyzzy447=D452=1"
|
||
origTrailer := "10=014"
|
||
rawMsg := bytes.NewBufferString(origHeader + origBody + origTrailer)
|
||
|
||
// When I reparse the message from the store during a resend request
|
||
s.Nil(ParseMessage(s.msg, rawMsg))
|
||
|
||
// And I update the headers for resend
|
||
s.msg.Header.SetField(tagOrigSendingTime, FIXString("20240415-03:43:17.923"))
|
||
s.msg.Header.SetField(tagSendingTime, FIXString("20240415-14:41:23.456"))
|
||
s.msg.Header.SetField(tagPossDupFlag, FIXBoolean(true))
|
||
|
||
// When I rebuild the message
|
||
rebuildBytes := s.msg.build()
|
||
|
||
// Then the repeating groups will not be in the correct order in the rebuilt message (note tags 447, 448, 452, 453)
|
||
expectedBytes := []byte("8=FIXT.1.19=19235=834=343=Y49=ISLD52=20240415-14:41:23.45656=TW122=20240415-03:43:17.9236=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=1448=xyzzy447=D452=110=018")
|
||
s.False(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedBytes, rebuildBytes)
|
||
expectedOutOfOrderBytes := []byte("8=FIXT.1.19=19235=834=343=Y49=ISLD52=20240415-14:41:23.45656=TW122=20240415-03:43:17.9236=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00447=D448=xyzzy452=1453=110=018")
|
||
s.True(bytes.Equal(expectedOutOfOrderBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedOutOfOrderBytes, rebuildBytes)
|
||
|
||
// But the bodyBytes will still be correct
|
||
origBodyBytes := []byte(origBody)
|
||
s.True(bytes.Equal(origBodyBytes, s.msg.bodyBytes), "Incorrect body bytes, \n expected: %s\n but was: %s", origBodyBytes, s.msg.bodyBytes)
|
||
|
||
// So when I combine the updated header + the original bodyBytes + the as-is trailer
|
||
resendBytes := s.msg.buildWithBodyBytes(s.msg.bodyBytes)
|
||
|
||
// Then the reparsed, rebuilt message will retain the correct ordering of repeating group tags during resend
|
||
s.True(bytes.Equal(expectedBytes, resendBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedBytes, resendBytes)
|
||
}
|
||
|
||
func (s *MessageSuite) TestReBuildWithRepeatingGroupMultipleEntriesInGroupForResend() {
|
||
// Given the following message with a repeating group that has 2 entries
|
||
origHeader := "8=FIXT.1.19=18435=834=349=ISLD52=20240415-03:43:17.92356=TW"
|
||
origBody := "6=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=2448=xyzzy447=D452=1448=foobar447=D452=3"
|
||
origTrailer := "10=152"
|
||
rawMsg := bytes.NewBufferString(origHeader + origBody + origTrailer)
|
||
|
||
// When I reparse the message from the store during a resend request
|
||
s.Nil(ParseMessage(s.msg, rawMsg))
|
||
|
||
// And I update the headers for resend
|
||
s.msg.Header.SetField(tagOrigSendingTime, FIXString("20240415-03:43:17.923"))
|
||
s.msg.Header.SetField(tagSendingTime, FIXString("20240415-14:41:23.456"))
|
||
s.msg.Header.SetField(tagPossDupFlag, FIXBoolean(true))
|
||
|
||
// The bodyBytes will still be correct
|
||
origBodyBytes := []byte(origBody)
|
||
s.True(bytes.Equal(origBodyBytes, s.msg.bodyBytes), "Incorrect body bytes, \n expected: %s\n but was: %s", origBodyBytes, s.msg.bodyBytes)
|
||
|
||
// So when I combine the updated header + the original bodyBytes + the as-is trailer
|
||
resendBytes := s.msg.buildWithBodyBytes(origBodyBytes)
|
||
|
||
// Then the reparsed, rebuilt message will retain the correct ordering of repeating group tags during resend
|
||
expectedResendHeader := "8=FIXT.1.19=21535=834=343=Y49=ISLD52=20240415-14:41:23.45656=TW122=20240415-03:43:17.923"
|
||
expectedResendBody := "6=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=2448=xyzzy447=D452=1448=foobar447=D452=3"
|
||
expectedResendTrailer := "10=147"
|
||
expectedResendBytes := []byte(expectedResendHeader + expectedResendBody + expectedResendTrailer)
|
||
s.True(bytes.Equal(expectedResendBytes, resendBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedResendBytes, resendBytes)
|
||
}
|
||
|
||
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")))
|
||
|
||
builder := s.msg.reverseRoute()
|
||
|
||
var testCases = []struct {
|
||
tag Tag
|
||
expectedValue string
|
||
}{
|
||
{tagTargetCompID, "TW"},
|
||
{tagTargetSubID, "KK"},
|
||
{tagTargetLocationID, "JV"},
|
||
{tagSenderCompID, "ISLD"},
|
||
{tagSenderSubID, "AP"},
|
||
{tagSenderLocationID, "RY"},
|
||
{tagDeliverToCompID, "JCD"},
|
||
{tagDeliverToSubID, "CS"},
|
||
{tagDeliverToLocationID, "BB"},
|
||
{tagOnBehalfOfCompID, "MG"},
|
||
{tagOnBehalfOfSubID, "CB"},
|
||
{tagOnBehalfOfLocationID, "BH"},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
var field FIXString
|
||
s.Nil(builder.Header.GetField(tc.tag, &field))
|
||
|
||
s.Equal(tc.expectedValue, string(field))
|
||
}
|
||
}
|
||
|
||
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")))
|
||
builder := s.msg.reverseRoute()
|
||
|
||
s.False(builder.Header.Has(tagDeliverToCompID), "Should not reverse if empty")
|
||
}
|
||
|
||
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")))
|
||
|
||
builder := s.msg.reverseRoute()
|
||
|
||
s.False(builder.Header.Has(tagDeliverToLocationID), "delivertolocation id not supported in fix40")
|
||
|
||
s.False(builder.Header.Has(tagOnBehalfOfLocationID), "onbehalfof location id not supported in fix40")
|
||
}
|
||
|
||
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"
|
||
msgBuf := bytes.NewBufferString(msgString)
|
||
s.Nil(ParseMessage(s.msg, msgBuf))
|
||
|
||
dest := NewMessage()
|
||
s.msg.CopyInto(dest)
|
||
|
||
checkFieldInt(s, dest.Header.FieldMap, int(tagMsgSeqNum), 2)
|
||
checkFieldInt(s, dest.Body.FieldMap, 21, 3)
|
||
checkFieldString(s, dest.Body.FieldMap, 11, "ID")
|
||
s.Equal(len(dest.bodyBytes), len(s.msg.bodyBytes))
|
||
|
||
// copying decouples the message from its input buffer, so the raw message will be re-rendered
|
||
renderedString := "8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP115=JCD116=CS128=MG129=CB142=JV143=RY144=BB145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=033"
|
||
s.Equal(dest.String(), renderedString)
|
||
|
||
s.True(reflect.DeepEqual(s.msg.bodyBytes, dest.bodyBytes))
|
||
s.True(s.msg.IsMsgTypeOf("D"))
|
||
s.Equal(s.msg.ReceiveTime, dest.ReceiveTime)
|
||
|
||
s.True(reflect.DeepEqual(s.msg.fields, dest.fields))
|
||
|
||
// update the source message to validate the copy is truly deep
|
||
newMsgString := "8=FIX.4.49=4935=A52=20140615-19:49:56553=my_user554=secret10=072"
|
||
s.Nil(ParseMessage(s.msg, bytes.NewBufferString(newMsgString)))
|
||
s.True(s.msg.IsMsgTypeOf("A"))
|
||
s.Equal(s.msg.String(), newMsgString)
|
||
s.Equal(string(s.msg.Bytes()), newMsgString)
|
||
|
||
// clear the source buffer also
|
||
msgBuf.Reset()
|
||
|
||
s.True(dest.IsMsgTypeOf("D"))
|
||
s.Equal(dest.String(), renderedString)
|
||
s.Equal(string(dest.Bytes()), renderedString)
|
||
}
|
||
|
||
func checkFieldInt(s *MessageSuite, fields FieldMap, tag, expected int) {
|
||
toCheck, _ := fields.GetInt(Tag(tag))
|
||
s.Equal(expected, toCheck)
|
||
}
|
||
|
||
func checkFieldString(s *MessageSuite, fields FieldMap, tag int, expected string) {
|
||
toCheck, err := fields.GetString(Tag(tag))
|
||
s.NoError(err)
|
||
s.Equal(expected, toCheck)
|
||
}
|