Add a shared SQLite test database helper (#754)

This commit is contained in:
Flaminel authored and GitHub committed 2026-08-31 17:04:44 +03:00
1 parent 47bd8b0e50
commit caebcf550d
3 files changed
+72 -32

No files matched your search

@@ -2,6 +2,7 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using Cleanuparr.Domain.Enums;
using Cleanuparr.Infrastructure.Tests.TestHelpers;
using Cleanuparr.Persistence;
using Cleanuparr.Persistence.Models.Configuration.Arr;
using Cleanuparr.Persistence.Models.Configuration.BlacklistSync;
@@ -43,11 +44,11 @@ public class SeedParityTests
throw new SkipException($"Docker is unavailable, skipping seed parity test: {exception.Message}");
}
string sqlitePath = Path.Combine(Path.GetTempPath(), $"cleanuparr-seed-parity-{Guid.NewGuid():N}.db");
using SqliteTestDatabase sqlite = SqliteTestDatabase.Create("seed-parity");
try
{
SeedSnapshot sqliteSnapshot = await LoadSqliteSeedAsync(sqlitePath);
SeedSnapshot sqliteSnapshot = await LoadSqliteSeedAsync(sqlite);
SeedSnapshot postgresSnapshot = await LoadPostgresSeedAsync(postgresContainer);
AssertSingletonMatches("general_configs", sqliteSnapshot.GeneralConfig, postgresSnapshot.GeneralConfig);
@@ -62,23 +63,12 @@ public class SeedParityTests
finally
{
await postgresContainer.DisposeAsync();
if (File.Exists(sqlitePath))
{
File.Delete(sqlitePath);
}
}
}
private static async Task<SeedSnapshot> LoadSqliteSeedAsync(string sqlitePath)
private static async Task<SeedSnapshot> LoadSqliteSeedAsync(SqliteTestDatabase sqlite)
{
DbContextOptionsBuilder<DataContext> optionsBuilder = new();
optionsBuilder
.UseSqlite($"Data Source={sqlitePath}", options => options.MigrationsAssembly("Cleanuparr.Persistence.Sqlite"))
.UseLowerCaseNamingConvention()
.UseSnakeCaseNamingConvention();
await using DataContext context = new(optionsBuilder.Options, new SqliteDatabaseProvider());
await using DataContext context = sqlite.CreateContext<DataContext>();
await context.Database.MigrateAsync();
return await LoadSnapshotAsync(context);
@@ -1,4 +1,5 @@
using Cleanuparr.Domain.Enums;
using Cleanuparr.Infrastructure.Tests.TestHelpers;
using Cleanuparr.Persistence;
using Cleanuparr.Persistence.Models.Events;
using Cleanuparr.Persistence.Providers;
@@ -42,11 +43,11 @@ public class TimelineParityTests
throw new SkipException($"Docker is unavailable, skipping timeline parity test: {exception.Message}");
}
string sqlitePath = Path.Combine(Path.GetTempPath(), $"cleanuparr-timeline-parity-{Guid.NewGuid():N}.db");
using SqliteTestDatabase sqlite = SqliteTestDatabase.Create("timeline-parity");
try
{
List<string> sqliteLabels = await BucketLabelsSqliteAsync(sqlitePath, size);
List<string> sqliteLabels = await BucketLabelsSqliteAsync(sqlite, size);
List<string> postgresLabels = await BucketLabelsPostgresAsync(postgresContainer, size);
postgresLabels.ShouldBe(sqliteLabels);
@@ -54,28 +55,17 @@ public class TimelineParityTests
finally
{
await postgresContainer.DisposeAsync();
if (File.Exists(sqlitePath))
{
File.Delete(sqlitePath);
}
}
}
private static async Task<List<string>> BucketLabelsSqliteAsync(string sqlitePath, TimelineBucketSize size)
private static async Task<List<string>> BucketLabelsSqliteAsync(
SqliteTestDatabase sqlite, TimelineBucketSize size)
{
SqliteDatabaseProvider provider = new();
DbContextOptionsBuilder<EventsContext> builder = new();
builder
.UseSqlite($"Data Source={sqlitePath}", options => options.MigrationsAssembly("Cleanuparr.Persistence.Sqlite"))
.UseLowerCaseNamingConvention()
.UseSnakeCaseNamingConvention();
await using EventsContext context = new(builder.Options, provider);
await using EventsContext context = sqlite.CreateContext<EventsContext>();
await context.Database.MigrateAsync();
await SeedAsync(context);
return await BucketLabelsAsync(context, provider, size);
return await BucketLabelsAsync(context, sqlite.Provider, size);
}
private static async Task<List<string>> BucketLabelsPostgresAsync(PostgreSqlContainer container, TimelineBucketSize size)
@@ -0,0 +1,60 @@
using Cleanuparr.Persistence.Providers;
using Microsoft.EntityFrameworkCore;
namespace Cleanuparr.Infrastructure.Tests.TestHelpers;
/// <summary>
/// A throwaway file-backed SQLite database, wired the way the app wires one.
/// Dispose deletes the file and its journals.
/// </summary>
public sealed class SqliteTestDatabase : IDisposable
{
private readonly string _path;
private SqliteTestDatabase(string path)
{
_path = path;
}
/// <summary>
/// The provider the contexts were built with.
/// </summary>
public SqliteDatabaseProvider Provider { get; } = new();
/// <summary>
/// Names the temp file after the caller.
/// </summary>
public static SqliteTestDatabase Create(string name) =>
new(Path.Combine(Path.GetTempPath(), $"cleanuparr-{name}-{Guid.NewGuid():N}.db"));
/// <summary>
/// A context pointing at this database, unmigrated.
/// </summary>
public TContext CreateContext<TContext>()
where TContext : DbContext
{
DbContextOptionsBuilder<TContext> optionsBuilder = new();
optionsBuilder
.UseSqlite(
$"Data Source={_path}",
options => options.MigrationsAssembly("Cleanuparr.Persistence.Sqlite"))
.UseLowerCaseNamingConvention()
.UseSnakeCaseNamingConvention();
return (TContext)Activator.CreateInstance(
typeof(TContext),
optionsBuilder.Options,
Provider)!;
}
public void Dispose()
{
foreach (string file in new[] { _path, $"{_path}-wal", $"{_path}-shm" })
{
if (File.Exists(file))
{
File.Delete(file);
}
}
}
}