-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathDataModelTestBase.cs
More file actions
186 lines (162 loc) · 6.41 KB
/
DataModelTestBase.cs
File metadata and controls
186 lines (162 loc) · 6.41 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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
using Microsoft.Data.Sqlite;
using SIL.Harmony.Changes;
using SIL.Harmony.Sample;
using SIL.Harmony.Sample.Changes;
using SIL.Harmony.Sample.Models;
using SIL.Harmony.Tests.Mocks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using SIL.Harmony.Db;
namespace SIL.Harmony.Tests;
public class DataModelTestBase : IAsyncLifetime
{
protected readonly ServiceProvider _services;
protected readonly Guid _localClientId = Guid.NewGuid();
private readonly bool _performanceTest;
public readonly DataModel DataModel;
public readonly SampleDbContext DbContext;
internal readonly CrdtRepository CrdtRepository;
protected readonly MockTimeProvider MockTimeProvider = new();
public DataModelTestBase(bool saveToDisk = false, bool alwaysValidate = true,
Action<IServiceCollection>? configure = null, bool performanceTest = false) : this(saveToDisk
? new SqliteConnection("Data Source=test.db")
: new SqliteConnection("Data Source=:memory:"), alwaysValidate, configure, performanceTest)
{
}
public DataModelTestBase() : this(new SqliteConnection("Data Source=:memory:"))
{
}
public DataModelTestBase(SqliteConnection connection, bool alwaysValidate = true, Action<IServiceCollection>? configure = null, bool performanceTest = false)
{
_performanceTest = performanceTest;
var serviceCollection = new ServiceCollection().AddCrdtDataSample(builder =>
{
builder.UseSqlite(connection, true);
}, performanceTest)
.Configure<CrdtConfig>(config => config.AlwaysValidateCommits = alwaysValidate)
.Replace(ServiceDescriptor.Singleton<IHybridDateTimeProvider>(MockTimeProvider));
configure?.Invoke(serviceCollection);
_services = serviceCollection.BuildServiceProvider();
DbContext = _services.GetRequiredService<SampleDbContext>();
DbContext.Database.OpenConnection();
DbContext.Database.EnsureCreated();
DataModel = _services.GetRequiredService<DataModel>();
CrdtRepository = _services.GetRequiredService<CrdtRepository>();
}
public DataModelTestBase ForkDatabase(bool alwaysValidate = true)
{
var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
var existingConnection = DbContext.Database.GetDbConnection() as SqliteConnection;
if (existingConnection is null) throw new InvalidOperationException("Database is not SQLite");
existingConnection.BackupDatabase(connection);
var newTestBase = new DataModelTestBase(connection, alwaysValidate, performanceTest: _performanceTest);
newTestBase.SetCurrentDate(currentDate.DateTime);
return newTestBase;
}
public void SetCurrentDate(DateTime dateTime)
{
currentDate = dateTime;
}
private static int _instanceCount = 0;
private DateTimeOffset currentDate = new(new DateTime(2000, 1, 1, 0, 0, 0).AddHours(_instanceCount++));
public DateTimeOffset NextDate() => currentDate = currentDate.AddDays(1);
public async ValueTask<Commit> WriteNextChange(IChange change, bool add = true)
{
return await WriteChange(clientId: _localClientId, NextDate(), change, add);
}
public async ValueTask<Commit> WriteNextChange(IEnumerable<IChange> changes, bool add = true)
{
return await WriteChange(_localClientId, NextDate(), changes, add);
}
public async ValueTask<Commit> WriteChangeAfter(Commit after, IChange change)
{
return await WriteChange(_localClientId, after.DateTime.AddHours(1), change);
}
public async ValueTask<Commit> WriteChangeBefore(Commit before, IChange change, bool add = true)
{
return await WriteChange(_localClientId, before.DateTime.AddHours(-1), change, add);
}
protected async ValueTask<Commit> WriteChange(Guid clientId,
DateTimeOffset dateTime,
IChange change,
bool add = true)
{
return await WriteChange(clientId, dateTime, [change], add);
}
protected async ValueTask<Commit> WriteChange(Guid clientId,
DateTimeOffset dateTime,
IEnumerable<IChange> changes,
bool add = true)
{
if (!add)
return new Commit
{
ClientId = clientId,
HybridDateTime = new HybridDateTime(dateTime, 0),
ChangeEntities = changes.Select((change, index) => new ChangeEntity<IChange>
{
Change = change, Index = index, CommitId = change.CommitId, EntityId = change.EntityId
}).ToList()
};
MockTimeProvider.SetNextDateTime(dateTime);
return await DataModel.AddChanges(clientId, changes);
}
protected async Task AddCommitsViaSync(IEnumerable<Commit> commits)
{
await ((ISyncable)DataModel).AddRangeFromSync(commits);
}
public IChange SetWord(Guid entityId, string value)
{
return new SetWordTextChange(entityId, value);
}
public IChange DeleteWord(Guid entityId)
{
return new DeleteChange<Word>(entityId);
}
public IChange SetTag(Guid entityId, string value)
{
return new SetTagChange(entityId, value);
}
public IChange TagWord(Guid wordId, Guid tagId, Guid entityId = default)
{
return new TagWordChange(new WordTag { Id = entityId, WordId = wordId, TagId = tagId });
}
public IChange DeleteTag(Guid entityId)
{
return new DeleteChange<Tag>(entityId);
}
public IChange NewDefinition(Guid wordId,
string text,
string partOfSpeech,
double order = 0,
Guid? definitionId = default)
{
return new NewDefinitionChange(definitionId ?? Guid.NewGuid())
{
WordId = wordId,
Text = text,
PartOfSpeech = partOfSpeech,
Order = order
};
}
public virtual Task InitializeAsync()
{
return Task.CompletedTask;
}
public async Task DisposeAsync()
{
await _services.DisposeAsync();
}
protected IEnumerable<object> AllData()
{
return DbContext.Commits
.Include(c => c.ChangeEntities)
.Include(c => c.Snapshots)
.DefaultOrder()
.ToArray()
.OfType<object>()
.Concat(DbContext.Set<Word>().OrderBy(w => w.Text));
}
}