@@ -9,13 +9,29 @@ namespace EssentialCSharp.Web.Tests;
99[ ClassDataSource < WebApplicationFactory > ( Shared = SharedType . PerClass ) ]
1010public class McpApiTokenServiceTests ( WebApplicationFactory factory )
1111{
12+ private async Task < ( string UserId , McpApiTokenService TokenService ) > ArrangeAsync ( string prefix )
13+ {
14+ string userId = await McpTestHelper . CreateUserAsync ( factory , prefix ) ;
15+ var tokenService = factory . Services . CreateScope ( ) . ServiceProvider
16+ . GetRequiredService < McpApiTokenService > ( ) ;
17+ return ( userId , tokenService ) ;
18+ }
19+
20+ private async Task < McpApiTokenService > FillToLimitAsync ( string userId )
21+ {
22+ var tokenService = factory . Services . CreateScope ( ) . ServiceProvider
23+ . GetRequiredService < McpApiTokenService > ( ) ;
24+ for ( int i = 0 ; i < McpApiTokenService . MaxTokensPerUser ; i ++ )
25+ {
26+ await tokenService . CreateTokenAsync ( userId , $ "token-{ i } ") ;
27+ }
28+ return tokenService ;
29+ }
30+
1231 [ Test ]
1332 public async Task CreateTokenAsync_WithoutExpiry_UsesSixMonthDefault ( )
1433 {
15- string userId = await McpTestHelper . CreateUserAsync ( factory , "mcp-default-expiry" ) ;
16-
17- using var scope = factory . Services . CreateScope ( ) ;
18- var tokenService = scope . ServiceProvider . GetRequiredService < McpApiTokenService > ( ) ;
34+ var ( userId , tokenService ) = await ArrangeAsync ( "mcp-default-expiry" ) ;
1935
2036 ( _ , var entity ) = await tokenService . CreateTokenAsync ( userId , "default-expiry" ) ;
2137
@@ -27,10 +43,7 @@ await Assert.That(entity.ExpiresAt!.Value)
2743 [ Test ]
2844 public async Task CreateTokenAsync_WithExpiryWithinSixMonths_UsesRequestedExpiry ( )
2945 {
30- string userId = await McpTestHelper . CreateUserAsync ( factory , "mcp-custom-expiry" ) ;
31-
32- using var scope = factory . Services . CreateScope ( ) ;
33- var tokenService = scope . ServiceProvider . GetRequiredService < McpApiTokenService > ( ) ;
46+ var ( userId , tokenService ) = await ArrangeAsync ( "mcp-custom-expiry" ) ;
3447 DateTime requestedExpiry = DateTime . UtcNow . AddMonths ( 3 ) ;
3548
3649 ( _ , var entity ) = await tokenService . CreateTokenAsync ( userId , "custom-expiry" , requestedExpiry ) ;
@@ -42,10 +55,7 @@ public async Task CreateTokenAsync_WithExpiryWithinSixMonths_UsesRequestedExpiry
4255 [ Test ]
4356 public async Task CreateTokenAsync_WithExpiryBeyondSixMonths_Throws ( )
4457 {
45- string userId = await McpTestHelper . CreateUserAsync ( factory , "mcp-max-expiry" ) ;
46-
47- using var scope = factory . Services . CreateScope ( ) ;
48- var tokenService = scope . ServiceProvider . GetRequiredService < McpApiTokenService > ( ) ;
58+ var ( userId , tokenService ) = await ArrangeAsync ( "mcp-max-expiry" ) ;
4959 DateTime requestedExpiry = McpApiTokenService . GetDefaultExpirationUtc ( DateTime . UtcNow ) . AddDays ( 2 ) ;
5060
5161 await Assert . That ( ( ) => tokenService . CreateTokenAsync ( userId , "too-long" , requestedExpiry ) )
@@ -56,16 +66,11 @@ await Assert.That(() => tokenService.CreateTokenAsync(userId, "too-long", reques
5666 [ Test ]
5767 public async Task CreateTokenAsync_WithExplicitCreatedAt_UsesReferenceTimeForDefaultExpiry ( )
5868 {
59- string userId = await McpTestHelper . CreateUserAsync ( factory , "mcp-explicit-created-at" ) ;
60-
61- using var scope = factory . Services . CreateScope ( ) ;
62- var tokenService = scope . ServiceProvider . GetRequiredService < McpApiTokenService > ( ) ;
69+ var ( userId , tokenService ) = await ArrangeAsync ( "mcp-explicit-created-at" ) ;
6370 DateTime createdAtUtc = new ( 2026 , 4 , 30 , 23 , 59 , 59 , DateTimeKind . Utc ) ;
6471
6572 ( _ , var entity ) = await tokenService . CreateTokenAsync (
66- userId ,
67- "explicit-created-at" ,
68- createdAtUtc : createdAtUtc ) ;
73+ userId , "explicit-created-at" , createdAtUtc : createdAtUtc ) ;
6974
7075 await Assert . That ( entity . CreatedAt ) . IsEqualTo ( createdAtUtc ) ;
7176 await Assert . That ( entity . ExpiresAt ) . IsNotNull ( ) ;
@@ -76,10 +81,7 @@ await Assert.That(entity.ExpiresAt!.Value)
7681 [ Test ]
7782 public async Task GetActiveTokenCountAsync_NoTokens_ReturnsZero ( )
7883 {
79- string userId = await McpTestHelper . CreateUserAsync ( factory , "mcp-count-zero" ) ;
80-
81- using var scope = factory . Services . CreateScope ( ) ;
82- var tokenService = scope . ServiceProvider . GetRequiredService < McpApiTokenService > ( ) ;
84+ var ( userId , tokenService ) = await ArrangeAsync ( "mcp-count-zero" ) ;
8385
8486 int count = await tokenService . GetActiveTokenCountAsync ( userId ) ;
8587
@@ -89,10 +91,7 @@ public async Task GetActiveTokenCountAsync_NoTokens_ReturnsZero()
8991 [ Test ]
9092 public async Task GetActiveTokenCountAsync_ActiveTokens_CountsAll ( )
9193 {
92- string userId = await McpTestHelper . CreateUserAsync ( factory , "mcp-count-active" ) ;
93-
94- using var scope = factory . Services . CreateScope ( ) ;
95- var tokenService = scope . ServiceProvider . GetRequiredService < McpApiTokenService > ( ) ;
94+ var ( userId , tokenService ) = await ArrangeAsync ( "mcp-count-active" ) ;
9695
9796 await tokenService . CreateTokenAsync ( userId , "token-1" ) ;
9897 await tokenService . CreateTokenAsync ( userId , "token-2" ) ;
@@ -106,10 +105,7 @@ public async Task GetActiveTokenCountAsync_ActiveTokens_CountsAll()
106105 [ Test ]
107106 public async Task GetActiveTokenCountAsync_RevokedToken_ExcludedFromCount ( )
108107 {
109- string userId = await McpTestHelper . CreateUserAsync ( factory , "mcp-count-revoked" ) ;
110-
111- using var scope = factory . Services . CreateScope ( ) ;
112- var tokenService = scope . ServiceProvider . GetRequiredService < McpApiTokenService > ( ) ;
108+ var ( userId , tokenService ) = await ArrangeAsync ( "mcp-count-revoked" ) ;
113109
114110 await tokenService . CreateTokenAsync ( userId , "active-token" ) ;
115111 ( _ , var revokedEntity ) = await tokenService . CreateTokenAsync ( userId , "revoked-token" ) ;
@@ -123,12 +119,8 @@ public async Task GetActiveTokenCountAsync_RevokedToken_ExcludedFromCount()
123119 [ Test ]
124120 public async Task GetActiveTokenCountAsync_ExpiredToken_ExcludedFromCount ( )
125121 {
126- string userId = await McpTestHelper . CreateUserAsync ( factory , "mcp-count-expired" ) ;
122+ var ( userId , tokenService ) = await ArrangeAsync ( "mcp-count-expired" ) ;
127123
128- using var scope = factory . Services . CreateScope ( ) ;
129- var tokenService = scope . ServiceProvider . GetRequiredService < McpApiTokenService > ( ) ;
130-
131- // Create a token that has already expired:
132124 // createdAt 7 months ago → max expiry = 1 month ago; use 2 months ago as expiresAt
133125 DateTime createdAt = DateTime . UtcNow . AddMonths ( - 7 ) ;
134126 DateTime pastExpiry = DateTime . UtcNow . AddMonths ( - 2 ) ;
@@ -144,15 +136,8 @@ await tokenService.CreateTokenAsync(userId, "expired-token",
144136 [ Test ]
145137 public async Task CreateTokenAsync_AtMaxLimit_ThrowsTokenLimitExceededException ( )
146138 {
147- string userId = await McpTestHelper . CreateUserAsync ( factory , "mcp-at-limit" ) ;
148-
149- using var scope = factory . Services . CreateScope ( ) ;
150- var tokenService = scope . ServiceProvider . GetRequiredService < McpApiTokenService > ( ) ;
151-
152- for ( int i = 0 ; i < McpApiTokenService . MaxTokensPerUser ; i ++ )
153- {
154- await tokenService . CreateTokenAsync ( userId , $ "token-{ i } ") ;
155- }
139+ var ( userId , _) = await ArrangeAsync ( "mcp-at-limit" ) ;
140+ var tokenService = await FillToLimitAsync ( userId ) ;
156141
157142 await Assert . That ( ( ) => tokenService . CreateTokenAsync ( userId , "one-too-many" ) )
158143 . Throws < TokenLimitExceededException > ( ) ;
@@ -161,20 +146,12 @@ await Assert.That(() => tokenService.CreateTokenAsync(userId, "one-too-many"))
161146 [ Test ]
162147 public async Task CreateTokenAsync_AfterRevokingAtLimit_AllowsNewToken ( )
163148 {
164- string userId = await McpTestHelper . CreateUserAsync ( factory , "mcp-revoke-then-create" ) ;
165-
166- using var scope = factory . Services . CreateScope ( ) ;
167- var tokenService = scope . ServiceProvider . GetRequiredService < McpApiTokenService > ( ) ;
168-
169- // Fill up to limit
170- McpApiToken ? lastEntity = null ;
171- for ( int i = 0 ; i < McpApiTokenService . MaxTokensPerUser ; i ++ )
172- {
173- ( _ , lastEntity ) = await tokenService . CreateTokenAsync ( userId , $ "token-{ i } ") ;
174- }
149+ var ( userId , _) = await ArrangeAsync ( "mcp-revoke-then-create" ) ;
150+ var tokenService = await FillToLimitAsync ( userId ) ;
175151
176- // Revoke one
177- await tokenService . RevokeTokenAsync ( lastEntity ! . Id , userId ) ;
152+ // Revoke the last token to free a slot
153+ var tokens = await tokenService . GetUserTokensAsync ( userId ) ;
154+ await tokenService . RevokeTokenAsync ( tokens [ 0 ] . Id , userId ) ;
178155
179156 // Should now succeed — active count dropped below max
180157 ( _ , var newEntity ) = await tokenService . CreateTokenAsync ( userId , "replacement" ) ;
0 commit comments