Skip to content

Commit

Permalink
Refactor and stabilize Send/Receive tests (#715)
Browse files Browse the repository at this point in the history
* Refactor Send/Receive tests

* move `InitLocalFlexProjectWithRepo` into SR Fixture because it only works when the fixture is used

* Refactor SendReceiveAfterProjectReset and other things

* Fix mistakes

---------

Co-authored-by: Kevin Hahn <hahn.kev@gmail.com>
  • Loading branch information
myieye and hahn-kev authored Apr 12, 2024
1 parent cffb9b9 commit c222245
Show file tree
Hide file tree
Showing 14 changed files with 393 additions and 295 deletions.
2 changes: 1 addition & 1 deletion backend/LexBoxApi/Controllers/ProjectController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public async Task<ActionResult> FinishResetProject(string code)
return Ok();
}

[HttpDelete("project/{id}")]
[HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesDefaultResponseType]
Expand Down
23 changes: 3 additions & 20 deletions backend/LexBoxApi/Services/HgService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ private void InitRepoAt(string code)
{
var repoDirectory = new DirectoryInfo(PrefixRepoFilePath(code));
repoDirectory.Create();
CopyFilesRecursively(
FileUtils.CopyFilesRecursively(
new DirectoryInfo("Services/HgEmptyRepo"),
repoDirectory
repoDirectory,
Permissions
);
}

Expand Down Expand Up @@ -177,24 +178,6 @@ await Task.Run(() =>
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute |
UnixFileMode.SetUser;

private static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
target.UnixFileMode = Permissions;
foreach (var dir in source.EnumerateDirectories())
{
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
}

foreach (var file in source.EnumerateFiles())
{
var destFileName = Path.Combine(target.FullName, file.Name);
var destFile = file.CopyTo(destFileName);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
destFile.UnixFileMode = Permissions;
}
}

private static void SetPermissionsRecursively(DirectoryInfo rootDir)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return;
Expand Down
19 changes: 19 additions & 0 deletions backend/LexCore/Utils/FileUtils.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Globalization;
using System.Runtime.InteropServices;

namespace LexCore.Utils;

Expand All @@ -10,4 +11,22 @@ public static string ToTimestamp(DateTimeOffset dateTime)
// make it file-system friendly
return timestamp.Replace(':', '-');
}

public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target, UnixFileMode? permissions = null)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
target.UnixFileMode = permissions ?? source.UnixFileMode;
foreach (var dir in source.EnumerateDirectories())
{
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name), permissions);
}

foreach (var file in source.EnumerateFiles())
{
var destFileName = Path.Combine(target.FullName, file.Name);
var destFile = file.CopyTo(destFileName);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
destFile.UnixFileMode = permissions ?? file.UnixFileMode;
}
}
}
7 changes: 4 additions & 3 deletions backend/Testing/ApiTests/ApiTestBase.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http.Json;
using System.Text.Json.Nodes;
using LexBoxApi.Auth;
using Shouldly;
using Testing.LexCore.Utils;
using Testing.Services;
Expand All @@ -17,7 +15,10 @@ public class ApiTestBase

public ApiTestBase()
{
HttpClient = new HttpClient(_httpClientHandler);
HttpClient = new HttpClient(_httpClientHandler)
{
BaseAddress = new Uri(BaseUrl)
};
}

public async Task<string> LoginAs(string user, string password)
Expand Down
4 changes: 2 additions & 2 deletions backend/Testing/ApiTests/NewProjectRaceCondition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public async Task CanCreateMultipleProjectsAndQueryThemRightAway()
}
finally
{
await HttpClient.DeleteAsync($"{BaseUrl}/api/project/project/{project1Id}");
await HttpClient.DeleteAsync($"{BaseUrl}/api/project/project/{project2Id}");
await HttpClient.DeleteAsync($"{BaseUrl}/api/project/{project1Id}");
await HttpClient.DeleteAsync($"{BaseUrl}/api/project/{project2Id}");
}
}

Expand Down
63 changes: 63 additions & 0 deletions backend/Testing/Fixtures/SendReceiveFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.IO.Compression;
using System.Runtime.CompilerServices;
using Chorus.VcsDrivers.Mercurial;
using LexCore.Utils;
using Shouldly;
using SIL.Progress;
using Testing.ApiTests;
using Testing.Services;
using static Testing.Services.Constants;

namespace Testing.Fixtures;

public class SendReceiveFixture : IAsyncLifetime
{
private readonly DirectoryInfo _templateRepo = new(Path.Join(BasePath, "_template-repo_"));
public ApiTestBase AdminApiTester { get; } = new();

public async Task InitializeAsync()
{
DeletePreviousTestFiles();
await DownloadTemplateRepo();
await AdminApiTester.LoginAs(AdminAuth.Username, AdminAuth.Password);
}

public Task DisposeAsync()
{
return Task.CompletedTask;
}

private static void DeletePreviousTestFiles()
{
if (Directory.Exists(BasePath)) Directory.Delete(BasePath, true);
}

private async Task DownloadTemplateRepo()
{
await using var stream = await AdminApiTester.HttpClient.GetStreamAsync("https://drive.google.com/uc?export=download&id=1w357T1Ti7bDwEof4HPBUZ5gB7WSKA5O2");
using var zip = new ZipArchive(stream);
zip.ExtractToDirectory(_templateRepo.FullName);
}

public ProjectConfig InitLocalFlexProjectWithRepo(HgProtocol? protocol = null, [CallerMemberName] string projectName = "")
{
var projectConfig = Utils.GetNewProjectConfig(protocol, projectName);
InitLocalFlexProjectWithRepo(projectConfig);
return projectConfig;
}

public void InitLocalFlexProjectWithRepo(ProjectPath projectPath)
{
var projectDir = Directory.CreateDirectory(projectPath.Dir);
FileUtils.CopyFilesRecursively(_templateRepo, projectDir);
File.Move(Path.Join(projectPath.Dir, "kevin-test-01.fwdata"), projectPath.FwDataFile);
Directory.EnumerateFiles(projectPath.Dir).ShouldContain(projectPath.FwDataFile);

// hack around the fact that our send and receive won't create a repo from scratch.
var progress = new NullProgress();
HgRunner.Run("hg init", projectPath.Dir, 1, progress);
HgRunner.Run("hg branch 7500002.7000072", projectPath.Dir, 1, progress);
HgRunner.Run($"hg add Lexicon.fwstub", projectPath.Dir, 1, progress);
HgRunner.Run("""hg commit -m "first commit" """, projectPath.Dir, 1, progress);
}
}
11 changes: 11 additions & 0 deletions backend/Testing/Services/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Testing.Services;

public static class Constants
{
public static readonly string BasePath = Path.Join(Path.GetTempPath(), "SR_Tests");
public static readonly SendReceiveAuth ManagerAuth = new("manager", TestingEnvironmentVariables.DefaultPassword);
public static readonly SendReceiveAuth AdminAuth = new("admin", TestingEnvironmentVariables.DefaultPassword);
public static readonly SendReceiveAuth InvalidPass = new("manager", "incorrect_pass");
public static readonly SendReceiveAuth InvalidUser = new("invalid_user", TestingEnvironmentVariables.DefaultPassword);
public static readonly SendReceiveAuth UnauthorizedUser = new("user", TestingEnvironmentVariables.DefaultPassword);
}
46 changes: 41 additions & 5 deletions backend/Testing/Services/SendReceiveService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using Chorus;
using Nini.Ini;
using Shouldly;
using SIL.Progress;
using Testing.Logging;
using Xunit.Abstractions;
Expand All @@ -10,9 +10,9 @@ namespace Testing.Services;

public record SendReceiveAuth(string Username, string Password);

public record SendReceiveParams(string ProjectCode, string BaseUrl, string DestDir)
public record SendReceiveParams(string ProjectCode, string BaseUrl, string Dir) : ProjectPath(ProjectCode, Dir)
{
public string FwDataFile { get; } = Path.Join(DestDir, $"{ProjectCode}.fwdata");
public SendReceiveParams(HgProtocol protocol, ProjectPath project) : this(project.Code, protocol.GetTestHostName(), project.Dir) { }
}

public class SendReceiveService
Expand Down Expand Up @@ -78,7 +78,32 @@ public async Task<string> GetHgVersion()
return output;
}

public string CloneProject(SendReceiveParams sendReceiveParams, SendReceiveAuth auth)
public string RunCloneSendReceive(SendReceiveParams sendReceiveParams, SendReceiveAuth auth)
{
var projectDir = sendReceiveParams.Dir;
var fwDataFile = sendReceiveParams.FwDataFile;

// Clone
var cloneResult = CloneProject(sendReceiveParams, auth);

Directory.Exists(projectDir).ShouldBeTrue($"Directory {projectDir} not found. Clone response: {cloneResult}");
Directory.EnumerateFiles(projectDir).ShouldContain(fwDataFile);
var fwDataFileInfo = new FileInfo(fwDataFile);
fwDataFileInfo.Length.ShouldBeGreaterThan(0);
var fwDataFileOriginalLength = fwDataFileInfo.Length;

// SendReceive
var srResult = SendReceiveProject(sendReceiveParams, auth);

srResult.ShouldContain("no changes from others");
fwDataFileInfo.Refresh();
fwDataFileInfo.Exists.ShouldBeTrue();
fwDataFileInfo.Length.ShouldBe(fwDataFileOriginalLength);

return $"Clone: {cloneResult}{Environment.NewLine}SendReceive: {srResult}";
}

public string CloneProject(SendReceiveParams sendReceiveParams, SendReceiveAuth auth, bool validateOutput = true)
{
var (projectCode, baseUrl, destDir) = sendReceiveParams;
var (username, password) = auth;
Expand Down Expand Up @@ -113,10 +138,16 @@ public string CloneProject(SendReceiveParams sendReceiveParams, SendReceiveAuth
string cloneResult;
LfMergeBridge.LfMergeBridge.Execute("Language_Forge_Clone", progress, flexBridgeOptions, out cloneResult);
cloneResult += $"{Environment.NewLine}Progress out: {progress.Text}";

if (validateOutput)
{
Utils.ValidateSendReceiveOutput(cloneResult);
}

return cloneResult;
}

public string SendReceiveProject(SendReceiveParams sendReceiveParams, SendReceiveAuth auth, string commitMessage = "Testing")
public string SendReceiveProject(SendReceiveParams sendReceiveParams, SendReceiveAuth auth, string commitMessage = "Testing", bool validateOutput = true)
{
var (projectCode, baseUrl, destDir) = sendReceiveParams;
var (username, password) = auth;
Expand Down Expand Up @@ -161,6 +192,11 @@ public string SendReceiveProject(SendReceiveParams sendReceiveParams, SendReceiv

cloneResult += "Progress out: " + progress.Text;

if (validateOutput)
{
Utils.ValidateSendReceiveOutput(cloneResult);
}

return cloneResult;
}
}
2 changes: 2 additions & 0 deletions backend/Testing/Services/TestingEnvironmentVariables.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public static class TestingEnvironmentVariables
public static string ProjectCode = Environment.GetEnvironmentVariable("TEST_PROJECT_CODE") ?? "sena-3";
public static string DefaultPassword = Environment.GetEnvironmentVariable("TEST_DEFAULT_PASSWORD") ?? "pass";

public static int HgRefreshInterval = 5000;

public static string GetTestHostName(this HgProtocol protocol)
{
return protocol switch
Expand Down
Loading

0 comments on commit c222245

Please sign in to comment.