194 lines
6.8 KiB
C#
194 lines
6.8 KiB
C#
using System.Text;
|
|
using MBN_STOCK_WEBVIEW.Infrastructure;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.Infrastructure.Tests;
|
|
|
|
public sealed class ViTrustedManualFileStoreTests
|
|
{
|
|
[Fact]
|
|
public async Task ReadAsync_imports_the_original_nine_column_cp949_shape()
|
|
{
|
|
using var directory = new TemporaryDirectory();
|
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
|
var cp949 = Encoding.GetEncoding(949);
|
|
await File.WriteAllBytesAsync(
|
|
Path.Combine(directory.Path, "VI발동.dat"),
|
|
cp949.GetBytes(
|
|
"PKR7005930003^삼성전자^^^^^^^\r\n" +
|
|
"PKR7016360000^삼성증권^^^^^^^\r\n"));
|
|
using var store = CreateStore(directory.Path);
|
|
|
|
var actual = await store.ReadAsync();
|
|
|
|
Assert.Equal(
|
|
[
|
|
new ViTrustedManualItem("PKR7005930003", "삼성전자"),
|
|
new ViTrustedManualItem("PKR7016360000", "삼성증권")
|
|
],
|
|
actual);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WriteAsync_round_trips_order_duplicates_and_version()
|
|
{
|
|
using var directory = new TemporaryDirectory();
|
|
using var store = CreateStore(directory.Path);
|
|
ViTrustedManualItem[] expected =
|
|
[
|
|
new("005930", "삼성전자"),
|
|
new("016360", "삼성증권"),
|
|
new("005930", "삼성전자")
|
|
];
|
|
|
|
var write = await store.WriteAsync(expected);
|
|
|
|
Assert.True(write.Persisted);
|
|
Assert.Matches("^[a-f0-9]{64}$", write.Snapshot.RowVersion);
|
|
Assert.Equal(expected, await store.ReadAsync());
|
|
Assert.Empty(Directory.EnumerateFiles(directory.Path, "*.tmp"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Empty_list_is_an_original_compatible_no_op()
|
|
{
|
|
using var directory = new TemporaryDirectory();
|
|
using var store = CreateStore(directory.Path);
|
|
|
|
var firstNoOp = await store.WriteAsync(Array.Empty<ViTrustedManualItem>());
|
|
Assert.False(firstNoOp.Persisted);
|
|
Assert.False(File.Exists(Path.Combine(directory.Path, "VI발동.dat")));
|
|
Assert.Empty(await store.ReadAsync());
|
|
|
|
var expected = new[] { new ViTrustedManualItem("005930", "삼성전자") };
|
|
var written = await store.WriteAsync(expected);
|
|
var secondNoOp = await store.WriteAsync(Array.Empty<ViTrustedManualItem>());
|
|
|
|
Assert.False(secondNoOp.Persisted);
|
|
Assert.Equal(written.Snapshot.RowVersion, secondNoOp.Snapshot.RowVersion);
|
|
Assert.Equal(expected, await store.ReadAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Missing_file_is_an_empty_versioned_first_run_list()
|
|
{
|
|
using var directory = new TemporaryDirectory();
|
|
using var store = CreateStore(directory.Path);
|
|
|
|
var snapshot = await store.ReadSnapshotAsync();
|
|
|
|
Assert.Empty(snapshot.Items);
|
|
Assert.Equal(
|
|
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
snapshot.RowVersion);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadAsync_rejects_nonempty_legacy_extra_columns()
|
|
{
|
|
using var directory = new TemporaryDirectory();
|
|
await File.WriteAllTextAsync(
|
|
Path.Combine(directory.Path, "VI발동.dat"),
|
|
"005930^삼성전자^unexpected^^^^^^\r\n");
|
|
using var store = CreateStore(directory.Path);
|
|
|
|
await Assert.ThrowsAsync<ViTrustedManualDataException>(() => store.ReadAsync());
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("", "삼성전자")]
|
|
[InlineData("005930", "")]
|
|
[InlineData("005^930", "삼성전자")]
|
|
[InlineData("005930", "삼성\n전자")]
|
|
[InlineData(" 005930", "삼성전자")]
|
|
[InlineData("005930", "삼성전자 ")]
|
|
[InlineData("005930", "삼성,전자")]
|
|
[InlineData("005930", "😀")]
|
|
public async Task WriteAsync_rejects_invalid_or_non_cp949_values(string code, string name)
|
|
{
|
|
using var directory = new TemporaryDirectory();
|
|
using var store = CreateStore(directory.Path);
|
|
|
|
await Assert.ThrowsAsync<ViTrustedManualDataException>(() =>
|
|
store.WriteAsync([new ViTrustedManualItem(code, name)]));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WriteAsync_caps_the_original_five_row_scene_at_twenty_pages()
|
|
{
|
|
using var directory = new TemporaryDirectory();
|
|
using var store = CreateStore(directory.Path);
|
|
var items = Enumerable.Range(1, ViTrustedManualFileStore.MaximumItemCount + 1)
|
|
.Select(index => new ViTrustedManualItem(index.ToString("D6"), $"종목{index}"))
|
|
.ToArray();
|
|
|
|
await Assert.ThrowsAsync<ViTrustedManualDataException>(() => store.WriteAsync(items));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WriteAsync_honors_pre_cancelled_token()
|
|
{
|
|
using var directory = new TemporaryDirectory();
|
|
using var store = CreateStore(directory.Path);
|
|
using var cancellation = new CancellationTokenSource();
|
|
cancellation.Cancel();
|
|
|
|
await Assert.ThrowsAnyAsync<OperationCanceledException>(() =>
|
|
store.WriteAsync(
|
|
[new ViTrustedManualItem("005930", "삼성전자")],
|
|
cancellation.Token));
|
|
Assert.Empty(Directory.EnumerateFiles(directory.Path));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Canonical_snapshot_version_covers_all_twenty_pages()
|
|
{
|
|
using var directory = new TemporaryDirectory();
|
|
using var store = CreateStore(directory.Path);
|
|
var items = Enumerable.Range(1, ViTrustedManualFileStore.MaximumItemCount)
|
|
.Select(index => new ViTrustedManualItem(
|
|
$"P{index:D6}",
|
|
$"현실적인긴종목명테스트{index}"))
|
|
.ToArray();
|
|
|
|
var written = await store.WriteAsync(items);
|
|
var snapshot = await store.ReadSnapshotAsync();
|
|
|
|
Assert.True(written.Persisted);
|
|
Assert.Equal(100, snapshot.Items.Count);
|
|
Assert.Equal(written.Snapshot.RowVersion, snapshot.RowVersion);
|
|
Assert.Matches("^[a-f0-9]{64}$", snapshot.RowVersion);
|
|
}
|
|
|
|
private static ViTrustedManualFileStore CreateStore(string directory)
|
|
{
|
|
TrustedManualDirectory.PreparePrivateWritableDirectory(directory);
|
|
return new ViTrustedManualFileStore(directory);
|
|
}
|
|
|
|
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.
|
|
}
|
|
}
|
|
}
|
|
}
|