fix(board): commit a search only after its query succeeds and report read failures
A failed read on a UI path (sort change, clear, refresh after a write, opening a card, the tag picker, the tags tab) used to escape as an unhandled exception. Reads now run before the view is cleared and report Error_Read_* instead; the active filter changes only once its query has run. DataWritten and TagsChanged fire only after a successful refresh, so a broken database shows one dialog.
This commit is contained in:
1 parent
23f7806e2d
commit
f9f0e71453
9 files changed
+216
-33
No files matched your search
@@ -285,6 +285,52 @@ public class BoardViewModelTests
|
||||
Assert.AreEqual(0, fixture.Repository.GetCards().Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FailedSearchQueryIsReportedAndKeepsThePreviousResultAndFilter()
|
||||
{
|
||||
using var fixture = new Fixture();
|
||||
ColumnModel column = fixture.Repository.AddColumn("A", "");
|
||||
fixture.Repository.AddCard(column.Id, "alpha", "", []);
|
||||
fixture.Repository.AddCard(column.Id, "beta", "", []);
|
||||
BoardViewModel board = fixture.CreateBoard();
|
||||
board.SearchText = "alpha";
|
||||
await board.SearchCommand.ExecuteAsync(null);
|
||||
CompiledSearch? alphaFilter = board.ActiveFilter;
|
||||
|
||||
// Every read fails from now on.
|
||||
fixture.Repository.Dispose();
|
||||
board.SearchText = "beta";
|
||||
await board.SearchCommand.ExecuteAsync(null);
|
||||
|
||||
var error = (MessageDialogViewModel)fixture.Dialogs.Shown.Single();
|
||||
Assert.AreEqual(Resources.Error_Read_Title, error.Title);
|
||||
Assert.AreSame(alphaFilter, board.ActiveFilter);
|
||||
CollectionAssert.AreEqual(new[] { "alpha" }, board.Columns.Single().Cards.Select(card => card.Title).ToArray());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ReadFailuresOnBoardCommandsAreReportedInsteadOfThrown()
|
||||
{
|
||||
using var fixture = new Fixture();
|
||||
ColumnModel column = fixture.Repository.AddColumn("A", "");
|
||||
CardModel card = fixture.Repository.AddCard(column.Id, "a", "", []);
|
||||
BoardViewModel board = fixture.CreateBoard();
|
||||
var editor = CardEditorDialogViewModel.ForExistingCard(fixture.Repository, fixture.Dialogs, card, confirmDiscard: false);
|
||||
fixture.Repository.Dispose();
|
||||
|
||||
board.SelectedSort = board.SortOptions.Single(option => (CardSortOption)option.Value == CardSortOption.Title);
|
||||
await board.ClearSearchCommand.ExecuteAsync(null);
|
||||
await board.Columns.Single().Cards.Single().EditCommand.ExecuteAsync(null);
|
||||
await board.Columns.Single().Cards.Single().MoveToCommand.ExecuteAsync(column.Id);
|
||||
await editor.AddTagCommand.ExecuteAsync(null);
|
||||
|
||||
// Sort, clear, edit, the failed move with its refresh, and the tag picker.
|
||||
string read = Resources.Error_Read_Title;
|
||||
CollectionAssert.AreEqual(
|
||||
new[] { read, read, read, Resources.Error_Operation_Title, read, read },
|
||||
fixture.Dialogs.Shown.Select(dialog => ((MessageDialogViewModel)dialog).Title).ToArray());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TagPickerReportsInvalidColorAndDuplicateNameWithoutClosing()
|
||||
{
|
||||
|
||||
@@ -138,7 +138,7 @@ public class TagsAndSearchViewModelTests
|
||||
board.SearchText = "alpha";
|
||||
await board.SearchCommand.ExecuteAsync(null);
|
||||
Assert.IsTrue(board.HasSearchText);
|
||||
board.ClearSearchCommand.Execute(null);
|
||||
await board.ClearSearchCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.AreEqual("", board.SearchText);
|
||||
Assert.IsFalse(board.HasSearchText);
|
||||
@@ -312,6 +312,24 @@ public class TagsAndSearchViewModelTests
|
||||
Assert.AreEqual(0, fixture.Repository.GetTags().Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TagReadFailuresAreReportedInsteadOfThrown()
|
||||
{
|
||||
using var fixture = new Fixture();
|
||||
fixture.Repository.AddTag("bug", new RgbColor(1, 2, 3), "");
|
||||
fixture.Config.Confirmations.DeleteTag = false;
|
||||
TagsViewModel tags = fixture.CreateTags();
|
||||
|
||||
// Every read and write fails from now on.
|
||||
fixture.Repository.Dispose();
|
||||
await tags.Tags[0].DeleteCommand.ExecuteAsync(null);
|
||||
|
||||
CollectionAssert.AreEqual(
|
||||
new[] { Resources.Error_Operation_Title, Resources.Error_Read_Title },
|
||||
fixture.Dialogs.Shown.Select(dialog => ((MessageDialogViewModel)dialog).Title).ToArray());
|
||||
Assert.AreEqual("bug", tags.Tags.Single().Tag.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Workspace wiring and export
|
||||
@@ -334,7 +352,7 @@ public class TagsAndSearchViewModelTests
|
||||
Assert.AreEqual(0, workspace.Board.Columns.Sum(column => column.CardCount));
|
||||
|
||||
// Re-tag through the repository, then a board write updates the usage count.
|
||||
workspace.Board.ClearSearchCommand.Execute(null);
|
||||
await workspace.Board.ClearSearchCommand.ExecuteAsync(null);
|
||||
TagModel ui = fixture.Repository.AddTag("ui", new RgbColor(4, 5, 6), "");
|
||||
fixture.Repository.UpdateCard(card.Id, "a", "", [ui.Id]);
|
||||
Assert.AreEqual(0, workspace.Tags.Tags.Count);
|
||||
|
||||
Reference in new issue
Block a user