Skip to content

Commit c860e96

Browse files
authored
(#204) Cosmos, LiteDb, and InMemory controller tests (#216)
* (#204) Added Cosmos controller test suite * (#204) Updated composite index settings to support the Cosmos live controller tests. * (#204) Added InMemory and LiteDb controller test suite.
1 parent 704529d commit c860e96

File tree

15 files changed

+303
-30
lines changed

15 files changed

+303
-30
lines changed

infra/modules/cosmos.bicep

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,88 @@ param tags object = {}
2020

2121
/*********************************************************************************/
2222

23+
var compositeIndices = [
24+
[
25+
{ path: '/BestPictureWinner', order: 'ascending' }
26+
{ path: '/id', order: 'ascending' }
27+
]
28+
[
29+
{ path: '/BestPictureWinner', order: 'descending' }
30+
{ path: '/id', order: 'ascending' }
31+
]
32+
[
33+
{ path: '/Duration', order: 'ascending' }
34+
{ path: '/id', order: 'ascending' }
35+
]
36+
[
37+
{ path: '/Duration', order: 'descending' }
38+
{ path: '/id', order: 'ascending' }
39+
]
40+
[
41+
{ path: '/Rating', order: 'ascending' }
42+
{ path: '/id', order: 'ascending' }
43+
]
44+
[
45+
{ path: '/Rating', order: 'descending' }
46+
{ path: '/id', order: 'ascending' }
47+
]
48+
[
49+
{ path: '/ReleaseDate', order: 'ascending' }
50+
{ path: '/id', order: 'ascending' }
51+
]
52+
[
53+
{ path: '/ReleaseDate', order: 'descending' }
54+
{ path: '/id', order: 'ascending' }
55+
]
56+
[
57+
{ path: '/Title', order: 'ascending' }
58+
{ path: '/id', order: 'ascending' }
59+
]
60+
[
61+
{ path: '/Title', order: 'descending' }
62+
{ path: '/id', order: 'ascending' }
63+
]
64+
[
65+
{ path: '/UpdatedAt', order: 'ascending' }
66+
{ path: '/id', order: 'ascending' }
67+
]
68+
[
69+
{ path: '/UpdatedAt', order: 'descending' }
70+
{ path: '/id', order: 'ascending' }
71+
]
72+
[
73+
{ path: '/Year', order: 'ascending' }
74+
{ path: '/id', order: 'ascending' }
75+
]
76+
[
77+
{ path: '/Year', order: 'descending' }
78+
{ path: '/id', order: 'ascending' }
79+
]
80+
[
81+
{ path: '/Year', order: 'ascending' }
82+
{ path: '/Title', order: 'ascending' }
83+
{ path: '/id', order: 'ascending' }
84+
]
85+
[
86+
{ path: '/Year', order: 'descending' }
87+
{ path: '/Title', order: 'ascending' }
88+
{ path: '/id', order: 'ascending' }
89+
]
90+
[
91+
{ path: '/Year', order: 'ascending' }
92+
{ path: '/Title', order: 'descending' }
93+
{ path: '/id', order: 'ascending' }
94+
]
95+
[
96+
{ path: '/Year', order: 'descending' }
97+
{ path: '/Title', order: 'descending' }
98+
{ path: '/id', order: 'ascending' }
99+
]
100+
]
101+
102+
/*********************************************************************************/
103+
104+
23105
resource cosmos_account 'Microsoft.DocumentDB/databaseAccounts@2024-02-15-preview' = {
24106
name: serverName
25107
location: location
@@ -70,12 +152,7 @@ resource cosmos_container 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/co
70152
excludedPaths: [
71153
{ path: '/_etag/?' }
72154
]
73-
compositeIndexes: [
74-
[
75-
{ path: '/UpdatedAt', order: 'ascending' }
76-
{ path: '/Id', order: 'ascending' }
77-
]
78-
]
155+
compositeIndexes: compositeIndices
79156
}
80157
defaultTtl: 86400
81158
}

tests/CommunityToolkit.Datasync.Server.LiteDb.Test/LiteDbRepository_Tests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using CommunityToolkit.Datasync.TestCommon;
6+
using CommunityToolkit.Datasync.TestCommon.Databases;
67
using CommunityToolkit.Datasync.TestCommon.Models;
78
using LiteDB;
89

tests/CommunityToolkit.Datasync.Server.Test/Helpers/LiveControllerTests.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@ public abstract class LiveControllerTests<TEntity> : BaseTest where TEntity : cl
1919
protected virtual bool CanRunLiveTests() => true;
2020

2121
/// <summary>
22-
/// Some tests require the ability to run math queries. This method returns true if the
23-
/// service can run those tests. Notably, Cosmos can't run those tests.
22+
/// The driver name - used for skipping tests when the driver doesn't support a feature.
2423
/// </summary>
25-
protected virtual bool CanRunMathQueryTests() => true;
24+
protected virtual string DriverName { get; } = "Default";
2625

2726
/// <summary>
2827
/// The actual test class must provide an implementation that retrieves the entity through

tests/CommunityToolkit.Datasync.Server.Test/Helpers/LiveTestsCollection.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ namespace CommunityToolkit.Datasync.Server.Test.Helpers;
99
/// </summary>
1010
public class DatabaseFixture
1111
{
12-
12+
public bool AzureSqlIsInitialized { get; set; } = false;
13+
public bool CosmosIsInitialized { get; set; } = false;
14+
public bool PgIsInitialized { get; set; } = false;
1315
}
1416

1517
[CollectionDefinition("LiveTestsCollection", DisableParallelization = true)]

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,23 @@ public class AzureSQL_Controller_Tests : LiveControllerTests<AzureSqlEntityMovie
1919
private readonly Random random = new();
2020
private readonly string connectionString;
2121
private readonly List<AzureSqlEntityMovie> movies;
22-
private readonly Lazy<AzureSqlDbContext> _context;
2322

2423
public AzureSQL_Controller_Tests(DatabaseFixture fixture, ITestOutputHelper output) : base()
2524
{
2625
this._fixture = fixture;
2726
this.connectionString = Environment.GetEnvironmentVariable("DATASYNC_AZSQL_CONNECTIONSTRING");
2827
if (!string.IsNullOrEmpty(this.connectionString))
2928
{
30-
this._context = new Lazy<AzureSqlDbContext>(() => AzureSqlDbContext.CreateContext(this.connectionString, output));
29+
output.WriteLine($"AzureSqlIsInitialized = {this._fixture.AzureSqlIsInitialized}");
30+
Context = AzureSqlDbContext.CreateContext(this.connectionString, output, clearEntities: !this._fixture.AzureSqlIsInitialized);
3131
this.movies = [.. Context.Movies.AsNoTracking()];
32+
this._fixture.AzureSqlIsInitialized = true;
3233
}
3334
}
3435

35-
private AzureSqlDbContext Context { get => this._context.Value; }
36+
private AzureSqlDbContext Context { get; set; }
37+
38+
protected override string DriverName { get; } = "AzureSQL";
3639

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server.EntityFrameworkCore;
6+
using CommunityToolkit.Datasync.Server.Test.Helpers;
7+
using CommunityToolkit.Datasync.TestCommon.Databases;
8+
using Microsoft.EntityFrameworkCore;
9+
using Xunit.Abstractions;
10+
11+
namespace CommunityToolkit.Datasync.Server.Test.Live;
12+
13+
[ExcludeFromCodeCoverage]
14+
[Collection("LiveTestsCollection")]
15+
public class Cosmos_Controller_Tests : LiveControllerTests<CosmosEntityMovie>
16+
{
17+
#region Setup
18+
private readonly DatabaseFixture _fixture;
19+
private readonly Random random = new();
20+
private readonly string connectionString;
21+
private readonly List<CosmosEntityMovie> movies;
22+
23+
public Cosmos_Controller_Tests(DatabaseFixture fixture, ITestOutputHelper output) : base()
24+
{
25+
this._fixture = fixture;
26+
this.connectionString = Environment.GetEnvironmentVariable("DATASYNC_COSMOS_CONNECTIONSTRING");
27+
if (!string.IsNullOrEmpty(this.connectionString))
28+
{
29+
// Note: we don't clear entities on every run to speed up the test runs. This can only be done because
30+
// the tests are read-only (associated with the query and get capabilities). If the test being run writes
31+
// 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;
36+
}
37+
}
38+
39+
private CosmosDbContext Context { get; set; }
40+
41+
protected override string DriverName { get; } = "Cosmos";
42+
43+
protected override bool CanRunLiveTests() => !string.IsNullOrEmpty(this.connectionString);
44+
45+
protected override Task<CosmosEntityMovie> GetEntityAsync(string id)
46+
=> Task.FromResult(Context.Movies.AsNoTracking().SingleOrDefault(m => m.Id == id));
47+
48+
protected override Task<int> GetEntityCountAsync()
49+
=> Task.FromResult(Context.Movies.Count());
50+
51+
protected override Task<IRepository<CosmosEntityMovie>> GetPopulatedRepositoryAsync()
52+
=> Task.FromResult<IRepository<CosmosEntityMovie>>(new EntityTableRepository<CosmosEntityMovie>(Context));
53+
54+
protected override Task<string> GetRandomEntityIdAsync(bool exists)
55+
=> Task.FromResult(exists ? this.movies[this.random.Next(this.movies.Count)].Id : Guid.NewGuid().ToString());
56+
#endregion
57+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server.InMemory;
6+
using CommunityToolkit.Datasync.Server.Test.Helpers;
7+
using CommunityToolkit.Datasync.TestCommon.Databases;
8+
9+
namespace CommunityToolkit.Datasync.Server.Test.Live;
10+
11+
[ExcludeFromCodeCoverage]
12+
[Collection("LiveTestsCollection")]
13+
public class InMemory_Controller_Tests : LiveControllerTests<InMemoryMovie>
14+
{
15+
#region Setup
16+
private readonly Random random = new();
17+
private InMemoryRepository<InMemoryMovie> repository;
18+
19+
protected override string DriverName { get; } = "InMemory";
20+
21+
protected override Task<InMemoryMovie> GetEntityAsync(string id)
22+
=> Task.FromResult(this.repository.GetEntity(id));
23+
24+
protected override Task<int> GetEntityCountAsync()
25+
=> Task.FromResult(this.repository.GetEntities().Count);
26+
27+
protected override Task<IRepository<InMemoryMovie>> GetPopulatedRepositoryAsync()
28+
{
29+
this.repository = new InMemoryRepository<InMemoryMovie>(TestCommon.TestData.Movies.OfType<InMemoryMovie>());
30+
return Task.FromResult<IRepository<InMemoryMovie>>(this.repository);
31+
}
32+
33+
protected override Task<string> GetRandomEntityIdAsync(bool exists)
34+
=> Task.FromResult(exists ? this.repository.GetEntities()[this.random.Next(this.repository.GetEntities().Count)].Id : Guid.NewGuid().ToString());
35+
#endregion
36+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server.Abstractions.Http;
6+
using CommunityToolkit.Datasync.Server.InMemory;
7+
using CommunityToolkit.Datasync.Server.LiteDb;
8+
using CommunityToolkit.Datasync.Server.Test.Helpers;
9+
using CommunityToolkit.Datasync.TestCommon.Databases;
10+
using LiteDB;
11+
12+
namespace CommunityToolkit.Datasync.Server.Test.Live;
13+
14+
[ExcludeFromCodeCoverage]
15+
[Collection("LiveTestsCollection")]
16+
public class LiteDb_Controller_Tests : LiveControllerTests<LiteDbMovie>, IDisposable
17+
{
18+
#region Setup
19+
private readonly Random random = new();
20+
private string dbFilename;
21+
private LiteDatabase database;
22+
private ILiteCollection<LiteDbMovie> collection;
23+
private LiteDbRepository<LiteDbMovie> repository;
24+
private readonly List<LiteDbMovie> movies = [];
25+
26+
protected override Task<LiteDbMovie> GetEntityAsync(string id)
27+
=> Task.FromResult(this.collection.FindById(id));
28+
29+
protected override Task<int> GetEntityCountAsync()
30+
=> Task.FromResult(this.collection.Count());
31+
32+
protected override Task<IRepository<LiteDbMovie>> GetPopulatedRepositoryAsync()
33+
{
34+
this.dbFilename = Path.GetTempFileName();
35+
this.database = new LiteDatabase($"Filename={this.dbFilename};Connection=direct;InitialSize=0");
36+
this.collection = this.database.GetCollection<LiteDbMovie>("litedbmovies");
37+
38+
foreach (LiteDbMovie movie in TestCommon.TestData.Movies.OfType<LiteDbMovie>())
39+
{
40+
movie.UpdatedAt = DateTimeOffset.Now;
41+
movie.Version = Guid.NewGuid().ToByteArray();
42+
this.collection.Insert(movie);
43+
this.movies.Add(movie);
44+
}
45+
46+
this.repository = new LiteDbRepository<LiteDbMovie>(this.database);
47+
return Task.FromResult<IRepository<LiteDbMovie>>(this.repository);
48+
}
49+
50+
protected override Task<string> GetRandomEntityIdAsync(bool exists)
51+
=> Task.FromResult(exists ? this.movies[this.random.Next(this.movies.Count)].Id : Guid.NewGuid().ToString());
52+
53+
public void Dispose()
54+
{
55+
Dispose(true);
56+
GC.SuppressFinalize(this);
57+
}
58+
59+
protected virtual void Dispose(bool disposing)
60+
{
61+
if (disposing)
62+
{
63+
this.database?.Dispose();
64+
if (!string.IsNullOrEmpty(this.dbFilename))
65+
{
66+
File.Delete(this.dbFilename);
67+
}
68+
}
69+
}
70+
#endregion
71+
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,23 @@ public class PgSQL_Controller_Tests : LiveControllerTests<PgEntityMovie>
1919
private readonly Random random = new();
2020
private readonly string connectionString;
2121
private readonly List<PgEntityMovie> movies;
22-
private readonly Lazy<PgDbContext> _context;
2322

2423
public PgSQL_Controller_Tests(DatabaseFixture fixture, ITestOutputHelper output) : base()
2524
{
2625
this._fixture = fixture;
2726
this.connectionString = Environment.GetEnvironmentVariable("DATASYNC_PGSQL_CONNECTIONSTRING");
2827
if (!string.IsNullOrEmpty(this.connectionString))
2928
{
30-
this._context = new Lazy<PgDbContext>(() => PgDbContext.CreateContext(this.connectionString, output));
29+
output.WriteLine($"PgIsInitialized = {this._fixture.PgIsInitialized}");
30+
Context = PgDbContext.CreateContext(this.connectionString, output, clearEntities: !this._fixture.PgIsInitialized);
3131
this.movies = Context.Movies.AsNoTracking().ToList();
32+
this._fixture.PgIsInitialized = true;
3233
}
3334
}
3435

35-
private PgDbContext Context { get => this._context.Value; }
36+
private PgDbContext Context { get; set; }
37+
38+
protected override string DriverName { get; } = "PgSQL";
3639

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

tests/CommunityToolkit.Datasync.TestCommon/CommunityToolkit.Datasync.TestCommon.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@
2525
<ProjectReference Include="..\..\src\CommunityToolkit.Datasync.Server.Abstractions\CommunityToolkit.Datasync.Server.Abstractions.csproj" />
2626
<ProjectReference Include="..\..\src\CommunityToolkit.Datasync.Server.EntityFrameworkCore\CommunityToolkit.Datasync.Server.EntityFrameworkCore.csproj" />
2727
<ProjectReference Include="..\..\src\CommunityToolkit.Datasync.Server.InMemory\CommunityToolkit.Datasync.Server.InMemory.csproj" />
28+
<ProjectReference Include="..\..\src\CommunityToolkit.Datasync.Server.LiteDb\CommunityToolkit.Datasync.Server.LiteDb.csproj" />
2829
</ItemGroup>
2930
</Project>

0 commit comments

Comments
 (0)