Migrate remaining legacy operator workflows
This commit is contained in:
@@ -0,0 +1,566 @@
|
||||
using System.Text.Json;
|
||||
using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;
|
||||
using MBN_STOCK_WEBVIEW.Infrastructure;
|
||||
using MMoneyCoderSharp.Data;
|
||||
|
||||
namespace MBN_STOCK_WEBVIEW.Infrastructure.Tests;
|
||||
|
||||
public sealed class LegacyManualOperatorDataImporterTests
|
||||
{
|
||||
private static readonly string[] DestinationFiles =
|
||||
[
|
||||
"개인.dat",
|
||||
"외국인.dat",
|
||||
"기관.dat",
|
||||
"VI발동.dat"
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public async Task Fixed_source_reads_the_four_original_files_without_modifying_them()
|
||||
{
|
||||
using var sourceDirectory = new TemporaryDirectory();
|
||||
WriteSourceFiles(sourceDirectory.Path);
|
||||
var before = DestinationFiles.ToDictionary(
|
||||
name => name,
|
||||
name => File.ReadAllBytes(Path.Combine(sourceDirectory.Path, name)));
|
||||
var source = new FixedLegacyManualOperatorDataSource(
|
||||
sourceDirectory.Path,
|
||||
TimeSpan.Zero);
|
||||
|
||||
var snapshot = await source.ReadAsync();
|
||||
|
||||
Assert.Equal(5, snapshot.IndividualRows.Count);
|
||||
Assert.Equal(5, snapshot.ForeignRows.Count);
|
||||
Assert.Equal(5, snapshot.InstitutionRows.Count);
|
||||
Assert.Equal(3, snapshot.ViItems.Count);
|
||||
Assert.Matches("^[a-f0-9]{64}$", snapshot.SourceSha256);
|
||||
Assert.All(DestinationFiles, name =>
|
||||
Assert.Equal(before[name], File.ReadAllBytes(Path.Combine(sourceDirectory.Path, name))));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Import_commits_all_four_files_and_the_versioned_marker_last()
|
||||
{
|
||||
using var sourceDirectory = new TemporaryDirectory();
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
WriteSourceFiles(sourceDirectory.Path);
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
var now = new DateTimeOffset(2026, 7, 12, 1, 2, 3, TimeSpan.Zero);
|
||||
using var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
new FixedLegacyManualOperatorDataSource(sourceDirectory.Path, TimeSpan.Zero),
|
||||
() => now);
|
||||
|
||||
var result = await importer.ImportAsync();
|
||||
|
||||
Assert.Equal(1, result.MarkerVersion);
|
||||
Assert.Equal(15, result.NetSellRowCount);
|
||||
Assert.Equal(3, result.ViItemCount);
|
||||
Assert.Equal(now, result.ImportedAt);
|
||||
Assert.All(DestinationFiles, name =>
|
||||
Assert.True(File.Exists(Path.Combine(destinationDirectory.Path, name))));
|
||||
var markerPath = Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.MarkerFileName);
|
||||
Assert.True(File.Exists(markerPath));
|
||||
using var marker = JsonDocument.Parse(await File.ReadAllBytesAsync(markerPath));
|
||||
Assert.Equal(1, marker.RootElement.GetProperty("schemaVersion").GetInt32());
|
||||
Assert.Equal(result.SourceSha256, marker.RootElement.GetProperty("sourceSha256").GetString());
|
||||
Assert.Matches(
|
||||
"^[a-f0-9]{64}$",
|
||||
marker.RootElement.GetProperty("productionSha256").GetString()!);
|
||||
Assert.Equal(15, marker.RootElement.GetProperty("netSellRowCount").GetInt32());
|
||||
Assert.Equal(3, marker.RootElement.GetProperty("viItemCount").GetInt32());
|
||||
Assert.False(File.Exists(Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.IntentFileName)));
|
||||
Assert.Equal(
|
||||
LegacyManualOperatorDataRuntimeState.ReadyImportedData,
|
||||
LegacyManualOperatorDataRuntimeGuard.Inspect(destinationDirectory.Path).State);
|
||||
|
||||
using var netStore = new S5025TrustedManualFileStore(destinationDirectory.Path);
|
||||
using var viStore = new ViTrustedManualFileStore(destinationDirectory.Path);
|
||||
Assert.Equal(Rows(), await netStore.ReadAsync(S5025ManualAudience.Individual));
|
||||
Assert.Equal(3, (await viStore.ReadAsync()).Count);
|
||||
|
||||
var serializedResult = JsonSerializer.Serialize(result);
|
||||
var serializedMarker = await File.ReadAllTextAsync(markerPath);
|
||||
Assert.DoesNotContain(sourceDirectory.Path, serializedResult, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.DoesNotContain(sourceDirectory.Path, serializedMarker, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.DoesNotContain("source\\repos", serializedMarker, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Empty_vi_file_keeps_its_distinct_zero_length_contract_after_import_and_restart()
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
var snapshot = new LegacyManualOperatorSourceSnapshot(
|
||||
Rows(),
|
||||
Rows(),
|
||||
Rows(),
|
||||
Array.Empty<LegacyManualViItem>(),
|
||||
new string('c', 64));
|
||||
using var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
new CountingSource(snapshot));
|
||||
|
||||
var result = await importer.ImportAsync();
|
||||
|
||||
Assert.Equal(0, result.ViItemCount);
|
||||
Assert.Equal(
|
||||
0,
|
||||
new FileInfo(Path.Combine(destinationDirectory.Path, "VI발동.dat")).Length);
|
||||
Assert.Equal(
|
||||
LegacyManualOperatorDataRuntimeState.ReadyImportedData,
|
||||
LegacyManualOperatorDataRuntimeGuard.Inspect(destinationDirectory.Path).State);
|
||||
using var viStore = new ViTrustedManualFileStore(destinationDirectory.Path);
|
||||
Assert.Empty(await viStore.ReadAsync());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("개인.dat")]
|
||||
[InlineData("외국인.dat")]
|
||||
[InlineData("기관.dat")]
|
||||
[InlineData("VI발동.dat")]
|
||||
public async Task Any_existing_private_data_file_blocks_the_whole_import_without_overwrite(
|
||||
string existingFile)
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
var path = Path.Combine(destinationDirectory.Path, existingFile);
|
||||
var original = new byte[] { 1, 2, 3, 4 };
|
||||
await File.WriteAllBytesAsync(path, original);
|
||||
var source = new CountingSource(Snapshot());
|
||||
using var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
source);
|
||||
|
||||
var error = await Assert.ThrowsAsync<LegacyManualOperatorImportException>(
|
||||
() => importer.ImportAsync());
|
||||
|
||||
Assert.Equal(LegacyManualOperatorImportFailure.DestinationNotEmpty, error.Failure);
|
||||
Assert.False(error.OutcomeUnknown);
|
||||
Assert.Equal(0, source.ReadCount);
|
||||
Assert.Equal(original, await File.ReadAllBytesAsync(path));
|
||||
Assert.Single(Directory.EnumerateFileSystemEntries(destinationDirectory.Path));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Marker_blocks_a_second_import_before_the_source_is_read_again()
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
var source = new CountingSource(Snapshot());
|
||||
using var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
source);
|
||||
|
||||
_ = await importer.ImportAsync();
|
||||
var error = await Assert.ThrowsAsync<LegacyManualOperatorImportException>(
|
||||
() => importer.ImportAsync());
|
||||
|
||||
Assert.Equal(LegacyManualOperatorImportFailure.AlreadyImported, error.Failure);
|
||||
Assert.False(error.OutcomeUnknown);
|
||||
Assert.Equal(1, source.ReadCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Preexisting_intent_and_partial_data_fail_closed_before_source_or_store_use()
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
var intentPath = Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.IntentFileName);
|
||||
await File.WriteAllTextAsync(intentPath, "{}");
|
||||
var partialPath = Path.Combine(destinationDirectory.Path, DestinationFiles[0]);
|
||||
var partialBytes = LegacyManualOperatorDataCodec.SerializeNetSell(Rows());
|
||||
await File.WriteAllBytesAsync(partialPath, partialBytes);
|
||||
|
||||
var inspection = LegacyManualOperatorDataRuntimeGuard.Inspect(
|
||||
destinationDirectory.Path);
|
||||
var source = new CountingSource(Snapshot());
|
||||
using var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
source);
|
||||
var error = await Assert.ThrowsAsync<LegacyManualOperatorImportException>(
|
||||
() => importer.ImportAsync());
|
||||
|
||||
Assert.Equal(LegacyManualOperatorDataRuntimeState.InterruptedImport, inspection.State);
|
||||
Assert.False(inspection.IsReady);
|
||||
Assert.Equal(LegacyManualOperatorImportFailure.InterruptedImport, error.Failure);
|
||||
Assert.True(error.OutcomeUnknown);
|
||||
Assert.Equal(0, source.ReadCount);
|
||||
Assert.Equal(partialBytes, await File.ReadAllBytesAsync(partialPath));
|
||||
Assert.Equal("{}", await File.ReadAllTextAsync(intentPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Existing_legacy_files_without_marker_or_intent_remain_runtime_eligible()
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
await File.WriteAllBytesAsync(
|
||||
Path.Combine(destinationDirectory.Path, DestinationFiles[0]),
|
||||
LegacyManualOperatorDataCodec.SerializeNetSell(Rows()));
|
||||
|
||||
var inspection = LegacyManualOperatorDataRuntimeGuard.Inspect(
|
||||
destinationDirectory.Path);
|
||||
|
||||
Assert.True(inspection.IsReady);
|
||||
Assert.Equal(
|
||||
LegacyManualOperatorDataRuntimeState.ReadyLegacyData,
|
||||
inspection.State);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
[InlineData(3)]
|
||||
[InlineData(4)]
|
||||
public async Task Failure_after_each_data_move_rolls_back_data_and_intent(
|
||||
int failAfterMove)
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
using var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
new CountingSource(Snapshot()),
|
||||
utcNow: null,
|
||||
afterDataFileMoved: movedCount =>
|
||||
{
|
||||
if (movedCount == failAfterMove)
|
||||
{
|
||||
throw new IOException("injected move failure");
|
||||
}
|
||||
});
|
||||
|
||||
var error = await Assert.ThrowsAsync<LegacyManualOperatorImportException>(
|
||||
() => importer.ImportAsync());
|
||||
|
||||
Assert.Equal(LegacyManualOperatorImportFailure.ImportFailed, error.Failure);
|
||||
Assert.False(error.OutcomeUnknown);
|
||||
Assert.All(DestinationFiles, name =>
|
||||
Assert.False(File.Exists(Path.Combine(destinationDirectory.Path, name))));
|
||||
Assert.False(File.Exists(Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.MarkerFileName)));
|
||||
Assert.False(File.Exists(Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.IntentFileName)));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
[InlineData(3)]
|
||||
[InlineData(4)]
|
||||
public async Task Abrupt_termination_after_each_move_leaves_intent_and_blocks_all_reuse(
|
||||
int crashAfterMove)
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
using (var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
new CountingSource(Snapshot()),
|
||||
utcNow: null,
|
||||
afterDataFileMoved: movedCount =>
|
||||
{
|
||||
if (movedCount == crashAfterMove)
|
||||
{
|
||||
throw new LegacyManualOperatorImportCrashSimulationException();
|
||||
}
|
||||
}))
|
||||
{
|
||||
await Assert.ThrowsAsync<LegacyManualOperatorImportCrashSimulationException>(
|
||||
() => importer.ImportAsync());
|
||||
}
|
||||
|
||||
Assert.True(File.Exists(Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.IntentFileName)));
|
||||
Assert.Equal(
|
||||
crashAfterMove,
|
||||
DestinationFiles.Count(name => File.Exists(Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
name))));
|
||||
Assert.Equal(
|
||||
LegacyManualOperatorDataRuntimeState.InterruptedImport,
|
||||
LegacyManualOperatorDataRuntimeGuard.Inspect(destinationDirectory.Path).State);
|
||||
|
||||
var secondSource = new CountingSource(Snapshot());
|
||||
using var secondImporter = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
secondSource);
|
||||
var error = await Assert.ThrowsAsync<LegacyManualOperatorImportException>(
|
||||
() => secondImporter.ImportAsync());
|
||||
Assert.Equal(LegacyManualOperatorImportFailure.InterruptedImport, error.Failure);
|
||||
Assert.True(error.OutcomeUnknown);
|
||||
Assert.Equal(0, secondSource.ReadCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Abrupt_termination_after_completed_marker_keeps_stale_intent_fail_closed()
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
using (var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
new CountingSource(Snapshot()),
|
||||
utcNow: null,
|
||||
afterDataFileMoved: null,
|
||||
afterMarkerCommitted: () =>
|
||||
throw new LegacyManualOperatorImportCrashSimulationException()))
|
||||
{
|
||||
await Assert.ThrowsAsync<LegacyManualOperatorImportCrashSimulationException>(
|
||||
() => importer.ImportAsync());
|
||||
}
|
||||
|
||||
Assert.All(DestinationFiles, name =>
|
||||
Assert.True(File.Exists(Path.Combine(destinationDirectory.Path, name))));
|
||||
Assert.True(File.Exists(Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.MarkerFileName)));
|
||||
Assert.True(File.Exists(Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.IntentFileName)));
|
||||
var inspection = LegacyManualOperatorDataRuntimeGuard.Inspect(
|
||||
destinationDirectory.Path);
|
||||
Assert.False(inspection.IsReady);
|
||||
Assert.Equal(LegacyManualOperatorDataRuntimeState.InterruptedImport, inspection.State);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Completed_marker_with_missing_or_modified_data_is_rejected()
|
||||
{
|
||||
using var missingDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(missingDirectory.Path);
|
||||
using (var importer = new LegacyManualOperatorDataImporter(
|
||||
missingDirectory.Path,
|
||||
new CountingSource(Snapshot())))
|
||||
{
|
||||
_ = await importer.ImportAsync();
|
||||
}
|
||||
File.Delete(Path.Combine(missingDirectory.Path, DestinationFiles[0]));
|
||||
Assert.Equal(
|
||||
LegacyManualOperatorDataRuntimeState.InvalidImportedState,
|
||||
LegacyManualOperatorDataRuntimeGuard.Inspect(missingDirectory.Path).State);
|
||||
|
||||
using var modifiedDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(modifiedDirectory.Path);
|
||||
using (var importer = new LegacyManualOperatorDataImporter(
|
||||
modifiedDirectory.Path,
|
||||
new CountingSource(Snapshot())))
|
||||
{
|
||||
_ = await importer.ImportAsync();
|
||||
}
|
||||
await File.AppendAllTextAsync(
|
||||
Path.Combine(modifiedDirectory.Path, DestinationFiles[0]),
|
||||
"tamper");
|
||||
Assert.Equal(
|
||||
LegacyManualOperatorDataRuntimeState.InvalidImportedState,
|
||||
LegacyManualOperatorDataRuntimeGuard.Inspect(modifiedDirectory.Path).State);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Completed_marker_allows_later_verified_operator_edits()
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
using (var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
new CountingSource(Snapshot())))
|
||||
{
|
||||
_ = await importer.ImportAsync();
|
||||
}
|
||||
|
||||
var editedRows = Rows().ToArray();
|
||||
editedRows[0] = editedRows[0] with { LeftAmount = "9,999" };
|
||||
using (var store = new S5025TrustedManualFileStore(destinationDirectory.Path))
|
||||
{
|
||||
await store.WriteAsync(S5025ManualAudience.Individual, editedRows);
|
||||
}
|
||||
|
||||
Assert.Equal(
|
||||
LegacyManualOperatorDataRuntimeState.ReadyImportedData,
|
||||
LegacyManualOperatorDataRuntimeGuard.Inspect(destinationDirectory.Path).State);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Commit_failure_rolls_back_every_file_created_by_the_operation_without_retry()
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
var source = new CountingSource(Snapshot());
|
||||
using var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
source,
|
||||
utcNow: null,
|
||||
afterDataFileMoved: movedCount =>
|
||||
{
|
||||
if (movedCount == 1)
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(destinationDirectory.Path, "외국인.dat"));
|
||||
}
|
||||
});
|
||||
|
||||
var error = await Assert.ThrowsAsync<LegacyManualOperatorImportException>(
|
||||
() => importer.ImportAsync());
|
||||
|
||||
Assert.Equal(LegacyManualOperatorImportFailure.ImportFailed, error.Failure);
|
||||
Assert.False(error.OutcomeUnknown);
|
||||
Assert.Equal(1, source.ReadCount);
|
||||
Assert.False(File.Exists(Path.Combine(destinationDirectory.Path, "개인.dat")));
|
||||
Assert.False(File.Exists(Path.Combine(destinationDirectory.Path, "기관.dat")));
|
||||
Assert.False(File.Exists(Path.Combine(destinationDirectory.Path, "VI발동.dat")));
|
||||
Assert.False(File.Exists(Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.MarkerFileName)));
|
||||
Assert.Empty(Directory.EnumerateFiles(destinationDirectory.Path, "*.tmp"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Unsafe_rollback_is_reported_as_outcome_unknown_and_is_never_retried()
|
||||
{
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
var source = new CountingSource(Snapshot());
|
||||
using var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
source,
|
||||
utcNow: null,
|
||||
afterDataFileMoved: movedCount =>
|
||||
{
|
||||
if (movedCount != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
File.WriteAllText(Path.Combine(destinationDirectory.Path, "개인.dat"), "tampered");
|
||||
Directory.CreateDirectory(Path.Combine(destinationDirectory.Path, "외국인.dat"));
|
||||
});
|
||||
|
||||
var error = await Assert.ThrowsAsync<LegacyManualOperatorImportException>(
|
||||
() => importer.ImportAsync());
|
||||
|
||||
Assert.Equal(LegacyManualOperatorImportFailure.RollbackFailed, error.Failure);
|
||||
Assert.True(error.OutcomeUnknown);
|
||||
Assert.Equal(1, source.ReadCount);
|
||||
Assert.True(File.Exists(Path.Combine(destinationDirectory.Path, "개인.dat")));
|
||||
Assert.False(File.Exists(Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.MarkerFileName)));
|
||||
Assert.True(File.Exists(Path.Combine(
|
||||
destinationDirectory.Path,
|
||||
LegacyManualOperatorDataImporter.IntentFileName)));
|
||||
Assert.Equal(
|
||||
LegacyManualOperatorDataRuntimeState.InterruptedImport,
|
||||
LegacyManualOperatorDataRuntimeGuard.Inspect(destinationDirectory.Path).State);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Invalid_source_format_creates_no_private_file_and_does_not_disclose_the_path()
|
||||
{
|
||||
using var sourceDirectory = new TemporaryDirectory();
|
||||
using var destinationDirectory = new TemporaryDirectory();
|
||||
WriteSourceFiles(sourceDirectory.Path);
|
||||
await File.WriteAllTextAsync(Path.Combine(sourceDirectory.Path, "기관.dat"), "invalid");
|
||||
PreparePrivate(destinationDirectory.Path);
|
||||
using var importer = new LegacyManualOperatorDataImporter(
|
||||
destinationDirectory.Path,
|
||||
new FixedLegacyManualOperatorDataSource(sourceDirectory.Path, TimeSpan.Zero));
|
||||
|
||||
var error = await Assert.ThrowsAsync<LegacyManualOperatorImportException>(
|
||||
() => importer.ImportAsync());
|
||||
|
||||
Assert.Equal(LegacyManualOperatorImportFailure.SourceInvalid, error.Failure);
|
||||
Assert.DoesNotContain(sourceDirectory.Path, error.Message, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Empty(Directory.EnumerateFileSystemEntries(destinationDirectory.Path));
|
||||
}
|
||||
|
||||
private static void WriteSourceFiles(string directory)
|
||||
{
|
||||
foreach (var fileName in DestinationFiles.Take(3))
|
||||
{
|
||||
File.WriteAllBytes(
|
||||
Path.Combine(directory, fileName),
|
||||
LegacyManualOperatorDataCodec.SerializeNetSell(Rows()));
|
||||
}
|
||||
|
||||
File.WriteAllBytes(
|
||||
Path.Combine(directory, "VI발동.dat"),
|
||||
LegacyManualOperatorDataCodec.SerializeVi(ViItems()));
|
||||
}
|
||||
|
||||
private static LegacyManualOperatorSourceSnapshot Snapshot() => new(
|
||||
Rows(),
|
||||
Rows(),
|
||||
Rows(),
|
||||
ViItems(),
|
||||
new string('a', 64));
|
||||
|
||||
private static IReadOnlyList<S5025TrustedManualRow> Rows() =>
|
||||
[
|
||||
new("삼성전자", "1,000", "SK하이닉스", "-2,000"),
|
||||
new("현대차", "2", "기아", "-2"),
|
||||
new("NAVER", "3", "카카오", "-3"),
|
||||
new("LG화학", "4", "포스코", "-4"),
|
||||
new("한화", "5", "셀트리온", "-5")
|
||||
];
|
||||
|
||||
private static IReadOnlyList<LegacyManualViItem> ViItems() =>
|
||||
[
|
||||
new("PKR7005930003", "삼성전자"),
|
||||
new("PKR7016360000", "삼성증권"),
|
||||
new("PKR7005930003", "삼성전자")
|
||||
];
|
||||
|
||||
private static void PreparePrivate(string path) =>
|
||||
TrustedManualDirectory.PreparePrivateWritableDirectory(path);
|
||||
|
||||
private sealed class CountingSource : ILegacyManualOperatorDataSource
|
||||
{
|
||||
private readonly LegacyManualOperatorSourceSnapshot _snapshot;
|
||||
|
||||
public CountingSource(LegacyManualOperatorSourceSnapshot snapshot)
|
||||
{
|
||||
_snapshot = snapshot;
|
||||
}
|
||||
|
||||
public int ReadCount { get; private set; }
|
||||
|
||||
public Task<LegacyManualOperatorSourceSnapshot> ReadAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ReadCount++;
|
||||
return Task.FromResult(_snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TemporaryDirectory : IDisposable
|
||||
{
|
||||
public TemporaryDirectory()
|
||||
{
|
||||
Path = System.IO.Path.Combine(
|
||||
System.IO.Path.GetTempPath(),
|
||||
Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(Path);
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(Path, recursive: true);
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
// Cleanup must not hide the test result.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user