@@ -119,6 +119,126 @@ public async Task RefreshAsync_WhenProviderChangesFromTenantsToEmpty_KeepsDefaul
119119 }
120120 }
121121
122+ [ Fact ]
123+ public async Task DeactivateTenantsAsync_WhenRefreshIsInProgress_WaitsForRefreshBeforeDeactivating ( )
124+ {
125+ var tenant = new Tenant { Id = "tenant-1" , Name = "Tenant 1" } ;
126+ var timeout = TimeSpan . FromSeconds ( 5 ) ;
127+ var refreshStarted = new TaskCompletionSource ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
128+ var completeRefresh = new TaskCompletionSource < IEnumerable < Tenant > > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
129+ var listRequests = 0 ;
130+ var tenantsProvider = Substitute . For < ITenantsProvider > ( ) ;
131+ tenantsProvider . ListAsync ( Arg . Any < CancellationToken > ( ) ) . Returns ( async _ =>
132+ {
133+ if ( Interlocked . Increment ( ref listRequests ) == 1 )
134+ {
135+ return new [ ] { tenant } ;
136+ }
137+
138+ refreshStarted . TrySetResult ( ) ;
139+ return await completeRefresh . Task ;
140+ } ) ;
141+
142+ var ( tenantService , serviceProvider ) = await CreateTenantServiceAsync ( tenantsProvider ) ;
143+ Task ? refreshTask = null ;
144+ Task ? deactivateTask = null ;
145+
146+ try
147+ {
148+ await tenantService . ListAsync ( ) ;
149+
150+ refreshTask = tenantService . RefreshAsync ( ) ;
151+ await refreshStarted . Task . WaitAsync ( timeout ) ;
152+
153+ deactivateTask = tenantService . DeactivateTenantsAsync ( ) ;
154+
155+ Assert . False ( deactivateTask . IsCompleted ) ;
156+
157+ completeRefresh . SetResult ( [ tenant ] ) ;
158+ await refreshTask . WaitAsync ( timeout ) ;
159+ await deactivateTask . WaitAsync ( timeout ) ;
160+
161+ Assert . Empty ( await tenantService . ListAsync ( ) ) ;
162+ }
163+ finally
164+ {
165+ completeRefresh . TrySetResult ( [ tenant ] ) ;
166+
167+ if ( refreshTask != null )
168+ {
169+ await refreshTask . WaitAsync ( timeout ) ;
170+ }
171+
172+ if ( deactivateTask != null )
173+ {
174+ await deactivateTask . WaitAsync ( timeout ) ;
175+ }
176+
177+ if ( tenantService is IAsyncDisposable disposable )
178+ {
179+ await disposable . DisposeAsync ( ) ;
180+ }
181+
182+ await serviceProvider . DisposeAsync ( ) ;
183+ }
184+ }
185+
186+ [ Fact ]
187+ public async Task DeactivateTenantsAsync_WhenInitializationIsInProgress_WaitsForInitializationBeforeDeactivating ( )
188+ {
189+ var tenant = new Tenant { Id = "tenant-1" , Name = "Tenant 1" } ;
190+ var timeout = TimeSpan . FromSeconds ( 5 ) ;
191+ var initializationStarted = new TaskCompletionSource ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
192+ var completeInitialization = new TaskCompletionSource < IEnumerable < Tenant > > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
193+ var tenantsProvider = Substitute . For < ITenantsProvider > ( ) ;
194+ tenantsProvider . ListAsync ( Arg . Any < CancellationToken > ( ) ) . Returns ( async _ =>
195+ {
196+ initializationStarted . TrySetResult ( ) ;
197+ return await completeInitialization . Task ;
198+ } ) ;
199+
200+ var ( tenantService , serviceProvider ) = await CreateTenantServiceAsync ( tenantsProvider ) ;
201+ Task < IEnumerable < Tenant > > ? initializationTask = null ;
202+ Task ? deactivateTask = null ;
203+
204+ try
205+ {
206+ initializationTask = tenantService . ListAsync ( ) ;
207+ await initializationStarted . Task . WaitAsync ( timeout ) ;
208+
209+ deactivateTask = tenantService . DeactivateTenantsAsync ( ) ;
210+
211+ Assert . False ( deactivateTask . IsCompleted ) ;
212+
213+ completeInitialization . SetResult ( [ tenant ] ) ;
214+ await initializationTask . WaitAsync ( timeout ) ;
215+ await deactivateTask . WaitAsync ( timeout ) ;
216+
217+ Assert . Empty ( await tenantService . ListAsync ( ) ) ;
218+ }
219+ finally
220+ {
221+ completeInitialization . TrySetResult ( [ tenant ] ) ;
222+
223+ if ( initializationTask != null )
224+ {
225+ await initializationTask . WaitAsync ( timeout ) ;
226+ }
227+
228+ if ( deactivateTask != null )
229+ {
230+ await deactivateTask . WaitAsync ( timeout ) ;
231+ }
232+
233+ if ( tenantService is IAsyncDisposable disposable )
234+ {
235+ await disposable . DisposeAsync ( ) ;
236+ }
237+
238+ await serviceProvider . DisposeAsync ( ) ;
239+ }
240+ }
241+
122242 private static Task < ( ITenantService TenantService , ServiceProvider ServiceProvider ) > CreateTenantServiceAsync ( IEnumerable < Tenant > tenants , Func < List < Tenant > > ? tenantsFactory = null )
123243 {
124244 var tenantList = tenants . ToList ( ) ;
@@ -127,6 +247,11 @@ public async Task RefreshAsync_WhenProviderChangesFromTenantsToEmpty_KeepsDefaul
127247 var tenantsProvider = Substitute . For < ITenantsProvider > ( ) ;
128248 tenantsProvider . ListAsync ( Arg . Any < CancellationToken > ( ) ) . Returns ( _ => getTenants ( ) ) ;
129249
250+ return CreateTenantServiceAsync ( tenantsProvider ) ;
251+ }
252+
253+ private static Task < ( ITenantService TenantService , ServiceProvider ServiceProvider ) > CreateTenantServiceAsync ( ITenantsProvider tenantsProvider )
254+ {
130255 var services = new ServiceCollection ( ) ;
131256 services . AddSingleton ( _ => tenantsProvider ) ;
132257 services . AddSingleton < ITenantScopeFactory , DefaultTenantScopeFactory > ( ) ;
0 commit comments