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,93 @@ public async Task VerifyWaitForOnSurrealDbBlocksDependentResources()
244247
245248        await  app . StopAsync ( ) ; 
246249    } 
250+     
251+     [ Fact ] 
252+     public  async  Task  VerifyWithInitFiles ( ) 
253+     { 
254+         // Creates a script that should be executed when the container is initialized. 
255+     
256+         using  var  cts  =  new  CancellationTokenSource ( TimeSpan . FromMinutes ( 10 ) ) ; 
257+         var  pipeline  =  new  ResiliencePipelineBuilder ( ) 
258+             . AddRetry ( new ( )  {  MaxRetryAttempts  =  10 ,  BackoffType  =  DelayBackoffType . Linear ,  Delay  =  TimeSpan . FromSeconds ( 2 ) ,  ShouldHandle  =  new  PredicateBuilder ( ) . Handle < SurrealDbException > ( )  } ) 
259+             . Build ( ) ; 
260+     
261+         var  initDirPath  =  Path . Combine ( Path . GetTempPath ( ) ,  Path . GetRandomFileName ( ) ) ; 
262+     
263+         Directory . CreateDirectory ( initDirPath ) ; 
264+         
265+         var  initFilePath  =  Path . Combine ( initDirPath ,  "init.surql" ) ; 
266+     
267+         var  surrealNsName  =  "ns1" ; 
268+         var  surrealDbName  =  "db1" ; 
269+         
270+         try 
271+         { 
272+             await  File . WriteAllTextAsync ( 
273+                 initFilePath ,  
274+         $ """ 
275+                 USE NS { surrealNsName } ; 
276+                 USE DB { surrealDbName } ; 
277+                  
278+                 CREATE car SET Brand = "BatMobile"; 
279+                 """  ,  
280+                 cts . Token 
281+             ) ; 
282+     
283+             using  var  builder  =  TestDistributedApplicationBuilder . Create ( testOutputHelper ) ; 
284+     
285+             var  surrealServer  =  builder 
286+                 . AddSurrealServer ( "surreal" ) 
287+                 . WithInitFiles ( initFilePath ) ; 
288+     
289+             var  ns  =  surrealServer . AddNamespace ( surrealNsName ) ; 
290+             var  db  =  ns . AddDatabase ( surrealDbName ) ; 
291+     
292+             using  var  app  =  builder . Build ( ) ; 
293+     
294+             await  app . StartAsync ( cts . Token ) ; 
295+                 
296+             var  rns  =  app . Services . GetRequiredService < ResourceNotificationService > ( ) ; 
297+             
298+             await  rns . WaitForResourceAsync ( db . Resource . Name ,  KnownResourceStates . Running ,  cts . Token ) ; 
299+     
300+             var  hb  =  Host . CreateApplicationBuilder ( ) ; 
301+     
302+             hb . Configuration . AddInMemoryCollection ( new  Dictionary < string ,  string ? > 
303+             { 
304+                 [ $ "ConnectionStrings:{ db . Resource . Name } "]  =  await  db . Resource . ConnectionStringExpression . GetValueAsync ( cts . Token ) 
305+             } ) ; 
306+     
307+             hb . AddSurrealClient ( db . Resource . Name ) ; 
308+     
309+             using  var  host  =  hb . Build ( ) ; 
310+     
311+             await  host . StartAsync ( cts . Token ) ; 
312+ 
313+             // Wait until the database is available 
314+             await  rns . WaitForResourceHealthyAsync ( db . Resource . Name ,  cts . Token ) ; 
315+     
316+             await  pipeline . ExecuteAsync ( async  token => 
317+             { 
318+                 var  client  =  host . Services . GetRequiredService < SurrealDbClient > ( ) ; 
319+ 
320+                 var  cars  =  ( await  client . Select < Car > ( Car . Table ,  token ) ) . ToList ( ) ; 
321+                 var  car  =  Assert . Single ( cars ) ; 
322+                 Assert . Equal ( "BatMobile" ,  car . Brand ) ; 
323+             } ,  cts . Token ) ; 
324+         } 
325+         finally 
326+         { 
327+             try 
328+             { 
329+                 Directory . Delete ( initDirPath ) ; 
330+             } 
331+             catch 
332+             { 
333+                 // Don't fail test if we can't clean the temporary folder 
334+             } 
335+         } 
336+     } 
247337
248338    private  static async  Task  CreateTestData ( SurrealDbClient  surrealDbClient ,  CancellationToken  ct ) 
249339    { 
@@ -252,10 +342,10 @@ private static async Task CreateTestData(SurrealDbClient surrealDbClient, Cancel
252342
253343    private  static async  Task  AssertTestData ( SurrealDbClient  surrealDbClient ,  CancellationToken  ct ) 
254344    { 
255-         var  records  =  await  surrealDbClient . Select < Todo > ( Todo . Table ) ; 
345+         var  records  =  await  surrealDbClient . Select < Todo > ( Todo . Table ,   ct ) ; 
256346        Assert . Equal ( _generatedTodoCount ,  records . Count ( ) ) ; 
257347
258-         var  firstRecord  =  await  surrealDbClient . Select < Todo > ( ( Todo . Table ,  "1" ) ) ; 
348+         var  firstRecord  =  await  surrealDbClient . Select < Todo > ( ( Todo . Table ,  "1" ) ,   ct ) ; 
259349        Assert . NotNull ( firstRecord ) ; 
260350        Assert . Equivalent ( firstRecord ,  _todoList [ 0 ] ) ; 
261351    } 
@@ -278,4 +368,11 @@ public TodoFaker()
278368            RuleFor ( o =>  o . IsComplete ,  f =>  f . Random . Bool ( ) ) ; 
279369        } 
280370    } 
371+ 
372+     private  sealed  class  Car  :  SurrealRecord 
373+     { 
374+         internal  const  string  Table  =  "car" ; 
375+ 
376+         public  string  Brand  {  get ;  set ;  }  =  string . Empty ; 
377+     } 
281378} 
0 commit comments