Skip to content

Commit f83e69c

Browse files
author
Adrian Hall
committed
(#199) Fixes to support async initialization within tests for Cosmos.
1 parent 578670b commit f83e69c

File tree

5 files changed

+83
-55
lines changed

5 files changed

+83
-55
lines changed

tests/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test/CosmosEntityTableRepository_Tests.cs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,42 @@
77
using Microsoft.EntityFrameworkCore;
88
using Xunit.Abstractions;
99

10+
#pragma warning disable CS9113 // Parameter is unread.
11+
1012
namespace CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test;
1113

1214
[ExcludeFromCodeCoverage]
1315
[Collection("LiveTestsCollection")]
14-
public class CosmosEntityTableRepository_Tests : RepositoryTests<CosmosEntityMovie>
16+
public class CosmosEntityTableRepository_Tests(DatabaseFixture fixture, ITestOutputHelper output) : RepositoryTests<CosmosEntityMovie>(), IAsyncLifetime
1517
{
1618
#region Setup
17-
private readonly DatabaseFixture _fixture;
1819
private readonly Random random = new();
19-
private readonly string connectionString;
20-
private readonly List<CosmosEntityMovie> movies;
21-
private readonly Lazy<CosmosDbContext> _context;
20+
private readonly string connectionString = Environment.GetEnvironmentVariable("DATASYNC_COSMOS_CONNECTIONSTRING");
21+
private List<CosmosEntityMovie> movies = [];
2222

23-
public CosmosEntityTableRepository_Tests(DatabaseFixture fixture, ITestOutputHelper output) : base()
23+
public async Task InitializeAsync()
2424
{
25-
this._fixture = fixture;
26-
this.connectionString = Environment.GetEnvironmentVariable("DATASYNC_COSMOS_CONNECTIONSTRING");
2725
if (!string.IsNullOrEmpty(this.connectionString))
2826
{
29-
this._context = new Lazy<CosmosDbContext>(() => CosmosDbContext.CreateContext(this.connectionString, output));
30-
this.movies = Context.Movies.AsNoTracking().ToList();
27+
Context = await CosmosDbContext.CreateContextAsync(this.connectionString, output);
28+
this.movies = await Context.Movies.AsNoTracking().ToListAsync();
3129
}
3230
}
3331

34-
private CosmosDbContext Context { get => this._context.Value; }
32+
public async Task DisposeAsync()
33+
{
34+
await Context.DisposeAsync();
35+
}
36+
37+
private CosmosDbContext Context { get; set; }
3538

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

38-
protected override Task<CosmosEntityMovie> GetEntityAsync(string id)
39-
=> Task.FromResult(Context.Movies.AsNoTracking().SingleOrDefault(m => m.Id == id));
41+
protected override async Task<CosmosEntityMovie> GetEntityAsync(string id)
42+
=> await Context.Movies.AsNoTracking().SingleOrDefaultAsync(m => m.Id == id);
4043

41-
protected override Task<int> GetEntityCountAsync()
42-
=> Task.FromResult(Context.Movies.Count());
44+
protected override async Task<int> GetEntityCountAsync()
45+
=> await Context.Movies.CountAsync();
4346

4447
protected override Task<IRepository<CosmosEntityMovie>> GetPopulatedRepositoryAsync()
4548
=> Task.FromResult<IRepository<CosmosEntityMovie>>(new EntityTableRepository<CosmosEntityMovie>(Context));

tests/CommunityToolkit.Datasync.Server.Test/Live/Cosmos_Controller_Tests.cs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,32 @@ namespace CommunityToolkit.Datasync.Server.Test.Live;
1212

1313
[ExcludeFromCodeCoverage]
1414
[Collection("LiveTestsCollection")]
15-
public class Cosmos_Controller_Tests : LiveControllerTests<CosmosEntityMovie>
15+
public class Cosmos_Controller_Tests(DatabaseFixture fixture, ITestOutputHelper output) : LiveControllerTests<CosmosEntityMovie>(), IAsyncLifetime
1616
{
1717
#region Setup
18-
private readonly DatabaseFixture _fixture;
1918
private readonly Random random = new();
20-
private readonly string connectionString;
21-
private readonly List<CosmosEntityMovie> movies;
19+
private readonly string connectionString = Environment.GetEnvironmentVariable("DATASYNC_COSMOS_CONNECTIONSTRING");
20+
private List<CosmosEntityMovie> movies = [];
2221

23-
public Cosmos_Controller_Tests(DatabaseFixture fixture, ITestOutputHelper output) : base()
22+
public async Task InitializeAsync()
2423
{
25-
this._fixture = fixture;
26-
this.connectionString = Environment.GetEnvironmentVariable("DATASYNC_COSMOS_CONNECTIONSTRING");
2724
if (!string.IsNullOrEmpty(this.connectionString))
2825
{
2926
// Note: we don't clear entities on every run to speed up the test runs. This can only be done because
3027
// the tests are read-only (associated with the query and get capabilities). If the test being run writes
3128
// to the database then change clearEntities to true.
32-
output.WriteLine($"CosmosIsInitialized = {this._fixture.CosmosIsInitialized}");
33-
Context = CosmosDbContext.CreateContext(this.connectionString, output, clearEntities: !this._fixture.CosmosIsInitialized);
34-
this.movies = [.. Context.Movies.AsNoTracking()];
35-
this._fixture.CosmosIsInitialized = true;
29+
output.WriteLine($"CosmosIsInitialized = {fixture.CosmosIsInitialized}");
30+
Context = await CosmosDbContext.CreateContextAsync(this.connectionString, output, clearEntities: !fixture.CosmosIsInitialized);
31+
this.movies = await Context.Movies.AsNoTracking().ToListAsync();
32+
fixture.CosmosIsInitialized = true;
33+
}
34+
}
35+
36+
public async Task DisposeAsync()
37+
{
38+
if (Context is not null)
39+
{
40+
await Context.DisposeAsync();
3641
}
3742
}
3843

@@ -42,11 +47,11 @@ public Cosmos_Controller_Tests(DatabaseFixture fixture, ITestOutputHelper output
4247

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

45-
protected override Task<CosmosEntityMovie> GetEntityAsync(string id)
46-
=> Task.FromResult(Context.Movies.AsNoTracking().SingleOrDefault(m => m.Id == id));
50+
protected override async Task<CosmosEntityMovie> GetEntityAsync(string id)
51+
=> await Context.Movies.AsNoTracking().SingleOrDefaultAsync(m => m.Id == id);
4752

48-
protected override Task<int> GetEntityCountAsync()
49-
=> Task.FromResult(Context.Movies.Count());
53+
protected override async Task<int> GetEntityCountAsync()
54+
=> await Context.Movies.CountAsync();
5055

5156
protected override Task<IRepository<CosmosEntityMovie>> GetPopulatedRepositoryAsync()
5257
=> Task.FromResult<IRepository<CosmosEntityMovie>>(new EntityTableRepository<CosmosEntityMovie>(Context));

tests/CommunityToolkit.Datasync.TestCommon/Databases/Base/BaseDbContext.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,36 @@ protected void PopulateDatabase()
8080

8181
SaveChanges();
8282
}
83+
84+
protected async Task<int> PopulateDatabaseAsync()
85+
{
86+
int entityCount = await Movies.CountAsync();
87+
if (entityCount > 0)
88+
{
89+
return 0;
90+
}
91+
92+
List<TEntity> movies = [.. TestData.Movies.OfType<TEntity>()];
93+
MovieIds = movies.ConvertAll(m => m.Id);
94+
95+
// Make sure we are populating with the right data
96+
bool setUpdatedAt = Attribute.IsDefined(typeof(TEntity).GetProperty("UpdatedAt")!, typeof(UpdatedByRepositoryAttribute));
97+
bool setVersion = Attribute.IsDefined(typeof(TEntity).GetProperty("Version")!, typeof(UpdatedByRepositoryAttribute));
98+
foreach (TEntity movie in movies)
99+
{
100+
if (setUpdatedAt)
101+
{
102+
movie.UpdatedAt = DateTimeOffset.UtcNow;
103+
}
104+
105+
if (setVersion)
106+
{
107+
movie.Version = Guid.NewGuid().ToByteArray();
108+
}
109+
110+
Movies.Add(movie);
111+
}
112+
113+
return await SaveChangesAsync();
114+
}
83115
}

tests/CommunityToolkit.Datasync.TestCommon/Databases/CosmosDb/CosmosDbContext.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace CommunityToolkit.Datasync.TestCommon.Databases;
1111
[ExcludeFromCodeCoverage]
1212
public class CosmosDbContext(DbContextOptions<CosmosDbContext> options) : BaseDbContext<CosmosDbContext, CosmosEntityMovie>(options)
1313
{
14-
public static CosmosDbContext CreateContext(string connectionString, ITestOutputHelper output = null, bool clearEntities = true)
14+
public static async Task<CosmosDbContext> CreateContextAsync(string connectionString, ITestOutputHelper output = null, bool clearEntities = true)
1515
{
1616
if (string.IsNullOrEmpty(connectionString))
1717
{
@@ -23,20 +23,21 @@ public static CosmosDbContext CreateContext(string connectionString, ITestOutput
2323
.EnableLogging(output);
2424
CosmosDbContext context = new(optionsBuilder.Options);
2525

26-
context.InitializeDatabase(clearEntities);
27-
context.PopulateDatabase();
26+
await context.InitializeDatabaseAsync(clearEntities);
27+
await context.PopulateDatabaseAsync();
2828
return context;
2929
}
3030

31-
internal void InitializeDatabase(bool clearEntities)
31+
internal async Task InitializeDatabaseAsync(bool clearEntities)
3232
{
3333
if (clearEntities)
3434
{
35-
// NOTE: sync-over-async is used here. This is bad, but it is only used in the test suite.
36-
// The test suite is not performance sensitive, so this is acceptable.
37-
List<CosmosEntityMovie> movies = Movies.ToListAsync().Result;
38-
RemoveRange(movies);
39-
SaveChanges();
35+
List<CosmosEntityMovie> movies = await Movies.ToListAsync();
36+
if (movies.Count > 0)
37+
{
38+
RemoveRange(movies);
39+
await SaveChangesAsync();
40+
}
4041
}
4142
}
4243

tests/CommunityToolkit.Datasync.TestCommon/RepositoryTests.cs

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,20 +86,7 @@ public async Task AsQueryableAsync_CanRetrieveFilteredLists()
8686
IRepository<TEntity> Repository = await GetPopulatedRepositoryAsync();
8787
int expected = TestData.Movies.Count<TEntity>(m => m.Rating == MovieRating.R);
8888
IQueryable<TEntity> queryable = await Repository.AsQueryableAsync();
89-
List<TEntity> actual = queryable.Where(m => m.Rating == MovieRating.R).ToList();
90-
91-
actual.Should().HaveCount(expected);
92-
}
93-
94-
[SkippableFact]
95-
public async Task AsQueryableAsync_CanSelectFromList()
96-
{
97-
Skip.IfNot(CanRunLiveTests());
98-
99-
IRepository<TEntity> Repository = await GetPopulatedRepositoryAsync();
100-
int expected = TestData.Movies.Count<TEntity>(m => m.Rating == MovieRating.R);
101-
IQueryable<TEntity> queryable = await Repository.AsQueryableAsync();
102-
var actual = queryable.Where(m => m.Rating == MovieRating.R).Select(m => new { m.Id, m.Title }).ToList();
89+
IList<TEntity> actual = await Repository.ToListAsync(queryable.Where(m => m.Rating == MovieRating.R));
10390

10491
actual.Should().HaveCount(expected);
10592
}
@@ -111,7 +98,7 @@ public async Task AsQueryableAsync_CanUseTopAndSkip()
11198

11299
IRepository<TEntity> Repository = await GetPopulatedRepositoryAsync();
113100
IQueryable<TEntity> queryable = await Repository.AsQueryableAsync();
114-
List<TEntity> actual = queryable.Where(m => m.Rating == MovieRating.R).Skip(5).Take(20).ToList();
101+
IList<TEntity> actual = await Repository.ToListAsync(queryable.Where(m => m.Rating == MovieRating.R).Skip(5).Take(20));
115102

116103
actual.Should().HaveCount(20);
117104
}
@@ -126,7 +113,7 @@ public async Task AsQueryableAsync_CanRetrievePagedDatasyncQuery()
126113

127114
IRepository<TEntity> Repository = await GetPopulatedRepositoryAsync();
128115
IQueryable<TEntity> queryable = await Repository.AsQueryableAsync();
129-
List<TEntity> actual = queryable.Where(m => m.UpdatedAt > DateTimeOffset.UnixEpoch && !m.Deleted).OrderBy(m => m.UpdatedAt).Skip(10).Take(10).ToList();
116+
IList<TEntity> actual = await Repository.ToListAsync(queryable.Where(m => m.UpdatedAt > DateTimeOffset.UnixEpoch && !m.Deleted).OrderBy(m => m.UpdatedAt).Skip(10).Take(10));
130117

131118
actual.Should().HaveCount(10);
132119
}

0 commit comments

Comments
 (0)