1313using Microsoft . EntityFrameworkCore ;
1414using Microsoft . Extensions . DependencyInjection ;
1515using Microsoft . Extensions . DependencyInjection . Extensions ;
16+ using Microsoft . Extensions . Hosting ;
1617
1718namespace API . Tests . Integration ;
1819
@@ -33,7 +34,12 @@ public sealed class KenkuApplicationFactory : WebApplicationFactory<Program>
3334 new ServiceCollection ( ) . AddEntityFrameworkInMemoryDatabase ( ) . BuildServiceProvider ( ) ;
3435
3536 /// <summary>Base URL of the local server outbound metadata requests should be redirected to.</summary>
36- public required string OutboundHttpTarget { get ; init ; }
37+ public string OutboundHttpTarget { get ; init ; } = "http://localhost:1" ;
38+
39+ /// <summary>When set, the three core contexts (Series, Jobs, Actions) are backed by this Postgres
40+ /// database instead of the default InMemory stores. Used by concurrency and migration tests that
41+ /// need a real relational engine.</summary>
42+ public string ? PostgresConnectionString { get ; init ; }
3743
3844 /// <summary>Optional stub for the connectors' HTTP edge, so a connector flow can be driven without
3945 /// real network access. When set, it replaces the registered <see cref="IHttpRequester"/>.</summary>
@@ -70,11 +76,22 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
7076 builder . UseSetting ( "Kenku:AppData" , Path . Combine ( Path . GetTempPath ( ) , "kenku-test-" + _id ) ) ;
7177 builder . ConfigureTestServices ( services =>
7278 {
73- UseInMemory < SeriesContext > ( services ) ;
74- UseInMemory < NotificationsContext > ( services ) ;
75- UseInMemory < LibraryContext > ( services ) ;
76- UseInMemory < ActionsContext > ( services ) ;
77- UseInMemory < global ::API . Schema . JobsContext . JobsContext > ( services ) ;
79+ if ( PostgresConnectionString is not null )
80+ {
81+ UseNpgsql < SeriesContext > ( services , PostgresConnectionString ) ;
82+ UseNpgsql < global ::API . Schema . JobsContext . JobsContext > ( services , PostgresConnectionString ) ;
83+ UseNpgsql < ActionsContext > ( services , PostgresConnectionString ) ;
84+ UseInMemory < NotificationsContext > ( services ) ;
85+ UseInMemory < LibraryContext > ( services ) ;
86+ }
87+ else
88+ {
89+ UseInMemory < SeriesContext > ( services ) ;
90+ UseInMemory < NotificationsContext > ( services ) ;
91+ UseInMemory < LibraryContext > ( services ) ;
92+ UseInMemory < ActionsContext > ( services ) ;
93+ UseInMemory < global ::API . Schema . JobsContext . JobsContext > ( services ) ;
94+ }
7895
7996 RouteOutboundHttp < MangaDexVolumeResolver > ( services ) ;
8097 RouteOutboundHttp < MangaDexSearchService > ( services ) ;
@@ -118,6 +135,20 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
118135 } ) ;
119136 }
120137
138+ protected override IHost CreateHost ( IHostBuilder builder )
139+ {
140+ var host = base . CreateHost ( builder ) ;
141+ if ( PostgresConnectionString is not null )
142+ {
143+ using var scope = host . Services . CreateScope ( ) ;
144+ var sp = scope . ServiceProvider ;
145+ sp . GetRequiredService < SeriesContext > ( ) . Database . MigrateAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
146+ sp . GetRequiredService < global ::API . Schema . JobsContext . JobsContext > ( ) . Database . MigrateAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
147+ sp . GetRequiredService < ActionsContext > ( ) . Database . MigrateAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
148+ }
149+ return host ;
150+ }
151+
121152 private void UseInMemory < TContext > ( IServiceCollection services ) where TContext : DbContext
122153 {
123154 services . RemoveAll < DbContextOptions < TContext > > ( ) ;
@@ -127,6 +158,13 @@ private void UseInMemory<TContext>(IServiceCollection services) where TContext :
127158 . UseInternalServiceProvider ( _efProvider ) ) ;
128159 }
129160
161+ private static void UseNpgsql < TContext > ( IServiceCollection services , string connectionString ) where TContext : DbContext
162+ {
163+ services . RemoveAll < DbContextOptions < TContext > > ( ) ;
164+ services . RemoveAll < TContext > ( ) ;
165+ services . AddDbContext < TContext > ( o => o . UseNpgsql ( connectionString ) ) ;
166+ }
167+
130168 // Replaces the typed client's primary handler so its (absolute) requests are redirected to the
131169 // local server. The resolver's own configuration (e.g. the User-Agent) is left intact.
132170 private void RouteOutboundHttp < TClient > ( IServiceCollection services ) where TClient : class =>
0 commit comments