Summary
Test assets using <PackageReference Include="MSTest" Version="$MSTestVersion$" /> (≈20 csprojs in test/IntegrationTests/) can fail to restore with NU1102 when the local-build chain demands a Microsoft.Testing.Platform prerelease that isn't in any of the test-asset's configured feeds.
Concrete repro: see the MSTestJUnitReportMTPRetry asset in test/IntegrationTests/MSTest.Acceptance.IntegrationTests/JUnitReportTests.cs — failed during PR #9029 CI runs and several previous builds.
Failure chain
- Test-asset csproj:
<PackageReference Include="MSTest" Version="$MSTestVersion$" /> → resolves to local-built MSTest 4.3.0-ci.
MSTest 4.3.0-ci's packed nuspec hardcodes:
<dependency id="Microsoft.Testing.Extensions.CodeCoverage" version="18.9.0-preview.26309.4" />
(Pinned via eng/Versions.props → MicrosoftTestingExtensionsCodeCoverageVersion.)
Microsoft.Testing.Extensions.CodeCoverage 18.9.0-preview.26309.4 lives only on the dotnet-tools feed. Its nuspec transitively demands:
<dependency id="Microsoft.Testing.Platform" version="2.3.0-preview.26308.7" />
- Test-asset's
NuGet.config (TestAsset.GetNuGetConfig) lists 6 feeds — none includes dotnet-tools. The closest matches:
local-shipping has MTP 2.3.0-ci — sorts below 2.3.0-preview.26308.7 in SemVer 2.0 (c < p).
dotnet-public maxes out at MTP 2.2.3.
- NuGet emits NU1102 for MTP. The error misleadingly points at the local feed instead of the actual upstream constraint.
Root cause
Two-part design weakness in the test-asset infrastructure:
- No exact-match pinning:
Version="$X$" is interpreted by NuGet as [$X$, ) — open-ended minimum. Drift to higher prereleases from any configured feed is technically allowed.
- No local-folder priority:
packageSourceMapping in the generated NuGet.config uses <package pattern="*"/> for every feed — no priority for local. There is no isolation of testfx-family packages.
Combined with MSTest's packed nuspec hardcoding a CodeCoverage prerelease that only exists on dotnet-tools, this creates a fragile resolution chain that explodes for any test asset using the MSTest meta package.
Proposed fixes (mix and match)
A. Add dotnet-tools to TestAsset's default NuGet.config
Smallest mechanical change; mirrors the repo's top-level NuGet.config. Restores parity with the main build's restore chain.
<add key="local-tmp-packages" value="..." />
<add key="dotnet-public" value="..." />
+<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
<add key="dotnet10" value="..." />
<add key="dotnet11" value="..." />
B. Exact-match pinning in test-asset csprojs
Use Version="[$X$]" everywhere. Hardens against version drift on direct PackageReferences.
- <PackageReference Include="MSTest" Version="$MSTestVersion$" />
+ <PackageReference Include="MSTest" Version="[$MSTestVersion$]" />
C. Per-package packageSourceMapping
Force testfx-family packages to come only from local feeds (and dotnet-tools for CodeCoverage previews):
<packageSource key="local-shipping">
<package pattern="Microsoft.Testing.*" />
<package pattern="MSTest*" />
</packageSource>
<packageSource key="dotnet-tools">
<package pattern="Microsoft.Testing.Extensions.CodeCoverage" />
</packageSource>
<packageSource key="dotnet-public">
<package pattern="*" />
</packageSource>
D. Downgrade CodeCoverage dep in MSTest's packed nuspec to nearest stable
Cleanest at the source — pin to 18.8.0 (stable, on dotnet-public) for the packed metadata while still consuming the preview at build time. Requires coordinating with the VSTest CodeCoverage release train and verifying no behavioral regression.
Recommended path
I'd suggest A + B + C combined:
- A restores feed parity (smallest change, instant relief).
- B prevents direct-ref drift (general hardening).
- C makes future failures fail-fast with a clearer error message.
D is the right long-term solution but is a larger conversation about how MSTest's nuspec dependencies should be versioned (always-stable vs. always-preview-matched).
Related
Summary
Test assets using
<PackageReference Include="MSTest" Version="$MSTestVersion$" />(≈20 csprojs intest/IntegrationTests/) can fail to restore with NU1102 when the local-build chain demands aMicrosoft.Testing.Platformprerelease that isn't in any of the test-asset's configured feeds.Concrete repro: see the
MSTestJUnitReportMTPRetryasset intest/IntegrationTests/MSTest.Acceptance.IntegrationTests/JUnitReportTests.cs— failed during PR #9029 CI runs and several previous builds.Failure chain
<PackageReference Include="MSTest" Version="$MSTestVersion$" />→ resolves to local-builtMSTest 4.3.0-ci.MSTest 4.3.0-ci's packed nuspec hardcodes:eng/Versions.props→MicrosoftTestingExtensionsCodeCoverageVersion.)Microsoft.Testing.Extensions.CodeCoverage 18.9.0-preview.26309.4lives only on thedotnet-toolsfeed. Its nuspec transitively demands:NuGet.config(TestAsset.GetNuGetConfig) lists 6 feeds — none includesdotnet-tools. The closest matches:local-shippinghasMTP 2.3.0-ci— sorts below2.3.0-preview.26308.7in SemVer 2.0 (c<p).dotnet-publicmaxes out atMTP 2.2.3.Root cause
Two-part design weakness in the test-asset infrastructure:
Version="$X$"is interpreted by NuGet as[$X$, )— open-ended minimum. Drift to higher prereleases from any configured feed is technically allowed.packageSourceMappingin the generatedNuGet.configuses<package pattern="*"/>for every feed — no priority for local. There is no isolation of testfx-family packages.Combined with
MSTest's packed nuspec hardcoding aCodeCoverageprerelease that only exists ondotnet-tools, this creates a fragile resolution chain that explodes for any test asset using theMSTestmeta package.Proposed fixes (mix and match)
A. Add
dotnet-toolsto TestAsset's defaultNuGet.configSmallest mechanical change; mirrors the repo's top-level
NuGet.config. Restores parity with the main build's restore chain.<add key="local-tmp-packages" value="..." /> <add key="dotnet-public" value="..." /> +<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" /> <add key="dotnet10" value="..." /> <add key="dotnet11" value="..." />B. Exact-match pinning in test-asset csprojs
Use
Version="[$X$]"everywhere. Hardens against version drift on direct PackageReferences.C. Per-package
packageSourceMappingForce testfx-family packages to come only from local feeds (and
dotnet-toolsfor CodeCoverage previews):D. Downgrade
CodeCoveragedep inMSTest's packed nuspec to nearest stableCleanest at the source — pin to
18.8.0(stable, ondotnet-public) for the packed metadata while still consuming the preview at build time. Requires coordinating with the VSTestCodeCoveragerelease train and verifying no behavioral regression.Recommended path
I'd suggest A + B + C combined:
D is the right long-term solution but is a larger conversation about how MSTest's nuspec dependencies should be versioned (always-stable vs. always-preview-matched).
Related
mainbuild741048f7(internal AzDO build2996915).