Skip to content

Commit fb8c9d0

Browse files
committed
Add additional tests. Refactor to test error behavior.
1 parent 4233f94 commit fb8c9d0

File tree

1 file changed

+150
-82
lines changed

1 file changed

+150
-82
lines changed

src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPoolTest.cs

Lines changed: 150 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Threading;
88
using System.Threading.Tasks;
99
using System.Transactions;
10+
using Microsoft.Data.Common;
1011
using Microsoft.Data.Common.ConnectionString;
1112
using Microsoft.Data.ProviderBase;
1213
using Microsoft.Data.SqlClient.ConnectionPool;
@@ -16,17 +17,19 @@ namespace Microsoft.Data.SqlClient.UnitTests
1617
{
1718
public class ChannelDbConnectionPoolTest
1819
{
19-
private readonly ChannelDbConnectionPool pool;
20-
private readonly DbConnectionPoolGroup dbConnectionPoolGroup;
21-
private readonly DbConnectionPoolGroupOptions poolGroupOptions;
22-
private readonly DbConnectionFactory connectionFactory;
23-
private readonly DbConnectionPoolIdentity identity;
24-
private readonly DbConnectionPoolProviderInfo connectionPoolProviderInfo;
25-
26-
public ChannelDbConnectionPoolTest()
20+
private ChannelDbConnectionPool pool;
21+
private DbConnectionFactory connectionFactory;
22+
private DbConnectionPoolGroup dbConnectionPoolGroup;
23+
private DbConnectionPoolGroupOptions poolGroupOptions;
24+
private DbConnectionPoolIdentity identity;
25+
private DbConnectionPoolProviderInfo connectionPoolProviderInfo;
26+
27+
private static readonly DbConnectionFactory SuccessfulConnectionFactory = new SuccessfulDbConnectionFactory();
28+
private static readonly DbConnectionFactory TimeoutConnectionFactory = new TimeoutDbConnectionFactory();
29+
30+
private void Setup(DbConnectionFactory connectionFactory)
2731
{
28-
// Use a stubbed connection factory to avoid network code
29-
connectionFactory = new SuccessfulConnectionFactory();
32+
this.connectionFactory = connectionFactory;
3033
identity = DbConnectionPoolIdentity.NoIdentity;
3134
connectionPoolProviderInfo = new DbConnectionPoolProviderInfo();
3235
poolGroupOptions = new DbConnectionPoolGroupOptions(
@@ -54,8 +57,11 @@ public ChannelDbConnectionPoolTest()
5457
[InlineData(1)]
5558
[InlineData(5)]
5659
[InlineData(10)]
57-
public void TestGetConnectionFromEmptyPoolSync_ShouldCreateNewConnection(int numConnections)
60+
public void GetConnectionFromEmptyPoolSync_ShouldCreateNewConnection(int numConnections)
5861
{
62+
// Arrange
63+
Setup(SuccessfulConnectionFactory);
64+
5965
// Act
6066
for (int i = 0; i < numConnections; i++)
6167
{
@@ -81,8 +87,11 @@ out internalConnection
8187
[InlineData(1)]
8288
[InlineData(5)]
8389
[InlineData(10)]
84-
public async Task TestGetConnectionFromEmptyPoolAsync_ShouldCreateNewConnection(int numConnections)
90+
public async Task GetConnectionFromEmptyPoolAsync_ShouldCreateNewConnection(int numConnections)
8591
{
92+
// Arrange
93+
Setup(SuccessfulConnectionFactory);
94+
8695
// Act
8796
for (int i = 0; i < numConnections; i++)
8897
{
@@ -106,8 +115,106 @@ out internalConnection
106115
Assert.Equal(numConnections, pool.Count);
107116
}
108117

109-
// Test that requests to get connection from the pool fails when max pool size is reached
110-
118+
[Fact]
119+
public void GetConnectionRespectsMaxPoolSize()
120+
{
121+
// Arrange
122+
Setup(SuccessfulConnectionFactory);
123+
124+
for (int i = 0; i < poolGroupOptions.MaxPoolSize; i++)
125+
{
126+
DbConnectionInternal internalConnection = null;
127+
var completed = pool.TryGetConnection(
128+
new SqlConnection(),
129+
taskCompletionSource: null,
130+
new DbConnectionOptions("", null),
131+
out internalConnection
132+
);
133+
134+
Assert.True(completed);
135+
Assert.NotNull(internalConnection);
136+
}
137+
138+
try
139+
{
140+
// Act
141+
DbConnectionInternal extraConnection = null;
142+
var exceeded = pool.TryGetConnection(
143+
new SqlConnection("Timeout=1"),
144+
taskCompletionSource: null,
145+
new DbConnectionOptions("", null),
146+
out extraConnection
147+
);
148+
}
149+
catch (Exception ex)
150+
{
151+
// Assert
152+
Assert.IsType<InvalidOperationException>(ex);
153+
Assert.Equal("Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.", ex.Message);
154+
}
155+
156+
// Assert
157+
Assert.Equal(poolGroupOptions.MaxPoolSize, pool.Count);
158+
}
159+
160+
[Fact]
161+
[ActiveIssue("Requires ReturnInternalConnection to be implemented in ChannelDbConnectionPool")]
162+
public void ConnectionsAreReused()
163+
{
164+
// Arrange
165+
Setup(SuccessfulConnectionFactory);
166+
DbConnectionInternal internalConnection1 = null;
167+
DbConnectionInternal internalConnection2 = null;
168+
169+
// Act: Get the first connection
170+
var completed1 = pool.TryGetConnection(
171+
new SqlConnection(),
172+
null,
173+
new DbConnectionOptions("", null),
174+
out internalConnection1
175+
);
176+
177+
// Assert: First connection should succeed
178+
Assert.True(completed1);
179+
Assert.NotNull(internalConnection1);
180+
181+
// Act: Return the first connection to the pool
182+
pool.ReturnInternalConnection(internalConnection1, null);
183+
184+
// Act: Get the second connection (should reuse the first one)
185+
var completed2 = pool.TryGetConnection(
186+
new SqlConnection(),
187+
null,
188+
new DbConnectionOptions("", null),
189+
out internalConnection2
190+
);
191+
192+
// Assert: Second connection should succeed and reuse the first connection
193+
Assert.True(completed2);
194+
Assert.NotNull(internalConnection2);
195+
Assert.Same(internalConnection1, internalConnection2);
196+
}
197+
198+
[Fact]
199+
public void GetConnectionWithTimeout_ShouldThrowTimeoutException()
200+
{
201+
// Arrange
202+
Setup(TimeoutConnectionFactory);
203+
DbConnectionInternal internalConnection = null;
204+
205+
// Act & Assert
206+
var ex = Assert.Throws<InvalidOperationException>(() =>
207+
{
208+
var completed = pool.TryGetConnection(
209+
new SqlConnection(),
210+
taskCompletionSource: null,
211+
new DbConnectionOptions("", null),
212+
out internalConnection
213+
);
214+
});
215+
216+
Assert.Equal("Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.", ex.Message);
217+
}
111218

112219
#region Property Tests
113220

@@ -243,49 +350,10 @@ public void TestTransactionEnded()
243350
{
244351
Assert.Throws<NotImplementedException>(() => pool.TransactionEnded(null!, null!));
245352
}
246-
[Fact]
247-
public void TestGetConnectionFailsWhenMaxPoolSizeReached()
248-
{
249-
// Arrange
250-
for (int i = 0; i < poolGroupOptions.MaxPoolSize; i++)
251-
{
252-
DbConnectionInternal internalConnection = null;
253-
var completed = pool.TryGetConnection(
254-
new SqlConnection(),
255-
taskCompletionSource: null,
256-
new DbConnectionOptions("", null),
257-
out internalConnection
258-
);
259-
260-
Assert.True(completed);
261-
Assert.NotNull(internalConnection);
262-
}
263-
264-
try
265-
{
266-
// Act
267-
DbConnectionInternal extraConnection = null;
268-
var exceeded = pool.TryGetConnection(
269-
//TODO: set timeout to make this faster
270-
new SqlConnection(),
271-
taskCompletionSource: null,
272-
new DbConnectionOptions("", null),
273-
out extraConnection
274-
);
275-
} catch (Exception ex)
276-
{
277-
// Assert
278-
Assert.IsType<InvalidOperationException>(ex);
279-
Assert.Equal("Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.", ex.Message);
280-
}
281-
282-
// Assert
283-
Assert.Equal(poolGroupOptions.MaxPoolSize, pool.Count);
284-
}
285353
#endregion
286354

287355
#region Test classes
288-
internal class SuccessfulConnectionFactory : DbConnectionFactory
356+
internal class SuccessfulDbConnectionFactory : DbConnectionFactory
289357
{
290358
protected override DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, IDbConnectionPool pool, DbConnection owningConnection)
291359
{
@@ -347,89 +415,89 @@ internal override void SetInnerConnectionTo(DbConnection owningObject, DbConnect
347415
#endregion
348416
}
349417

350-
internal class SuccessfulDbConnectionInternal : DbConnectionInternal
418+
internal class TimeoutDbConnectionFactory : DbConnectionFactory
351419
{
352-
#region Not Implemented Members
353-
public override string ServerVersion => throw new NotImplementedException();
354-
355-
public override DbTransaction BeginTransaction(System.Data.IsolationLevel il)
420+
protected override DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, IDbConnectionPool pool, DbConnection owningConnection)
356421
{
357-
throw new NotImplementedException();
422+
throw ADP.PooledOpenTimeout();
358423
}
359424

360-
public override void EnlistTransaction(Transaction transaction)
425+
#region Not Implemented Members
426+
public override DbProviderFactory ProviderFactory => throw new NotImplementedException();
427+
428+
protected override DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous)
361429
{
362430
throw new NotImplementedException();
363431
}
364432

365-
protected override void Activate(Transaction transaction)
433+
protected override DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions(DbConnectionOptions options)
366434
{
367435
throw new NotImplementedException();
368436
}
369437

370-
protected override void Deactivate()
438+
protected override int GetObjectId(DbConnection connection)
371439
{
372440
throw new NotImplementedException();
373441
}
374-
#endregion
375-
}
376442

377-
internal class TimeoutConnectionFactory : DbConnectionFactory
378-
{
379-
protected override DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, IDbConnectionPool pool, DbConnection owningConnection)
443+
internal override DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection)
380444
{
381-
return new SuccessfulDbConnectionInternal();
445+
throw new NotImplementedException();
382446
}
383447

384-
#region Not Implemented Members
385-
public override DbProviderFactory ProviderFactory => throw new NotImplementedException();
386-
387-
protected override DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous)
448+
internal override DbConnectionInternal GetInnerConnection(DbConnection connection)
388449
{
389450
throw new NotImplementedException();
390451
}
391452

392-
protected override DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions(DbConnectionOptions options)
453+
internal override void PermissionDemand(DbConnection outerConnection)
393454
{
394455
throw new NotImplementedException();
395456
}
396457

397-
protected override int GetObjectId(DbConnection connection)
458+
internal override void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup)
398459
{
399460
throw new NotImplementedException();
400461
}
401462

402-
internal override DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection)
463+
internal override void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to)
403464
{
404465
throw new NotImplementedException();
405466
}
406467

407-
internal override DbConnectionInternal GetInnerConnection(DbConnection connection)
468+
internal override bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from)
408469
{
409470
throw new NotImplementedException();
410471
}
411472

412-
internal override void PermissionDemand(DbConnection outerConnection)
473+
internal override void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to)
413474
{
414475
throw new NotImplementedException();
415476
}
477+
#endregion
478+
}
416479

417-
internal override void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup)
480+
internal class SuccessfulDbConnectionInternal : DbConnectionInternal
481+
{
482+
#region Not Implemented Members
483+
public override string ServerVersion => throw new NotImplementedException();
484+
485+
public override DbTransaction BeginTransaction(System.Data.IsolationLevel il)
418486
{
419487
throw new NotImplementedException();
420488
}
421489

422-
internal override void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to)
490+
public override void EnlistTransaction(Transaction transaction)
423491
{
424492
throw new NotImplementedException();
425493
}
426494

427-
internal override bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from)
495+
protected override void Activate(Transaction transaction)
428496
{
429497
throw new NotImplementedException();
430498
}
431499

432-
internal override void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to)
500+
protected override void Deactivate()
433501
{
434502
throw new NotImplementedException();
435503
}

0 commit comments

Comments
 (0)