1+ using Microsoft . Data . SqlClient ;
2+ using Simpleverse . Repository . Db . SqlServer ;
3+ using StackExchange . Profiling . Data ;
4+ using System ;
5+ using System . Collections . Generic ;
6+ using System . Threading . Tasks ;
7+ using Xunit ;
8+ using Xunit . Abstractions ;
9+
10+ namespace Simpleverse . Repository . Db . Test . SqlServer
11+ {
12+ [ Collection ( "SqlServerCollection" ) ]
13+ public class ReleaseAppLockTests : DatabaseTestFixture
14+ {
15+ public ReleaseAppLockTests ( DatabaseFixture fixture , ITestOutputHelper output )
16+ : base ( fixture , output )
17+ {
18+ }
19+
20+ [ Fact ]
21+ public async Task ReleaseAppLockAsync_WithTransaction_SuccessfullyReleasesLock ( )
22+ {
23+ using var connection = _fixture . GetProfiledConnection ( ) ;
24+ connection . Open ( ) ;
25+ var sqlConnection = ( SqlConnection ) ( ( ProfiledDbConnection ) connection ) . WrappedConnection ;
26+ using var transaction = sqlConnection . BeginTransaction ( ) ;
27+ var key = "test_lock_transaction_501" ;
28+
29+ var lockAcquired = await sqlConnection . GetAppLockAsync ( key , transaction : transaction ) ;
30+ Assert . True ( lockAcquired ) ;
31+
32+ // Act
33+ var result = await sqlConnection . ReleaseAppLockAsync ( key , transaction : transaction ) ;
34+
35+ // Assert
36+ Assert . True ( result ) ;
37+
38+ transaction . Commit ( ) ;
39+ }
40+
41+ [ Fact ]
42+ public async Task ReleaseAppLockAsync_WithSession_SuccessfullyReleasesLock ( )
43+ {
44+ using var connection = _fixture . GetProfiledConnection ( ) ;
45+ connection . Open ( ) ;
46+ var sqlConnection = ( SqlConnection ) ( ( ProfiledDbConnection ) connection ) . WrappedConnection ;
47+
48+ var key = "test_lock_session_502" ;
49+ var lockAcquired = await sqlConnection . GetAppLockAsync ( key , transaction : null ) ;
50+ Assert . True ( lockAcquired ) ;
51+
52+ // Act
53+ var result = await sqlConnection . ReleaseAppLockAsync ( key , transaction : null ) ;
54+
55+ // Assert
56+ Assert . True ( result ) ;
57+ }
58+
59+ [ Fact ]
60+ public async Task ReleaseAppLockAsync_LockNotHeld_ThrowsSqlException ( )
61+ {
62+ using var connection = _fixture . GetProfiledConnection ( ) ;
63+ connection . Open ( ) ;
64+ var sqlConnection = ( SqlConnection ) ( ( ProfiledDbConnection ) connection ) . WrappedConnection ;
65+ using var transaction = sqlConnection . BeginTransaction ( ) ;
66+ var key = "test_lock_not_held_503" ;
67+
68+ // Act & Assert
69+ await Assert . ThrowsAsync < SqlException > ( async ( ) =>
70+ {
71+ await sqlConnection . ReleaseAppLockAsync ( key , transaction : transaction ) ;
72+ } ) ;
73+
74+ transaction . Rollback ( ) ;
75+ }
76+
77+ [ Fact ]
78+ public async Task ReleaseAppLockAsync_WrongLockOwner_ThrowsSqlException ( )
79+ {
80+ using var connection = _fixture . GetProfiledConnection ( ) ;
81+ await connection . OpenAsync ( ) ;
82+ var sqlConnection = ( SqlConnection ) ( ( ProfiledDbConnection ) connection ) . WrappedConnection ;
83+ var key = "test_lock_wrong_owner_504" ;
84+
85+ // Acquire lock at session level (without transaction)
86+ await sqlConnection . GetAppLockAsync ( key , transaction : null ) ;
87+
88+ // Now create a transaction and try to release the session-level lock
89+ using var transaction = await sqlConnection . BeginTransactionAsync ( ) ;
90+
91+ // Act & Assert
92+ await Assert . ThrowsAsync < SqlException > ( async ( ) =>
93+ {
94+ await sqlConnection . ReleaseAppLockAsync ( key , transaction : transaction ) ;
95+ } ) ;
96+
97+ // Cleanup
98+ await Assert . ThrowsAsync < InvalidOperationException > ( async ( ) =>
99+ {
100+ await sqlConnection . ReleaseAppLockAsync ( key , transaction : null ) ;
101+ } ) ;
102+
103+ transaction . Rollback ( ) ;
104+ }
105+
106+ [ Fact ]
107+ public async Task ReleaseAppLockAsync_KeyTooLong_ThrowsArgumentOutOfRangeException ( )
108+ {
109+ using var connection = _fixture . GetProfiledConnection ( ) ;
110+ connection . Open ( ) ;
111+ var sqlConnection = ( SqlConnection ) ( ( ProfiledDbConnection ) connection ) . WrappedConnection ;
112+ using var transaction = sqlConnection . BeginTransaction ( ) ;
113+ var key = new string ( 'a' , 256 ) ;
114+
115+ // Act & Assert
116+ await Assert . ThrowsAsync < ArgumentOutOfRangeException > ( async ( ) =>
117+ {
118+ await sqlConnection . ReleaseAppLockAsync ( key , transaction : transaction ) ;
119+ } ) ;
120+
121+ transaction . Rollback ( ) ;
122+ }
123+
124+ [ Fact ]
125+ public async Task ReleaseAppLockAsync_MultipleKeys_SuccessfullyReleasesAll ( )
126+ {
127+ using var connection = _fixture . GetProfiledConnection ( ) ;
128+ connection . Open ( ) ;
129+ var sqlConnection = ( SqlConnection ) ( ( ProfiledDbConnection ) connection ) . WrappedConnection ;
130+ using var transaction = sqlConnection . BeginTransaction ( ) ;
131+ var keys = new List < string > { "test_lock_505" , "test_lock_506" , "test_lock_507" } ;
132+
133+ foreach ( var key in keys )
134+ {
135+ await sqlConnection . GetAppLockAsync ( key , transaction : transaction ) ;
136+ }
137+
138+ // Act
139+ var result = await sqlConnection . ReleaseAppLockAsync ( keys , transaction : transaction ) ;
140+
141+ // Assert
142+ Assert . True ( result ) ;
143+
144+ transaction . Commit ( ) ;
145+ }
146+
147+ [ Fact ]
148+ public async Task ReleaseAppLockAsync_MultipleKeys_SomeNotHeld_ThrowsSqlException ( )
149+ {
150+ using var connection = _fixture . GetProfiledConnection ( ) ;
151+ connection . Open ( ) ;
152+ var sqlConnection = ( SqlConnection ) ( ( ProfiledDbConnection ) connection ) . WrappedConnection ;
153+ using var transaction = sqlConnection . BeginTransaction ( ) ;
154+ var keys = new List < string > { "test_lock_508" , "test_lock_not_held_509" , "test_lock_510" } ;
155+
156+ // Acquire only two of the three locks
157+ await sqlConnection . GetAppLockAsync ( keys [ 0 ] , transaction : transaction ) ;
158+ await sqlConnection . GetAppLockAsync ( keys [ 2 ] , transaction : transaction ) ;
159+
160+ // Act & Assert - Should throw when trying to release lock that's not held
161+ await Assert . ThrowsAsync < SqlException > ( async ( ) =>
162+ {
163+ await sqlConnection . ReleaseAppLockAsync ( keys , transaction : transaction ) ;
164+ } ) ;
165+
166+ transaction . Rollback ( ) ;
167+ }
168+
169+ [ Fact ]
170+ public async Task ReleaseAppLockAsync_AfterTransactionCommit_LockIsReleased ( )
171+ {
172+ using var connection = _fixture . GetProfiledConnection ( ) ;
173+ connection . Open ( ) ;
174+ var sqlConnection = ( SqlConnection ) ( ( ProfiledDbConnection ) connection ) . WrappedConnection ;
175+
176+ var key = "test_lock_auto_release_511" ;
177+
178+ using ( var transaction = sqlConnection . BeginTransaction ( ) )
179+ {
180+ await sqlConnection . GetAppLockAsync ( key , transaction : transaction ) ;
181+ transaction . Commit ( ) ;
182+ }
183+
184+ // Act & Assert
185+ using ( var transaction = sqlConnection . BeginTransaction ( ) )
186+ {
187+ await Assert . ThrowsAsync < SqlException > ( async ( ) =>
188+ {
189+ await sqlConnection . ReleaseAppLockAsync ( key , transaction : transaction ) ;
190+ } ) ;
191+
192+ transaction . Rollback ( ) ;
193+ }
194+ }
195+ }
196+ }
0 commit comments