Skip to content

Commit

Permalink
(#239) IAsyncLifetime support for PgSQL live tests
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianhall committed Jan 31, 2025
1 parent f30aa6c commit bc86d00
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,45 @@
using Microsoft.EntityFrameworkCore;
using Xunit.Abstractions;

#pragma warning disable CS9113 // Parameter is unread.

namespace CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test;

[ExcludeFromCodeCoverage]
[Collection("LiveTestsCollection")]
public class PgEntityTableRepository_Tests : RepositoryTests<PgEntityMovie>
public class PgEntityTableRepository_Tests(DatabaseFixture fixture, ITestOutputHelper output) : RepositoryTests<PgEntityMovie>, IAsyncLifetime
{
#region Setup
private readonly DatabaseFixture _fixture;
private readonly Random random = new();
private readonly string connectionString;
private readonly List<PgEntityMovie> movies;
private readonly Lazy<PgDbContext> _context;
private readonly string connectionString = Environment.GetEnvironmentVariable("DATASYNC_PGSQL_CONNECTIONSTRING");
private List<PgEntityMovie> movies = [];

public PgEntityTableRepository_Tests(DatabaseFixture fixture, ITestOutputHelper output) : base()
public async Task InitializeAsync()
{
this._fixture = fixture;
this.connectionString = Environment.GetEnvironmentVariable("DATASYNC_PGSQL_CONNECTIONSTRING");
if (!string.IsNullOrEmpty(this.connectionString))
{
this._context = new Lazy<PgDbContext>(() => PgDbContext.CreateContext(this.connectionString, output));
this.movies = Context.Movies.AsNoTracking().ToList();
Context = await PgDbContext.CreateContextAsync(this.connectionString, output);
this.movies = await Context.Movies.AsNoTracking().ToListAsync();
}
}

public async Task DisposeAsync()
{
if (Context is not null)
{
await Context.DisposeAsync();
}
}

private PgDbContext Context { get => this._context.Value; }
private PgDbContext Context { get; set; }

protected override bool CanRunLiveTests() => !string.IsNullOrEmpty(this.connectionString);

protected override Task<PgEntityMovie> GetEntityAsync(string id)
=> Task.FromResult(Context.Movies.AsNoTracking().SingleOrDefault(m => m.Id == id));
protected override async Task<PgEntityMovie> GetEntityAsync(string id)
=> await Context.Movies.AsNoTracking().SingleOrDefaultAsync(m => m.Id == id);

protected override Task<int> GetEntityCountAsync()
=> Task.FromResult(Context.Movies.Count());
protected override async Task<int> GetEntityCountAsync()
=> await Context.Movies.CountAsync();

protected override Task<IRepository<PgEntityMovie>> GetPopulatedRepositoryAsync()
=> Task.FromResult<IRepository<PgEntityMovie>>(new EntityTableRepository<PgEntityMovie>(Context));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public async Task InitializeAsync()
// Note: we don't clear entities on every run to speed up the test runs. This can only be done because
// the tests are read-only (associated with the query and get capabilities). If the test being run writes
// to the database then change clearEntities to true.
output.WriteLine($"CosmosIsInitialized = {fixture.AzureSqlIsInitialized}");
output.WriteLine($"AzureSqlIsInitialized = {fixture.AzureSqlIsInitialized}");
Context = await AzureSqlDbContext.CreateContextAsync(this.connectionString, output, clearEntities: !fixture.AzureSqlIsInitialized);
this.movies = await Context.Movies.AsNoTracking().ToListAsync();
fixture.AzureSqlIsInitialized = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public async Task InitializeAsync()
// Note: we don't clear entities on every run to speed up the test runs. This can only be done because
// the tests are read-only (associated with the query and get capabilities). If the test being run writes
// to the database then change clearEntities to true.
output.WriteLine($"CosmosIsInitialized = {fixture.MysqlIsInitialized}");
output.WriteLine($"MysqlIsInitialized = {fixture.MysqlIsInitialized}");
Context = await MysqlDbContext.CreateContextAsync(this.connectionString, output, clearEntities: !fixture.MysqlIsInitialized);
this.movies = await Context.Movies.AsNoTracking().ToListAsync();
fixture.MysqlIsInitialized = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,32 @@ namespace CommunityToolkit.Datasync.Server.Test.Live;

[ExcludeFromCodeCoverage]
[Collection("LiveTestsCollection")]
public class PgSQL_Controller_Tests : LiveControllerTests<PgEntityMovie>
public class PgSQL_Controller_Tests(DatabaseFixture fixture, ITestOutputHelper output) : LiveControllerTests<PgEntityMovie>, IAsyncLifetime
{
#region Setup
private readonly DatabaseFixture _fixture;
private readonly Random random = new();
private readonly string connectionString;
private readonly List<PgEntityMovie> movies;
private readonly string connectionString = Environment.GetEnvironmentVariable("DATASYNC_PGSQL_CONNECTIONSTRING");
private List<PgEntityMovie> movies = [];

public PgSQL_Controller_Tests(DatabaseFixture fixture, ITestOutputHelper output) : base()
public async Task InitializeAsync()
{
this._fixture = fixture;
this.connectionString = Environment.GetEnvironmentVariable("DATASYNC_PGSQL_CONNECTIONSTRING");
if (!string.IsNullOrEmpty(this.connectionString))
{
output.WriteLine($"PgIsInitialized = {this._fixture.PgIsInitialized}");
Context = PgDbContext.CreateContext(this.connectionString, output, clearEntities: !this._fixture.PgIsInitialized);
// Note: we don't clear entities on every run to speed up the test runs. This can only be done because
// the tests are read-only (associated with the query and get capabilities). If the test being run writes
// to the database then change clearEntities to true.
output.WriteLine($"PgIsInitialized = {fixture.PgIsInitialized}");
Context = await PgDbContext.CreateContextAsync(this.connectionString, output, clearEntities: !fixture.PgIsInitialized);
this.movies = Context.Movies.AsNoTracking().ToList();
this._fixture.PgIsInitialized = true;
fixture.PgIsInitialized = true;
}
}

public async Task DisposeAsync()
{
if (Context is not null)
{
await Context.DisposeAsync();
}
}

Expand All @@ -39,11 +47,11 @@ public PgSQL_Controller_Tests(DatabaseFixture fixture, ITestOutputHelper output)

protected override bool CanRunLiveTests() => !string.IsNullOrEmpty(this.connectionString);

protected override Task<PgEntityMovie> GetEntityAsync(string id)
=> Task.FromResult(Context.Movies.AsNoTracking().SingleOrDefault(m => m.Id == id));
protected override async Task<PgEntityMovie> GetEntityAsync(string id)
=> await Context.Movies.AsNoTracking().SingleOrDefaultAsync(m => m.Id == id);

protected override Task<int> GetEntityCountAsync()
=> Task.FromResult(Context.Movies.Count());
protected override async Task<int> GetEntityCountAsync()
=> await Context.Movies.CountAsync();

protected override Task<IRepository<PgEntityMovie>> GetPopulatedRepositoryAsync()
=> Task.FromResult<IRepository<PgEntityMovie>>(new EntityTableRepository<PgEntityMovie>(Context));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace CommunityToolkit.Datasync.TestCommon.Databases;
[ExcludeFromCodeCoverage]
public class PgDbContext(DbContextOptions<PgDbContext> options) : BaseDbContext<PgDbContext, PgEntityMovie>(options)
{
public static PgDbContext CreateContext(string connectionString, ITestOutputHelper output = null, bool clearEntities = true)
public static async Task<PgDbContext> CreateContextAsync(string connectionString, ITestOutputHelper output = null, bool clearEntities = true)
{
if (string.IsNullOrEmpty(connectionString))
{
Expand All @@ -22,36 +22,37 @@ public static PgDbContext CreateContext(string connectionString, ITestOutputHelp
.EnableLogging(output);
PgDbContext context = new(optionsBuilder.Options);

context.InitializeDatabase(clearEntities);
context.PopulateDatabase();
await context.InitializeDatabaseAsync(clearEntities);
await context.PopulateDatabaseAsync();
return context;
}

internal void InitializeDatabase(bool clearEntities)
internal async Task InitializeDatabaseAsync(bool clearEntities)
{
const string datasyncTrigger = @"
const string datasyncTrigger = """
CREATE OR REPLACE FUNCTION {0}_datasync() RETURNS trigger AS $$
BEGIN
NEW.""UpdatedAt"" = NOW() AT TIME ZONE 'UTC';
NEW.""Version"" = convert_to(gen_random_uuid()::text, 'UTF8');
NEW."UpdatedAt" = NOW() AT TIME ZONE 'UTC';
NEW."Version" = convert_to(gen_random_uuid()::text, 'UTF8');
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER
{0}_datasync
BEFORE INSERT OR UPDATE ON
""{0}""
"{0}"
FOR EACH ROW EXECUTE PROCEDURE
{0}_datasync();
";
""";

Database.EnsureCreated();
ExecuteRawSqlOnEachEntity(datasyncTrigger);
await Database.EnsureCreatedAsync();
await ExecuteRawSqlOnEachEntityAsync(datasyncTrigger);

if (clearEntities)
{
ExecuteRawSqlOnEachEntity(@"DELETE FROM ""{0}""");
await ExecuteRawSqlOnEachEntityAsync(@"DELETE FROM ""{0}""");
}
}

Expand Down

0 comments on commit bc86d00

Please sign in to comment.