Complete legacy operator UI and playout migration
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
using System.Text;
|
||||
using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;
|
||||
using MBN_STOCK_WEBVIEW.Infrastructure;
|
||||
|
||||
namespace MBN_STOCK_WEBVIEW.Infrastructure.Tests;
|
||||
|
||||
public sealed class S5025TrustedManualFileStoreTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(S5025ManualAudience.Individual, "개인.dat")]
|
||||
[InlineData(S5025ManualAudience.Foreign, "외국인.dat")]
|
||||
[InlineData(S5025ManualAudience.Institution, "기관.dat")]
|
||||
public async Task WriteAsync_round_trips_the_closed_audience_file(
|
||||
S5025ManualAudience audience,
|
||||
string expectedFileName)
|
||||
{
|
||||
using var directory = new TemporaryDirectory();
|
||||
using var store = CreateStore(directory.Path);
|
||||
|
||||
await store.WriteAsync(audience, Rows());
|
||||
|
||||
Assert.True(File.Exists(Path.Combine(directory.Path, expectedFileName)));
|
||||
var actual = await store.ReadAsync(audience);
|
||||
Assert.Equal(Rows(), actual);
|
||||
Assert.Empty(Directory.EnumerateFiles(directory.Path, "*.tmp"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsync_preserves_the_legacy_cp949_terminal_record()
|
||||
{
|
||||
using var directory = new TemporaryDirectory();
|
||||
using var store = CreateStore(directory.Path);
|
||||
|
||||
await store.WriteAsync(S5025ManualAudience.Individual, Rows());
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
var cp949 = Encoding.GetEncoding(949);
|
||||
var bytes = await File.ReadAllBytesAsync(Path.Combine(directory.Path, "개인.dat"));
|
||||
var text = cp949.GetString(bytes);
|
||||
Assert.EndsWith("\r\n\r\n", text, StringComparison.Ordinal);
|
||||
Assert.Equal(5, text.Split("\r\n", StringSplitOptions.None).Length - 2);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(4)]
|
||||
[InlineData(6)]
|
||||
public async Task WriteAsync_requires_exactly_five_rows(int rowCount)
|
||||
{
|
||||
using var directory = new TemporaryDirectory();
|
||||
using var store = CreateStore(directory.Path);
|
||||
|
||||
await Assert.ThrowsAsync<S5025TrustedManualDataException>(() =>
|
||||
store.WriteAsync(
|
||||
S5025ManualAudience.Individual,
|
||||
Rows().Take(rowCount).Concat(Enumerable.Repeat(Rows()[0], Math.Max(0, rowCount - 5)))
|
||||
.Take(rowCount)
|
||||
.ToArray()));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bad^name")]
|
||||
[InlineData("bad\nname")]
|
||||
public async Task WriteAsync_rejects_delimiter_and_control_characters(string value)
|
||||
{
|
||||
using var directory = new TemporaryDirectory();
|
||||
using var store = CreateStore(directory.Path);
|
||||
var rows = Rows().ToArray();
|
||||
rows[0] = rows[0] with { LeftName = value };
|
||||
|
||||
await Assert.ThrowsAsync<S5025TrustedManualDataException>(() =>
|
||||
store.WriteAsync(S5025ManualAudience.Individual, rows));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsync_rejects_text_that_cp949_cannot_encode()
|
||||
{
|
||||
using var directory = new TemporaryDirectory();
|
||||
using var store = CreateStore(directory.Path);
|
||||
var rows = Rows().ToArray();
|
||||
rows[0] = rows[0] with { LeftName = "😀" };
|
||||
|
||||
await Assert.ThrowsAsync<S5025TrustedManualDataException>(() =>
|
||||
store.WriteAsync(S5025ManualAudience.Individual, rows));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsync_honors_pre_cancelled_token_without_creating_a_file()
|
||||
{
|
||||
using var directory = new TemporaryDirectory();
|
||||
using var store = CreateStore(directory.Path);
|
||||
using var cancellation = new CancellationTokenSource();
|
||||
cancellation.Cancel();
|
||||
|
||||
await Assert.ThrowsAnyAsync<OperationCanceledException>(() =>
|
||||
store.WriteAsync(S5025ManualAudience.Individual, Rows(), cancellation.Token));
|
||||
Assert.Empty(Directory.EnumerateFiles(directory.Path));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsync_rejects_unknown_audience_before_creating_a_file()
|
||||
{
|
||||
using var directory = new TemporaryDirectory();
|
||||
using var store = CreateStore(directory.Path);
|
||||
|
||||
await Assert.ThrowsAsync<S5025TrustedManualDataException>(() =>
|
||||
store.WriteAsync((S5025ManualAudience)99, Rows()));
|
||||
Assert.Empty(Directory.EnumerateFiles(directory.Path));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsync_rejects_an_existing_reparse_destination()
|
||||
{
|
||||
using var directory = new TemporaryDirectory();
|
||||
using var outside = new TemporaryDirectory();
|
||||
var target = Path.Combine(outside.Path, "outside.dat");
|
||||
await File.WriteAllTextAsync(target, "outside");
|
||||
var link = Path.Combine(directory.Path, "개인.dat");
|
||||
if (!TryCreateFileSymbolicLink(link, target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var store = CreateStore(directory.Path);
|
||||
await Assert.ThrowsAsync<S5025TrustedManualDataException>(() =>
|
||||
store.WriteAsync(S5025ManualAudience.Individual, Rows()));
|
||||
Assert.Equal("outside", await File.ReadAllTextAsync(target));
|
||||
}
|
||||
|
||||
private static IReadOnlyList<S5025TrustedManualRow> Rows() =>
|
||||
[
|
||||
new("삼성전자", "1,234", "SK하이닉스", "-987"),
|
||||
new("현대차", "2", "기아", "-2"),
|
||||
new("NAVER", "3", "카카오", "-3"),
|
||||
new("LG화학", "4", "포스코", "-4"),
|
||||
new("한화", "5", "셀트리온", "-5")
|
||||
];
|
||||
|
||||
private static S5025TrustedManualFileStore CreateStore(string directory)
|
||||
{
|
||||
TrustedManualDirectory.PreparePrivateWritableDirectory(directory);
|
||||
return new S5025TrustedManualFileStore(directory);
|
||||
}
|
||||
|
||||
private static bool TryCreateFileSymbolicLink(string link, string target)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.CreateSymbolicLink(link, target);
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception) when (exception is
|
||||
UnauthorizedAccessException or IOException or PlatformNotSupportedException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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