diff --git a/.copilot/skills/apex-migration/SKILL.md b/.copilot/skills/apex-migration/SKILL.md
new file mode 100644
index 00000000000..f57411d17e4
--- /dev/null
+++ b/.copilot/skills/apex-migration/SKILL.md
@@ -0,0 +1,295 @@
+---
+name: apex-migration
+description: >-
+ Migrate NuGet PowerShell E2E tests to C# Apex tests. Use this skill whenever the user asks to
+ migrate, convert, or port a PowerShell end-to-end test from test/EndToEnd/tests/ to an Apex test
+ in test/NuGet.Tests.Apex/. Also trigger when the user mentions "Apex test", "migrate PS test",
+ "E2E test migration", "PMC test", or references any PowerShell test function like
+ Install-PackageTest or Update-PackageTest and wants it rewritten in C#. Even if the user just
+ says "migrate this test" while looking at a PS E2E file, use this skill.
+---
+
+# Migrating PowerShell E2E Tests to Apex Tests
+
+PowerShell E2E tests live in `test/EndToEnd/tests/`. Apex tests live in
+`test/NuGet.Tests.Apex/NuGet.Tests.Apex/NuGetEndToEndTests/`. The goal is to migrate PS tests
+to C# Apex tests that run the **exact same scenario**, then remove the PS test function.
+
+## Workflow
+
+1. **Read the PS test** — understand what it does: which project type, which PMC commands, what assertions.
+2. **Pick the right Apex file** — match the scenario to an existing test class (see File Placement below).
+3. **Translate** — use the mappings in this skill to convert each PS construct to its Apex equivalent.
+4. **Verify** — run `get_errors` or build the Apex project to confirm it compiles cleanly.
+5. **Remove the PS function** — delete the migrated function from the PS test file.
+6. **If already covered** — if an existing Apex test already covers the same scenario, just delete the PS test. No new Apex test needed.
+7. **Update this skill** — if you discovered new mappings, gotchas, or corrections, add them to the appropriate section of this file (e.g., new rows in the mapping tables, new bullets in Common gotchas).
+
+## File placement
+
+Choose the target file by **interaction surface**, not project type:
+
+| Interaction surface | Apex file |
+|---|---|
+| PMC commands (Install-Package, Update-Package, etc.) | `NuGetConsoleTestCase.cs` |
+| NuGet UI / Package Manager dialog | `NuGetUITestCase.cs` |
+| IVsPackageInstaller / IVsServices API | `IVsServicesTestCase.cs` |
+| Sync/binding redirect scenarios | `SyncPackageTestCase.cs` |
+| Audit / vulnerability scenarios | `NuGetAuditTests.cs` |
+| .NET Core project-creation / restore / source-mapping | `NetCoreProjectTestCase.cs` |
+
+PMC tests for PackageReference projects still go in `NuGetConsoleTestCase.cs` — the deciding
+factor is whether the test exercises the PMC console, not the project's package management style.
+
+## Template mapping
+
+| PowerShell function | Apex `ProjectTemplate` | Package management | Verified |
+|---|---|---|---|
+| `New-ConsoleApplication` | `ProjectTemplate.ConsoleApplication` | packages.config | ✅ |
+| `New-ClassLibrary` | `ProjectTemplate.ClassLibrary` | packages.config | ✅ |
+| `New-WebSite` | `ProjectTemplate.WebSiteEmpty` | packages.config | ✅ |
+| `New-WebApplication` | `ProjectTemplate.WebApplicationEmpty` | packages.config | ❌ |
+| `New-WPFApplication` | `ProjectTemplate.WPFApplication` | packages.config | ❌ |
+| `New-MvcApplication` | `ProjectTemplate.WebApplicationEmptyMvc` | packages.config | ❌ |
+| `New-FSharpLibrary` | `ProjectTemplate.FSharpLibrary` | PackageReference | ❌ |
+| `New-NetCoreConsoleApp` | `ProjectTemplate.NetCoreConsoleApp` | PackageReference | ✅ |
+| `New-NetStandardClassLib` | `ProjectTemplate.NetStandardClassLib` | PackageReference | ✅ |
+
+| PowerShell function | Apex equivalent |
+|---|---|
+| `New-SolutionFolder 'Name'` | `testContext.SolutionService.AddSolutionFolder("Name")` |
+
+The package management style determines which assertion methods to use — packages.config projects
+use `AssertPackageInPackagesConfig`, while PackageReference projects use `AssertPackageInAssetsFile`.
+
+> **Note:** This table covers the most common PS project factories. Some PS tests use specialized
+> factories like `New-ClassLibraryNET46`, `New-BuildIntegratedProj`, `New-UwpPackageRefClassLibrary`,
+> or `New-NetCoreConsoleMultipleTargetFrameworksApp`. These don't have a 1:1 `ProjectTemplate` enum
+> value — check the Apex `ProjectTemplate` enum and existing tests for the closest match, or create
+> a standard template and modify the csproj afterward (e.g., for multi-targeting).
+
+## Command execution
+
+| Scenario | Apex API |
+|---|---|
+| Standard install with `-Version` | `nugetConsole.InstallPackageFromPMC(packageName, packageVersion)` |
+| Install with extra flags (`-Source`, `-WhatIf`, `-IgnoreDependencies`) | `nugetConsole.Execute($"Install-Package {packageName} -ProjectName {project.Name} -Source {source}")` |
+| Standard uninstall | `nugetConsole.UninstallPackageFromPMC(packageName)` |
+| Standard update with `-Version` | `nugetConsole.UpdatePackageFromPMC(packageName, packageVersion)` |
+| Update with `-Safe`, `-Reinstall`, etc. | `nugetConsole.Execute($"Update-Package {packageName} -Safe")` |
+| Any raw PMC command | `nugetConsole.Execute(command)` |
+
+**Key rule:** Both `InstallPackageFromPMC()` and `UpdatePackageFromPMC()` always inject `-Version`.
+If the original PS test does **not** use `-Version`, use `Execute()` with the raw command string
+instead — using the helper changes the semantics.
+
+**PowerShell session state is accessible.** `nugetConsole.Execute()` runs in a live PMC PowerShell
+session. It can execute **any** PowerShell command, not just NuGet commands. This means PS session
+state — global variables (`$global:InstallVar`), registered functions
+(`Test-Path function:\Get-World`), environment checks — can all be queried and asserted via
+`Execute()` + `IsMessageFoundInPMC()`. Do not skip tests just because they assert PS session state.
+
+## Assertion mapping
+
+| PowerShell assertion | Apex equivalent |
+|---|---|
+| `Assert-Package $p PackageName Version` (packages.config) | `CommonUtility.AssertPackageInPackagesConfig(VisualStudio, testContext.Project, packageName, version, Logger)` |
+| `Assert-Package $p PackageName` (no version, packages.config) | `CommonUtility.AssertPackageInPackagesConfig(VisualStudio, testContext.Project, packageName, Logger)` |
+| `Assert-Package $p PackageName Version` (PackageReference) | `CommonUtility.AssertPackageInAssetsFile(VisualStudio, testContext.Project, packageName, version, Logger)` |
+| `Assert-Throws { ... } $expectedMessage` | `nugetConsole.IsMessageFoundInPMC(expectedMessage)` — PMC errors appear as text, not C# exceptions |
+| `Assert-Null (Get-ProjectPackage ...)` / not installed | `CommonUtility.AssertPackageNotInPackagesConfig(VisualStudio, testContext.Project, packageName, Logger)` |
+| `Assert-NoPackage $p PackageName Version` (PackageReference) | `CommonUtility.AssertPackageNotInAssetsFile(VisualStudio, testContext.Project, packageName, version, Logger)` |
+| `Assert-PackageReference $p PackageName Version` | `CommonUtility.AssertPackageReferenceExists(VisualStudio, testContext.Project, packageName, version, Logger)` |
+| `Assert-NoPackageReference $p PackageName` | `CommonUtility.AssertPackageReferenceDoesNotExist(VisualStudio, testContext.Project, packageName, Logger)` |
+
+## Package sources
+
+| PowerShell source | Apex equivalent |
+|---|---|
+| `$context.RepositoryRoot` / `$context.RepositoryPath` | `testContext.PackageSource` — create packages with `CommonUtility.CreatePackageInSourceAsync()` |
+| No `-Source` (uses nuget.org) | Create a local package with `CommonUtility.CreatePackageInSourceAsync(testContext.PackageSource, ...)` — never depend on nuget.org |
+| Hardcoded invalid sources (`http://example.com`, `ftp://...`) | Use the same hardcoded strings directly |
+
+### Creating test packages
+
+For simple packages:
+```csharp
+await CommonUtility.CreatePackageInSourceAsync(testContext.PackageSource, packageName, packageVersion);
+```
+
+For packages with dependencies:
+```csharp
+await CommonUtility.CreateDependenciesPackageInSourceAsync(
+ testContext.PackageSource, packageName, packageVersion, dependencyName, dependencyVersion);
+```
+
+For .NET Framework-specific packages:
+```csharp
+await CommonUtility.CreateNetFrameworkPackageInSourceAsync(
+ testContext.PackageSource, packageName, packageVersion);
+```
+
+## NuGet.Config manipulation
+
+PS tests that use `Get-VSComponentModel` + `ISettings` to modify NuGet config at runtime can be
+migrated by pre-configuring `SimpleTestPathContext` before passing it to `ApexTestContext`.
+
+**Via Settings API** (preferred):
+```csharp
+using var simpleTestPathContext = new SimpleTestPathContext();
+simpleTestPathContext.Settings.AddSource("PrivateRepo", privatePath);
+
+using var testContext = new ApexTestContext(VisualStudio, projectTemplate, Logger,
+ simpleTestPathContext: simpleTestPathContext);
+```
+
+**Via raw config file** (for settings not covered by the API like `dependencyVersion` or `bindingRedirects`):
+```csharp
+using var simpleTestPathContext = new SimpleTestPathContext();
+File.WriteAllText(simpleTestPathContext.NuGetConfig,
+ $@"
+
+
+
+
+
+
+
+
+");
+
+using var testContext = new ApexTestContext(VisualStudio, projectTemplate, Logger,
+ simpleTestPathContext: simpleTestPathContext);
+```
+
+## Test structure patterns
+
+### Error-path tests (no package creation needed) — synchronous
+
+```csharp
+[TestMethod]
+[Timeout(DefaultTimeout)]
+public void DescriptiveTestName_Fails()
+{
+ using var testContext = new ApexTestContext(VisualStudio, ProjectTemplate.ConsoleApplication, Logger);
+
+ var packageName = "Rules";
+ var source = @"c:\temp\data";
+ var expectedMessage = $"Unable to find package '{packageName}' at source '{source}'. Source not found.";
+
+ var nugetConsole = GetConsole(testContext.Project);
+ nugetConsole.Execute($"Install-Package {packageName} -ProjectName {testContext.Project.Name} -Source {source}");
+
+ Assert.IsTrue(
+ nugetConsole.IsMessageFoundInPMC(expectedMessage),
+ $"Expected error message was not found in PMC output. Actual output: {nugetConsole.GetText()}");
+}
+```
+
+### Success-path tests (need package creation) — async
+
+```csharp
+[TestMethod]
+[Timeout(DefaultTimeout)]
+public async Task DescriptiveTestNameAsync()
+{
+ using var testContext = new ApexTestContext(VisualStudio, ProjectTemplate.ConsoleApplication, Logger);
+
+ var packageName = "TestPackage";
+ var packageVersion = "1.0.0";
+ await CommonUtility.CreatePackageInSourceAsync(testContext.PackageSource, packageName, packageVersion);
+
+ var nugetConsole = GetConsole(testContext.Project);
+ nugetConsole.InstallPackageFromPMC(packageName, packageVersion);
+
+ CommonUtility.AssertPackageInPackagesConfig(VisualStudio, testContext.Project, packageName, packageVersion, Logger);
+}
+```
+
+### Data-driven tests (multiple project templates)
+
+When the same scenario applies to multiple project types, use `[DataTestMethod]`:
+```csharp
+[DataTestMethod]
+[DataRow(ProjectTemplate.NetCoreConsoleApp)]
+[DataRow(ProjectTemplate.NetStandardClassLib)]
+[Timeout(DefaultTimeout)]
+public async Task InstallPackageForMultipleProjectTypesAsync(ProjectTemplate projectTemplate)
+{
+ using var testContext = new ApexTestContext(VisualStudio, projectTemplate, Logger);
+ // ... test body
+}
+```
+
+### Multi-targeted project tests
+
+To create a multi-targeted project, modify the csproj after project creation:
+```csharp
+using var testContext = new ApexTestContext(VisualStudio, ProjectTemplate.NetCoreConsoleApp, Logger);
+// Modify csproj to multi-target via XDocument:
+// change to net8.0;netstandard2.0
+```
+
+## Style rules
+
+- Use `using var` (inline using declaration), not `using (var ...) { }`.
+- Place migrated tests before the static helper methods (`GetNetCoreTemplates`, etc.) in the file.
+- Method names: `{Action}FromPMC{Scenario}[_Fails|Async]`. Suffix with `_Fails` for error tests,
+ `Async` for async tests.
+- Always include `[Timeout(DefaultTimeout)]`.
+- Always include `nugetConsole.GetText()` in assertion failure messages for diagnostics.
+- Use `var` for local variables except value tuples (use decomposed names).
+- The test class inherits `SharedVisualStudioHostTestClass` which provides `VisualStudio` and `Logger`.
+- Get PMC console via `GetConsole(testContext.Project)` helper method in the test class.
+
+## Tests that should NOT be migrated
+
+Skip PS tests that:
+- Use `Assert-BindingRedirect` — binding redirect tests are already `[SkipTest]` in PS and not
+ worth migrating.
+- Depend on **DTE project hierarchy semantics** (e.g., `Get-ProjectItem` to check tree structure,
+ parent/child relationships). However, if the PS test only uses `Get-ProjectItem` /
+ `Get-ProjectItemPath` to verify a **file exists on disk**, migrate it using filesystem assertions
+ instead: `File.Exists(path)`, XML reads on the project file, or
+ `CommonUtility.WaitForFileExists()`.
+
+## After migration checklist
+
+1. ✅ Remove the migrated function from the PS test file.
+2. ✅ If a PS test is already covered by an existing Apex test (duplicate), just delete the PS
+ test — no new Apex test needed.
+3. ✅ Build the Apex project or run `get_errors` to verify it compiles cleanly.
+4. ✅ Verify assertion methods match the project's package management style
+ (packages.config vs PackageReference).
+
+## Common gotchas
+
+- **Console width**: PMC output assertions are text-sensitive. The Apex infrastructure forces
+ console width to 1024 to avoid wrapping issues.
+- **Restore timing**: After install/update operations, the Apex infrastructure handles waiting for
+ restore completion. You generally don't need explicit waits.
+- **nuget.org dependency**: PS tests that don't specify `-Source` implicitly use nuget.org. Always
+ replace this with local package creation via `CreatePackageInSourceAsync` — tests must not depend
+ on external feeds.
+- **`NuGetApexTestService` limitations**: It does NOT expose `ISolutionManager` or VS DTE project
+ item inspection. Only `IVsPackageInstaller`, `IVsSolutionRestoreStatusProvider`,
+ `IVsPackageUninstaller`, `IVsPathContextProvider2`, and `IVsUIShell` are available.
+- **IVs error-path tests**: `NuGetApexTestService.InstallPackage()` swallows
+ `InvalidOperationException` and logs it — it does NOT rethrow. For error-path IVs tests, assert
+ that the package was NOT installed (`AssertPackageNotInPackagesConfig`) rather than trying to
+ catch exceptions.
+- **Feature renaming**: Some PS tests use older feature names (e.g., "PackageNameSpace"). When
+ migrating, use the current feature name (e.g., "PackageSourceMapping") in test method names and
+ comments.
+- **IVs tests use `EnvDTE.Project`**: IVs API methods like `InstallPackage()` take
+ `project.UniqueName` (from `EnvDTE.Project`), not a `ProjectTestExtension`. Get it via
+ `VisualStudio.Dte.Solution.Projects.Item(1)`.
+- **`_pathContext` vs `testContext`**: `IVsServicesTestCase` uses a class-level
+ `SimpleTestPathContext _pathContext` (initialized in constructor), not per-test `ApexTestContext`.
+ PMC tests in `NuGetConsoleTestCase` use per-test `ApexTestContext`.
+- **Never overwrite `SimpleTestPathContext`'s NuGet.config**: Using `CreateConfigurationFile` to
+ write a full config replaces defaults like `globalPackagesFolder`, `fallbackPackageFolders`, and
+ `httpCacheFolder` — causing packages to pollute the user's real global packages folder. Instead
+ use `simpleTestPathContext.Settings.AddSource()` and `AddPackageSourceMapping()` to layer config
+ on top of the defaults.
+
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index 4759ce7542b..055e68b4375 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -29,152 +29,4 @@
- **TryCreate/TryGet patterns** — out params need `?`, callers use `!` after the success guard. Out parameters that are guaranteed non-null when the method returns true should be annotated with `[NotNullWhen(true)]`. Don't annotate `[NotNullWhen]` unless it's actually true for all code paths.
- **Work in batches** — group related files, fix source, fix cascading, build, repeat. If this means we need multiple pull requests for enabling nullable, that's fine. Don't try to do it all in one go.
-## Migrating PowerShell E2E Tests to Apex Tests
-### Overview
-
-PowerShell E2E tests live in `test/EndToEnd/tests/`. Apex tests live in `test/NuGet.Tests.Apex/NuGet.Tests.Apex/NuGetEndToEndTests/`. The goal is to migrate PS tests to C# Apex tests that run the **exact same scenario**, then remove the PS test function.
-
-### Template mapping
-
-| PowerShell function | Apex `ProjectTemplate` | Package management | Verified |
-|---|---|---|---|
-| `New-ConsoleApplication` | `ProjectTemplate.ConsoleApplication` | packages.config | ✅ |
-| `New-ClassLibrary` | `ProjectTemplate.ClassLibrary` | packages.config | ✅ |
-| `New-WebSite` | `ProjectTemplate.WebSiteEmpty` | packages.config | ✅ |
-| `New-WebApplication` | `ProjectTemplate.WebApplicationEmpty` | packages.config | ❌ |
-| `New-WPFApplication` | `ProjectTemplate.WPFApplication` | packages.config | ❌ |
-| `New-MvcApplication` | `ProjectTemplate.WebApplicationEmptyMvc` | packages.config | ❌ |
-| `New-FSharpLibrary` | `ProjectTemplate.FSharpLibrary` | PackageReference | ❌ |
-| `New-NetCoreConsoleApp` | `ProjectTemplate.NetCoreConsoleApp` | PackageReference | ✅ |
-| `New-NetStandardClassLib` | `ProjectTemplate.NetStandardClassLib` | PackageReference | ✅ |
-
-| PowerShell function | Apex equivalent |
-|---|---|
-| `New-SolutionFolder 'Name'` | `testContext.SolutionService.AddSolutionFolder("Name")` |
-
-### Command execution
-
-| Scenario | Apex API |
-|---|---|
-| Standard install with `-Version` | `nugetConsole.InstallPackageFromPMC(packageName, packageVersion)` |
-| Install with extra flags (`-Source`, `-WhatIf`, `-IgnoreDependencies`) | `nugetConsole.Execute($"Install-Package {packageName} -ProjectName {project.Name} -Source {source}")` |
-| Standard uninstall | `nugetConsole.UninstallPackageFromPMC(packageName)` |
-| Standard update | `nugetConsole.UpdatePackageFromPMC(packageName, packageVersion)` |
-| Any raw PMC command | `nugetConsole.Execute(command)` |
-
-**Rule:** If the PS test does not use `-Version`, use `Execute()` with the raw command string. `InstallPackageFromPMC()` always adds `-Version`.
-
-**Important:** `nugetConsole.Execute()` runs in a live PMC PowerShell session. It can execute **any** PowerShell command, not just NuGet commands. This means PS session state — global variables (`$global:InstallVar`), registered functions (`Test-Path function:\Get-World`), environment checks — can all be queried and asserted via `Execute()` + `IsMessageFoundInPMC()`. Do not skip tests just because they assert PS session state.
-
-### Assertion mapping
-
-| PowerShell assertion | Apex equivalent |
-|---|---|
-| `Assert-Package $p PackageName Version` (packages.config project) | `CommonUtility.AssertPackageInPackagesConfig(VisualStudio, testContext.Project, packageName, version, Logger)` |
-| `Assert-Package $p PackageName` (no version, packages.config) | `CommonUtility.AssertPackageInPackagesConfig(VisualStudio, testContext.Project, packageName, Logger)` |
-| `Assert-Package $p PackageName Version` (PackageReference project) | `CommonUtility.AssertPackageInAssetsFile(VisualStudio, testContext.Project, packageName, version, Logger)` |
-| `Assert-Throws { ... } $expectedMessage` | `nugetConsole.IsMessageFoundInPMC(expectedMessage)` — PMC errors appear as text, not C# exceptions |
-| `Assert-Null (Get-ProjectPackage ...)` / package not installed | `CommonUtility.AssertPackageNotInPackagesConfig(VisualStudio, testContext.Project, packageName, Logger)` |
-
-### Package sources
-
-| PowerShell source | Apex equivalent |
-|---|---|
-| `$context.RepositoryRoot` or `$context.RepositoryPath` | `testContext.PackageSource` — create packages with `CommonUtility.CreatePackageInSourceAsync()` |
-| No `-Source` (uses nuget.org) | Create a local package with `CommonUtility.CreatePackageInSourceAsync(testContext.PackageSource, ...)` — never depend on nuget.org |
-| Hardcoded invalid sources (`http://example.com`, `ftp://...`) | Use the same hardcoded strings directly |
-
-### NuGet.Config manipulation
-
-PS tests that use `Get-VSComponentModel` + `ISettings` to modify NuGet config at runtime can be migrated by pre-configuring `SimpleTestPathContext` before passing it to `ApexTestContext`.
-
-**Via Settings API** (preferred):
-```csharp
-using var simpleTestPathContext = new SimpleTestPathContext();
-simpleTestPathContext.Settings.AddSource("PrivateRepo", privatePath);
-// ... then pass it in:
-using var testContext = new ApexTestContext(VisualStudio, projectTemplate, Logger,
- simpleTestPathContext: simpleTestPathContext);
-```
-
-**Via raw config file** (for settings not covered by the API like `dependencyVersion` or `bindingRedirects`):
-```csharp
-using var simpleTestPathContext = new SimpleTestPathContext();
-File.WriteAllText(simpleTestPathContext.NuGetConfig,
- $@"
-
-
-
-
-
-
-
-
-");
-
-using var testContext = new ApexTestContext(VisualStudio, projectTemplate, Logger,
- simpleTestPathContext: simpleTestPathContext);
-```
-
-### Test structure patterns
-
-**Error-path tests** (no package creation needed) — synchronous:
-```csharp
-[TestMethod]
-[Timeout(DefaultTimeout)]
-public void DescriptiveTestName_Fails()
-{
- using var testContext = new ApexTestContext(VisualStudio, ProjectTemplate.ConsoleApplication, Logger);
-
- var packageName = "Rules";
- var source = @"c:\temp\data";
- var expectedMessage = $"Unable to find package '{packageName}' at source '{source}'. Source not found.";
-
- var nugetConsole = GetConsole(testContext.Project);
- nugetConsole.Execute($"Install-Package {packageName} -ProjectName {testContext.Project.Name} -Source {source}");
-
- Assert.IsTrue(
- nugetConsole.IsMessageFoundInPMC(expectedMessage),
- $"Expected error message was not found in PMC output. Actual output: {nugetConsole.GetText()}");
-}
-```
-
-**Success-path tests** (need package creation) — async:
-```csharp
-[TestMethod]
-[Timeout(DefaultTimeout)]
-public async Task DescriptiveTestNameAsync(/* or [DataTestMethod] with ProjectTemplate */)
-{
- using var testContext = new ApexTestContext(VisualStudio, ProjectTemplate.ConsoleApplication, Logger);
-
- var packageName = "TestPackage";
- var packageVersion = "1.0.0";
- await CommonUtility.CreatePackageInSourceAsync(testContext.PackageSource, packageName, packageVersion);
-
- var nugetConsole = GetConsole(testContext.Project);
- nugetConsole.InstallPackageFromPMC(packageName, packageVersion);
-
- CommonUtility.AssertPackageInPackagesConfig(VisualStudio, testContext.Project, packageName, packageVersion, Logger);
-}
-```
-
-### Style rules
-
-- Use `using var` (inline declaration), not `using (var ...) { }`.
-- Place migrated tests before the static helper methods (`GetNetCoreTemplates`, etc.) in the file.
-- Method names: `{Action}FromPMC{Scenario}[_Fails|Async]`. Suffix with `_Fails` for error tests, `Async` for async tests.
-- Always include `[Timeout(DefaultTimeout)]`.
-- Include `nugetConsole.GetText()` in assertion failure messages for diagnostics.
-
-### Tests that should NOT be migrated
-
-Skip PS tests that:
-- Use `Assert-BindingRedirect` — binding redirect tests are already `[SkipTest]` in PS and not worth migrating.
-- Use `Get-ProjectItem`, `Get-ProjectItemPath`, or other VS DTE project-item inspection not available in Apex.
-
-### After migration
-
-1. Remove the migrated function from the PS test file.
-2. If a PS test is already covered by an existing Apex test (duplicate), just delete the PS test — no new Apex test needed.
-3. Verify with `get_errors` that the Apex file compiles cleanly.
diff --git a/test/EndToEnd/tests/PackageNameSpaceTests.ps1 b/test/EndToEnd/tests/PackageNameSpaceTests.ps1
index 623f3f9c479..11ae04fec0c 100644
--- a/test/EndToEnd/tests/PackageNameSpaceTests.ps1
+++ b/test/EndToEnd/tests/PackageNameSpaceTests.ps1
@@ -117,477 +117,6 @@ function Test-PackageSourceMappingRestore-WithMultipleFeedsWithIdenticalPackages
}
}
-function Test-VsPackageInstallerServices-PackageSourceMappingInstall-WithSingleFeed-Succeed {
- [SkipTest('https://github.com/NuGet/Home/issues/12185')]
- param(
- $context
- )
-
- # Arrange
- $repoDirectory = $context.RepositoryRoot
- $nugetConfigPath = Join-Path $OutputPath 'nuget.config'
-
- $settingFileContent =@"
-
-
-
-
-
-
-
-
-
-
-
-
-"@
-
- try {
- # We have to create config file before creating solution, otherwise it's not effective for new solutions.
- $settingFileContent -f $repoDirectory | Out-File -Encoding "UTF8" $nugetConfigPath
-
- # $p = New-ConsoleApplication
- # Arrange
- $p = New-ClassLibrary
-
- # Act
- [API.Test.InternalAPITestHook]::InstallLatestPackageApi("SolutionLevelPkg", $false)
-
- # Assert
- Assert-Package $p SolutionLevelPkg 1.0.0
-
- $errorlist = Get-Errors
- Assert-AreEqual 0 $errorlist.Count
- }
- finally {
- Remove-Item $nugetConfigPath
- }
-}
-
-function Test-VsPackageInstallerServices-PackageSourceMappingInstall-WithSingleFeed-Fails {
- param(
- $context
- )
-
- # Arrange
- $repoDirectory = $context.RepositoryRoot
- $nugetConfigPath = Join-Path $OutputPath 'nuget.config'
-
- $settingFileContent =@"
-
-
-
-
-
-
-
-
-
-
-
-
-"@
-
- try {
- # We have to create config file before creating solution, otherwise it's not effective for new solutions.
- $settingFileContent -f $repoDirectory | Out-File -Encoding "UTF8" $nugetConfigPath
-
- # $p = New-ConsoleApplication
- # Arrange
- $p = New-ClassLibrary
-
- # Act & Assert
- # Even though SolutionLevelPkg package exist in $repoDirectory since package source mapping filter set SolutionLevelPkg can be restored only from SecretPackages repository so it'll fail.
- $exceptionMessage = "Exception calling `"InstallLatestPackageApi`" with `"2`" argument(s): `"Package 'SolutionLevelPkg 1.0.0' is not found in the following primary source(s): '"+ $repoDirectory + "'. Please verify all your online package sources are available (OR) package id, version are specified correctly.`""
- Assert-Throws { [API.Test.InternalAPITestHook]::InstallLatestPackageApi("SolutionLevelPkg", $false) } $exceptionMessage
- Assert-NoPackage $p SolutionLevelPkg 1.0.0
- }
- finally {
- Remove-Item $nugetConfigPath
- }
-}
-
-function Test-VsPackageInstallerServices-PackageSourceMappingInstall-WithMultipleFeedsWithIdenticalPackages-RestoresCorrectPackageWithSpecifiedVersion
-{
- [SkipTest('https://github.com/NuGet/Home/issues/12185')]
- param($context)
-
- # Arrange
- $repoDirectory = Join-Path $OutputPath "CustomPackages"
- $opensourceRepo = Join-Path $repoDirectory "opensourceRepo"
- $privateRepo = Join-Path $repoDirectory "privateRepo"
- $nugetConfigPath = Join-Path $OutputPath 'nuget.config'
-
- $settingFileContent =@"
-
-
-
-
-
-
-
-
-
-
-
-
-
-"@
- try {
- # We have to create config file before creating solution, otherwise it's not effective for new solutions.
- $settingFileContent -f $opensourceRepo,$privateRepo | Out-File -Encoding "UTF8" $nugetConfigPath
-
- $p = New-ConsoleApplication
-
- $projectDirectoryPath = $p.Properties.Item("FullPath").Value
- $packagesConfigPath = Join-Path $projectDirectoryPath 'packages.config'
- $projectDirectoryPath = $p.Properties.Item("FullPath").Value
- $solutionDirectory = Split-Path -Path $projectDirectoryPath -Parent
-
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $privateRepo "Thisisfromprivaterepo1.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "2.0.0" $privateRepo "Thisisfromprivaterepo2.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $opensourceRepo "Thisisfromopensourcerepo1.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "2.0.0" $opensourceRepo "Thisisfromopensourcerepo2.txt"
-
- # Act
- [API.Test.InternalAPITestHook]::InstallPackageApi("Contoso.MVC.ASP", "1.0.0")
-
- # Assert
- $packagesFolder = Join-Path $solutionDirectory "packages"
- $contosoNupkgFolder = Join-Path $packagesFolder "Contoso.MVC.ASP.1.0.0"
- Assert-PathExists(Join-Path $contosoNupkgFolder "Contoso.MVC.ASP.1.0.0.nupkg")
- # Make sure name squatting package from public repo not restored.
- $contentFolder = Join-Path $contosoNupkgFolder "content"
- Assert-PathExists(Join-Path $contentFolder "Thisisfromprivaterepo1.txt")
-
- $errorlist = Get-Errors
- Assert-AreEqual 0 $errorlist.Count
- }
- finally {
- Remove-Item -Recurse -Force $repoDirectory
- Remove-Item $nugetConfigPath
- }
-}
-
-function Test-VsPackageInstallerServices-PackageSourceMappingInstall-WithMultipleFeedsWithIdenticalPackages-RestoresCorrectPackageWithLatestVersion
-{
- [SkipTest('https://github.com/NuGet/Home/issues/12185')]
- param($context)
-
- # Arrange
- $repoDirectory = Join-Path $OutputPath "CustomPackages"
- $opensourceRepo = Join-Path $repoDirectory "opensourceRepo"
- $privateRepo = Join-Path $repoDirectory "privateRepo"
- $nugetConfigPath = Join-Path $OutputPath 'nuget.config'
-
- $settingFileContent =@"
-
-
-
-
-
-
-
-
-
-
-
-
-
-"@
- try {
- # We have to create config file before creating solution, otherwise it's not effective for new solutions.
- $settingFileContent -f $opensourceRepo,$privateRepo | Out-File -Encoding "UTF8" $nugetConfigPath
-
- $p = New-ConsoleApplication
-
- $projectDirectoryPath = $p.Properties.Item("FullPath").Value
- $packagesConfigPath = Join-Path $projectDirectoryPath 'packages.config'
- $projectDirectoryPath = $p.Properties.Item("FullPath").Value
- $solutionDirectory = Split-Path -Path $projectDirectoryPath -Parent
-
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $privateRepo "Thisisfromprivaterepo1.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "2.0.0" $privateRepo "Thisisfromprivaterepo2.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $opensourceRepo "Thisisfromopensourcerepo1.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "2.0.0" $opensourceRepo "Thisisfromopensourcerepo2.txt"
-
- # Act
- [API.Test.InternalAPITestHook]::InstallLatestPackageApi("Contoso.MVC.ASP", $false)
-
- # Assert
- $packagesFolder = Join-Path $solutionDirectory "packages"
- $contosoNupkgFolder = Join-Path $packagesFolder "Contoso.MVC.ASP.2.0.0"
- Assert-PathExists(Join-Path $contosoNupkgFolder "Contoso.MVC.ASP.2.0.0.nupkg")
- # Make sure name squatting package from public repo not restored.
- $contentFolder = Join-Path $contosoNupkgFolder "content"
- Assert-PathExists(Join-Path $contentFolder "Thisisfromprivaterepo2.txt")
-
- $errorlist = Get-Errors
- Assert-AreEqual 0 $errorlist.Count
- }
- finally {
- Remove-Item -Recurse -Force $repoDirectory
- Remove-Item $nugetConfigPath
- }
-}
-
-function Test-PC-PackageSourceMappingInstall-Succeed
-{
- [SkipTest('https://github.com/NuGet/Home/issues/12185')]
- param($context)
-
- # Arrange
- $nugetConfigPath = Join-Path $OutputPath 'nuget.config'
- $settingFileContent =@"
-
-
-
-
-
-
-
-
-
-
-
-
-"@
- try {
- # We have to create config file before creating solution, otherwise it's not effective for new solutions.
- $settingFileContent -f $context.RepositoryRoot | Out-File -Encoding "UTF8" $nugetConfigPath
-
- $p = New-ConsoleApplication
-
- # Act
- $p | Install-Package SolutionLevelPkg -Version 1.0
-
- # # Assert
- Assert-Package $p SolutionLevelPkg 1.0.0
- $errorlist = Get-Errors
- Assert-AreEqual 0 $errorlist.Count
- $warninglist = Get-Warnings
- Assert-AreEqual 0 $warninglist.Count
- }
- finally {
- Remove-Item $nugetConfigPath
- }
-}
-
-function Test-PC-PackageSourceMappingInstall-Fails
-{
- param($context)
-
- # Arrange
- $repoDirectory = $context.RepositoryRoot
- $privateRepo = Join-Path $repoDirectory "privateRepo"
-
- $nugetConfigPath = Join-Path $OutputPath 'nuget.config'
- $settingFileContent =@"
-
-
-
-
-
-
-
-
-
-
-
-
-
-"@
- try {
- # We have to create config file before creating solution, otherwise it's not effective for new solutions.
- $settingFileContent -f $context.RepositoryRoot,$privateRepo | Out-File -Encoding "UTF8" $nugetConfigPath
-
- $p = New-ConsoleApplication
-
- # Act & Assert
- # Even though SolutionLevelPkg package exist in $repoDirectory since package source mapping filter set SolutionLevelPkg can be restored only from PrivateRepository repository so it'll fail.
- $exceptionMessage = "Package 'SolutionLevelPkg 1.0' is not found in the following primary source(s): '" + $context.RepositoryRoot + "," + $privateRepo + "'. Please verify all your online package sources are available (OR) package id, version are specified correctly."
- Assert-Throws { $p | Install-Package SolutionLevelPkg -Version 1.0 } $exceptionMessage
- Assert-NoPackage $p SolutionLevelPkg 1.0.0
- }
- finally {
- Remove-Item $nugetConfigPath
- }
-}
-
-function Test-PC-PackageSourceMappingInstall-WithCorrectSourceOption-Succeed
-{
- [SkipTest('https://github.com/NuGet/Home/issues/12185')]
- param($context)
-
- # Arrange
- $repoDirectory = Join-Path $OutputPath "CustomPackages"
- $opensourceRepo = Join-Path $repoDirectory "opensourceRepo"
- $privateRepo = Join-Path $repoDirectory "privateRepo"
- $nugetConfigPath = Join-Path $OutputPath 'nuget.config'
-
- $settingFileContent =@"
-
-
-
-
-
-
-
-
-
-
-
-
-
-"@
- try {
- # We have to create config file before creating solution, otherwise it's not effective for new solutions.
- $settingFileContent -f $opensourceRepo,$privateRepo | Out-File -Encoding "UTF8" $nugetConfigPath
-
- $p = New-ConsoleApplication
- $projectDirectoryPath = $p.Properties.Item("FullPath").Value
- $packagesConfigPath = Join-Path $projectDirectoryPath 'packages.config'
- $projectDirectoryPath = $p.Properties.Item("FullPath").Value
- $solutionDirectory = Split-Path -Path $projectDirectoryPath -Parent
-
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $privateRepo "Thisisfromprivaterepo1.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $opensourceRepo "Thisisfromopensourcerepo1.txt"
-
- # Act
- $p | Install-Package Contoso.MVC.ASP -Source $privateRepo
-
- # Assert
- Assert-Package $p Contoso.MVC.ASP 1.0.0
- $packagesFolder = Join-Path $solutionDirectory "packages"
- $contosoNupkgFolder = Join-Path $packagesFolder "Contoso.MVC.ASP.1.0.0"
- Assert-PathExists(Join-Path $contosoNupkgFolder "Contoso.MVC.ASP.1.0.0.nupkg")
- # Make sure name squatting package from public repo not restored.
- $contentFolder = Join-Path $contosoNupkgFolder "content"
- Assert-PathExists(Join-Path $contentFolder "Thisisfromprivaterepo1.txt")
-
- $errorlist = Get-Errors
- Assert-AreEqual 0 $errorlist.Count
- }
- finally {
- Remove-Item -Recurse -Force $repoDirectory
- Remove-Item $nugetConfigPath
- }
-}
-
-function Test-PC-PackageSourceMappingInstall-WithWrongSourceOption-Fails
-{
- param($context)
-
- # Arrange
- $repoDirectory = Join-Path $OutputPath "CustomPackages"
- $opensourceRepo = Join-Path $repoDirectory "opensourceRepo"
- $privateRepo = Join-Path $repoDirectory "privateRepo"
- $nugetConfigPath = Join-Path $OutputPath 'nuget.config'
-
- $settingFileContent =@"
-
-
-
-
-
-
-
-
-
-
-
-
-
-"@
- try {
- # We have to create config file before creating solution, otherwise it's not effective for new solutions.
- $settingFileContent -f $opensourceRepo,$privateRepo | Out-File -Encoding "UTF8" $nugetConfigPath
-
- $p = New-ConsoleApplication
- $projectDirectoryPath = $p.Properties.Item("FullPath").Value
- $packagesConfigPath = Join-Path $projectDirectoryPath 'packages.config'
- $projectDirectoryPath = $p.Properties.Item("FullPath").Value
- $solutionDirectory = Split-Path -Path $projectDirectoryPath -Parent
-
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $privateRepo "Thisisfromprivaterepo1.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $opensourceRepo "Thisisfromopensourcerepo1.txt"
-
- # Act & Assert
- $exceptionMessage = "Package 'Contoso.MVC.ASP 1.0.0' is not found in the following primary source(s): '"+ $opensourceRepo + "'. Please verify all your online package sources are available (OR) package id, version are specified correctly."
- Assert-Throws { $p | Install-Package Contoso.MVC.ASP -Source $opensourceRepo } $exceptionMessage
- Assert-NoPackage $p SolutionLevelPkg 1.0.0
- }
- finally {
- Remove-Item -Recurse -Force $repoDirectory
- Remove-Item $nugetConfigPath
- }
-}
-
-function Test-PC-PackageSourceMappingUpdate-WithCorrectSourceOption-Succeed
-{
- [SkipTest('https://github.com/NuGet/Home/issues/12185')]
- param($context)
-
- # Arrange
- $repoDirectory = Join-Path $OutputPath "CustomPackages"
- $opensourceRepo = Join-Path $repoDirectory "opensourceRepo"
- $privateRepo = Join-Path $repoDirectory "privateRepo"
- $nugetConfigPath = Join-Path $OutputPath 'nuget.config'
-
- $settingFileContent =@"
-
-
-
-
-
-
-
-
-
-
-
-
-
-"@
- try {
- # We have to create config file before creating solution, otherwise it's not effective for new solutions.
- $settingFileContent -f $opensourceRepo,$privateRepo | Out-File -Encoding "UTF8" $nugetConfigPath
-
- $p = New-ConsoleApplication
- $projectDirectoryPath = $p.Properties.Item("FullPath").Value
- $packagesConfigPath = Join-Path $projectDirectoryPath 'packages.config'
- $projectDirectoryPath = $p.Properties.Item("FullPath").Value
- $solutionDirectory = Split-Path -Path $projectDirectoryPath -Parent
-
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $privateRepo "Thisisfromprivaterepo1.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "2.0.0" $privateRepo "Thisisfromprivaterepo2.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $opensourceRepo "Thisisfromopensourcerepo1.txt"
- CreateCustomTestPackage "Contoso.MVC.ASP" "1.0.0" $opensourceRepo "Thisisfromopensourcerepo2.txt"
-
- # Act
- $p | Install-Package Contoso.MVC.ASP -Version 1.0 -Source $privateRepo
- Assert-Package $p Contoso.MVC.ASP 1.0.0
-
- $p | Update-Package Contoso.MVC.ASP -Version 2.0 -Source $privateRepo
- Assert-Package $p Contoso.MVC.ASP 2.0.0
-
- # Assert
- $packagesFolder = Join-Path $solutionDirectory "packages"
- $contosoNupkgFolder = Join-Path $packagesFolder "Contoso.MVC.ASP.2.0.0"
- Assert-PathExists(Join-Path $contosoNupkgFolder "Contoso.MVC.ASP.2.0.0.nupkg")
- # Make sure name squatting package from public repo not restored.
- $contentFolder = Join-Path $contosoNupkgFolder "content"
- Assert-PathExists(Join-Path $contentFolder "Thisisfromprivaterepo2.txt")
-
- $errorlist = Get-Errors
- Assert-AreEqual 0 $errorlist.Count
- }
- finally {
- Remove-Item -Recurse -Force $repoDirectory
- Remove-Item $nugetConfigPath
- }
-}
-
# Create a custom test package
function CreateCustomTestPackage {
param(
diff --git a/test/NuGet.Tests.Apex/NuGet.Tests.Apex/NuGetEndToEndTests/IVsServicesTestCase.cs b/test/NuGet.Tests.Apex/NuGet.Tests.Apex/NuGetEndToEndTests/IVsServicesTestCase.cs
index d7764207d34..9baafc71e49 100644
--- a/test/NuGet.Tests.Apex/NuGet.Tests.Apex/NuGetEndToEndTests/IVsServicesTestCase.cs
+++ b/test/NuGet.Tests.Apex/NuGet.Tests.Apex/NuGetEndToEndTests/IVsServicesTestCase.cs
@@ -202,6 +202,62 @@ public async Task SimpleUpdateFromIVsInstaller_PackageSourceMapping_WithMultiple
Assert.IsTrue(File.Exists(uniqueContentFile), $"'{uniqueContentFile}' should exist");
}
+ [TestMethod]
+ [Timeout(DefaultTimeout)]
+ public async Task SimpleInstallFromIVsInstaller_PackageSourceMapping_WithMissingMappedSource_Fails()
+ {
+ // Arrange
+ await CommonUtility.CreatePackageInSourceAsync(_pathContext.PackageSource, TestPackageName, TestPackageVersionV1);
+ // Map the package pattern to a non-existent source "SecretPackages" — package exists in primary source but mapping won't route there.
+ _pathContext.Settings.AddPackageSourceMapping("SecretPackages", "Contoso.*", "Test.*");
+
+ NuGetApexTestService nugetTestService = GetNuGetTestService();
+
+ SolutionService solutionService = VisualStudio.Get();
+ solutionService.CreateEmptySolution("TestSolution", _pathContext.SolutionRoot);
+ ProjectTestExtension projExt = solutionService.AddProject(ProjectLanguage.CSharp, ProjectTemplate.ClassLibrary, CommonUtility.DefaultTargetFramework, "TestProject");
+ solutionService.SaveAll();
+ EnvDTE.Project project = VisualStudio.Dte.Solution.Projects.Item(1);
+
+ // Act — InstallPackage swallows InvalidOperationException, so the call returns without throwing.
+ nugetTestService.InstallPackage(project.UniqueName, TestPackageName);
+
+ // Assert
+ CommonUtility.AssertPackageNotInPackagesConfig(VisualStudio, projExt, TestPackageName, Logger);
+ }
+
+ [TestMethod]
+ [Timeout(DefaultTimeout)]
+ public async Task SimpleInstallLatestFromIVsInstaller_PackageSourceMapping_WithMultipleFeedsWithIdenticalPackages_InstallsCorrectPackage()
+ {
+ // Arrange
+ string secondarySourcePath = Directory.CreateDirectory(Path.Combine(_pathContext.SolutionRoot, SecondarySourceName)).FullName;
+ await CommonUtility.CreateNetFrameworkPackageInSourceAsync(secondarySourcePath, TestPackageName, TestPackageVersionV1, "Thisisfromsecondaryrepo1.txt");
+ await CommonUtility.CreateNetFrameworkPackageInSourceAsync(secondarySourcePath, TestPackageName, TestPackageVersionV2, "Thisisfromsecondaryrepo2.txt");
+ await CommonUtility.CreateNetFrameworkPackageInSourceAsync(_pathContext.PackageSource, TestPackageName, TestPackageVersionV1, "Thisisfromprivaterepo1.txt");
+ await CommonUtility.CreateNetFrameworkPackageInSourceAsync(_pathContext.PackageSource, TestPackageName, TestPackageVersionV2, "Thisisfromprivaterepo2.txt");
+ _pathContext.Settings.AddSource(SecondarySourceName, secondarySourcePath);
+ _pathContext.Settings.AddPackageSourceMapping(SecondarySourceName, "External.*", "Others.*");
+ _pathContext.Settings.AddPackageSourceMapping(PrimarySourceName, "Contoso.*", "Test.*");
+
+ NuGetApexTestService nugetTestService = GetNuGetTestService();
+
+ SolutionService solutionService = VisualStudio.Get();
+ solutionService.CreateEmptySolution("TestSolution", _pathContext.SolutionRoot);
+ ProjectTestExtension projExt = solutionService.AddProject(ProjectLanguage.CSharp, ProjectTemplate.ClassLibrary, CommonUtility.DefaultTargetFramework, "TestProject");
+ solutionService.SaveAll();
+ EnvDTE.Project project = VisualStudio.Dte.Solution.Projects.Item(1);
+
+ // Act — install latest (no version specified)
+ nugetTestService.InstallPackage(project.UniqueName, TestPackageName);
+
+ // Assert — latest version (2.0.0) should be installed from the mapped primary source
+ CommonUtility.AssertPackageInPackagesConfig(VisualStudio, projExt, TestPackageName, Logger);
+
+ string uniqueContentFile = Path.Combine(_pathContext.PackagesV2, TestPackageName + '.' + TestPackageVersionV2, "lib", "net45", "Thisisfromprivaterepo2.txt");
+ Assert.IsTrue(File.Exists(uniqueContentFile), $"'{uniqueContentFile}' should exist");
+ }
+
public override void Dispose()
{
_pathContext.Dispose();
diff --git a/test/NuGet.Tests.Apex/NuGet.Tests.Apex/NuGetEndToEndTests/NuGetConsoleTestCase.cs b/test/NuGet.Tests.Apex/NuGet.Tests.Apex/NuGetEndToEndTests/NuGetConsoleTestCase.cs
index f1b0e36a4e0..f7a141c2c91 100644
--- a/test/NuGet.Tests.Apex/NuGet.Tests.Apex/NuGetEndToEndTests/NuGetConsoleTestCase.cs
+++ b/test/NuGet.Tests.Apex/NuGet.Tests.Apex/NuGetEndToEndTests/NuGetConsoleTestCase.cs
@@ -488,6 +488,145 @@ public async Task UpdatePackageForPC_PackageSourceMapping_WithMultipleFeedsWithI
}
}
+ [DataTestMethod]
+ [DynamicData(nameof(GetPackagesConfigTemplates), DynamicDataSourceType.Method)]
+ [Timeout(DefaultTimeout)]
+ public async Task InstallPackageForPC_PackageSourceMapping_WithWrongMappedFeed_Fails(ProjectTemplate projectTemplate)
+ {
+ // Arrange
+ using var simpleTestPathContext = new SimpleTestPathContext();
+ var privateRepositoryPath = Path.Combine(simpleTestPathContext.SolutionRoot, "PrivateRepository");
+ Directory.CreateDirectory(privateRepositoryPath);
+
+ var packageName = "Pkg";
+ var packageVersion = "1.0.0";
+
+ await CommonUtility.CreatePackageInSourceAsync(simpleTestPathContext.PackageSource, packageName, packageVersion);
+ Assert.IsTrue(File.Exists(Path.Combine(simpleTestPathContext.PackageSource, $"{packageName}.{packageVersion}.nupkg")));
+ simpleTestPathContext.Settings.AddSource("PrivateRepository", privateRepositoryPath);
+ simpleTestPathContext.Settings.AddPackageSourceMapping("PrivateRepository", "Solution*");
+
+ using var testContext = new ApexTestContext(VisualStudio, projectTemplate, Logger, noAutoRestore: false, addNetStandardFeeds: false, simpleTestPathContext: simpleTestPathContext);
+ var nugetConsole = GetConsole(testContext.Project);
+
+ // Act
+ nugetConsole.InstallPackageFromPMC(packageName, packageVersion);
+
+ // Assert
+ CommonUtility.AssertPackageNotInPackagesConfig(VisualStudio, testContext.Project, packageName, Logger);
+ }
+
+ [DataTestMethod]
+ [DynamicData(nameof(GetPackagesConfigTemplates), DynamicDataSourceType.Method)]
+ [Timeout(DefaultTimeout)]
+ public async Task InstallPackageForPC_PackageSourceMapping_WithCorrectSourceOption_InstallsCorrectPackage(ProjectTemplate projectTemplate)
+ {
+ // Arrange
+ using var simpleTestPathContext = new SimpleTestPathContext();
+ var solutionDirectory = simpleTestPathContext.SolutionRoot;
+
+ var privateRepositoryPath = Path.Combine(solutionDirectory, "PrivateRepository");
+ Directory.CreateDirectory(privateRepositoryPath);
+
+ var packageName = "Contoso.MVC.ASP";
+ var packageVersion = "1.0.0";
+
+ await CommonUtility.CreateNetFrameworkPackageInSourceAsync(simpleTestPathContext.PackageSource, packageName, packageVersion, "Thisisfromopensourcerepo1.txt");
+ await CommonUtility.CreateNetFrameworkPackageInSourceAsync(privateRepositoryPath, packageName, packageVersion, "Thisisfromprivaterepo1.txt");
+ simpleTestPathContext.Settings.AddSource("PrivateRepository", privateRepositoryPath);
+ simpleTestPathContext.Settings.AddPackageSourceMapping("PrivateRepository", "Contoso.MVC.*");
+
+ using var testContext = new ApexTestContext(VisualStudio, projectTemplate, Logger, noAutoRestore: false, addNetStandardFeeds: false, simpleTestPathContext: simpleTestPathContext);
+ var nugetConsole = GetConsole(testContext.Project);
+
+ // Act
+ nugetConsole.InstallPackageFromPMC(packageName, packageVersion, privateRepositoryPath);
+
+ // Assert
+ CommonUtility.AssertPackageInPackagesConfig(VisualStudio, testContext.Project, packageName, packageVersion, Logger);
+
+ var packagesDirectory = Path.Combine(solutionDirectory, "packages");
+ var uniqueContentFile = Path.Combine(packagesDirectory, packageName + '.' + packageVersion, "lib", "net45", "Thisisfromprivaterepo1.txt");
+ Assert.IsTrue(File.Exists(uniqueContentFile), $"'{uniqueContentFile}' should exist");
+ }
+
+ [DataTestMethod]
+ [DynamicData(nameof(GetPackagesConfigTemplates), DynamicDataSourceType.Method)]
+ [Timeout(DefaultTimeout)]
+ public void InstallPackageForPC_PackageSourceMapping_WithWrongSourceOption_Fails(ProjectTemplate projectTemplate)
+ {
+ // Arrange
+ using var simpleTestPathContext = new SimpleTestPathContext();
+ var solutionDirectory = simpleTestPathContext.SolutionRoot;
+
+ var opensourceRepositoryPath = Path.Combine(solutionDirectory, "OpensourceRepository");
+ Directory.CreateDirectory(opensourceRepositoryPath);
+
+ var privateRepositoryPath = Path.Combine(solutionDirectory, "PrivateRepository");
+ Directory.CreateDirectory(privateRepositoryPath);
+
+ var packageName = "Contoso.MVC.ASP";
+ var packageVersion = "1.0.0";
+
+ simpleTestPathContext.Settings.AddSource("OpensourceRepository", opensourceRepositoryPath);
+ simpleTestPathContext.Settings.AddSource("PrivateRepository", privateRepositoryPath);
+ simpleTestPathContext.Settings.AddPackageSourceMapping("PrivateRepository", "Contoso.MVC.*");
+
+ using var testContext = new ApexTestContext(VisualStudio, projectTemplate, Logger, noAutoRestore: false, addNetStandardFeeds: false, simpleTestPathContext: simpleTestPathContext);
+ var nugetConsole = GetConsole(testContext.Project);
+
+ // Act — Install from the opensourceRepo where the package doesn't match the source mapping
+ nugetConsole.InstallPackageFromPMC(packageName, packageVersion, opensourceRepositoryPath);
+
+ // Assert
+ CommonUtility.AssertPackageNotInPackagesConfig(VisualStudio, testContext.Project, packageName, Logger);
+ }
+
+ [DataTestMethod]
+ [DynamicData(nameof(GetPackagesConfigTemplates), DynamicDataSourceType.Method)]
+ [Timeout(DefaultTimeout)]
+ public async Task UpdatePackageForPC_PackageSourceMapping_WithCorrectSourceOption_UpdatesCorrectPackage(ProjectTemplate projectTemplate)
+ {
+ // Arrange
+ using var simpleTestPathContext = new SimpleTestPathContext();
+ var solutionDirectory = simpleTestPathContext.SolutionRoot;
+
+ var opensourceRepositoryPath = Path.Combine(solutionDirectory, "OpensourceRepository");
+ Directory.CreateDirectory(opensourceRepositoryPath);
+
+ var privateRepositoryPath = Path.Combine(solutionDirectory, "PrivateRepository");
+ Directory.CreateDirectory(privateRepositoryPath);
+
+ var packageName = "Contoso.MVC.ASP";
+ var packageVersion1 = "1.0.0";
+ var packageVersion2 = "2.0.0";
+
+ await CommonUtility.CreateNetFrameworkPackageInSourceAsync(privateRepositoryPath, packageName, packageVersion1, "Thisisfromprivaterepo1.txt");
+ await CommonUtility.CreateNetFrameworkPackageInSourceAsync(privateRepositoryPath, packageName, packageVersion2, "Thisisfromprivaterepo2.txt");
+ await CommonUtility.CreateNetFrameworkPackageInSourceAsync(opensourceRepositoryPath, packageName, packageVersion1, "Thisisfromopensourcerepo1.txt");
+ await CommonUtility.CreateNetFrameworkPackageInSourceAsync(opensourceRepositoryPath, packageName, packageVersion2, "Thisisfromopensourcerepo2.txt");
+
+ simpleTestPathContext.Settings.AddSource("OpensourceRepository", opensourceRepositoryPath);
+ simpleTestPathContext.Settings.AddSource("PrivateRepository", privateRepositoryPath);
+ simpleTestPathContext.Settings.AddPackageSourceMapping("PrivateRepository", "Contoso.MVC.*");
+
+ using var testContext = new ApexTestContext(VisualStudio, projectTemplate, Logger, noAutoRestore: false, addNetStandardFeeds: false, simpleTestPathContext: simpleTestPathContext);
+ var nugetConsole = GetConsole(testContext.Project);
+
+ // Act
+ nugetConsole.InstallPackageFromPMC(packageName, packageVersion1, privateRepositoryPath);
+ CommonUtility.AssertPackageInPackagesConfig(VisualStudio, testContext.Project, packageName, packageVersion1, Logger);
+
+ nugetConsole.UpdatePackageFromPMC(packageName, packageVersion2, privateRepositoryPath);
+
+ // Assert
+ CommonUtility.AssertPackageInPackagesConfig(VisualStudio, testContext.Project, packageName, packageVersion2, Logger);
+
+ var packagesDirectory = Path.Combine(solutionDirectory, "packages");
+ var uniqueContentFile = Path.Combine(packagesDirectory, packageName + '.' + packageVersion2, "lib", "net45", "Thisisfromprivaterepo2.txt");
+ Assert.IsTrue(File.Exists(uniqueContentFile), $"'{uniqueContentFile}' should exist");
+ }
+
[Ignore("https://github.com/NuGet/Home/issues/12899")]
[DataTestMethod]
[DataRow(ProjectTemplate.ClassLibrary, false)]