302 lines
No EOL
9 KiB
C#
302 lines
No EOL
9 KiB
C#
using System.Text;
|
|
using LANCommander.SDK;
|
|
using LANCommander.SDK.Enums;
|
|
using LANCommander.SDK.Helpers;
|
|
using LANCommander.SDK.Utilities;
|
|
using LANCommander.Server.Data.Models;
|
|
using LANCommander.Server.Services;
|
|
using SharpCompress.Archives;
|
|
using SharpCompress.Archives.Zip;
|
|
using SharpCompress.Common;
|
|
using SharpCompress.Readers;
|
|
using SharpCompress.Readers.Zip;
|
|
using Shouldly;
|
|
|
|
namespace LANCommander.Server.Tests.Services;
|
|
|
|
[Collection("Application")]
|
|
public class SaveClientTests(ApplicationFixture fixture) : BaseTest(fixture)
|
|
{
|
|
[Fact]
|
|
public async Task ArchiveCreationWorks()
|
|
{
|
|
try
|
|
{
|
|
File.WriteAllText("test.txt", "Hello World!");
|
|
|
|
using (var archive = ZipArchive.CreateArchive())
|
|
{
|
|
archive.AddEntry("test.txt", "test.txt");
|
|
|
|
archive.SaveTo("test.zip", CompressionType.None);
|
|
|
|
var fileInfo = new FileInfo("test.zip");
|
|
|
|
fileInfo.Length.ShouldBe(126);
|
|
}
|
|
|
|
using (Stream stream = File.OpenRead("test.zip"))
|
|
using (var reader = ReaderFactory.OpenReader(stream))
|
|
{
|
|
while (reader.MoveToNextEntry())
|
|
{
|
|
reader.Entry.Key.ShouldBe("test.txt");
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (File.Exists(@"test.txt"))
|
|
File.Delete(@"test.txt");
|
|
|
|
if (File.Exists(@"test.zip"))
|
|
File.Delete(@"test.zip");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ArchiveCreationToStreamWorks()
|
|
{
|
|
try
|
|
{
|
|
File.WriteAllText("test.txt", "Hello World!");
|
|
File.WriteAllText("test.txt", "Hello World!");
|
|
|
|
using (var ms = new MemoryStream())
|
|
using (var archive = ZipArchive.CreateArchive())
|
|
{
|
|
archive.AddEntry("test.txt", "test.txt");
|
|
|
|
archive.SaveTo(ms, CompressionType.None);
|
|
|
|
ms.Position = 0;
|
|
|
|
ms.Length.ShouldBe(126);
|
|
|
|
using (var reader = ReaderFactory.OpenReader(ms))
|
|
{
|
|
while (reader.MoveToNextEntry())
|
|
{
|
|
reader.Entry.Key.ShouldBe("test.txt");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
}
|
|
finally
|
|
{
|
|
if (File.Exists(@"test.txt"))
|
|
File.Delete(@"test.txt");
|
|
|
|
if (File.Exists(@"test.zip"))
|
|
File.Delete(@"test.zip");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Regression for the reported bug: saves were written to a working-directory-relative location while
|
|
/// downloads looked under the config directory. Upload and download now share <see cref="GameSaveService.GetSavePath"/>,
|
|
/// which must anchor a relative storage location beneath the config directory.
|
|
/// </summary>
|
|
[Fact]
|
|
public void GetSavePath_RelativeStorageLocation_ResolvesUnderConfigDirectory()
|
|
{
|
|
var saveService = GetService<GameSaveService>();
|
|
|
|
var save = new GameSave
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
UserId = Guid.NewGuid(),
|
|
GameId = Guid.NewGuid(),
|
|
StorageLocation = new StorageLocation
|
|
{
|
|
Path = "Saves",
|
|
Type = StorageLocationType.Save,
|
|
},
|
|
};
|
|
|
|
var path = saveService.GetSavePath(save);
|
|
|
|
path.ShouldBe(Path.Combine(
|
|
AppPaths.GetConfigDirectory(),
|
|
"Saves",
|
|
save.UserId.ToString(),
|
|
save.GameId.ToString(),
|
|
save.Id.ToString()));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetSavePath_RootedStorageLocation_UsedVerbatim()
|
|
{
|
|
var saveService = GetService<GameSaveService>();
|
|
|
|
var rooted = Path.GetTempPath().TrimEnd(Path.DirectorySeparatorChar);
|
|
|
|
var save = new GameSave
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
UserId = Guid.NewGuid(),
|
|
GameId = Guid.NewGuid(),
|
|
StorageLocation = new StorageLocation
|
|
{
|
|
Path = rooted,
|
|
Type = StorageLocationType.Save,
|
|
},
|
|
};
|
|
|
|
var path = saveService.GetSavePath(save);
|
|
|
|
path.ShouldBe(Path.Combine(
|
|
rooted,
|
|
save.UserId.ToString(),
|
|
save.GameId.ToString(),
|
|
save.Id.ToString()));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SaveUploadWorksAsync()
|
|
{
|
|
var gameService = GetService<GameService>();
|
|
var saveService = GetService<GameSaveService>();
|
|
|
|
var user = await EnsureAdminUserCreatedAsync();
|
|
|
|
await AuthenticateAsync(TestConstants.AdminUserName, TestConstants.AdminInitialPassword);
|
|
|
|
var installDirectory = GetTemporaryDirectory();
|
|
var tempPath = await EnsureStorageLocationsExistAsync();
|
|
|
|
try
|
|
{
|
|
var game = new Game
|
|
{
|
|
Title = "Test Game",
|
|
SavePaths =
|
|
[
|
|
new SavePath
|
|
{
|
|
Path = "save",
|
|
WorkingDirectory = "{InstallDir}",
|
|
Type = SavePathType.File,
|
|
}
|
|
]
|
|
};
|
|
|
|
game = await gameService.AddAsync(game);
|
|
|
|
// Mock game install directory
|
|
var sdkGame = await GameClient.GetAsync(game.Id);
|
|
|
|
var gameInstallDirectory = await GameClient.GetInstallDirectory(sdkGame, installDirectory);
|
|
var manifest = await GameClient.GetManifestAsync(game.Id);
|
|
|
|
Directory.CreateDirectory(Path.Combine(gameInstallDirectory, ".lancommander"));
|
|
Directory.CreateDirectory(Path.Combine(gameInstallDirectory, "save"));
|
|
await ManifestHelper.WriteAsync(manifest, gameInstallDirectory);
|
|
|
|
var randomFiles = new List<string>();
|
|
|
|
for (var i = 0; i < 10; i++)
|
|
randomFiles.Add(Path.GetRandomFileName());
|
|
|
|
foreach (var file in randomFiles)
|
|
await File.WriteAllTextAsync(Path.Combine(gameInstallDirectory, "save", file), file);
|
|
|
|
SDK.Models.GameSave uploadedSave;
|
|
|
|
#region Pack and Upload Save
|
|
|
|
long packedSize = 0;
|
|
|
|
using (var savePacker = new SavePacker(gameInstallDirectory))
|
|
{
|
|
if (manifest?.SavePaths.Any() ?? false)
|
|
savePacker.AddPaths(manifest.SavePaths);
|
|
|
|
await savePacker.AddManifestAsync(manifest);
|
|
|
|
var stream = await savePacker.PackAsync();
|
|
|
|
using (var reader = ReaderFactory.OpenReader(stream, new ReaderOptions()
|
|
{
|
|
LeaveStreamOpen = true,
|
|
}))
|
|
{
|
|
var savePath = manifest.SavePaths.First();
|
|
var entries = new List<string>();
|
|
|
|
while (reader.MoveToNextEntry())
|
|
{
|
|
entries.Add(reader.Entry.Key);
|
|
}
|
|
|
|
foreach (var file in randomFiles)
|
|
entries.ShouldContain($"Files/{savePath.Id}/save/{file}");
|
|
|
|
entries.ShouldContain(ManifestHelper.ManifestFilename);
|
|
entries.Count.ShouldBe(11);
|
|
}
|
|
|
|
stream.Position = 0;
|
|
|
|
packedSize = stream.Length;
|
|
|
|
uploadedSave = await SaveClient.UploadAsync(stream, manifest);
|
|
}
|
|
|
|
#endregion
|
|
|
|
var createdSavePath = game.SavePaths!.First();
|
|
|
|
var saves = await saveService.GetAsync();
|
|
|
|
saves.Count.ShouldBe(1);
|
|
|
|
var uploadedSavePath = await saveService.GetSavePathAsync(game.Id, user.Id);
|
|
|
|
var exists = File.Exists(uploadedSavePath);
|
|
|
|
exists.ShouldBeTrue();
|
|
|
|
// Check file sizes
|
|
var fileInfo = new FileInfo(uploadedSavePath);
|
|
|
|
fileInfo.Length.ShouldBe(uploadedSave.Size);
|
|
fileInfo.Length.ShouldBe(packedSize);
|
|
|
|
// Check contents of file
|
|
using (var fs = File.OpenRead(uploadedSavePath))
|
|
using (var reader = ReaderFactory.OpenReader(fs, new ReaderOptions()
|
|
{
|
|
LeaveStreamOpen = true,
|
|
}))
|
|
{
|
|
var savePath = manifest.SavePaths.First();
|
|
var entries = new List<string>();
|
|
|
|
while (reader.MoveToNextEntry())
|
|
{
|
|
entries.Add(reader.Entry.Key);
|
|
}
|
|
|
|
foreach (var file in randomFiles)
|
|
entries.ShouldContain($"Files/{savePath.Id}/save/{file}");
|
|
|
|
entries.ShouldContain(ManifestHelper.ManifestFilename);
|
|
entries.Count.ShouldBe(11);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
}
|
|
finally
|
|
{
|
|
Directory.Delete(installDirectory, true);
|
|
Directory.Delete(tempPath, true);
|
|
}
|
|
}
|
|
} |