-
-
Notifications
You must be signed in to change notification settings - Fork 163
Expand file tree
/
Copy pathDapperTestContext.cs
More file actions
165 lines (142 loc) · 6.07 KB
/
DapperTestContext.cs
File metadata and controls
165 lines (142 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
using System.Text.Json;
using DapperExample;
using DapperExample.Data;
using DapperExample.Models;
using DapperExample.Repositories;
using DapperExample.TranslationToSql.DataModel;
using JetBrains.Annotations;
using JsonApiDotNetCore.Configuration;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using TestBuildingBlocks;
using Xunit.Abstractions;
namespace DapperTests.IntegrationTests;
[PublicAPI]
public sealed class DapperTestContext : IntegrationTest
{
private const string SqlServerClearAllTablesScript = """
EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL';
EXEC sp_MSForEachTable 'SET QUOTED_IDENTIFIER ON; DELETE FROM ?';
EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL';
""";
public static readonly DateTimeOffset FrozenTime = DefaultDateTimeUtc;
private readonly Lazy<WebApplicationFactory<TodoItem>> _lazyFactory;
private ITestOutputHelper? _testOutputHelper;
protected override JsonSerializerOptions SerializerOptions
{
get
{
var options = Factory.Services.GetRequiredService<IJsonApiOptions>();
return options.SerializerOptions;
}
}
public WebApplicationFactory<TodoItem> Factory => _lazyFactory.Value;
public DapperTestContext()
{
_lazyFactory = new Lazy<WebApplicationFactory<TodoItem>>(CreateFactory);
}
private WebApplicationFactory<TodoItem> CreateFactory()
{
#pragma warning disable CA2000 // Dispose objects before losing scope
// Justification: The child factory returned by WithWebHostBuilder() is owned by the parent factory, which disposes it.
return new WebApplicationFactory<TodoItem>().WithWebHostBuilder(builder =>
#pragma warning restore CA2000 // Dispose objects before losing scope
{
builder.UseSetting("ConnectionStrings:DapperExamplePostgreSql",
$"Host=localhost;Database=DapperExample-{Guid.NewGuid():N};User ID=postgres;Password=postgres;Include Error Detail=true");
builder.UseSetting("ConnectionStrings:DapperExampleMySql",
$"Host=localhost;Database=DapperExample-{Guid.NewGuid():N};User ID=root;Password=mysql;SSL Mode=None;AllowPublicKeyRetrieval=True");
builder.UseSetting("ConnectionStrings:DapperExampleSqlServer",
$"Server=localhost;Database=DapperExample-{Guid.NewGuid():N};User ID=sa;Password=Passw0rd!;TrustServerCertificate=true");
builder.UseSetting("Logging:LogLevel:DapperExample", "Debug");
builder.ConfigureLogging(loggingBuilder =>
{
if (_testOutputHelper != null)
{
#if !DEBUG
// Reduce logging output when running tests in ci-build.
loggingBuilder.ClearProviders();
#endif
loggingBuilder.Services.AddSingleton<ILoggerProvider>(_ => new XUnitLoggerProvider(_testOutputHelper, "DapperExample."));
}
});
builder.ConfigureServices(services =>
{
services.Replace(ServiceDescriptor.Singleton<TimeProvider>(new FrozenTimeProvider(FrozenTime)));
services.Replace(ServiceDescriptor.Singleton<SqlCaptureStore, SqlCaptureStore>());
});
});
}
public void SetTestOutputHelper(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
public async Task ClearAllTablesAsync(DbContext dbContext)
{
var dataModelService = Factory.Services.GetRequiredService<IDataModelService>();
DatabaseProvider databaseProvider = dataModelService.DatabaseProvider;
if (databaseProvider == DatabaseProvider.SqlServer)
{
await dbContext.Database.ExecuteSqlRawAsync(SqlServerClearAllTablesScript);
}
else
{
foreach (IEntityType entityType in dbContext.Model.GetEntityTypes())
{
string? tableName = entityType.GetTableName();
string escapedTableName = databaseProvider switch
{
DatabaseProvider.PostgreSql => $"\"{tableName}\"",
DatabaseProvider.MySql => $"`{tableName}`",
_ => throw new NotSupportedException($"Unsupported database provider '{databaseProvider}'.")
};
#pragma warning disable EF1002 // Risk of vulnerability to SQL injection.
// Justification: Table names cannot be parameterized.
await dbContext.Database.ExecuteSqlRawAsync($"DELETE FROM {escapedTableName}");
#pragma warning restore EF1002 // Risk of vulnerability to SQL injection.
}
}
}
public async Task RunOnDatabaseAsync(Func<AppDbContext, Task> asyncAction)
{
await using AsyncServiceScope scope = Factory.Services.CreateAsyncScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await asyncAction(dbContext);
}
public string AdaptSql(string text, bool hasClientGeneratedId = false)
{
var dataModelService = Factory.Services.GetRequiredService<IDataModelService>();
var adapter = new SqlTextAdapter(dataModelService.DatabaseProvider);
return adapter.Adapt(text, hasClientGeneratedId);
}
protected override HttpClient CreateClient()
{
return Factory.CreateClient();
}
public override async Task DisposeAsync()
{
try
{
if (_lazyFactory.IsValueCreated)
{
try
{
await RunOnDatabaseAsync(async dbContext => await dbContext.Database.EnsureDeletedAsync());
}
finally
{
await _lazyFactory.Value.DisposeAsync();
}
}
}
finally
{
await base.DisposeAsync();
}
}
}