55using  Aspire . Hosting ; 
66using  Aspire . Hosting . Utils ; 
77using  Bogus ; 
8+ using  CommunityToolkit . Aspire . Testing ; 
9+ using  Microsoft . Extensions . Configuration ; 
810using  Microsoft . Extensions . Diagnostics . HealthChecks ; 
911using  Microsoft . Extensions . Hosting ; 
12+ using  Polly ; 
1013using  SurrealDb . Net ; 
14+ using  SurrealDb . Net . Exceptions ; 
15+ using  System . Data ; 
1116using  Xunit . Abstractions ; 
1217using  SurrealRecord  =  SurrealDb . Net . Models . Record ; 
1318
@@ -33,7 +38,7 @@ static SurrealDbFunctionalTests()
3338    [ Fact ] 
3439    public  async  Task  VerifySurrealDbResource ( ) 
3540    { 
36-         var  cts  =  new  CancellationTokenSource ( TimeSpan . FromMinutes ( 5 ) ) ; 
41+         using   var  cts  =  new  CancellationTokenSource ( TimeSpan . FromMinutes ( 5 ) ) ; 
3742        var  ct  =  cts . Token ; 
3843        using  var  builder  =  TestDistributedApplicationBuilder . Create ( testOutputHelper ) ; 
3944
@@ -70,7 +75,8 @@ public async Task VerifySurrealDbResource()
7075    [ InlineData ( false ) ] 
7176    public  async  Task  WithDataShouldPersistStateBetweenUsages ( bool  useVolume ) 
7277    { 
73-         var  cts  =  new  CancellationTokenSource ( TimeSpan . FromMinutes ( 5 ) ) ; 
78+         using  var  cts  =  new  CancellationTokenSource ( TimeSpan . FromMinutes ( 5 ) ) ; 
79+         var  ct  =  cts . Token ; 
7480
7581        string ?  volumeName  =  null ; 
7682        string ?  bindMountPath  =  null ; 
@@ -110,30 +116,30 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
110116
111117            using  ( var  app  =  builder1 . Build ( ) ) 
112118            { 
113-                 await  app . StartAsync ( cts . Token ) ; 
119+                 await  app . StartAsync ( ct ) ; 
114120
115-                 await  app . ResourceNotifications . WaitForResourceHealthyAsync ( surrealServer1 . Resource . Name ,  cts . Token ) ; 
116-                 await  app . ResourceNotifications . WaitForResourceHealthyAsync ( db1 . Resource . Name ,  cts . Token ) ; 
121+                 await  app . ResourceNotifications . WaitForResourceHealthyAsync ( surrealServer1 . Resource . Name ,  ct ) ; 
122+                 await  app . ResourceNotifications . WaitForResourceHealthyAsync ( db1 . Resource . Name ,  ct ) ; 
117123
118124                try 
119125                { 
120126                    var  hb  =  Host . CreateApplicationBuilder ( ) ; 
121127
122-                     hb . Configuration [ $ "ConnectionStrings:{ db1 . Resource . Name } "]  =  await  db1 . Resource . ConnectionStringExpression . GetValueAsync ( cts . Token ) ; 
128+                     hb . Configuration [ $ "ConnectionStrings:{ db1 . Resource . Name } "]  =  await  db1 . Resource . ConnectionStringExpression . GetValueAsync ( ct ) ; 
123129
124130                    hb . AddSurrealClient ( db1 . Resource . Name ) ; 
125131
126132                    using  var  host  =  hb . Build ( ) ; 
127-                     await  host . StartAsync ( cts . Token ) ; 
133+                     await  host . StartAsync ( ct ) ; 
128134
129135                    await  using  var  surrealDbClient  =  host . Services . GetRequiredService < SurrealDbClient > ( ) ; 
130-                     await  CreateTestData ( surrealDbClient ,  cts . Token ) ; 
131-                     await  AssertTestData ( surrealDbClient ,  cts . Token ) ; 
136+                     await  CreateTestData ( surrealDbClient ,  ct ) ; 
137+                     await  AssertTestData ( surrealDbClient ,  ct ) ; 
132138                } 
133139                finally 
134140                { 
135141                    // Stops the container, or the Volume would still be in use 
136-                     await  app . StopAsync ( cts . Token ) ; 
142+                     await  app . StopAsync ( ct ) ; 
137143                } 
138144            } 
139145
@@ -159,28 +165,28 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
159165
160166            using  ( var  app  =  builder2 . Build ( ) ) 
161167            { 
162-                 await  app . StartAsync ( cts . Token ) ; 
168+                 await  app . StartAsync ( ct ) ; 
163169
164-                 await  app . ResourceNotifications . WaitForResourceHealthyAsync ( surrealServer2 . Resource . Name ,  cts . Token ) ; 
165-                 await  app . ResourceNotifications . WaitForResourceHealthyAsync ( db2 . Resource . Name ,  cts . Token ) ; 
170+                 await  app . ResourceNotifications . WaitForResourceHealthyAsync ( surrealServer2 . Resource . Name ,  ct ) ; 
171+                 await  app . ResourceNotifications . WaitForResourceHealthyAsync ( db2 . Resource . Name ,  ct ) ; 
166172
167173                try 
168174                { 
169175                    var  hb  =  Host . CreateApplicationBuilder ( ) ; 
170176
171-                     hb . Configuration [ $ "ConnectionStrings:{ db2 . Resource . Name } "]  =  await  db2 . Resource . ConnectionStringExpression . GetValueAsync ( cts . Token ) ; 
177+                     hb . Configuration [ $ "ConnectionStrings:{ db2 . Resource . Name } "]  =  await  db2 . Resource . ConnectionStringExpression . GetValueAsync ( ct ) ; 
172178
173179                    hb . AddSurrealClient ( db2 . Resource . Name ) ; 
174180
175181                    using  var  host  =  hb . Build ( ) ; 
176-                     await  host . StartAsync ( cts . Token ) ; 
182+                     await  host . StartAsync ( ct ) ; 
177183                    await  using  var  surrealDbClient  =  host . Services . GetRequiredService < SurrealDbClient > ( ) ; 
178-                     await  AssertTestData ( surrealDbClient ,  cts . Token ) ; 
184+                     await  AssertTestData ( surrealDbClient ,  ct ) ; 
179185                } 
180186                finally 
181187                { 
182188                    // Stops the container, or the Volume would still be in use 
183-                     await  app . StopAsync ( cts . Token ) ; 
189+                     await  app . StopAsync ( ct ) ; 
184190                } 
185191            } 
186192
@@ -209,14 +215,11 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
209215    [ Fact ] 
210216    public  async  Task  VerifyWaitForOnSurrealDbBlocksDependentResources ( ) 
211217    { 
212-         var  cts  =  new  CancellationTokenSource ( TimeSpan . FromMinutes ( 10 ) ) ; 
218+         using   var  cts  =  new  CancellationTokenSource ( TimeSpan . FromMinutes ( 10 ) ) ; 
213219        using  var  builder  =  TestDistributedApplicationBuilder . Create ( testOutputHelper ) ; 
214220
215221        var  healthCheckTcs  =  new  TaskCompletionSource < HealthCheckResult > ( ) ; 
216-         builder . Services . AddHealthChecks ( ) . AddAsyncCheck ( "blocking_check" ,  ( )  => 
217-         { 
218-             return  healthCheckTcs . Task ; 
219-         } ) ; 
222+         builder . Services . AddHealthChecks ( ) . AddAsyncCheck ( "blocking_check" ,  ( )  =>  healthCheckTcs . Task ) ; 
220223
221224        var  resource  =  builder . AddSurrealServer ( "resource" ) 
222225            . WithHealthCheck ( "blocking_check" ) ; 
@@ -244,6 +247,99 @@ public async Task VerifyWaitForOnSurrealDbBlocksDependentResources()
244247
245248        await  app . StopAsync ( ) ; 
246249    } 
250+     
251+     [ Fact ] 
252+     [ RequiresDocker ] 
253+     public  async  Task  VerifyWithInitFiles ( ) 
254+     { 
255+         // Creates a script that should be executed when the container is initialized. 
256+     
257+         using  var  cts  =  new  CancellationTokenSource ( TimeSpan . FromMinutes ( 10 ) ) ; 
258+         var  pipeline  =  new  ResiliencePipelineBuilder ( ) 
259+             . AddRetry ( new ( )  {  MaxRetryAttempts  =  10 ,  BackoffType  =  DelayBackoffType . Linear ,  Delay  =  TimeSpan . FromSeconds ( 2 ) ,  ShouldHandle  =  new  PredicateBuilder ( ) . Handle < SurrealDbException > ( )  } ) 
260+             . Build ( ) ; 
261+     
262+         var  initDirPath  =  Path . Combine ( Path . GetTempPath ( ) ,  Path . GetRandomFileName ( ) ) ; 
263+     
264+         Directory . CreateDirectory ( initDirPath ) ; 
265+         
266+         var  initFilePath  =  Path . Combine ( initDirPath ,  "init.surql" ) ; 
267+     
268+         var  surrealNsName  =  "ns1" ; 
269+         var  surrealDbName  =  "db1" ; 
270+         
271+         try 
272+         { 
273+             await  File . WriteAllTextAsync ( 
274+                 initFilePath ,  
275+         $ """ 
276+                 USE NS { surrealNsName } ; 
277+                 USE DB { surrealDbName } ; 
278+                  
279+                 CREATE car SET Brand = "BatMobile"; 
280+                 """  ,  
281+                 cts . Token 
282+             ) ; 
283+     
284+             using  var  builder  =  TestDistributedApplicationBuilder . Create ( testOutputHelper ) ; 
285+     
286+             var  surrealServer  =  builder 
287+                 . AddSurrealServer ( "surreal" ) 
288+                 . WithInitFiles ( initFilePath ) ; 
289+     
290+             var  ns  =  surrealServer . AddNamespace ( surrealNsName ) ; 
291+             var  db  =  ns . AddDatabase ( surrealDbName ) ; 
292+     
293+             using  var  app  =  builder . Build ( ) ; 
294+     
295+             await  app . StartAsync ( cts . Token ) ; 
296+                 
297+             var  rns  =  app . Services . GetRequiredService < ResourceNotificationService > ( ) ; 
298+             
299+             await  rns . WaitForResourceAsync ( db . Resource . Name ,  KnownResourceStates . Running ,  cts . Token ) ; 
300+     
301+             var  hb  =  Host . CreateApplicationBuilder ( ) ; 
302+     
303+             hb . Configuration . AddInMemoryCollection ( new  Dictionary < string ,  string ? > 
304+             { 
305+                 [ $ "ConnectionStrings:{ db . Resource . Name } "]  =  await  db . Resource . ConnectionStringExpression . GetValueAsync ( cts . Token ) 
306+             } ) ; 
307+     
308+             hb . AddSurrealClient ( db . Resource . Name ) ; 
309+     
310+             using  var  host  =  hb . Build ( ) ; 
311+     
312+             await  host . StartAsync ( cts . Token ) ; 
313+ 
314+             // Wait until the database is available 
315+             await  pipeline . ExecuteAsync ( async  token => 
316+             { 
317+                 var  client  =  host . Services . GetRequiredService < SurrealDbClient > ( ) ; 
318+                 bool  healthy  =  await  client . Health ( token ) ; 
319+                 Assert . True ( healthy ) ; 
320+             } ,  cts . Token ) ; 
321+     
322+             await  pipeline . ExecuteAsync ( async  token => 
323+             { 
324+                 var  client  =  host . Services . GetRequiredService < SurrealDbClient > ( ) ; 
325+ 
326+                 var  cars  =  ( await  client . Select < Car > ( Car . Table ,  token ) ) . ToList ( ) ; 
327+                 Assert . True ( cars . Count  ==  1 ) ; 
328+                 Assert . Equal ( "BatMobile" ,  cars [ 0 ] . Brand ) ; 
329+             } ,  cts . Token ) ; 
330+         } 
331+         finally 
332+         { 
333+             try 
334+             { 
335+                 Directory . Delete ( initDirPath ) ; 
336+             } 
337+             catch 
338+             { 
339+                 // Don't fail test if we can't clean the temporary folder 
340+             } 
341+         } 
342+     } 
247343
248344    private  static async  Task  CreateTestData ( SurrealDbClient  surrealDbClient ,  CancellationToken  ct ) 
249345    { 
@@ -252,10 +348,10 @@ private static async Task CreateTestData(SurrealDbClient surrealDbClient, Cancel
252348
253349    private  static async  Task  AssertTestData ( SurrealDbClient  surrealDbClient ,  CancellationToken  ct ) 
254350    { 
255-         var  records  =  await  surrealDbClient . Select < Todo > ( Todo . Table ) ; 
351+         var  records  =  await  surrealDbClient . Select < Todo > ( Todo . Table ,   ct ) ; 
256352        Assert . Equal ( _generatedTodoCount ,  records . Count ( ) ) ; 
257353
258-         var  firstRecord  =  await  surrealDbClient . Select < Todo > ( ( Todo . Table ,  "1" ) ) ; 
354+         var  firstRecord  =  await  surrealDbClient . Select < Todo > ( ( Todo . Table ,  "1" ) ,   ct ) ; 
259355        Assert . NotNull ( firstRecord ) ; 
260356        Assert . Equivalent ( firstRecord ,  _todoList [ 0 ] ) ; 
261357    } 
@@ -278,4 +374,11 @@ public TodoFaker()
278374            RuleFor ( o =>  o . IsComplete ,  f =>  f . Random . Bool ( ) ) ; 
279375        } 
280376    } 
377+ 
378+     private  sealed  class  Car  :  SurrealRecord 
379+     { 
380+         internal  const  string  Table  =  "car" ; 
381+ 
382+         public  string  Brand  {  get ;  set ;  }  =  string . Empty ; 
383+     } 
281384} 
0 commit comments