Files
MBN_STOCK_WEBVIEW/tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/ViTrustedManualFileStoreTests.cs

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.
}
}
}
}