Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,39 @@
dotnet list package, include-transitive) so the ExplorerExtension companion exe and
the MAUI head share a single Microsoft.WindowsAppSDK version. -->
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.2.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.9" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.9" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
<PackageVersion Include="System.CommandLine" Version="2.0.9" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.9" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.9" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.10" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.10" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.10" />
<PackageVersion Include="System.CommandLine" Version="2.0.10" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.10" />
<!-- TEMPORARY security override for CVE-2025-6965 (GHSA-2m69-gcr7-jv3q, High).
Microsoft.Data.Sqlite / Microsoft.EntityFrameworkCore.Sqlite 10.0.9 transitively pull
Microsoft.Data.Sqlite / Microsoft.EntityFrameworkCore.Sqlite 10.0.10 transitively pull
SQLitePCLRaw.bundle_e_sqlite3 2.1.11 -> SQLitePCLRaw.lib.e_sqlite3 2.1.11 (SQLite < 3.50.2).
There is no 2.1.x patch; lib.e_sqlite3 3.50.3 is a native-only package (no managed deps) that
satisfies the bundle's [2.1.11, ) range, so this swaps in the SQLite 3.50.3 binary while keeping
the SQLitePCLRaw 2.x managed core. Remove once Microsoft.Data.Sqlite ships the upstream fix
The upstream fix (SQLitePCLRaw 3.x) ships only in the 11.0.0-preview line; latest stable
Microsoft.Data.Sqlite 10.0.10 still references the vulnerable bundle 2.1.11. lib.e_sqlite3
3.53.3 is a native-only package (no managed deps) that satisfies the bundle's [2.1.11, )
range, so this swaps in the patched SQLite 3.53.3 binary while keeping the SQLitePCLRaw 2.x
managed core. Remove once Microsoft.Data.Sqlite ships the fix on a stable channel
(dotnet/efcore PR 38402, milestone 11.0-preview6). Tracked by:
https://github.com/microsoft/EventLogExpert/issues/604 -->
<PackageVersion Include="SQLitePCLRaw.lib.e_sqlite3" Version="3.50.3" />
<PackageVersion Include="Fluxor.Blazor.Web" Version="6.9.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="SQLitePCLRaw.lib.e_sqlite3" Version="3.53.3" />
<PackageVersion Include="Fluxor.Blazor.Web" Version="6.10.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageVersion Include="NSubstitute" Version="6.0.0" />
<PackageVersion Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
<PackageVersion Include="xunit.v3.mtp-off" Version="3.2.2" />
<PackageVersion Include="bunit" Version="2.7.2" />
<!-- Security override for CVE-2026-54570 (GHSA-pgww-w46g-26qg, moderate).
bunit 2.7.2 (test-only) transitively resolves AngleSharp 1.4.0, which mis-parses
MathML annotation-xml HTML integration points and does not escape &lt; or &gt; when
serializing attribute values (a mutation-XSS vector that can bypass sanitizers built
on AngleSharp). The fix first shipped in AngleSharp 1.5.0 and there is no 1.4.x patch,
so this pins the transitively-resolved core package to 1.5.2 (latest 1.5.x stable).
The sibling AngleSharp.Css / AngleSharp.Diffing packages are not covered by the
advisory. Remove once bunit ships an AngleSharp reference >= 1.5.0. -->
<PackageVersion Include="AngleSharp" Version="1.5.2" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,7 @@ public async Task ImportAsync_FreshlyImportedV4Db_ShouldDefaultDisabled_AndNotEn
Assert.Equal(DatabaseStatus.Ready, entry.Status);

preferences.Received().DisabledDatabasesPreference =
Arg.Is<List<string>>(disabled => disabled.Contains(Constants.TestDb1, StringComparer.OrdinalIgnoreCase));
Arg.Is<List<string>>(disabled => disabled != null && disabled.Contains(Constants.TestDb1, StringComparer.OrdinalIgnoreCase));
}

[Fact]
Expand Down Expand Up @@ -914,7 +914,7 @@ public async Task ImportAsync_ReimportedDb_NotOnSkipList_ShouldOverwriteAndPrese
Assert.True(entry.IsEnabled);

preferences.DidNotReceive().DisabledDatabasesPreference =
Arg.Is<List<string>>(disabled => disabled.Contains(Constants.TestDb1, StringComparer.OrdinalIgnoreCase));
Arg.Is<List<string>>(disabled => disabled != null && disabled.Contains(Constants.TestDb1, StringComparer.OrdinalIgnoreCase));
}

[Fact]
Expand Down Expand Up @@ -952,7 +952,7 @@ public async Task ImportAsync_ReimportedDb_OnSkipList_ShouldPreserveExistingFile
Assert.Equal(DatabaseStatus.Ready, entry.Status);

preferences.DidNotReceive().DisabledDatabasesPreference =
Arg.Is<List<string>>(disabled => disabled.Contains(Constants.TestDb1, StringComparer.OrdinalIgnoreCase));
Arg.Is<List<string>>(disabled => disabled != null && disabled.Contains(Constants.TestDb1, StringComparer.OrdinalIgnoreCase));
}

[Fact]
Expand Down Expand Up @@ -2040,7 +2040,7 @@ public void Toggle_WhenCalled_ShouldFlipIsEnabledAndPersist()
Assert.False(service.Entries[0].IsEnabled);

preferences.Received(1).DisabledDatabasesPreference =
Arg.Is<IEnumerable<string>>(disabled => disabled.Contains(Constants.TestDb1));
Arg.Is<IEnumerable<string>>(disabled => disabled != null && disabled.Contains(Constants.TestDb1));
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public async Task Migration_AddRangeThrows_LegacyDataPreserved_DispatchesLoadSuc
Assert.Empty(await realStore.LoadAllAsync(TestContext.Current.CancellationToken));
Assert.True(prefs.ContainsKey(FavoriteFiltersKey));
Assert.False(prefs.ContainsKey(MigrationSectionsKey));
dispatcher.Received(1).Dispatch(Arg.Is<LoadLibrarySuccessAction>(a => a.Entries.IsEmpty));
dispatcher.Received(1).Dispatch(Arg.Is<LoadLibrarySuccessAction>(a => a != null && a.Entries.IsEmpty));
dispatcher.DidNotReceive().Dispatch(Arg.Any<LoadLibraryFailureAction>());
}

Expand Down Expand Up @@ -198,7 +198,7 @@ public async Task Migration_EndToEnd_EmptyDb_LegacyFavoritesAndGroups_PopulatesS
Assert.Equal(2, filterSet.Filters.Count);
Assert.Equal(LibraryEntryOrigin.UserSaved, filterSet.Origin);

dispatcher.Received(1).Dispatch(Arg.Is<LoadLibrarySuccessAction>(a => a.Entries.Count == 3));
dispatcher.Received(1).Dispatch(Arg.Is<LoadLibrarySuccessAction>(a => a != null && a.Entries.Count == 3));
dispatcher.DidNotReceive().Dispatch(Arg.Any<LoadLibraryFailureAction>());

Assert.True(prefs.ContainsKey(FavoriteFiltersKey));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public async Task DisplayPrompt_WhenActiveHost_ShouldRouteInlineAndReturnTypedVa
// Assert
Assert.Equal("typed-value", result);
await host.Received(1).ShowInlineAlertAsync(
Arg.Is<InlineAlertRequest>(r => r.IsPrompt && r.Title == "Rename" && r.Message == "Enter new name"),
Arg.Is<InlineAlertRequest>(r => r != null && r.IsPrompt && r.Title == "Rename" && r.Message == "Enter new name"),
Arg.Any<CancellationToken>());
}

Expand Down Expand Up @@ -133,7 +133,7 @@ public async Task ShowAlert_ShouldMarshalThroughMainThreadService()

var mainThread = Substitute.For<IMainThreadService>();
mainThread.InvokeOnMainThreadAsync(Arg.Any<Func<Task>>())
.Returns(call => ((Func<Task>)call[0])());
.Returns(call => call.ArgAt<Func<Task>>(0)());

var sut = new AlertDialogService(
coordinator,
Expand Down Expand Up @@ -415,6 +415,7 @@ public async Task ShowAlertTwoButton_WhenActiveHost_ShouldRouteToHostInline()
Assert.False(standaloneCalled);
await host.Received(1).ShowInlineAlertAsync(
Arg.Is<InlineAlertRequest>(r =>
r != null &&
r.Title == "Confirm" &&
r.Message == "Are you sure?" &&
r.AcceptLabel == "Yes" &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ public async Task ApplyPendingTogglesAsync_FiveFilesWithTwoFailures_AllAttempted
_databases.Received(1).Toggle("c.db");
_databases.Received(1).Toggle("d.db");
_databases.Received(1).Toggle("e.db");
_errorBanners.Received(1).ReportError("Failed to Update Database", Arg.Is<string>(m => m.Contains("c.db", StringComparison.Ordinal)));
_errorBanners.Received(1).ReportError("Failed to Update Database", Arg.Is<string>(m => m.Contains("e.db", StringComparison.Ordinal)));
_errorBanners.Received(1).ReportError("Failed to Update Database", Arg.Is<string>(m => m != null && m.Contains("c.db", StringComparison.Ordinal)));
_errorBanners.Received(1).ReportError("Failed to Update Database", Arg.Is<string>(m => m != null && m.Contains("e.db", StringComparison.Ordinal)));
_errorBanners.Received(2).ReportError("Failed to Update Database", Arg.Any<string>());
}

Expand Down Expand Up @@ -136,7 +136,7 @@ public async Task ImportAsync_CallbackReturnsTrue_FileNotAddedToSkipSet()

await _databases.Received(1).ImportAsync(
Arg.Any<IEnumerable<string>>(),
Arg.Is<IReadOnlySet<string>>(s => !s.Contains("exists.db")),
Arg.Is<IReadOnlySet<string>>(s => s != null && !s.Contains("exists.db")),
Arg.Any<CancellationToken>());
}

Expand All @@ -158,7 +158,7 @@ Task<bool> ThrowingCallback(string _, CancellationToken __) =>
// Conflict defaults to Skip because overwriting is riskier than importing nothing.
await _databases.Received(1).ImportAsync(
Arg.Any<IEnumerable<string>>(),
Arg.Is<IReadOnlySet<string>>(s => s.Contains("exists.db")),
Arg.Is<IReadOnlySet<string>>(s => s != null && s.Contains("exists.db")),
Arg.Any<CancellationToken>());
_errorBanners.DidNotReceiveWithAnyArgs().ReportError(null!, null!);
}
Expand All @@ -175,7 +175,7 @@ public async Task ImportAsync_DatabaseServiceThrows_RoutesErrorBannerAndReturnsN
var sut = CreateSut();
var outcome = await sut.ImportAsync(cancellationToken: Ct);

_errorBanners.Received(1).ReportError("Import Failed", Arg.Is<string>(m => m.Contains("boom", StringComparison.Ordinal)));
_errorBanners.Received(1).ReportError("Import Failed", Arg.Is<string>(m => m != null && m.Contains("boom", StringComparison.Ordinal)));
_infoBanners.DidNotReceiveWithAnyArgs().ReportInfoBanner(null!, null!, default);
Assert.Equal(ImportOutcome.None, outcome);
}
Expand Down Expand Up @@ -218,7 +218,7 @@ public async Task ImportAsync_LongRunningImport_PostOpRoutesDirectlyToInfoBanner

_infoBanners.Received(1).ReportInfoBanner(
"Import Successful",
Arg.Is<string>(m => m.Contains("1 database has successfully been imported", StringComparison.Ordinal)),
Arg.Is<string>(m => m != null && m.Contains("1 database has successfully been imported", StringComparison.Ordinal)),
BannerSeverity.Info);
Assert.Equal(1, outcome.ImportedCount);
}
Expand Down Expand Up @@ -253,7 +253,7 @@ public async Task ImportAsync_NullCallbackWithExistingConflict_FileAddedToSkipSe

await _databases.Received(1).ImportAsync(
Arg.Any<IEnumerable<string>>(),
Arg.Is<IReadOnlySet<string>>(s => s.Contains("exists.db")),
Arg.Is<IReadOnlySet<string>>(s => s != null && s.Contains("exists.db")),
Arg.Any<CancellationToken>());
}

Expand Down Expand Up @@ -306,7 +306,7 @@ public async Task ImportAsync_SameBasenameSourcesInOneBatch_AddsNameToSkipSet()

await _databases.Received(1).ImportAsync(
Arg.Any<IEnumerable<string>>(),
Arg.Is<IReadOnlySet<string>>(skipFileNames => skipFileNames.Contains("A.db")),
Arg.Is<IReadOnlySet<string>>(skipFileNames => skipFileNames != null && skipFileNames.Contains("A.db")),
Arg.Any<CancellationToken>());
}

Expand Down Expand Up @@ -379,7 +379,7 @@ public async Task ImportAsync_ZeroImportedWithFailures_RoutesErrorBannerNotInfoB
var sut = CreateSut();
var outcome = await sut.ImportAsync(cancellationToken: Ct);

_errorBanners.Received(1).ReportError("Import Failed", Arg.Is<string>(m => m.Contains("A.db (bad)", StringComparison.Ordinal)));
_errorBanners.Received(1).ReportError("Import Failed", Arg.Is<string>(m => m != null && m.Contains("A.db (bad)", StringComparison.Ordinal)));
_infoBanners.DidNotReceiveWithAnyArgs().ReportInfoBanner(null!, null!, default);
Assert.Equal(0, outcome.ImportedCount);
Assert.False(outcome.DatabaseStateChanged);
Expand All @@ -398,7 +398,7 @@ public async Task ImportPathsAsync_EnableOnImportFalse_ImportsGivenPathsWithoutP
Assert.Equal(1, outcome.ImportedCount);
await _filePicker.DidNotReceiveWithAnyArgs().PickMultipleAsync(null!, null!);
await _databases.Received(1).ImportAsync(
Arg.Is<IEnumerable<string>>(paths => paths.SequenceEqual(new[] { @"C:\out\A.db" })),
Arg.Is<IEnumerable<string>>(paths => paths != null && paths.SequenceEqual(new[] { @"C:\out\A.db" })),
Arg.Any<IReadOnlySet<string>>(),
Arg.Any<CancellationToken>());
_databases.DidNotReceiveWithAnyArgs().Toggle(null!);
Expand Down Expand Up @@ -462,7 +462,7 @@ public async Task RemoveDatabaseAsync_DatabaseThrowsAfterSnapshotPopulated_Snaps
_logReload.Received(1).ReopenAfterDatabaseRemoval(Arg.Any<IReadOnlyList<LogReopenInfo>>());
_errorBanners.Received(1).ReportError(
"Failed to Remove Database",
Arg.Is<string>(m => m.Contains("removal failed mid-flight", StringComparison.Ordinal)));
Arg.Is<string>(m => m != null && m.Contains("removal failed mid-flight", StringComparison.Ordinal)));
}

[Fact]
Expand Down Expand Up @@ -601,7 +601,7 @@ public async Task RemoveDatabaseAsync_SuccessWithClosedLogs_ReopensLogsAndReport
Assert.True(outcome.Removed);
Assert.True(outcome.LogsReopened);
_logReload.Received(1).ReopenAfterDatabaseRemoval(
Arg.Is<IReadOnlyList<LogReopenInfo>>(l => l.Count == 1 && l[0].Name == "Application"));
Arg.Is<IReadOnlyList<LogReopenInfo>>(l => l != null && l.Count == 1 && l[0].Name == "Application"));
_errorBanners.DidNotReceiveWithAnyArgs().ReportError(null!, null!);
}

Expand Down Expand Up @@ -644,7 +644,7 @@ public async Task UpgradeDatabaseAsync_DatabaseThrows_ReportsErrorBannerAndClear
Assert.Equal(2, stateChangeCount);
_errorBanners.Received(1).ReportError(
"Database Upgrade Failed",
Arg.Is<string>(m => m.Contains("upgrade body failed", StringComparison.Ordinal)));
Arg.Is<string>(m => m != null && m.Contains("upgrade body failed", StringComparison.Ordinal)));
}

[Fact]
Expand Down Expand Up @@ -691,8 +691,8 @@ public async Task UpgradeDatabaseAsync_ResultFailedPopulated_ReportsOneErrorBann
var sut = CreateSut();
await sut.UpgradeDatabaseAsync("a.db", cancellationToken: Ct);

_errorBanners.Received(1).ReportError("Database Upgrade Failed", Arg.Is<string>(m => m.Contains("a.db", StringComparison.Ordinal) && m.Contains("schema-mismatch", StringComparison.Ordinal)));
_errorBanners.Received(1).ReportError("Database Upgrade Failed", Arg.Is<string>(m => m.Contains("b.db", StringComparison.Ordinal) && m.Contains("io-error", StringComparison.Ordinal)));
_errorBanners.Received(1).ReportError("Database Upgrade Failed", Arg.Is<string>(m => m != null && m.Contains("a.db", StringComparison.Ordinal) && m.Contains("schema-mismatch", StringComparison.Ordinal)));
_errorBanners.Received(1).ReportError("Database Upgrade Failed", Arg.Is<string>(m => m != null && m.Contains("b.db", StringComparison.Ordinal) && m.Contains("io-error", StringComparison.Ordinal)));
}

[Theory]
Expand Down Expand Up @@ -735,7 +735,7 @@ public async Task UpgradeDatabaseAsync_StateChangedSubscriberThrows_StateStillCl
Assert.False(sut.IsAnyUpgradeInFlight);
Assert.False(sut.IsUpgradeInFlight("a.db"));
await _databases.Received(1).UpgradeBatchAsync(
Arg.Is<IReadOnlyList<string>>(l => l.Count == 1 && l[0] == "a.db"),
Arg.Is<IReadOnlyList<string>>(l => l != null && l.Count == 1 && l[0] == "a.db"),
Arg.Any<UpgradeProgressScope>(),
Arg.Any<CancellationToken>());
}
Expand Down Expand Up @@ -769,7 +769,7 @@ public async Task UpgradeDatabaseAsync_Success_TracksInFlightStateAndRaisesEnter
Assert.False(wasInFlightDuringExit, "Exit event should fire after file is removed");
Assert.False(sut.IsAnyUpgradeInFlight);
await _databases.Received(1).UpgradeBatchAsync(
Arg.Is<IReadOnlyList<string>>(l => l.Count == 1 && l[0] == "a.db"),
Arg.Is<IReadOnlyList<string>>(l => l != null && l.Count == 1 && l[0] == "a.db"),
UpgradeProgressScope.ManageDatabasesTriggered,
Arg.Any<CancellationToken>());
}
Expand Down Expand Up @@ -842,8 +842,8 @@ public async Task UpgradeDatabasesAsync_PartialFailure_SurfacesEachFailureToErro
var actual = await sut.UpgradeDatabasesAsync(["a.db", "b.db", "c.db"], UpgradeProgressScope.ManageDatabasesTriggered, Ct);

Assert.Same(result, actual);
_errorBanners.Received(1).ReportError("Database Upgrade Failed", Arg.Is<string>(s => s.Contains("b.db") && s.Contains("schema mismatch")));
_errorBanners.Received(1).ReportError("Database Upgrade Failed", Arg.Is<string>(s => s.Contains("c.db") && s.Contains("io error")));
_errorBanners.Received(1).ReportError("Database Upgrade Failed", Arg.Is<string>(s => s != null && s.Contains("b.db") && s.Contains("schema mismatch")));
_errorBanners.Received(1).ReportError("Database Upgrade Failed", Arg.Is<string>(s => s != null && s.Contains("c.db") && s.Contains("io error")));
}

[Fact]
Expand Down Expand Up @@ -893,7 +893,7 @@ public async Task UpgradeDatabasesAsync_ServiceThrows_FallbackReturnedAndInFligh
Assert.NotNull(result);
Assert.Empty(result.Succeeded);
Assert.False(sut.IsAnyUpgradeInFlight);
_errorBanners.Received(1).ReportError("Database Upgrade Failed", Arg.Is<string>(s => s.Contains("disk full")));
_errorBanners.Received(1).ReportError("Database Upgrade Failed", Arg.Is<string>(s => s != null && s.Contains("disk full")));
}

private static DatabaseEntry CreateEntry(
Expand Down
Loading
Loading