Skip to content

Commit 3b1dc7c

Browse files
Test | Fix Transient Fault handling and other flaky unit tests (#4080)
* Test | Fix Transient Fault handling flaky tests * Attempt to fix * Fix the MultiPartIdentifier tests getting skipped * Fix serialization issue of SqlTypeWorkaroundsTests * Fix one more cases of possible error scenarios
1 parent 1f12619 commit 3b1dc7c

7 files changed

Lines changed: 77 additions & 44 deletions

File tree

src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlTypeWorkaroundsTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public void SqlBinaryCtor_NullInput()
6363
};
6464

6565
[Theory]
66-
[MemberData(nameof(SqlDecimalExtractData_NonNullInput_Data))]
66+
[MemberData(nameof(SqlDecimalExtractData_NonNullInput_Data), DisableDiscoveryEnumeration = true)]
6767
public void SqlDecimalExtractData_NonNullInput(SqlDecimal input)
6868
{
6969
// Act
@@ -156,7 +156,7 @@ public void SqlGuidCtor_ValidInput(byte[] input)
156156
};
157157

158158
[Theory]
159-
[MemberData(nameof(SqlMoneyCtor_Data))]
159+
[MemberData(nameof(SqlMoneyCtor_Data), DisableDiscoveryEnumeration = true)]
160160
public void SqlMoneyCtor(long input, SqlMoney expected)
161161
{
162162
// Act
@@ -177,7 +177,7 @@ public void SqlMoneyCtor(long input, SqlMoney expected)
177177
};
178178

179179
[Theory]
180-
[MemberData(nameof(SqlMoneyToSqlInternalRepresentation_NonNullInput_Data))]
180+
[MemberData(nameof(SqlMoneyToSqlInternalRepresentation_NonNullInput_Data), DisableDiscoveryEnumeration = true)]
181181
public void SqlMoneyToSqlInternalRepresentation_NonNullInput(SqlMoney input, long expected)
182182
{
183183
// Act

src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Microsoft.Data.SqlClient.UnitTests.SimulatedServerTests
1313
{
14-
[Trait("Category", "flaky")]
1514
[Collection("SimulatedServerTests")]
1615
public class ConnectionFailoverTests
1716
{
@@ -71,7 +70,7 @@ public void TransientFault_NoFailover_DoesNotClearPool(uint errorCode)
7170
Assert.Equal($"localhost,{initialServer.EndPoint.Port}", secondConnection.DataSource);
7271

7372
// 1 for the initial connection, 2 for the second connection
74-
Assert.Equal(3, initialServer.PreLoginCount);
73+
Assert.Equal(3, initialServer.PreLoginCount - initialServer.AbandonedPreLoginCount);
7574
// A failover should not be triggered, so prelogin count to the failover server should be 0
7675
Assert.Equal(0, failoverServer.PreLoginCount);
7776
}
@@ -219,6 +218,7 @@ public void NetworkDelay_ShouldConnectToPrimary()
219218
InitialCatalog = "master",// Required for failover partner to work
220219
ConnectTimeout = 5,
221220
Encrypt = false,
221+
Pooling = false, // Disable pooling to ensure a fresh connection attempt is made
222222
MultiSubnetFailover = false,
223223
#if NETFRAMEWORK
224224
TransparentNetworkIPResolution = false,
@@ -275,6 +275,7 @@ public void NetworkError_WithUserProvidedPartner_RetryDisabled_ShouldConnectToFa
275275
ConnectRetryCount = 0, // Disable retry
276276
FailoverPartner = $"localhost,{failoverServer.EndPoint.Port}", // User provided failover partner
277277
Encrypt = false,
278+
Pooling = false, // Disable pooling to ensure a fresh connection attempt is made on failover
278279
};
279280
using SqlConnection connection = new(builder.ConnectionString);
280281
try
@@ -326,6 +327,9 @@ public void NetworkError_WithUserProvidedPartner_RetryEnabled_ShouldConnectToFai
326327
ConnectRetryInterval = 1,
327328
FailoverPartner = $"localhost,{failoverServer.EndPoint.Port}", // User provided failover partner
328329
Encrypt = false,
330+
#if NETFRAMEWORK
331+
TransparentNetworkIPResolution = false,
332+
#endif
329333
};
330334
using SqlConnection connection = new(builder.ConnectionString);
331335
// Act
@@ -337,7 +341,8 @@ public void NetworkError_WithUserProvidedPartner_RetryEnabled_ShouldConnectToFai
337341
Assert.Equal(ConnectionState.Open, connection.State);
338342
Assert.Equal($"localhost,{failoverServer.EndPoint.Port}", connection.DataSource);
339343
Assert.Equal(1, server.PreLoginCount);
340-
Assert.Equal(1, failoverServer.PreLoginCount);
344+
Assert.Equal(0, server.Login7Count);
345+
Assert.Equal(1, failoverServer.PreLoginCount - failoverServer.AbandonedPreLoginCount);
341346
}
342347

343348
[Theory]
@@ -370,7 +375,8 @@ public void TransientFault_ShouldConnectToPrimary(uint errorCode)
370375
InitialCatalog = "master",
371376
ConnectTimeout = 30,
372377
ConnectRetryInterval = 1,
373-
Encrypt = false
378+
Encrypt = false,
379+
Pooling = false, // Disable pooling to ensure a fresh connection attempt is made
374380
};
375381
using SqlConnection connection = new(builder.ConnectionString);
376382

@@ -382,7 +388,7 @@ public void TransientFault_ShouldConnectToPrimary(uint errorCode)
382388
Assert.Equal($"localhost,{server.EndPoint.Port}", connection.DataSource);
383389

384390
// Failures should prompt the client to return to the original server, resulting in a login count of 2
385-
Assert.Equal(2, server.PreLoginCount);
391+
Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount);
386392
}
387393

388394
[Theory]
@@ -468,7 +474,7 @@ public void TransientFault_WithUserProvidedPartner_ShouldConnectToPrimary(uint e
468474
FailoverPartner = $"localhost:{failoverServer.EndPoint.Port}", // User provided failover partner
469475
};
470476
using SqlConnection connection = new(builder.ConnectionString);
471-
477+
472478
// Act
473479
connection.Open();
474480

@@ -477,7 +483,7 @@ public void TransientFault_WithUserProvidedPartner_ShouldConnectToPrimary(uint e
477483
Assert.Equal($"localhost,{server.EndPoint.Port}", connection.DataSource);
478484

479485
// Failures should prompt the client to return to the original server, resulting in a login count of 2
480-
Assert.Equal(2, server.PreLoginCount);
486+
Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount);
481487
}
482488

483489
[Theory]
@@ -580,6 +586,10 @@ public void TransientFault_IgnoreServerProvidedFailoverPartner_ShouldConnectToUs
580586
// Dispose of the server to trigger a failover
581587
server.Dispose();
582588

589+
// Clear the pool to ensure the next connection attempt doesn't reuse
590+
// the pooled connection to the now-disposed primary server.
591+
SqlConnection.ClearAllPools();
592+
583593
// Opening a new connection will use the failover partner stored in the pool group.
584594
// This will fail if the server provided failover partner was stored to the pool group.
585595
using SqlConnection failoverConnection = new(builder.ConnectionString);
@@ -593,9 +603,9 @@ public void TransientFault_IgnoreServerProvidedFailoverPartner_ShouldConnectToUs
593603
Assert.Equal(ConnectionState.Open, failoverConnection.State);
594604
Assert.Equal($"localhost,{failoverServer.EndPoint.Port}", failoverConnection.DataSource);
595605
// 1 for the initial connection
596-
Assert.Equal(1, server.PreLoginCount);
606+
Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount);
597607
// 1 for the failover connection
598-
Assert.Equal(1, failoverServer.PreLoginCount);
608+
Assert.Equal(1, failoverServer.PreLoginCount - failoverServer.AbandonedPreLoginCount);
599609
}
600610
}
601611
}

src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionRoutingTests.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
namespace Microsoft.Data.SqlClient.UnitTests.SimulatedServerTests
1212
{
13-
[Trait("Category", "flaky")]
1413
[Collection("SimulatedServerTests")]
1514
public class ConnectionRoutingTests
1615
{
@@ -58,8 +57,8 @@ public void TransientFaultAtRoutedLocation_ShouldReturnToGateway(uint errorCode)
5857
Assert.Equal($"localhost,{router.EndPoint.Port}", connection.DataSource);
5958

6059
// Failures should prompt the client to return to the original server, resulting in a login count of 2
61-
Assert.Equal(2, router.PreLoginCount);
62-
Assert.Equal(2, server.PreLoginCount);
60+
Assert.Equal(2, router.PreLoginCount - router.AbandonedPreLoginCount);
61+
Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount);
6362
}
6463

6564
[Theory]

src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionRoutingTestsAzure.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
namespace Microsoft.Data.SqlClient.UnitTests.SimulatedServerTests
1212
{
13-
[Trait("Category", "flaky")]
1413
[Collection("SimulatedServerTests")]
1514
public class ConnectionRoutingTestsAzure : IDisposable
1615
{
@@ -76,8 +75,8 @@ public void TransientFaultAtRoutedLocation_ShouldReturnToGateway(uint errorCode)
7675
Assert.Equal($"localhost,{router.EndPoint.Port}", connection.DataSource);
7776

7877
// Failures should prompt the client to return to the original server, resulting in a login count of 2
79-
Assert.Equal(2, router.PreLoginCount);
80-
Assert.Equal(2, server.PreLoginCount);
78+
Assert.Equal(2, router.PreLoginCount - router.AbandonedPreLoginCount);
79+
Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount);
8180
}
8281

8382
[Theory]

src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ public void ConnectionTest()
3030
{
3131
using TdsServer server = new(new TdsServerArguments() { });
3232
server.Start();
33-
var connStr = new SqlConnectionStringBuilder() {
33+
var connStr = new SqlConnectionStringBuilder()
34+
{
3435
DataSource = $"localhost,{server.EndPoint.Port}",
3536
Encrypt = SqlConnectionEncryptOption.Optional,
3637
}.ConnectionString;
@@ -44,7 +45,8 @@ public void IntegratedAuthConnectionTest()
4445
{
4546
using TdsServer server = new(new TdsServerArguments() { });
4647
server.Start();
47-
var connStr = new SqlConnectionStringBuilder() {
48+
var connStr = new SqlConnectionStringBuilder()
49+
{
4850
DataSource = $"localhost,{server.EndPoint.Port}",
4951
Encrypt = SqlConnectionEncryptOption.Optional,
5052
}.ConnectionString;
@@ -62,9 +64,10 @@ public void IntegratedAuthConnectionTest()
6264
[Fact]
6365
public async Task RequestEncryption_ServerDoesNotSupportEncryption_ShouldFail()
6466
{
65-
using TdsServer server = new(new TdsServerArguments() {Encryption = TDSPreLoginTokenEncryptionType.None });
67+
using TdsServer server = new(new TdsServerArguments() { Encryption = TDSPreLoginTokenEncryptionType.None });
6668
server.Start();
67-
var connStr = new SqlConnectionStringBuilder() {
69+
var connStr = new SqlConnectionStringBuilder()
70+
{
6871
DataSource = $"localhost,{server.EndPoint.Port}"
6972
}.ConnectionString;
7073

@@ -73,34 +76,35 @@ public async Task RequestEncryption_ServerDoesNotSupportEncryption_ShouldFail()
7376
Assert.Contains("The instance of SQL Server you attempted to connect to does not support encryption.", ex.Message, StringComparison.OrdinalIgnoreCase);
7477
}
7578

76-
[Trait("Category", "flaky")]
7779
[Theory]
7880
[InlineData(40613)]
7981
[InlineData(42108)]
8082
[InlineData(42109)]
8183
public async Task TransientFault_RetryEnabled_ShouldSucceed_Async(uint errorCode)
8284
{
8385
using TransientTdsErrorTdsServer server = new(
84-
new TransientTdsErrorTdsServerArguments()
86+
new TransientTdsErrorTdsServerArguments()
8587
{
86-
IsEnabledTransientError = true,
87-
Number = errorCode,
88+
IsEnabledTransientError = true,
89+
Number = errorCode,
8890
});
8991
server.Start();
9092
SqlConnectionStringBuilder builder = new()
9193
{
9294
DataSource = "localhost," + server.EndPoint.Port,
93-
Encrypt = SqlConnectionEncryptOption.Optional
95+
Encrypt = SqlConnectionEncryptOption.Optional,
96+
#if NETFRAMEWORK
97+
TransparentNetworkIPResolution = false
98+
#endif
9499
};
95100

96101
using SqlConnection connection = new(builder.ConnectionString);
97102
await connection.OpenAsync();
98103
Assert.Equal(ConnectionState.Open, connection.State);
99104
Assert.Equal($"localhost,{server.EndPoint.Port}", connection.DataSource);
100-
Assert.Equal(2, server.PreLoginCount);
105+
Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount);
101106
}
102107

103-
[Trait("Category", "flaky")]
104108
[Theory]
105109
[InlineData(40613)]
106110
[InlineData(42108)]
@@ -124,10 +128,9 @@ public void TransientFault_RetryEnabled_ShouldSucceed(uint errorCode)
124128
connection.Open();
125129
Assert.Equal(ConnectionState.Open, connection.State);
126130
Assert.Equal($"localhost,{server.EndPoint.Port}", connection.DataSource);
127-
Assert.Equal(2, server.PreLoginCount);
131+
Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount);
128132
}
129133

130-
[Trait("Category", "flaky")]
131134
[Theory]
132135
[InlineData(40613)]
133136
[InlineData(42108)]
@@ -152,10 +155,9 @@ public async Task TransientFault_RetryDisabled_ShouldFail_Async(uint errorCode)
152155
SqlException e = await Assert.ThrowsAsync<SqlException>(async () => await connection.OpenAsync());
153156
Assert.Equal((int)errorCode, e.Number);
154157
Assert.Equal(ConnectionState.Closed, connection.State);
155-
Assert.Equal(1, server.PreLoginCount);
158+
Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount);
156159
}
157160

158-
[Trait("Category", "flaky")]
159161
[Theory]
160162
[InlineData(40613)]
161163
[InlineData(42108)]
@@ -180,10 +182,9 @@ public void TransientFault_RetryDisabled_ShouldFail(uint errorCode)
180182
SqlException e = Assert.Throws<SqlException>(() => connection.Open());
181183
Assert.Equal((int)errorCode, e.Number);
182184
Assert.Equal(ConnectionState.Closed, connection.State);
183-
Assert.Equal(1, server.PreLoginCount);
185+
Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount);
184186
}
185187

186-
[Trait("Category", "flaky")]
187188
[Theory]
188189
[InlineData(false)]
189190
[InlineData(true)]
@@ -201,6 +202,7 @@ public async Task NetworkError_RetryEnabled_ShouldSucceed_Async(bool multiSubnet
201202
DataSource = "localhost," + server.EndPoint.Port,
202203
Encrypt = SqlConnectionEncryptOption.Optional,
203204
ConnectTimeout = 5,
205+
Pooling = false, // Disable pooling to ensure a fresh connection attempt is made
204206
MultiSubnetFailover = multiSubnetFailoverEnabled,
205207
#if NETFRAMEWORK
206208
TransparentNetworkIPResolution = multiSubnetFailoverEnabled
@@ -217,11 +219,10 @@ public async Task NetworkError_RetryEnabled_ShouldSucceed_Async(bool multiSubnet
217219
}
218220
else
219221
{
220-
Assert.Equal(1, server.PreLoginCount);
222+
Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount);
221223
}
222224
}
223225

224-
[Trait("Category", "flaky")]
225226
[Theory]
226227
[InlineData(true)]
227228
[InlineData(false)]
@@ -262,11 +263,10 @@ public async Task NetworkDelay_RetryDisabled_Async(bool multiSubnetFailoverEnabl
262263
}
263264
else
264265
{
265-
Assert.Equal(1, server.PreLoginCount);
266+
Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount);
266267
}
267268
}
268269

269-
[Trait("Category", "flaky")]
270270
[Theory]
271271
[InlineData(true)]
272272
[InlineData(false)]
@@ -307,7 +307,7 @@ public void NetworkDelay_RetryDisabled(bool multiSubnetFailoverEnabled)
307307
}
308308
else
309309
{
310-
Assert.Equal(1, server.PreLoginCount);
310+
Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount);
311311
}
312312
}
313313

@@ -468,7 +468,8 @@ public void ConnectionTimeoutTest(int timeout)
468468
//TODO: do we even need a server for this test?
469469
using TdsServer server = new();
470470
server.Start();
471-
var connStr = new SqlConnectionStringBuilder() {
471+
var connStr = new SqlConnectionStringBuilder()
472+
{
472473
DataSource = $"localhost,{server.EndPoint.Port}",
473474
ConnectTimeout = timeout,
474475
Encrypt = SqlConnectionEncryptOption.Optional

src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTdsServer.cs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ public delegate void OnAuthenticationCompletedDelegate(
6565
/// </summary>
6666
private int _preLoginCount = 0;
6767

68+
/// <summary>
69+
/// Counts Login7 requests to the server.
70+
/// </summary>
71+
protected int _login7Count = 0;
72+
6873
private TDSServerEndPoint _endpoint;
6974

7075
/// <summary>
@@ -107,6 +112,18 @@ public GenericTdsServer(T arguments, QueryEngine queryEngine)
107112
/// </summary>
108113
public int PreLoginCount => _preLoginCount;
109114

115+
/// <summary>
116+
/// Counts Login7 requests to the server.
117+
/// </summary>
118+
public int Login7Count => _login7Count;
119+
120+
/// <summary>
121+
/// Counts pre-login requests that did not result in a Login7 request,
122+
/// which indicates the client abandoned the connection (e.g. interval
123+
/// timer timeout during TNIR or failover).
124+
/// </summary>
125+
public int AbandonedPreLoginCount => _preLoginCount - _login7Count;
126+
110127
/// <summary>
111128
/// Property for setting server version for vector feature extension.
112129
/// </summary>
@@ -128,9 +145,12 @@ public void Start([CallerMemberName] string methodName = "")
128145
{
129146
throw new InvalidOperationException("Server is already started");
130147
}
131-
_endpoint = new TDSServerEndPoint(this) { ServerEndPoint = new IPEndPoint(IPAddress.Any, 0) };
132-
_endpoint.EndpointName = methodName;
133-
_endpoint.EventLog = Arguments.Log;
148+
_endpoint = new TDSServerEndPoint(this)
149+
{
150+
ServerEndPoint = new IPEndPoint(IPAddress.Any, 0),
151+
EndpointName = methodName,
152+
EventLog = Arguments.Log
153+
};
134154
_endpoint.Start();
135155
}
136156

@@ -225,6 +245,8 @@ public virtual TDSMessageCollection OnPreLoginRequest(ITDSServerSession session,
225245
/// </summary>
226246
public virtual TDSMessageCollection OnLogin7Request(ITDSServerSession session, TDSMessage request)
227247
{
248+
Interlocked.Increment(ref _login7Count);
249+
228250
// Inflate login7 request from the message
229251
TDSLogin7Token loginRequest = request[0] as TDSLogin7Token;
230252

0 commit comments

Comments
 (0)