Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions AcceptanceTest/definitions/server/fix40/10_MsgSeqNumLess.def
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ E8=FIX.4.09=5735=A34=149=ISLD52=00000000-00:00:0056=TW98=0108=3010=0

# sequence reset with gap fill flag set to Y, PosDupFlag set to Y
I8=FIX.4.035=434=149=TW52=<TIME>56=ISLD43=Y122=<TIME-1>36=20123=Y
I8=FIX.4.035=134=249=TW52=<TIME>56=ISLD112=HELLO
I8=FIX.4.035=134=2049=TW52=<TIME>56=ISLD112=HELLO
E8=FIX.4.09=5535=034=249=ISLD52=00000000-00:00:0056=TW112=HELLO10=0

# sequence reset with gap fill flag set to Y, PosDupFlag set to N
I8=FIX.4.035=434=149=TW52=<TIME>56=ISLD36=20123=Y
E8=FIX.4.09=9435=534=349=ISLD52=00000000-00:00:0056=TW58=MsgSeqNum too low, expecting 3 but received 110=0
I8=FIX.4.035=434=2049=TW52=<TIME>56=ISLD36=50123=Y
E8=FIX.4.09=9635=534=349=ISLD52=00000000-00:00:0056=TW58=MsgSeqNum too low, expecting 21 but received 2010=0
eDISCONNECT
9 changes: 5 additions & 4 deletions AcceptanceTest/definitions/server/fix41/10_MsgSeqNumLess.def
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ E8=FIX.4.19=5735=A34=149=ISLD52=00000000-00:00:0056=TW98=0108=3010=0

# sequence reset with gap fill flag set to Y, PosDupFlag set to Y
I8=FIX.4.135=434=149=TW52=<TIME>56=ISLD43=Y122=<TIME-1>36=20123=Y
I8=FIX.4.135=134=249=TW52=<TIME>56=ISLD112=HELLO
I8=FIX.4.135=134=2049=TW52=<TIME>56=ISLD112=HELLO
E8=FIX.4.19=5535=034=249=ISLD52=00000000-00:00:0056=TW112=HELLO10=0

# sequence reset with gap fill flag set to Y, PosDupFlag set to N
I8=FIX.4.135=434=149=TW52=<TIME>56=ISLD36=20123=Y
E8=FIX.4.19=9435=534=349=ISLD52=00000000-00:00:0056=TW58=MsgSeqNum too low, expecting 3 but received 110=0
eDISCONNECT
I8=FIX.4.135=434=2049=TW52=<TIME>56=ISLD36=50123=Y
E8=FIX.4.19=9635=534=349=ISLD52=00000000-00:00:0056=TW58=MsgSeqNum too low, expecting 21 but received 2010=0
eDISCONNECT

6 changes: 3 additions & 3 deletions AcceptanceTest/definitions/server/fix42/10_MsgSeqNumLess.def
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ E8=FIX.4.29=6135=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=3010=

# sequence reset with gap fill flag set to Y, PosDupFlag set to Y
I8=FIX.4.235=434=149=TW52=<TIME>56=ISLD43=Y122=<TIME-1>36=20123=Y
I8=FIX.4.235=134=249=TW52=<TIME>56=ISLD112=HELLO
I8=FIX.4.235=134=2049=TW52=<TIME>56=ISLD112=HELLO
E8=FIX.4.29=5935=034=249=ISLD52=00000000-00:00:00.00056=TW112=HELLO10=0

# sequence reset with gap fill flag set to Y, PosDupFlag set to N
I8=FIX.4.235=434=149=TW52=<TIME>56=ISLD36=20123=Y
E8=FIX.4.29=9835=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 3 but received 110=0
I8=FIX.4.235=434=2049=TW52=<TIME>56=ISLD36=50123=Y
E8=FIX.4.29=10035=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 21 but received 2010=0
eDISCONNECT
6 changes: 3 additions & 3 deletions AcceptanceTest/definitions/server/fix43/10_MsgSeqNumLess.def
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ E8=FIX.4.39=6135=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=3010=

# sequence reset with gap fill flag set to Y, PosDupFlag set to Y
I8=FIX.4.335=434=149=TW52=<TIME>56=ISLD43=Y122=<TIME-1>36=20123=Y
I8=FIX.4.335=134=249=TW52=<TIME>56=ISLD112=HELLO
I8=FIX.4.335=134=2049=TW52=<TIME>56=ISLD112=HELLO
E8=FIX.4.39=5935=034=249=ISLD52=00000000-00:00:00.00056=TW112=HELLO10=0

# sequence reset with gap fill flag set to Y, PosDupFlag set to N
I8=FIX.4.335=434=149=TW52=<TIME>56=ISLD36=20123=Y
E8=FIX.4.39=9835=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 3 but received 110=0
I8=FIX.4.335=434=2049=TW52=<TIME>56=ISLD36=20123=Y
E8=FIX.4.39=10035=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 21 but received 2010=0
eDISCONNECT
6 changes: 3 additions & 3 deletions AcceptanceTest/definitions/server/fix44/10_MsgSeqNumLess.def
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ E8=FIX.4.49=6135=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=3010=

# sequence reset with gap fill flag set to Y, PosDupFlag set to Y
I8=FIX.4.435=434=149=TW52=<TIME>56=ISLD43=Y122=<TIME-1>36=20123=Y
I8=FIX.4.435=134=249=TW52=<TIME>56=ISLD112=HELLO
I8=FIX.4.435=134=2049=TW52=<TIME>56=ISLD112=HELLO
E8=FIX.4.49=5935=034=249=ISLD52=00000000-00:00:00.00056=TW112=HELLO10=0

# sequence reset with gap fill flag set to Y, PosDupFlag set to N
I8=FIX.4.435=434=149=TW52=<TIME>56=ISLD36=20123=Y
E8=FIX.4.49=9835=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 3 but received 110=0
I8=FIX.4.435=434=2049=TW52=<TIME>56=ISLD36=20123=Y
E8=FIX.4.49=10035=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 21 but received 2010=0
eDISCONNECT
6 changes: 3 additions & 3 deletions AcceptanceTest/definitions/server/fix50/10_MsgSeqNumLess.def
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ E8=FIXT.1.19=6835=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=3011

# sequence reset with gap fill flag set to Y, PosDupFlag set to Y
I8=FIXT.1.135=434=149=TW52=<TIME>56=ISLD43=Y122=<TIME-1>36=20123=Y
I8=FIXT.1.135=134=249=TW52=<TIME>56=ISLD112=HELLO
I8=FIXT.1.135=134=2049=TW52=<TIME>56=ISLD112=HELLO
E8=FIXT.1.19=5935=034=249=ISLD52=00000000-00:00:00.00056=TW112=HELLO10=0

# sequence reset with gap fill flag set to Y, PosDupFlag set to N
I8=FIXT.1.135=434=149=TW52=<TIME>56=ISLD36=20123=Y
E8=FIXT.1.19=9835=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 3 but received 110=0
I8=FIXT.1.135=434=2049=TW52=<TIME>56=ISLD36=20123=Y
E8=FIXT.1.19=10035=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 21 but received 2010=0
eDISCONNECT
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ E8=FIXT.1.19=6835=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=3011

# sequence reset with gap fill flag set to Y, PosDupFlag set to Y
I8=FIXT.1.135=434=149=TW52=<TIME>56=ISLD43=Y122=<TIME-1>36=20123=Y
I8=FIXT.1.135=134=249=TW52=<TIME>56=ISLD112=HELLO
I8=FIXT.1.135=134=2049=TW52=<TIME>56=ISLD112=HELLO
E8=FIXT.1.19=5935=034=249=ISLD52=00000000-00:00:00.00056=TW112=HELLO10=0

# sequence reset with gap fill flag set to Y, PosDupFlag set to N
I8=FIXT.1.135=434=149=TW52=<TIME>56=ISLD36=20123=Y
E8=FIXT.1.19=9835=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 3 but received 110=0
I8=FIXT.1.135=434=2049=TW52=<TIME>56=ISLD36=20123=Y
E8=FIXT.1.19=10035=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 21 but received 2010=0
eDISCONNECT
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ E8=FIXT.1.19=6835=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=3011

# sequence reset with gap fill flag set to Y, PosDupFlag set to Y
I8=FIXT.1.135=434=149=TW52=<TIME>56=ISLD43=Y122=<TIME-1>36=20123=Y
I8=FIXT.1.135=134=249=TW52=<TIME>56=ISLD112=HELLO
I8=FIXT.1.135=134=2049=TW52=<TIME>56=ISLD112=HELLO
E8=FIXT.1.19=5935=034=249=ISLD52=00000000-00:00:00.00056=TW112=HELLO10=0

# sequence reset with gap fill flag set to Y, PosDupFlag set to N
I8=FIXT.1.135=434=149=TW52=<TIME>56=ISLD36=20123=Y
E8=FIXT.1.19=9835=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 3 but received 110=0
I8=FIXT.1.135=434=2049=TW52=<TIME>56=ISLD36=20123=Y
E8=FIXT.1.19=10035=534=349=ISLD52=00000000-00:00:00.00056=TW58=MsgSeqNum too low, expecting 21 but received 2010=0
eDISCONNECT
16 changes: 15 additions & 1 deletion QuickFIXn/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,11 @@ protected void NextSequenceReset(Message sequenceReset)
if (sequenceReset.IsSetField(Fields.Tags.GapFillFlag))
isGapFill = sequenceReset.GetBoolean(Fields.Tags.GapFillFlag);

if (!Verify(sequenceReset, isGapFill, isGapFill))
bool possDupFlag = false;
if (sequenceReset.Header.IsSetField(Fields.Tags.PossDupFlag))
possDupFlag = sequenceReset.Header.GetBoolean(Fields.Tags.PossDupFlag);

if (!Verify(sequenceReset, isGapFill, isGapFill && !possDupFlag))
return;

if (sequenceReset.IsSetField(Fields.Tags.NewSeqNo))
Expand All @@ -909,6 +913,11 @@ public bool Verify(Message msg, bool checkTooHigh = true, bool checkTooLow = tru
try
{
msgType = msg.Header.GetString(Fields.Tags.MsgType);

bool possDupFlag = false;
if (msg.Header.IsSetField(Fields.Tags.PossDupFlag))
possDupFlag = msg.Header.GetBoolean(Fields.Tags.PossDupFlag);

string senderCompId = msg.Header.GetString(Fields.Tags.SenderCompID);
string targetCompId = msg.Header.GetString(Fields.Tags.TargetCompID);

Expand All @@ -933,6 +942,11 @@ public bool Verify(Message msg, bool checkTooHigh = true, bool checkTooLow = tru
DoTargetTooLow(msg, msgSeqNum);
return false;
}
else if (possDupFlag && RequiresOrigSendingTime && !msg.Header.IsSetField(Fields.Tags.OrigSendingTime))
{
GenerateReject(msg, FixValues.SessionRejectReason.REQUIRED_TAG_MISSING, Fields.Tags.OrigSendingTime);
return false;
}

if (IsResendRequested)
{
Expand Down
211 changes: 211 additions & 0 deletions UnitTests/InitiatorSessionResendMessagesTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
using NUnit.Framework;
using QuickFix.Fields;
using QuickFix.FIX42;
using System.Text.RegularExpressions;
using UnitTests.SessionTestSupport;

namespace UnitTests
{

[TestFixture]
public class InitiatorSessionResendMessagesTest
{
private MockResponder _responder = new();

private QuickFix.SessionID _sessionID = new("unset", "unset", "unset");
private QuickFix.SessionSettings _settings = new();
private MockApplication _application = new();
private QuickFix.Session? _session = null;
private QuickFix.SettingsDictionary _config = new();
private SeqNumType _seqNum = 1;

[SetUp]
public void Setup()
{
_responder = new MockResponder();
_sessionID = new QuickFix.SessionID("FIX.4.2", "SENDER", "TARGET");
_application = new MockApplication();
_settings = new QuickFix.SessionSettings();

_config = new QuickFix.SettingsDictionary();
_config.SetBool(QuickFix.SessionSettings.PERSIST_MESSAGES, false);
_config.SetBool(QuickFix.SessionSettings.RESETSEQUENCE_MESSAGE_REQUIRES_ORIGSENDINGTIME, false);
_config.SetString(QuickFix.SessionSettings.CONNECTION_TYPE, "initiator");
_config.SetString(QuickFix.SessionSettings.START_TIME, "00:00:00");
_config.SetString(QuickFix.SessionSettings.END_TIME, "00:00:00");
_settings.Set(_sessionID, _config);

// initiator
_session = new QuickFix.Session(true, _application, new QuickFix.Store.MemoryStoreFactory(), _sessionID,
new QuickFix.DataDictionaryProvider(), new QuickFix.SessionSchedule(_config), 0, new QuickFix.Logger.ScreenLogFactory(_settings), new QuickFix.DefaultMessageFactory(), "blah");
_session.SetResponder(_responder);
_session.CheckLatency = false;

_seqNum = 1;
}

public void Logon()
{
SendLogon(new QuickFix.FIX42.Logon());
}

private void SendLogon(QuickFix.Message msg)
{
msg.Header.SetField(new QuickFix.Fields.TargetCompID(_sessionID.SenderCompID));
msg.Header.SetField(new QuickFix.Fields.SenderCompID(_sessionID.TargetCompID));
msg.Header.SetField(new QuickFix.Fields.MsgSeqNum(_seqNum++));
msg.Header.SetField(new QuickFix.Fields.SendingTime(System.DateTime.UtcNow));
msg.SetField(new QuickFix.Fields.HeartBtInt(1));
_session!.Next(msg.ConstructString());
}


public bool RESENT()
{
if (_responder.Dups.Count == 0)
return false;

_responder.Dups.Dequeue();
return true;
}


public void SendResendRequest(SeqNumType begin, SeqNumType end)
{
SendTheMessage(new QuickFix.FIX42.ResendRequest(
new QuickFix.Fields.BeginSeqNo(begin),
new QuickFix.Fields.EndSeqNo(end)));
}

private void SendTheMessage(QuickFix.Message msg)
{
SendTheMessage(msg, _seqNum++);
}

private void SendTheMessage(QuickFix.Message msg, SeqNumType msgSeqNum, bool possDupFlag = false)
{
msg.Header.SetField(new QuickFix.Fields.TargetCompID(_sessionID.SenderCompID));
msg.Header.SetField(new QuickFix.Fields.SenderCompID(_sessionID.TargetCompID));
msg.Header.SetField(new QuickFix.Fields.MsgSeqNum(msgSeqNum));
msg.Header.SetField(new QuickFix.Fields.PossDupFlag(possDupFlag));
_session.Next(msg.ConstructString());

Check warning on line 91 in UnitTests/InitiatorSessionResendMessagesTest.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 91 in UnitTests/InitiatorSessionResendMessagesTest.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
}

[Test]
public void TestSequenceResetNoGapFillIsProcessed()
{
// Default is false
Assert.That(_session.IgnorePossDupResendRequests, Is.EqualTo(false));

Check warning on line 98 in UnitTests/InitiatorSessionResendMessagesTest.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 98 in UnitTests/InitiatorSessionResendMessagesTest.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

_session.Next();// causes Logon send
// Logon
Logon();

// quote
var quote = new QuickFix.FIX42.Quote();

SendTheMessage(quote);
SendTheMessage(quote);
SendTheMessage(quote);

var seqReset = new SequenceReset(new NewSeqNo(_seqNum + 10));

SendTheMessage(seqReset);


Assert.That(_session.NextTargetMsgSeqNum, Is.EqualTo(_seqNum + 9));
}

[Test]
public void TestSequenceResetWithGapFillIsProcessed()
{
// Default is false
Assert.That(_session.IgnorePossDupResendRequests, Is.EqualTo(false));

Check warning on line 123 in UnitTests/InitiatorSessionResendMessagesTest.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 123 in UnitTests/InitiatorSessionResendMessagesTest.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

_session.Next();// causes Logon send
// Logon
Logon();

// quote
var quote = new QuickFix.FIX42.Quote();


SendTheMessage(quote);
SendTheMessage(quote);
SendTheMessage(quote);

var seqReset = new SequenceReset(new NewSeqNo(_seqNum + 10));
seqReset.GapFillFlag = new GapFillFlag(true);

SendTheMessage(seqReset);

Assert.That(_session.NextTargetMsgSeqNum, Is.EqualTo(_seqNum + 9));
}

[Test]
public void TestSequenceResetDuringResendRequestIsProcessed()
{
// Default is false
Assert.That(_session.IgnorePossDupResendRequests, Is.EqualTo(false));

Check warning on line 149 in UnitTests/InitiatorSessionResendMessagesTest.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 149 in UnitTests/InitiatorSessionResendMessagesTest.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
_session.RequiresOrigSendingTime = false;
_session.Next(); // causes Logon send
// Logon
Logon();

// quote
var quote = new QuickFix.FIX42.Quote();


SendTheMessage(quote, 2);
SendTheMessage(quote, 3);
SendTheMessage(quote, 10); // causes ResendRequest

Assert.That(_session.NextTargetMsgSeqNum, Is.EqualTo(4));

SendTheMessage(quote, 4);
SendTheMessage(quote, 5);

var seqReset = new SequenceReset(new NewSeqNo(9)); // already been sent 10 so will process it

SendTheMessage(seqReset, 6);
SendTheMessage(quote, 9);

Assert.That(_session.NextTargetMsgSeqNum, Is.EqualTo(11));
}

[Test]
public void TestSequenceResetWithGapFillAndPossDupFlagTrueDuringResendRequestIsProcessed()
{
// Default is false
Assert.That(_session.IgnorePossDupResendRequests, Is.EqualTo(false));

Check warning on line 180 in UnitTests/InitiatorSessionResendMessagesTest.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 180 in UnitTests/InitiatorSessionResendMessagesTest.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
_session.RequiresOrigSendingTime = false;
_session.Next(); // causes Logon send
// Logon
Logon();

// quote
var quote = new QuickFix.FIX42.Quote();

SendTheMessage(quote, 2);
SendTheMessage(quote, 3);
SendTheMessage(quote, 4);

_seqNum = 10;
// without changes the arrowed comments scenario happens
Logon(); // <--- Will cause resend from 5 and is queued (all cases)

SendTheMessage(quote, 5, true);
SendTheMessage(quote, 6, true);
SendTheMessage(quote, 7, true);
SendTheMessage(quote, 8, true);
SendTheMessage(quote, 9, true); // <--will cause logon message queued above to be replayed AND advance msgSeqNum to 11! (all cases)
var seqReset = new SequenceReset(new NewSeqNo(12));
seqReset.GapFillFlag = new GapFillFlag(true);
SendTheMessage(seqReset, 10, true); // <-- Without change this message will be ignored as too low but poss dup is Y so will NOT throw - in the changed version this WILL be honored

Assert.That(_session.NextTargetMsgSeqNum, Is.EqualTo(12));

Assert.That(_responder.MsgLookup.ContainsKey(QuickFix.Fields.MsgType.RESEND_REQUEST) && _responder.MsgLookup[QuickFix.Fields.MsgType.RESEND_REQUEST].Count == 1);
}
}
}
Loading