using GitConverter.Lib.Factories; using GitConverter.Lib.Models; using GitConverter.TestsApp.TestSupport; namespace GitConverter.TestsApp.Converters { /// /// Integration-style tests for CSV conversion flows. /// /// /// Purpose /// - Exercise end-to-end CSV conversion using the real ConversionService and CsvConverter /// against small sample files/archives placed under TestData\Csv. /// /// Behavior & expectations /// - Tests are defensive: they skip when no TestData is present so CI remains non-fatal. /// - For each sample the tests create isolated output and temp folders under a unique temp root. /// - When ConversionService or CsvConverter returns Success the test asserts that at least one file /// was created inside the output folder. When the result is Failure the test asserts a non-empty /// diagnostic message is present. /// /// Test data /// - Place representative, small CSV samples (or archives containing CSV) under TestData\Csv in the test project /// and mark them with CopyToOutputDirectory=PreserveNewest so they are available at runtime via AppContext.BaseDirectory. /// /// Notes /// - Integration tests avoid asserting exact filenames; they verify that outputs exist. When asserting suffixes /// prefer FileExtensionHelpers.FromOption(...) + ToDotExtension() to compute expectations. /// [Collection("Integration")] public class CsvConverterIntegrationTests : IDisposable { private readonly string _root; public CsvConverterIntegrationTests() { _root = Path.Combine(Path.GetTempPath(), "GitConverter.CsvTests", Guid.NewGuid().ToString("N")); Directory.CreateDirectory(_root); } public void Dispose() { try { if (Directory.Exists(_root)) Directory.Delete(_root, true); } catch { } } private static string TestDataCsvFolder => IntegrationTestConstants.TestDataCsvFolder; /// /// Execute CSV conversion against the first available CSV sample found under the configured test data folder. /// /// /// Preconditions: /// - Small CSV samples or archives must be present under and copied to test output. /// /// Test steps: /// 1. Locate the first *.csv file under the test data folder. If none exists the test is skipped (no failure). /// 2. Use to resolve a converter for the input file. A failed detection is treated /// as a test failure (indicates misconfiguration or regression in detection logic). /// 3. For each supported target option from : /// a. Create isolated output and temp directories under the per-test temp root. /// b. Invoke the converter's Convert method with the input, target, output and temp paths. /// c. On success (ConversionResult.IsSuccess == true) assert that one or more files were written to the output directory. /// d. On failure assert that the returned ConversionResult.Message is non-empty and fail the test with that message. /// /// Cleanup: /// - Output and temp folders created for a target are deleted in a finally block to avoid leaving artifacts. /// /// Logging & diagnostics: /// - Converters emit diagnostics via the configured logging adapter; this test surface captures filesystem outputs /// and conversion results to determine pass/fail. /// /// Determinism: /// - Tests deliberately do not assert on exact output filenames to remain resilient to implementation changes. /// - Sample artifacts should be small to keep tests fast and reliable in CI. /// [Fact(DisplayName = "CsvConverter.Convert direct call against first CSV (if present)")] public void Convert_SingleCsv_ToAllTargetOptions() { if (!Directory.Exists(TestDataCsvFolder)) return; var csvFile = Directory.EnumerateFiles(TestDataCsvFolder, "*.csv", SearchOption.TopDirectoryOnly).FirstOrDefault(); if (csvFile == null) return; var factory = new ConverterFactory(); foreach (var targetFormatOption in IntegrationTestConstants.TargetOptions) { var outDir = Path.Combine(_root, $"{Path.GetFileNameWithoutExtension(csvFile)}_{targetFormatOption}_out"); var tempDir = Path.Combine(_root, $"{Path.GetFileNameWithoutExtension(csvFile)}_{targetFormatOption}_tmp"); Directory.CreateDirectory(outDir); Directory.CreateDirectory(tempDir); try { // Resolve a converter for the input; treat detection failure as a test failure. if (!factory.TryCreateForInput(csvFile, out var converter, out var detectedSourceFormat, out var detectReason) || converter == null) { throw new Xunit.Sdk.XunitException($"Failed to detect converter for '{csvFile}': {detectReason ?? ""}"); } // Execute conversion ConversionResult result = converter.Convert(csvFile, detectedSourceFormat, targetFormatOption, outDir, tempDir); Assert.NotNull(result); if (result.IsSuccess) { var files = Directory.EnumerateFiles(outDir, "*", SearchOption.AllDirectories).ToList(); Assert.True(files.Count > 0, $"No output files for target format option '{targetFormatOption}' when converting '{csvFile}'."); } else { // On failure ensure a diagnostic message is present and fail clearly. Assert.False(string.IsNullOrWhiteSpace(result.Message), $"Conversion to '{targetFormatOption}' failed with empty message ({csvFile})."); throw new Xunit.Sdk.XunitException($"Conversion to '{targetFormatOption}' failed: {result.Message ?? ""} ({csvFile})."); } } finally { try { if (Directory.Exists(outDir)) Directory.Delete(outDir, true); } catch { } try { if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } catch { } } } } } }