Files
RolemasterDB/src/RolemasterDb.ImportTool.Tests/LookupServiceCurationIntegrationTests.cs

301 lines
12 KiB
C#

using Microsoft.EntityFrameworkCore;
using RolemasterDb.App.Data;
using RolemasterDb.App.Domain;
using RolemasterDb.App.Features;
namespace RolemasterDb.ImportTool.Tests;
public sealed class LookupServiceCurationIntegrationTests
{
[Fact]
public async Task Lookup_service_surfaces_and_persists_curated_state_and_source_image_metadata()
{
var databasePath = CreateEmptyDatabasePath();
var repositoryRoot = CreateTemporaryRepositoryRoot();
var locator = new CriticalImportArtifactLocator(new TestHostEnvironment(Path.Combine(repositoryRoot, "src", "RolemasterDb.App")));
await SeedCriticalResultAsync(databasePath, "slash/cells/source-cell.png", 2);
WriteSourceImage(repositoryRoot, "slash/cells/source-cell.png");
var lookupService = new LookupService(CreateDbContextFactory(databasePath), locator);
var resultId = await GetResultIdAsync(databasePath);
var initialResponse = await lookupService.GetCriticalCellEditorAsync("slash", resultId);
Assert.NotNull(initialResponse);
Assert.False(initialResponse!.IsCurated);
Assert.Equal(2, initialResponse.SourcePageNumber);
Assert.Equal($"/api/tables/critical/slash/cells/{resultId}/source-image", initialResponse.SourceImageUrl);
var updateRequest = new CriticalCellUpdateRequest(
initialResponse.RawCellText,
initialResponse.QuickParseInput,
initialResponse.DescriptionText,
initialResponse.RawAffixText,
initialResponse.ParseStatus,
initialResponse.ParsedJson,
true,
initialResponse.IsDescriptionOverridden,
initialResponse.IsRawAffixTextOverridden,
initialResponse.AreEffectsOverridden,
initialResponse.AreBranchesOverridden,
initialResponse.Effects,
initialResponse.Branches);
var updatedResponse = await lookupService.UpdateCriticalCellAsync("slash", resultId, updateRequest);
Assert.NotNull(updatedResponse);
Assert.True(updatedResponse!.IsCurated);
var tableDetail = await lookupService.GetCriticalTableAsync("slash");
Assert.NotNull(tableDetail);
Assert.Contains(tableDetail!.Cells, item => item.ResultId == resultId && item.IsCurated);
var reopenedResponse = await lookupService.GetCriticalCellEditorAsync("slash", resultId);
Assert.NotNull(reopenedResponse);
Assert.True(reopenedResponse!.IsCurated);
Assert.Equal(initialResponse.SourceImageUrl, reopenedResponse.SourceImageUrl);
}
[Fact]
public async Task Lookup_service_clears_curated_state_when_any_content_is_edited()
{
var databasePath = CreateEmptyDatabasePath();
var repositoryRoot = CreateTemporaryRepositoryRoot();
var locator = new CriticalImportArtifactLocator(new TestHostEnvironment(Path.Combine(repositoryRoot, "src", "RolemasterDb.App")));
await SeedCriticalResultAsync(databasePath, "slash/cells/source-cell.png", 2);
WriteSourceImage(repositoryRoot, "slash/cells/source-cell.png");
var lookupService = new LookupService(CreateDbContextFactory(databasePath), locator);
var resultId = await GetResultIdAsync(databasePath);
var initialResponse = await lookupService.GetCriticalCellEditorAsync("slash", resultId);
Assert.NotNull(initialResponse);
var markCuratedRequest = new CriticalCellUpdateRequest(
initialResponse!.RawCellText,
initialResponse.QuickParseInput,
initialResponse.DescriptionText,
initialResponse.RawAffixText,
initialResponse.ParseStatus,
initialResponse.ParsedJson,
true,
initialResponse.IsDescriptionOverridden,
initialResponse.IsRawAffixTextOverridden,
initialResponse.AreEffectsOverridden,
initialResponse.AreBranchesOverridden,
initialResponse.Effects,
initialResponse.Branches);
var curatedResponse = await lookupService.UpdateCriticalCellAsync("slash", resultId, markCuratedRequest);
Assert.NotNull(curatedResponse);
Assert.True(curatedResponse!.IsCurated);
var editedRequest = markCuratedRequest with
{
DescriptionText = "Edited description after curation."
};
var editedResponse = await lookupService.UpdateCriticalCellAsync("slash", resultId, editedRequest);
Assert.NotNull(editedResponse);
Assert.False(editedResponse!.IsCurated);
var reopenedResponse = await lookupService.GetCriticalCellEditorAsync("slash", resultId);
Assert.NotNull(reopenedResponse);
Assert.False(reopenedResponse!.IsCurated);
Assert.Equal("Edited description after curation.", reopenedResponse.DescriptionText);
}
[Fact]
public async Task Lookup_service_allows_marking_uncurated_reparsed_content_as_curated()
{
var databasePath = CreateEmptyDatabasePath();
var repositoryRoot = CreateTemporaryRepositoryRoot();
var locator = new CriticalImportArtifactLocator(new TestHostEnvironment(Path.Combine(repositoryRoot, "src", "RolemasterDb.App")));
await SeedCriticalResultAsync(databasePath, "slash/cells/source-cell.png", 2);
WriteSourceImage(repositoryRoot, "slash/cells/source-cell.png");
var lookupService = new LookupService(CreateDbContextFactory(databasePath), locator);
var resultId = await GetResultIdAsync(databasePath);
var initialResponse = await lookupService.GetCriticalCellEditorAsync("slash", resultId);
Assert.NotNull(initialResponse);
Assert.False(initialResponse!.IsCurated);
var reparsedResponse = await lookupService.ReparseCriticalCellAsync(
"slash",
resultId,
new CriticalCellUpdateRequest(
initialResponse.RawCellText,
"Edited quick parse input.",
initialResponse.DescriptionText,
initialResponse.RawAffixText,
initialResponse.ParseStatus,
initialResponse.ParsedJson,
initialResponse.IsCurated,
initialResponse.IsDescriptionOverridden,
initialResponse.IsRawAffixTextOverridden,
initialResponse.AreEffectsOverridden,
initialResponse.AreBranchesOverridden,
initialResponse.Effects,
initialResponse.Branches));
Assert.NotNull(reparsedResponse);
Assert.False(reparsedResponse!.IsCurated);
var curatedResponse = await lookupService.UpdateCriticalCellAsync(
"slash",
resultId,
new CriticalCellUpdateRequest(
reparsedResponse.RawCellText,
reparsedResponse.QuickParseInput,
reparsedResponse.DescriptionText,
reparsedResponse.RawAffixText,
reparsedResponse.ParseStatus,
reparsedResponse.ParsedJson,
true,
reparsedResponse.IsDescriptionOverridden,
reparsedResponse.IsRawAffixTextOverridden,
reparsedResponse.AreEffectsOverridden,
reparsedResponse.AreBranchesOverridden,
reparsedResponse.Effects,
reparsedResponse.Branches));
Assert.NotNull(curatedResponse);
Assert.True(curatedResponse!.IsCurated);
Assert.Equal("Edited quick parse input.", curatedResponse.QuickParseInput);
var reopenedResponse = await lookupService.GetCriticalCellEditorAsync("slash", resultId);
Assert.NotNull(reopenedResponse);
Assert.True(reopenedResponse!.IsCurated);
Assert.Equal("Edited quick parse input.", reopenedResponse.QuickParseInput);
}
[Fact]
public async Task Lookup_service_resolves_source_image_paths_only_when_artifacts_exist()
{
var databasePath = CreateEmptyDatabasePath();
var repositoryRoot = CreateTemporaryRepositoryRoot();
var locator = new CriticalImportArtifactLocator(new TestHostEnvironment(Path.Combine(repositoryRoot, "src", "RolemasterDb.App")));
await SeedCriticalResultAsync(databasePath, "slash/cells/missing.png", 1);
var lookupService = new LookupService(CreateDbContextFactory(databasePath), locator);
var resultId = await GetResultIdAsync(databasePath);
var missingPath = await lookupService.GetCriticalSourceImagePathAsync("slash", resultId);
Assert.Null(missingPath);
WriteSourceImage(repositoryRoot, "slash/cells/missing.png");
var resolvedPath = await lookupService.GetCriticalSourceImagePathAsync("slash", resultId);
Assert.NotNull(resolvedPath);
Assert.True(File.Exists(resolvedPath));
}
private static async Task SeedCriticalResultAsync(string databasePath, string sourceImagePath, int sourcePageNumber)
{
await using var dbContext = CreateDbContext(databasePath);
await dbContext.Database.EnsureCreatedAsync();
await RolemasterDbSchemaUpgrader.EnsureLatestAsync(dbContext);
var table = new CriticalTable
{
Slug = "slash",
DisplayName = "Slash Critical Strike Table",
Family = "standard",
SourceDocument = "Slash.pdf"
};
var column = new CriticalColumn
{
CriticalTable = table,
ColumnKey = "B",
Label = "B",
Role = "severity",
SortOrder = 2
};
var rollBand = new CriticalRollBand
{
CriticalTable = table,
Label = "36-40",
MinRoll = 36,
MaxRoll = 40,
SortOrder = 8
};
var result = new CriticalResult
{
CriticalTable = table,
CriticalColumn = column,
CriticalRollBand = rollBand,
IsCurated = false,
RawCellText = "Imported raw cell text",
DescriptionText = "Imported description",
ParseStatus = "partial",
ParsedJson = "{}",
SourcePageNumber = sourcePageNumber,
SourceImagePath = sourceImagePath,
SourceImageCropJson = "{\"pageNumber\":1}"
};
result.Effects.Add(new CriticalEffect
{
EffectCode = CriticalEffectCodes.DirectHits,
ValueInteger = 5,
IsPermanent = false,
SourceType = "symbol",
SourceText = "+5H"
});
dbContext.CriticalTables.Add(table);
dbContext.CriticalResults.Add(result);
await dbContext.SaveChangesAsync();
}
private static void WriteSourceImage(string repositoryRoot, string relativePath)
{
var fullPath = Path.Combine(repositoryRoot, "artifacts", "import", "critical", relativePath.Replace('/', Path.DirectorySeparatorChar));
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
File.WriteAllBytes(fullPath, [137, 80, 78, 71]);
}
private static async Task<int> GetResultIdAsync(string databasePath)
{
await using var dbContext = CreateDbContext(databasePath);
return await dbContext.CriticalResults
.Where(item => item.CriticalTable.Slug == "slash")
.Select(item => item.Id)
.SingleAsync();
}
private static RolemasterDbContext CreateDbContext(string databasePath)
{
var options = new DbContextOptionsBuilder<RolemasterDbContext>()
.UseSqlite($"Data Source={databasePath}")
.Options;
return new RolemasterDbContext(options);
}
private static IDbContextFactory<RolemasterDbContext> CreateDbContextFactory(string databasePath)
{
var options = new DbContextOptionsBuilder<RolemasterDbContext>()
.UseSqlite($"Data Source={databasePath}")
.Options;
return new TestRolemasterDbContextFactory(options);
}
private static string CreateEmptyDatabasePath() =>
Path.Combine(Path.GetTempPath(), $"lookup-service-curation-{Guid.NewGuid():N}.db");
private static string CreateTemporaryRepositoryRoot()
{
var repositoryRoot = Path.Combine(Path.GetTempPath(), $"rolemaster-repo-{Guid.NewGuid():N}");
Directory.CreateDirectory(Path.Combine(repositoryRoot, "src", "RolemasterDb.App"));
File.WriteAllText(Path.Combine(repositoryRoot, "RolemasterDB.slnx"), string.Empty);
return repositoryRoot;
}
}