@@ -1610,46 +1610,21 @@ public void Constructor_WithValidSmallPoolSizes_WorksCorrectly()
16101610
16111611 /// <summary>
16121612 /// Verifies that a connection creation failure enters the blocking-period error state when
1613- /// blocking is enabled for the pool.
1613+ /// blocking is enabled for the pool, and stays out of it when blocking is disabled. The
1614+ /// blocking policy is driven by the connection string's Pool Blocking Period:
1615+ /// Default/Auto enable blocking for a non-Azure host (localhost), AlwaysBlock forces it on,
1616+ /// and NeverBlock suppresses it. FR-006, FR-007.
16141617 /// </summary>
1615- [ Fact ]
1616- public void ErrorOccurred_FailureWithBlockingEnabled_BecomesTrue ( )
1617- {
1618- // Arrange
1619- // Default PoolBlockingPeriod is Auto; localhost is non-Azure so blocking is enabled.
1620- var dbConnectionPoolGroup = new DbConnectionPoolGroup (
1621- new SqlConnectionOptions ( "Data Source=localhost;" ) ,
1622- new ConnectionPoolKey ( "TestDataSource" , credential : null , accessToken : null , accessTokenCallback : null , sspiContextProvider : null ) ,
1623- new DbConnectionPoolGroupOptions (
1624- poolByIdentity : false ,
1625- minPoolSize : 0 ,
1626- maxPoolSize : 4 ,
1627- creationTimeout : 15 ,
1628- loadBalanceTimeout : 0 ,
1629- hasTransactionAffinity : true ,
1630- idleTimeout : 0 ) ) ;
1631- var pool = ConstructPool ( TimeoutConnectionFactory , dbConnectionPoolGroup : dbConnectionPoolGroup ) ;
1632-
1633- // Act
1634- Assert . False ( pool . ErrorOccurred ) ;
1635-
1636- Assert . Throws < InvalidOperationException > ( ( ) =>
1637- pool . TryGetConnection ( new SqlConnection ( ) , taskCompletionSource : null , TimeoutTimer . StartNew ( TimeSpan . FromSeconds ( 15 ) ) , out _ ) ) ;
1638-
1639- // Assert
1640- Assert . True ( pool . ErrorOccurred ) ;
1641- }
1642-
1643- /// <summary>
1644- /// Verifies that a connection creation failure does not enter the blocking-period error state
1645- /// when the connection string disables blocking with NeverBlock.
1646- /// </summary>
1647- [ Fact ]
1648- public void ErrorOccurred_FailureWithNeverBlock_StaysFalse ( )
1618+ [ Theory ]
1619+ [ InlineData ( "" , true ) ] // Default (unspecified) => Auto => blocks for localhost
1620+ [ InlineData ( "Pool Blocking Period=Auto;" , true ) ] // Auto => blocks for non-Azure host
1621+ [ InlineData ( "Pool Blocking Period=NeverBlock;" , false ) ]
1622+ [ InlineData ( "Pool Blocking Period=AlwaysBlock;" , true ) ]
1623+ public void ErrorOccurred_OnFailure_FollowsBlockingPeriod ( string blockingPeriodClause , bool expectErrorOccurred )
16491624 {
16501625 // Arrange
16511626 var dbConnectionPoolGroup = new DbConnectionPoolGroup (
1652- new SqlConnectionOptions ( "Data Source=localhost;Pool Blocking Period=NeverBlock; " ) ,
1627+ new SqlConnectionOptions ( $ "Data Source=localhost;{ blockingPeriodClause } ") ,
16531628 new ConnectionPoolKey ( "TestDataSource" , credential : null , accessToken : null , accessTokenCallback : null , sspiContextProvider : null ) ,
16541629 new DbConnectionPoolGroupOptions (
16551630 poolByIdentity : false ,
@@ -1662,40 +1637,13 @@ public void ErrorOccurred_FailureWithNeverBlock_StaysFalse()
16621637 var pool = ConstructPool ( TimeoutConnectionFactory , dbConnectionPoolGroup : dbConnectionPoolGroup ) ;
16631638
16641639 // Act
1665- Assert . Throws < InvalidOperationException > ( ( ) =>
1666- pool . TryGetConnection ( new SqlConnection ( ) , taskCompletionSource : null , TimeoutTimer . StartNew ( TimeSpan . FromSeconds ( 15 ) ) , out _ ) ) ;
1667-
1668- // Assert - FR-007: NeverBlock must not enter the error state.
16691640 Assert . False ( pool . ErrorOccurred ) ;
1670- }
16711641
1672- /// <summary>
1673- /// Verifies that a connection creation failure enters the blocking-period error state when
1674- /// the connection string explicitly enables AlwaysBlock.
1675- /// </summary>
1676- [ Fact ]
1677- public void ErrorOccurred_FailureWithAlwaysBlock_BecomesTrue ( )
1678- {
1679- // Arrange
1680- var dbConnectionPoolGroup = new DbConnectionPoolGroup (
1681- new SqlConnectionOptions ( "Data Source=localhost;Pool Blocking Period=AlwaysBlock;" ) ,
1682- new ConnectionPoolKey ( "TestDataSource" , credential : null , accessToken : null , accessTokenCallback : null , sspiContextProvider : null ) ,
1683- new DbConnectionPoolGroupOptions (
1684- poolByIdentity : false ,
1685- minPoolSize : 0 ,
1686- maxPoolSize : 4 ,
1687- creationTimeout : 15 ,
1688- loadBalanceTimeout : 0 ,
1689- hasTransactionAffinity : true ,
1690- idleTimeout : 0 ) ) ;
1691- var pool = ConstructPool ( TimeoutConnectionFactory , dbConnectionPoolGroup : dbConnectionPoolGroup ) ;
1692-
1693- // Act
16941642 Assert . Throws < InvalidOperationException > ( ( ) =>
16951643 pool . TryGetConnection ( new SqlConnection ( ) , taskCompletionSource : null , TimeoutTimer . StartNew ( TimeSpan . FromSeconds ( 15 ) ) , out _ ) ) ;
16961644
16971645 // Assert
1698- Assert . True ( pool . ErrorOccurred ) ;
1646+ Assert . Equal ( expectErrorOccurred , pool . ErrorOccurred ) ;
16991647 }
17001648
17011649 /// <summary>
@@ -1956,6 +1904,9 @@ public async Task RateLimiter_LeaseReleaseWakesRateLimitedWaiter_CreatesPhysical
19561904 "Timed out waiting for the first open to begin physical creation." ) ;
19571905
19581906 // Caller B is denied a permit (A holds it) and must fall back to the idle-channel wait.
1907+ // A denied AttemptAcquire increments the limiter's TotalFailedLeases, so watching that
1908+ // counter rise is how we confirm B was refused a permit and is now parked on the idle
1909+ // channel waiting for a wakeup (rather than still racing to acquire).
19591910 long failedLeasesBefore = rateLimiter. GetStatistics( ) ! . TotalFailedLeases;
19601911 Task < DbConnectionInternal ? > requestB = Open ( new SqlConnection ( ) ) ;
19611912 Assert. True (
0 commit comments