-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add integration test for syncing sena-3
- Loading branch information
Showing
6 changed files
with
277 additions
and
4 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
backend/FwLite/FwLiteProjectSync.Tests/Fixtures/LexboxConfig.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System.ComponentModel.DataAnnotations; | ||
|
||
namespace FwLiteProjectSync.Tests.Fixtures; | ||
|
||
public class LexboxConfig | ||
{ | ||
[Required, Url, RegularExpression(@"^.+/$", ErrorMessage = "Must end with '/'")] | ||
public required string LexboxUrl { get; set; } | ||
public string HgWebUrl => $"{LexboxUrl}hg/"; | ||
[Required] | ||
public required string LexboxUsername { get; set; } | ||
[Required] | ||
public required string LexboxPassword { get; set; } | ||
} |
31 changes: 31 additions & 0 deletions
31
backend/FwLite/FwLiteProjectSync.Tests/Fixtures/MercurialTestHelper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using SIL.CommandLineProcessing; | ||
using SIL.PlatformUtilities; | ||
using SIL.Progress; | ||
|
||
namespace FwLiteProjectSync.Tests.Fixtures; | ||
|
||
public static class MercurialTestHelper | ||
{ | ||
public static string HgCommand => | ||
Path.Combine("Mercurial", Platform.IsWindows ? "hg.exe" : "hg"); | ||
|
||
private static string RunHgCommand(string repoPath, string args) | ||
{ | ||
var result = CommandLineRunner.Run(HgCommand, args, repoPath, 120, new NullProgress()); | ||
if (result.ExitCode == 0) return result.StandardOutput; | ||
throw new Exception( | ||
$"hg {args} failed.\nStdOut: {result.StandardOutput}\nStdErr: {result.StandardError}"); | ||
|
||
} | ||
|
||
public static void HgClean(string repoPath, string exclude) | ||
{ | ||
RunHgCommand(repoPath, $"purge --no-confirm --exclude {exclude}"); | ||
} | ||
|
||
public static void HgUpdate(string repoPath, string rev) | ||
{ | ||
RunHgCommand(repoPath, $"update \"{rev}\""); | ||
} | ||
} | ||
|
114 changes: 114 additions & 0 deletions
114
backend/FwLite/FwLiteProjectSync.Tests/Fixtures/Sena3SyncFixture.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
using System.IO.Compression; | ||
using System.Net.Http.Json; | ||
using System.Runtime.CompilerServices; | ||
using FwDataMiniLcmBridge; | ||
using FwDataMiniLcmBridge.Api; | ||
using FwDataMiniLcmBridge.LcmUtils; | ||
using FwDataMiniLcmBridge.Tests.Fixtures; | ||
using LcmCrdt; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.DependencyInjection.Extensions; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
using MiniLcm; | ||
using SIL.IO; | ||
using SIL.Progress; | ||
|
||
namespace FwLiteProjectSync.Tests.Fixtures; | ||
|
||
public class Sena3Fixture : IAsyncLifetime | ||
{ | ||
private readonly SyncFixture syncFixture; | ||
public CrdtFwdataProjectSyncService SyncService => | ||
Services.GetRequiredService<CrdtFwdataProjectSyncService>(); | ||
public IServiceProvider Services => syncFixture.Services; | ||
private readonly LexboxConfig lexboxConfig; | ||
private readonly HttpClient http; | ||
public CrdtMiniLcmApi CrdtApi { get; set; } = null!; | ||
public FwDataMiniLcmApi FwDataApi { get; set; } = null!; | ||
private bool AlreadyLoggedIn { get; set; } = false; | ||
|
||
public Sena3Fixture() | ||
{ | ||
syncFixture = SyncFixture.Create(services => | ||
{ | ||
services.AddOptions<LexboxConfig>() | ||
.BindConfiguration("LexboxConfig") | ||
.ValidateDataAnnotations() | ||
.ValidateOnStart(); | ||
// TODO: How do I set default values if and only if they're not already set (e.g., via environment variables)? | ||
services.Configure<LexboxConfig>(c => | ||
{ | ||
c.LexboxUrl = "http://localhost/"; | ||
c.LexboxUsername = "admin"; | ||
c.LexboxPassword = "pass"; | ||
}); | ||
}, nameof(Sena3Fixture)); // TODO: Or create the project name in the constructor rather than in InitializeAsync, then pass it in here | ||
lexboxConfig = Services.GetRequiredService<IOptions<LexboxConfig>>().Value; | ||
var factory = Services.GetRequiredService<IHttpClientFactory>(); | ||
http = factory.CreateClient(nameof(Sena3Fixture)); | ||
} | ||
|
||
public async Task InitializeAsync() | ||
{ | ||
var sena3MasterCopy = await DownloadSena3(); | ||
var projectName = "sena-3_" + Guid.NewGuid().ToString("N"); | ||
|
||
var projectsFolder = Services.GetRequiredService<IOptions<FwDataBridgeConfig>>().Value | ||
.ProjectsFolder; | ||
if (Path.Exists(projectsFolder)) Directory.Delete(projectsFolder, true); | ||
Directory.CreateDirectory(projectsFolder); | ||
var fwDataProject = new FwDataProject(projectName, projectsFolder); | ||
var fwDataProjectPath = Path.Combine(fwDataProject.ProjectsPath, fwDataProject.Name); | ||
DirectoryHelper.Copy(sena3MasterCopy, fwDataProjectPath); | ||
File.Move(Path.Combine(fwDataProjectPath, "sena-3.fwdata"), fwDataProject.FilePath); | ||
|
||
Services.GetRequiredService<IProjectLoader>().LoadCache(fwDataProject); | ||
FwDataApi = Services.GetRequiredService<FwDataFactory>().GetFwDataMiniLcmApi(projectName, false); | ||
|
||
var crdtProjectsFolder = | ||
Services.GetRequiredService<IOptions<LcmCrdtConfig>>().Value.ProjectPath; | ||
if (Path.Exists(crdtProjectsFolder)) Directory.Delete(crdtProjectsFolder, true); | ||
Directory.CreateDirectory(crdtProjectsFolder); | ||
var crdtProject = await Services.GetRequiredService<ProjectsService>() | ||
.CreateProject(new(projectName, FwProjectId: FwDataApi.ProjectId, SeedNewProjectData: false)); | ||
CrdtApi = (CrdtMiniLcmApi) await Services.OpenCrdtProject(crdtProject); | ||
} | ||
|
||
public async Task DisposeAsync() | ||
{ | ||
await syncFixture.DisposeAsync(); | ||
} | ||
|
||
public async Task<Stream> DownloadProjectBackupStream(string code) | ||
{ | ||
var backupUrl = new Uri($"{lexboxConfig.LexboxUrl}api/project/backupProject/{code}"); | ||
var result = await http.GetAsync(backupUrl); | ||
return await result.Content.ReadAsStreamAsync(); | ||
} | ||
|
||
public async Task LoginAs(string lexboxUsername, string lexboxPassword) | ||
{ | ||
if (AlreadyLoggedIn) return; | ||
await http.PostAsync($"{lexboxConfig.LexboxUrl}api/login", JsonContent.Create(new { EmailOrUsername=lexboxUsername, Password=lexboxPassword })); | ||
AlreadyLoggedIn = true; | ||
} | ||
|
||
public async Task<string> DownloadSena3() | ||
{ | ||
|
||
var tempFolder = Path.Combine(Path.GetTempPath(), nameof(Sena3Fixture)); | ||
var sena3MasterCopy = Path.Combine(tempFolder, "sena-3"); | ||
if (!Directory.Exists(sena3MasterCopy) || !File.Exists(Path.Combine(sena3MasterCopy, "sena-3.fwdata"))) | ||
{ | ||
await LoginAs(lexboxConfig.LexboxUsername, lexboxConfig.LexboxPassword); | ||
Directory.CreateDirectory(sena3MasterCopy); | ||
var zipStream = await DownloadProjectBackupStream("sena-3"); | ||
ZipFile.ExtractToDirectory(zipStream, sena3MasterCopy); | ||
MercurialTestHelper.HgUpdate(sena3MasterCopy, "tip"); | ||
LfMergeBridge.LfMergeBridge.ReassembleFwdataFile(new NullProgress(), false, Path.Combine(sena3MasterCopy, "sena-3.fwdata")); | ||
MercurialTestHelper.HgClean(sena3MasterCopy, "sena-3.fwdata"); | ||
} | ||
return sena3MasterCopy; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 100 additions & 0 deletions
100
backend/FwLite/FwLiteProjectSync.Tests/Sena3SyncTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
using FluentAssertions.Equivalency; | ||
using FwLiteProjectSync.Tests.Fixtures; | ||
using LcmCrdt; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using MiniLcm; | ||
using MiniLcm.Models; | ||
using SystemTextJsonPatch; | ||
|
||
namespace FwLiteProjectSync.Tests; | ||
|
||
public class Sena3SyncTests : IClassFixture<Sena3Fixture>, IAsyncLifetime | ||
{ | ||
private readonly Sena3Fixture _fixture; | ||
private readonly CrdtFwdataProjectSyncService _syncService; | ||
|
||
private readonly Guid _complexEntryId = Guid.NewGuid(); | ||
private Entry _testEntry = new Entry | ||
{ | ||
Id = Guid.NewGuid(), | ||
LexemeForm = { Values = { { "en", "Apple" } } }, | ||
Note = { Values = { { "en", "this is a test note" } } }, | ||
Senses = | ||
[ | ||
new Sense | ||
{ | ||
Gloss = { Values = { { "en", "Apple" } } }, | ||
Definition = { Values = { { "en", "a round fruit with a hard, crisp skin" } } }, | ||
ExampleSentences = | ||
[ | ||
new ExampleSentence { Sentence = { Values = { { "en", "I went to the store to buy an apple." } } } } | ||
] | ||
} | ||
] | ||
}; | ||
|
||
public async Task InitializeAsync() | ||
{ | ||
await _fixture.FwDataApi.CreateEntry(_testEntry); | ||
await _fixture.FwDataApi.CreateEntry(new Entry() | ||
{ | ||
Id = _complexEntryId, | ||
LexemeForm = { { "en", "Pineapple" } }, | ||
Components = | ||
[ | ||
new ComplexFormComponent() | ||
{ | ||
Id = Guid.NewGuid(), | ||
ComplexFormEntryId = _complexEntryId, | ||
ComplexFormHeadword = "Pineapple", | ||
ComponentEntryId = _testEntry.Id, | ||
ComponentHeadword = "Apple" | ||
} | ||
] | ||
}); | ||
} | ||
|
||
public async Task DisposeAsync() | ||
{ | ||
await foreach (var entry in _fixture.FwDataApi.GetEntries()) | ||
{ | ||
await _fixture.FwDataApi.DeleteEntry(entry.Id); | ||
} | ||
foreach (var entry in await _fixture.CrdtApi.GetEntries().ToArrayAsync()) | ||
{ | ||
await _fixture.CrdtApi.DeleteEntry(entry.Id); | ||
} | ||
} | ||
|
||
public Sena3SyncTests(Sena3Fixture fixture) | ||
{ | ||
_fixture = fixture; | ||
_syncService = _fixture.SyncService; | ||
} | ||
|
||
[Fact] | ||
public async Task FirstSena3SyncJustDoesAnImport() | ||
{ | ||
var crdtApi = _fixture.CrdtApi; | ||
var fwdataApi = _fixture.FwDataApi; | ||
await _syncService.Sync(crdtApi, fwdataApi); | ||
|
||
var crdtEntries = await crdtApi.GetEntries().ToArrayAsync(); | ||
var fwdataEntries = await fwdataApi.GetEntries().ToArrayAsync(); | ||
crdtEntries.Should().BeEquivalentTo(fwdataEntries, | ||
options => options.For(e => e.Components).Exclude(c => c.Id) | ||
.For(e => e.ComplexForms).Exclude(c => c.Id)); | ||
} | ||
|
||
[Fact] | ||
public async Task SecondSena3SyncDoesNothing() | ||
{ | ||
var crdtApi = _fixture.CrdtApi; | ||
var fwdataApi = _fixture.FwDataApi; | ||
await _syncService.Sync(crdtApi, fwdataApi); | ||
var secondSync = await _syncService.Sync(crdtApi, fwdataApi); | ||
secondSync.CrdtChanges.Should().Be(0); | ||
secondSync.FwdataChanges.Should().Be(0); | ||
} | ||
} |