diff --git a/Directory.Packages.props b/Directory.Packages.props
index 84380e44..e4b09159 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -18,7 +18,6 @@
-
diff --git a/src/Yavsc.Blogs.Tests/BlogApiTests.cs b/src/Yavsc.Blogs.Tests/BlogApiTests.cs
index 4ac14c50..fd64555a 100644
--- a/src/Yavsc.Blogs.Tests/BlogApiTests.cs
+++ b/src/Yavsc.Blogs.Tests/BlogApiTests.cs
@@ -30,11 +30,10 @@ public sealed class BlogApiTests : IClassFixture
}
/// Reset the in-memory database to a known empty state.
- /// The fixture now uses SQLite in-memory (see forgejo#3), which
- /// shares its store across the lifetime of the
- /// instance, so without a
- /// per-test reset the test order would leak state between
- /// tests.
+ /// UseInMemoryDatabase shares its store across the
+ /// lifetime of the instance,
+ /// so without a per-test reset the test order would leak
+ /// state between tests.
private void ResetDatabase()
{
using var scope = _fixture.Services.CreateScope();
diff --git a/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs b/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs
index a76a37a7..6e39fd00 100644
--- a/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs
+++ b/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs
@@ -48,19 +48,6 @@ namespace Yavsc.Blogs.Tests;
///
public sealed class BlogsWebServerFixture : WebHostFixture
{
- // SQLite in-memory database is created once and shared across all
- // DbContext instances for the test lifetime. The connection must
- // stay open: closing it destroys the in-memory database. The
- // Microsoft.Data.Sqlite pool will then open additional connections
- // to the same in-memory store, as long as the original connection
- // is alive. This is the SQLite equivalent of the EF Core
- // InMemoryDatabaseRoot we used to use.
- private Microsoft.Data.Sqlite.SqliteConnection? _sharedSqliteConnection;
- // Legacy field kept to make the migration diff readable. The
- // InMemory provider path is no longer used by this fixture, but
- // removing it is out of scope for the SQLite-in-memory migration
- // (forgejo#3 follow-up).
- [System.Obsolete("Replaced by SQLite in-memory (forgejo#3).")]
private InMemoryDatabaseRoot? _inMemoryRoot;
protected override WebApplication BuildApp(WebApplicationBuilder builder)
@@ -71,18 +58,15 @@ public sealed class BlogsWebServerFixture : WebHostFixture
// against an empty table returns an empty list, which is
// exactly what the first test wants to assert.
//
- // We use SQLite in-memory (not the EF Core InMemory provider)
- // because the InMemory provider cannot materialise navigation
- // properties from IdentityServer8 entity types (see forgejo#3).
- // SQLite in-memory is a transient, file-less store that
- // executes real SQL, so navigation properties work as
- // expected. The shared SqliteConnection keeps the database
- // alive for the test lifetime, mirroring the
- // InMemoryDatabaseRoot pattern we used previously.
- _sharedSqliteConnection = new Microsoft.Data.Sqlite.SqliteConnection("Data Source=:memory:");
- _sharedSqliteConnection.Open();
+ // Share a single InMemoryDatabaseRoot across the test
+ // lifetime so POST + GET on the same fixture see the same
+ // store. Without the root, EF Core's In-Memory provider
+ // creates independent stores per DbContext in some
+ // configurations, and the second request would see an
+ // empty list even after the first wrote a row.
+ _inMemoryRoot = new InMemoryDatabaseRoot();
builder.Services.AddDbContext(opt =>
- opt.UseSqlite(_sharedSqliteConnection));
+ opt.UseInMemoryDatabase("Yavsc.Blogs.Tests", _inMemoryRoot));
// Trivial file-system auth: the GET index path never calls
// into it, but the DI container needs an instance.
diff --git a/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj b/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj
index 831097b0..ec1f7f0a 100644
--- a/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj
+++ b/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj
@@ -17,7 +17,6 @@
-
diff --git a/src/Yavsc.Org.Tests/TestWebApplicationFactory.cs b/src/Yavsc.Org.Tests/TestWebApplicationFactory.cs
index af40bb78..dfd6edea 100644
--- a/src/Yavsc.Org.Tests/TestWebApplicationFactory.cs
+++ b/src/Yavsc.Org.Tests/TestWebApplicationFactory.cs
@@ -2,11 +2,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Yavsc.Models;
using Yavsc.Tests.Shared;
namespace Yavsc.Org.Tests;
@@ -28,21 +24,6 @@ namespace Yavsc.Org.Tests;
///
public class TestWebApplicationFactory : WebApplicationFactory
{
- // SQLite in-memory: the connection must stay open for the lifetime
- // of the host, otherwise the in-memory database is destroyed and
- // every new DbContext sees an empty store. We hold the connection
- // here so it is disposed only when the factory is disposed. The
- // Microsoft.Data.Sqlite pool reuses the underlying in-memory store
- // across additional connections opened against the same connection
- // string, as long as the original connection is alive. This is the
- // SQLite equivalent of the EF Core InMemoryDatabaseRoot pattern.
- private readonly SqliteConnection _sharedSqliteConnection = new("Data Source=:memory:");
-
- public TestWebApplicationFactory()
- {
- _sharedSqliteConnection.Open();
- }
-
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
// UseEnvironment("Testing") puts the host in a dedicated
@@ -57,25 +38,6 @@ public class TestWebApplicationFactory : WebApplicationFactory
builder.ConfigureTestServices(services =>
{
- // The production Program.Main calls AddConfiguration("org")
- // and then AddIdentityDBAndStores which calls
- // GetConnectionString("YavscConnection"). The result is
- // "Data Source=:memory:" (from appsettings-org.Testing.json),
- // and the production code path in HostingExtensions routes
- // that to UseSqlite. However, the EF Core in-memory test
- // pattern needs all DbContext instances to see the same
- // store; with a raw "Data Source=:memory:" connection string,
- // each connection opens its own private database. We
- // therefore drop the production DbContext registration and
- // re-register ApplicationDbContext with the shared
- // SqliteConnection held by this factory. Tests that need
- // the schema to exist call EnsureCreated on the resulting
- // DbContext (e.g. ClientControllerCollectionTests seeds a
- // Client row in its constructor).
- services.RemoveAll>();
- services.AddDbContext(opt =>
- opt.UseSqlite(_sharedSqliteConnection));
-
// Replace the production IAuthorizationPolicyProvider with
// the test one. The default registered by AddAuthorization
// becomes irrelevant: any GetPolicyAsync call is routed here.
@@ -86,37 +48,6 @@ public class TestWebApplicationFactory : WebApplicationFactory
// TestUserMiddleware runs after UseAuthentication/Authorization.
services.AddTransient();
services.AddTransient();
-
- // Run EnsureCreated once at host start. With SQLite in-memory
- // and a shared connection, this creates the schema once
- // and the schema persists for the host lifetime. The
- // test code (e.g. ClientControllerCollectionTests seed) can
- // then write rows without having to call EnsureCreated
- // itself. EnsureCreated is idempotent: re-running it on
- // an existing schema is a no-op.
- services.AddHostedService();
});
}
-
- private sealed class SqliteEnsureCreatedHostedService : IHostedService
- {
- private readonly IServiceProvider _services;
- public SqliteEnsureCreatedHostedService(IServiceProvider services)
- {
- _services = services;
- }
- public Task StartAsync(CancellationToken cancellationToken)
- {
- using var scope = _services.CreateScope();
- var db = scope.ServiceProvider.GetRequiredService();
- return db.Database.EnsureCreatedAsync(cancellationToken);
- }
- public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
- }
-
- protected override void Dispose(bool disposing)
- {
- if (disposing) _sharedSqliteConnection.Dispose();
- base.Dispose(disposing);
- }
}
diff --git a/src/Yavsc.Org.Tests/WebServerFixture.cs b/src/Yavsc.Org.Tests/WebServerFixture.cs
index ebb50fc7..ed0bd69d 100644
--- a/src/Yavsc.Org.Tests/WebServerFixture.cs
+++ b/src/Yavsc.Org.Tests/WebServerFixture.cs
@@ -75,20 +75,7 @@ public sealed class WebServerFixture : WebHostFixture
// that plus the in-memory overrides below.
builder.AddConfiguration(null).AddInMemoryCollection(new Dictionary
{
- // The EF Core in-memory provider cannot materialise
- // entity types from IdentityServer8 (see forgejo#3):
- // it crashes with IndexOutOfRangeException on the
- // multi-Include query in ClientController.LoadClientAsync
- // and on per-collection LoadAsync. SQLite in-memory is
- // a transient, file-less store that uses the same
- // connection string semantics as the InMemory provider
- // ("keep the connection open for the host lifetime")
- // but actually executes SQL, so it handles
- // navigation-property entities correctly. The
- // HostingExtensions code path detects this connection
- // string and routes to UseSqlite. See
- // doc/testing.md for the test-driver policy.
- [$"ConnectionStrings:{YavscConstants.YavscConnectionStringName}"] = "Data Source=:memory:",
+ [$"ConnectionStrings:{YavscConstants.YavscConnectionStringName}"] = "InMemory",
// SMTP test config: UserName non-null so MailSender
// exercises the Authenticate branch — the
// RecordingSmtpClient captures it.
diff --git a/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj b/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj
index 5d049626..79a6bae1 100644
--- a/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj
+++ b/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj
@@ -53,7 +53,6 @@
-