11using System . Net ;
22using Microsoft . AspNetCore . Hosting ;
33using Microsoft . AspNetCore . Mvc . Testing ;
4+ using Microsoft . Data . SqlClient ;
45using Microsoft . EntityFrameworkCore ;
56using Microsoft . Extensions . DependencyInjection ;
67using Microsoft . Extensions . Logging ;
@@ -17,9 +18,13 @@ public sealed class StartupTests : IDisposable
1718{
1819 readonly WebApplicationFactory < Program > factory ;
1920
21+ readonly SqlServerFixture sqlServer ;
22+ readonly string databaseName = SqlServerFixture . NewDatabaseName ( ) ;
23+
2024 public StartupTests ( SqlServerFixture sqlServer )
2125 {
22- var connectionString = sqlServer . ConnectionString ( SqlServerFixture . NewDatabaseName ( ) ) ;
26+ this . sqlServer = sqlServer ;
27+ var connectionString = sqlServer . ConnectionString ( databaseName ) ;
2328 factory = new WebApplicationFactory < Program > ( ) . WithWebHostBuilder ( b => b
2429 . UseEnvironment ( "Production" )
2530 . UseSetting ( "ConnectionStrings:Default" , connectionString ) ) ;
@@ -62,5 +67,43 @@ public async Task Pages_retry_database_outages_but_the_migration_does_not()
6267 Assert . False ( migrationDb . Database . CreateExecutionStrategy ( ) . RetriesOnFailure ) ;
6368 }
6469
70+ [ Theory ]
71+ [ InlineData ( "/healthz/live" ) ]
72+ [ InlineData ( "/healthz/ready" ) ]
73+ public async Task Health_checks_report_healthy ( string path )
74+ {
75+ using var client = factory . CreateClient ( ) ;
76+
77+ var response = await client . GetAsync ( path , TestContext . Current . CancellationToken ) ;
78+
79+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
80+ Assert . Equal ( "Healthy" , await response . Content . ReadAsStringAsync ( TestContext . Current . CancellationToken ) ) ;
81+ }
82+
83+ [ Fact ]
84+ public async Task Without_the_database_only_readiness_fails ( )
85+ {
86+ using var client = factory . CreateClient ( ) ;
87+ await DropDatabaseAsync ( ) ;
88+
89+ var live = await client . GetAsync ( "/healthz/live" , TestContext . Current . CancellationToken ) ;
90+ var ready = await client . GetAsync ( "/healthz/ready" , TestContext . Current . CancellationToken ) ;
91+
92+ Assert . Equal ( HttpStatusCode . OK , live . StatusCode ) ;
93+ Assert . Equal ( HttpStatusCode . ServiceUnavailable , ready . StatusCode ) ;
94+ Assert . Equal ( "Unhealthy" , await ready . Content . ReadAsStringAsync ( TestContext . Current . CancellationToken ) ) ;
95+ }
96+
97+ // The app can't start without its database (it migrates at startup), so the database goes away afterwards.
98+ async Task DropDatabaseAsync ( )
99+ {
100+ SqlConnection . ClearAllPools ( ) ;
101+ await using var connection = new SqlConnection ( sqlServer . ConnectionString ( "master" ) ) ;
102+ await connection . OpenAsync ( TestContext . Current . CancellationToken ) ;
103+ await using var command = new SqlCommand (
104+ $ "ALTER DATABASE [{ databaseName } ] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [{ databaseName } ];", connection ) ;
105+ await command . ExecuteNonQueryAsync ( TestContext . Current . CancellationToken ) ;
106+ }
107+
65108 public void Dispose ( ) => factory . Dispose ( ) ;
66109}
0 commit comments