Skip to content

Commit

Permalink
isolate sena3 sync tests rather than using the same project for each …
Browse files Browse the repository at this point in the history
…test
  • Loading branch information
hahn-kev committed Nov 28, 2024
1 parent de9f95f commit c3f71aa
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 78 deletions.
75 changes: 34 additions & 41 deletions backend/FwLite/FwLiteProjectSync.Tests/Fixtures/Sena3SyncFixture.cs
Original file line number Diff line number Diff line change
@@ -1,85 +1,78 @@
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 LexCore.Utils;
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 AsyncServiceScope _services;
private static readonly HttpClient http = new HttpClient();

public CrdtFwdataProjectSyncService SyncService =>
_services.ServiceProvider.GetRequiredService<CrdtFwdataProjectSyncService>();

public IServiceProvider Services => _services.ServiceProvider;
private IDisposable _cleanup;
private readonly HttpClient http;
public CrdtMiniLcmApi CrdtApi { get; set; } = null!;
public FwDataMiniLcmApi FwDataApi { get; set; } = null!;
private bool AlreadyLoggedIn { get; set; } = false;

public Sena3Fixture()
public async Task InitializeAsync()
{
var services = new ServiceCollection()
.AddSyncServices(nameof(Sena3Fixture), false);
var rootServiceProvider = services.BuildServiceProvider();
_cleanup = Defer.Action(() => rootServiceProvider.Dispose());
_services = rootServiceProvider.CreateAsyncScope();
var factory = Services.GetRequiredService<IHttpClientFactory>();
http = factory.CreateClient(nameof(Sena3Fixture));
var fwProjectsFolder = rootServiceProvider.GetRequiredService<IOptions<FwDataBridgeConfig>>()
.Value
.ProjectsFolder;
if (Path.Exists(fwProjectsFolder)) Directory.Delete(fwProjectsFolder, true);
Directory.CreateDirectory(fwProjectsFolder);

var crdtProjectsFolder =
rootServiceProvider.GetRequiredService<IOptions<LcmCrdtConfig>>().Value.ProjectPath;
if (Path.Exists(crdtProjectsFolder)) Directory.Delete(crdtProjectsFolder, true);
rootServiceProvider.Dispose();

Directory.CreateDirectory(crdtProjectsFolder);
await DownloadSena3();
}

public async Task InitializeAsync()
public async Task<(CrdtMiniLcmApi CrdtApi, FwDataMiniLcmApi FwDataApi, IServiceProvider services, IDisposable cleanup)> SetupProjects()
{
var sena3MasterCopy = await DownloadSena3();

var rootServiceProvider = new ServiceCollection()
.AddSyncServices(nameof(Sena3Fixture), false)
.BuildServiceProvider();
var cleanup = Defer.Action(() => rootServiceProvider.Dispose());
var services = rootServiceProvider.CreateAsyncScope().ServiceProvider;
var projectName = "sena-3_" + Guid.NewGuid().ToString("N");

var projectsFolder = Services.GetRequiredService<IOptions<FwDataBridgeConfig>>().Value
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);
var fwDataMiniLcmApi = services.GetRequiredService<FwDataFactory>().GetFwDataMiniLcmApi(fwDataProject, false);

FwDataApi = Services.GetRequiredService<FwDataFactory>().GetFwDataMiniLcmApi(fwDataProject, 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);
var crdtProject = await services.GetRequiredService<ProjectsService>()
.CreateProject(new(projectName, FwProjectId: fwDataMiniLcmApi.ProjectId, SeedNewProjectData: false));
var crdtMiniLcmApi = (CrdtMiniLcmApi)await services.OpenCrdtProject(crdtProject);
return (crdtMiniLcmApi, fwDataMiniLcmApi, services, cleanup);
}

public async Task DisposeAsync()
public Task DisposeAsync()
{
await _services.DisposeAsync();
_cleanup.Dispose();
return Task.CompletedTask;
}

public async Task<Stream> DownloadSena3ProjectBackupStream()
private async Task<Stream> DownloadSena3ProjectBackupStream()
{
var backupUrl = new Uri("https://drive.google.com/uc?export=download&id=1I-hwc0RHoQqW774gbS5qR-GHa1E7BlsS");
var result = await http.GetAsync(backupUrl, HttpCompletionOption.ResponseHeadersRead);
return await result.Content.ReadAsStreamAsync();
}
public async Task<string> DownloadSena3()

private async Task<string> DownloadSena3()
{
var tempFolder = Path.Combine(Path.GetTempPath(), nameof(Sena3Fixture));
var sena3MasterCopy = Path.Combine(tempFolder, "sena-3");
Expand Down
74 changes: 37 additions & 37 deletions backend/FwLite/FwLiteProjectSync.Tests/Sena3SyncTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using FluentAssertions.Equivalency;
using FluentAssertions.Execution;
using FwDataMiniLcmBridge.Api;
using FwLiteProjectSync.Tests.Fixtures;
using LcmCrdt;
using Microsoft.EntityFrameworkCore;
Expand All @@ -13,21 +14,28 @@ namespace FwLiteProjectSync.Tests;
public class Sena3SyncTests : IClassFixture<Sena3Fixture>, IAsyncLifetime
{
private readonly Sena3Fixture _fixture;
private readonly CrdtFwdataProjectSyncService _syncService;
private CrdtFwdataProjectSyncService _syncService = null!;
private CrdtMiniLcmApi _crdtApi = null!;
private FwDataMiniLcmApi _fwDataApi = null!;
private IDisposable? _cleanup;

public async Task InitializeAsync()

public Sena3SyncTests(Sena3Fixture fixture)
{
_fixture.FwDataApi.EntryCount.Should().BeGreaterThan(100, "project should be loaded and have entries");
_fixture = fixture;
}

public async Task DisposeAsync()
public async Task InitializeAsync()
{
(_crdtApi, _fwDataApi, var services, _cleanup) = await _fixture.SetupProjects();
_syncService = services.GetRequiredService<CrdtFwdataProjectSyncService>();
_fwDataApi.EntryCount.Should().BeGreaterThan(100, "project should be loaded and have entries");
}

public Sena3SyncTests(Sena3Fixture fixture)
public Task DisposeAsync()
{
_fixture = fixture;
_syncService = _fixture.SyncService;
_cleanup?.Dispose();
return Task.CompletedTask;
}

private void ShouldAllBeEquivalentTo(Dictionary<Guid, Entry> crdtEntries, Dictionary<Guid, Entry> fwdataEntries)
Expand All @@ -50,70 +58,64 @@ private void ShouldAllBeEquivalentTo(Dictionary<Guid, Entry> crdtEntries, Dictio
//by default the first sync is an import, this will skip that so that the sync will actually sync data
private async Task BypassImport()
{
await _syncService.SaveProjectSnapshot(_fixture.FwDataApi.Project, new ([], [], []));
await _syncService.SaveProjectSnapshot(_fwDataApi.Project, new ([], [], []));
}

//this lets us query entries when there is no writing system
private async Task WorkaroundMissingWritingSystems()
{
//must have at least one writing system to query for entries
await _fixture.CrdtApi.CreateWritingSystem(WritingSystemType.Vernacular, (await _fixture.FwDataApi.GetWritingSystems()).Vernacular.First());
await _crdtApi.CreateWritingSystem(WritingSystemType.Vernacular, (await _fwDataApi.GetWritingSystems()).Vernacular.First());

}

[Fact]
public async Task DryRunImport_MakesNoChanges()
{
var crdtApi = _fixture.CrdtApi;
await WorkaroundMissingWritingSystems();
crdtApi.GetEntries().ToBlockingEnumerable().Should().BeEmpty();
var fwdataApi = _fixture.FwDataApi;
await _syncService.SyncDryRun(crdtApi, fwdataApi);
_crdtApi.GetEntries().ToBlockingEnumerable().Should().BeEmpty();
await _syncService.SyncDryRun(_crdtApi, _fwDataApi);
//should still be empty
crdtApi.GetEntries().ToBlockingEnumerable().Should().BeEmpty();
_crdtApi.GetEntries().ToBlockingEnumerable().Should().BeEmpty();
}

[Fact]
public async Task DryRunImport_MakesTheSameChangesAsImport()
{
var dryRunSyncResult = await _syncService.SyncDryRun(_fixture.CrdtApi, _fixture.FwDataApi);
var syncResult = await _syncService.Sync(_fixture.CrdtApi, _fixture.FwDataApi);
var dryRunSyncResult = await _syncService.SyncDryRun(_crdtApi, _fwDataApi);
var syncResult = await _syncService.Sync(_crdtApi, _fwDataApi);
dryRunSyncResult.Should().BeEquivalentTo(syncResult);
}

[Fact]
public async Task DryRunSync_MakesNoChanges()
{
await BypassImport();
var crdtApi = _fixture.CrdtApi;
await WorkaroundMissingWritingSystems();
crdtApi.GetEntries().ToBlockingEnumerable().Should().BeEmpty();
var fwdataApi = _fixture.FwDataApi;
await _syncService.SyncDryRun(crdtApi, fwdataApi);
_crdtApi.GetEntries().ToBlockingEnumerable().Should().BeEmpty();
await _syncService.SyncDryRun(_crdtApi, _fwDataApi);
//should still be empty
crdtApi.GetEntries().ToBlockingEnumerable().Should().BeEmpty();
_crdtApi.GetEntries().ToBlockingEnumerable().Should().BeEmpty();
}

[Fact(Skip = "this test is waiting for syncing ComplexFormTypes and WritingSystems")]
public async Task DryRunSync_MakesTheSameChangesAsImport()
{
await BypassImport();
var dryRunSyncResult = await _syncService.SyncDryRun(_fixture.CrdtApi, _fixture.FwDataApi);
var syncResult = await _syncService.Sync(_fixture.CrdtApi, _fixture.FwDataApi);
var dryRunSyncResult = await _syncService.SyncDryRun(_crdtApi, _fwDataApi);
var syncResult = await _syncService.Sync(_crdtApi, _fwDataApi);
dryRunSyncResult.Should().BeEquivalentTo(syncResult);
}

[Fact]
public async Task FirstSena3SyncJustDoesAnSync()
{
var crdtApi = _fixture.CrdtApi;
var fwdataApi = _fixture.FwDataApi;
var results = await _syncService.Sync(crdtApi, fwdataApi);
var results = await _syncService.Sync(_crdtApi, _fwDataApi);
results.FwdataChanges.Should().Be(0);
results.CrdtChanges.Should().BeGreaterThanOrEqualTo(fwdataApi.EntryCount);
results.CrdtChanges.Should().BeGreaterThanOrEqualTo(_fwDataApi.EntryCount);

var crdtEntries = await crdtApi.GetEntries().ToDictionaryAsync(e => e.Id);
var fwdataEntries = await fwdataApi.GetEntries().ToDictionaryAsync(e => e.Id);
var crdtEntries = await _crdtApi.GetEntries().ToDictionaryAsync(e => e.Id);
var fwdataEntries = await _fwDataApi.GetEntries().ToDictionaryAsync(e => e.Id);
ShouldAllBeEquivalentTo(crdtEntries, fwdataEntries);
}

Expand All @@ -122,22 +124,20 @@ public async Task SyncWithoutImport_CrdtShouldMatchFwdata()
{
await BypassImport();

var results = await _syncService.Sync(_fixture.CrdtApi, _fixture.FwDataApi);
var results = await _syncService.Sync(_crdtApi, _fwDataApi);
results.FwdataChanges.Should().Be(0);
results.CrdtChanges.Should().BeGreaterThan(_fixture.FwDataApi.EntryCount);
results.CrdtChanges.Should().BeGreaterThan(_fwDataApi.EntryCount);

var crdtEntries = await _fixture.CrdtApi.GetEntries().ToDictionaryAsync(e => e.Id);
var fwdataEntries = await _fixture.FwDataApi.GetEntries().ToDictionaryAsync(e => e.Id);
var crdtEntries = await _crdtApi.GetEntries().ToDictionaryAsync(e => e.Id);
var fwdataEntries = await _fwDataApi.GetEntries().ToDictionaryAsync(e => e.Id);
ShouldAllBeEquivalentTo(crdtEntries, fwdataEntries);
}

[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);
await _syncService.Sync(_crdtApi, _fwDataApi);
var secondSync = await _syncService.Sync(_crdtApi, _fwDataApi);
secondSync.CrdtChanges.Should().Be(0);
secondSync.FwdataChanges.Should().Be(0);
}
Expand Down

0 comments on commit c3f71aa

Please sign in to comment.