Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,96 @@ private async Task<List<Virtue>> GetVirtueTestDataAsync(string csvFileName)
return records;
}

/// <summary>
/// Loads Virtue records from an absolute CSV path (used to feed the real taxonomy CSV,
/// which carries the fully-translated <c>*_ar/_fa/_zh</c> columns, into the mind-map path).
/// </summary>
private static async Task<List<Virtue>> LoadVirtuesFromPathAsync(string csvPath)
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HeaderValidated = null,
MissingFieldFound = null
};
using var reader = new StreamReader(csvPath);
using var csv = new CsvReader(reader, config);
csv.Context.RegisterClassMap<VirtueClassMap>();

var records = new List<Virtue>();
await foreach (var record in csv.GetRecordsAsync<Virtue>())
{
records.Add(record);
}
return records;
}

/// <summary>
/// Walks up from the test bin directory to locate the committed Virtues taxonomy CSV
/// (<c>Cards/Fallacies/Argumentum Virtues - Taxonomy.csv</c>, at the repo root in every checkout).
/// </summary>
private static string FindRepoVirtuesCsv()
{
var dir = new DirectoryInfo(System.AppContext.BaseDirectory);
for (int i = 0; i < 12 && dir != null; i++, dir = dir.Parent)
{
var candidate = Path.Combine(dir.FullName, "Cards", "Fallacies", "Argumentum Virtues - Taxonomy.csv");
if (File.Exists(candidate)) return candidate;
}
return null;
}

/// <summary>
/// #665 empirical guard — the Virtue mind map must render NATIVE script (not French fallback)
/// for ar/fa/zh once the entity + MindMapLocalization tables are wired. Generates a real .mm
/// from the actual taxonomy CSV (FreeMind GUI disabled via <c>FreeMindPath=""</c>), applying the
/// production localization exactly as the pipeline does, and asserts the target Unicode block is
/// present in the node text. This is the regression that would have caught the "Virtues mind maps
/// in French instead of the target languages" defect.
/// </summary>
[Theory]
[InlineData("ar", 0x0600, 0x06FF)] // Arabic block
[InlineData("fa", 0x0600, 0x06FF)] // Persian (Arabic script + Persian extensions, both in this block)
[InlineData("zh", 0x4E00, 0x9FFF)] // CJK Unified Ideographs
public async Task VirtueMindMap_GeneratesNativeScript_ForArFaZh(string lang, int lo, int hi)
{
// Arrange — real taxonomy CSV (carries the translated *_ar/_fa/_zh columns).
var csvPath = FindRepoVirtuesCsv();
csvPath.Should().NotBeNull("the committed Virtues taxonomy CSV must be locatable from the test bin dir");

var virtues = await LoadVirtuesFromPathAsync(csvPath);
virtues.Should().NotBeEmpty();

// Apply the production MindMapLocalization for the target language (rewrites the FR-suffixed
// source tokens in the expressions to the per-language Virtue properties), as the pipeline does.
var generator = new VirtueMindMapDocumentConfig { DocumentName = $"virtues-{lang}.mm" };
foreach (var localization in _config.LocalizationConfig.MindMapLocalization)
{
localization.DoReflectionTranslate(generator, lang);
}

var originalInteractive = Program.IsInteractive;
try
{
Program.IsInteractive = false; // no interactive SVG prompt
// Act — .mm is serialized before any SVG export; FreeMindPath="" ⇒ no GUI, XSLT throwaway.
await generator.GenerateMindMapFile(virtues, _config, _tempTestDirectory, lang);
}
finally
{
Program.IsInteractive = originalInteractive;
}

var mmPath = Path.Combine(_tempTestDirectory, generator.DocumentName);
File.Exists(mmPath).Should().BeTrue($"the {lang} Virtue mind map .mm must be generated");
var mm = await File.ReadAllTextAsync(mmPath);

// Assert — native script present in node text (would be ~0 if it fell back to French).
var nativeCount = mm.Count(ch => ch >= lo && ch <= hi);
nativeCount.Should().BeGreaterThan(50,
$"the generated '{lang}' Virtue mind map must contain native-script node text " +
$"(Unicode U+{lo:X4}–U+{hi:X4}), not French fallback — got {nativeCount} native code points");
}

[Theory]
[InlineData("en", "TextEn", "DescEn", "ExampleEn", "LinkEnFallback", "Subfamily", "Subsubfamily", "Family")]
[InlineData("ru", "TextRu", "DescRu", "Exampleru", "LinkRuFallback", "SubfamilyRu", "SubsubfamilyRu", "FamilyRu")]
Expand Down
Loading
Loading