feat: advance legacy UI behavior parity
This commit is contained in:
@@ -221,6 +221,111 @@ public sealed class LegacyComparisonWorkflowTests
|
||||
Assert.False(snapshot.IsBusy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Uc3_AllowsExactDuplicatePairsAndMovesOrDeletesOnlyTheSelectedStableRow()
|
||||
{
|
||||
var data = new FakeDataService { DomesticResults = [Samsung, SkHynix] };
|
||||
var store = new FakePairStore();
|
||||
var controller = CreateController(data, store);
|
||||
var search = await controller.SearchDomesticAsync(string.Empty);
|
||||
controller.ChooseDomesticResult(search.DomesticSearch.Results[0].SelectionId);
|
||||
controller.ChooseDomesticResult(search.DomesticSearch.Results[1].SelectionId);
|
||||
|
||||
var firstAdd = await controller.AddDraftPairAsync();
|
||||
var firstRowId = Assert.Single(firstAdd.SavedPairs).RowId;
|
||||
var secondAdd = await controller.AddDraftPairAsync();
|
||||
var secondRowId = secondAdd.SavedPairs[1].RowId;
|
||||
|
||||
Assert.NotEqual(firstRowId, secondRowId);
|
||||
Assert.Equal(
|
||||
new[] { firstRowId, secondRowId },
|
||||
secondAdd.SavedPairs.Select(pair => pair.RowId));
|
||||
Assert.All(
|
||||
secondAdd.SavedPairs,
|
||||
pair => Assert.Equal("SK하이닉스 ↔ 삼성전자", pair.DisplayLabel));
|
||||
Assert.Collection(
|
||||
store.Writes[^1],
|
||||
row => Assert.Equal(
|
||||
new LegacyComparisonPairWriteRow(
|
||||
1,
|
||||
"SK하이닉스",
|
||||
"코스피",
|
||||
"삼성전자",
|
||||
"코스피"),
|
||||
row),
|
||||
row => Assert.Equal(
|
||||
new LegacyComparisonPairWriteRow(
|
||||
2,
|
||||
"SK하이닉스",
|
||||
"코스피",
|
||||
"삼성전자",
|
||||
"코스피"),
|
||||
row));
|
||||
|
||||
controller.SelectSavedPair(secondRowId);
|
||||
var moved = await controller.MoveSelectedPairAsync(
|
||||
LegacyComparisonPairMoveDirection.Up);
|
||||
|
||||
Assert.Equal(
|
||||
new[] { secondRowId, firstRowId },
|
||||
moved.SavedPairs.Select(pair => pair.RowId));
|
||||
Assert.Equal(secondRowId, moved.SelectedPairId);
|
||||
Assert.True(moved.SavedPairs[0].IsSelected);
|
||||
Assert.False(moved.SavedPairs[1].IsSelected);
|
||||
|
||||
var deleted = await controller.DeleteSelectedPairAsync();
|
||||
|
||||
var remaining = Assert.Single(deleted.SavedPairs);
|
||||
Assert.Equal(firstRowId, remaining.RowId);
|
||||
Assert.True(remaining.IsSelected);
|
||||
Assert.Equal(firstRowId, deleted.SelectedPairId);
|
||||
Assert.Single(store.Writes[^1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Uc3_DuplicatePairSaveRoundTripPreservesPhysicalRowOrder()
|
||||
{
|
||||
var data = new FakeDataService();
|
||||
data.Resolve(Samsung, "삼성전자", "코스피");
|
||||
data.Resolve(SkHynix, "SK하이닉스", "코스피");
|
||||
data.Resolve(Apple, "APPLE", string.Empty);
|
||||
var store = new FakePairStore
|
||||
{
|
||||
Bytes = EncodeCp949(
|
||||
("삼성전자", "코스피", "SK하이닉스", "코스피"),
|
||||
("삼성전자", "코스피", "SK하이닉스", "코스피"),
|
||||
("코스피 지수", string.Empty, "APPLE", string.Empty))
|
||||
};
|
||||
var controller = CreateController(data, store);
|
||||
var loaded = await controller.LoadSavedPairsAsync();
|
||||
var thirdRowId = loaded.SavedPairs[2].RowId;
|
||||
controller.SelectSavedPair(thirdRowId);
|
||||
|
||||
var moved = await controller.MoveSelectedPairAsync(
|
||||
LegacyComparisonPairMoveDirection.Up);
|
||||
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"삼성전자 ↔ SK하이닉스",
|
||||
"코스피 지수 ↔ APPLE",
|
||||
"삼성전자 ↔ SK하이닉스"
|
||||
},
|
||||
moved.SavedPairs.Select(pair => pair.DisplayLabel));
|
||||
Assert.Equal(new[] { 1, 2, 3 }, store.Writes[^1].Select(row => row.RowNumber));
|
||||
|
||||
var reloadedStore = new FakePairStore
|
||||
{
|
||||
Bytes = EncodeCp949(store.Writes[^1])
|
||||
};
|
||||
var reloaded = await CreateController(data, reloadedStore).LoadSavedPairsAsync();
|
||||
|
||||
Assert.Equal(
|
||||
moved.SavedPairs.Select(pair => pair.DisplayLabel),
|
||||
reloaded.SavedPairs.Select(pair => pair.DisplayLabel));
|
||||
Assert.Equal(3, reloaded.SavedPairs.Select(pair => pair.RowId).Distinct().Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SavedPairMoveAndDelete_PreserveActiveRowAndPersistPhysicalOrder()
|
||||
{
|
||||
@@ -424,6 +529,13 @@ public sealed class LegacyComparisonWorkflowTests
|
||||
return encoding.GetBytes(string.Join("\r\n", lines) + "\r\n");
|
||||
}
|
||||
|
||||
private static byte[] EncodeCp949(IReadOnlyList<LegacyComparisonPairWriteRow> rows) =>
|
||||
EncodeCp949(rows.Select(row => (
|
||||
row.FirstName,
|
||||
row.FirstMarket,
|
||||
row.SecondName,
|
||||
row.SecondMarket)).ToArray());
|
||||
|
||||
private sealed class FakeDataService : ILegacyComparisonDataService
|
||||
{
|
||||
private readonly Dictionary<LegacyComparisonStoredTargetReference, LegacyComparisonTarget>
|
||||
|
||||
@@ -59,6 +59,108 @@ public sealed class LegacyOperatorComparisonIntegrationTests
|
||||
row => Assert.Equal("5087", row.SceneAlias));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAllComparisonPairs_NoPreservesRowsAndPerformsNoWrite()
|
||||
{
|
||||
var (controller, store) = await CreateControllerWithSavedPairAsync();
|
||||
var writesBeforeRequest = store.Writes.Count;
|
||||
|
||||
var requested = await controller.DeleteComparisonPairAsync(all: true);
|
||||
|
||||
Assert.Equal("모두 삭제하겠습니까?", requested.Dialog?.Message);
|
||||
Assert.Equal("MmoneyCoder", requested.Dialog?.Caption);
|
||||
Assert.True(requested.Dialog?.IsConfirmation);
|
||||
Assert.Single(requested.Comparison!.SavedPairs);
|
||||
Assert.Equal(writesBeforeRequest, store.Writes.Count);
|
||||
|
||||
var dismissed = controller.DismissDialog();
|
||||
Assert.True(dismissed.Dialog?.IsConfirmation);
|
||||
Assert.Equal(writesBeforeRequest, store.Writes.Count);
|
||||
|
||||
var canceled = controller.CancelDialog();
|
||||
|
||||
Assert.Null(canceled.Dialog);
|
||||
Assert.Single(canceled.Comparison!.SavedPairs);
|
||||
Assert.Equal(writesBeforeRequest, store.Writes.Count);
|
||||
|
||||
await controller.ConfirmDialogAsync();
|
||||
Assert.Equal(writesBeforeRequest, store.Writes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAllComparisonPairs_YesConsumesConfirmationBeforeSingleWrite()
|
||||
{
|
||||
var (controller, store) = await CreateControllerWithSavedPairAsync();
|
||||
var writesBeforeRequest = store.Writes.Count;
|
||||
await controller.DeleteComparisonPairAsync(all: true);
|
||||
|
||||
var confirmed = await controller.ConfirmDialogAsync();
|
||||
|
||||
Assert.Null(confirmed.Dialog);
|
||||
Assert.Empty(confirmed.Comparison!.SavedPairs);
|
||||
Assert.Equal(writesBeforeRequest + 1, store.Writes.Count);
|
||||
Assert.Empty(store.Writes[^1]);
|
||||
|
||||
await controller.ConfirmDialogAsync();
|
||||
Assert.Equal(writesBeforeRequest + 1, store.Writes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAllComparisonPairs_ChangedListInvalidatesPendingConfirmation()
|
||||
{
|
||||
var (controller, store) = await CreateControllerWithSavedPairAsync();
|
||||
var writesBeforeRequest = store.Writes.Count;
|
||||
var requested = await controller.DeleteComparisonPairAsync(all: true);
|
||||
var rowId = Assert.Single(requested.Comparison!.SavedPairs).RowId;
|
||||
controller.SelectComparisonPair(rowId);
|
||||
await controller.DeleteComparisonPairAsync(all: true);
|
||||
|
||||
var rejected = await controller.ConfirmDialogAsync();
|
||||
|
||||
Assert.Single(rejected.Comparison!.SavedPairs);
|
||||
Assert.Equal(writesBeforeRequest, store.Writes.Count);
|
||||
Assert.Equal(LegacyOperatorStatusKind.Error, rejected.StatusKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAllComparisonPairs_EmptyListStillPromptsAndYesDoesNotWrite()
|
||||
{
|
||||
var store = new MemoryComparisonPairStore();
|
||||
var controller = new LegacyOperatorController(
|
||||
new EmptyStockLookup(),
|
||||
comparisonWorkflow: new LegacyComparisonWorkflowController(
|
||||
new StaticComparisonDataService(),
|
||||
store));
|
||||
await controller.SelectTabAsync(LegacyOperatorTabId.Comparison);
|
||||
|
||||
var requested = await controller.DeleteComparisonPairAsync(all: true);
|
||||
|
||||
Assert.Equal("모두 삭제하겠습니까?", requested.Dialog?.Message);
|
||||
Assert.True(requested.Dialog?.IsConfirmation);
|
||||
var confirmed = await controller.ConfirmDialogAsync();
|
||||
Assert.Empty(confirmed.Comparison!.SavedPairs);
|
||||
Assert.Empty(store.Writes);
|
||||
}
|
||||
|
||||
private static async Task<(LegacyOperatorController Controller,
|
||||
MemoryComparisonPairStore Store)> CreateControllerWithSavedPairAsync()
|
||||
{
|
||||
var store = new MemoryComparisonPairStore();
|
||||
var controller = new LegacyOperatorController(
|
||||
new EmptyStockLookup(),
|
||||
comparisonWorkflow: new LegacyComparisonWorkflowController(
|
||||
new StaticComparisonDataService(),
|
||||
store));
|
||||
await controller.SelectTabAsync(LegacyOperatorTabId.Comparison);
|
||||
var searched = await controller.SearchComparisonDomesticAsync("반도체");
|
||||
controller.ChooseComparisonDomesticResult(
|
||||
searched.Comparison!.DomesticSearch.Results[0].SelectionId);
|
||||
controller.ChooseComparisonDomesticResult(
|
||||
searched.Comparison.DomesticSearch.Results[1].SelectionId);
|
||||
await controller.AddComparisonPairAsync();
|
||||
return (controller, store);
|
||||
}
|
||||
|
||||
private sealed class EmptyStockLookup : ILegacyStockLookup
|
||||
{
|
||||
public Task<LegacyStockSearchData> SearchAsync(
|
||||
|
||||
Reference in New Issue
Block a user