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
6 changes: 5 additions & 1 deletion .copilot/skills/apex-migration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,17 @@ using var testContext = new ApexTestContext(VisualStudio, ProjectTemplate.NetCor

## Style rules

Follow the repo-wide coding guidelines first — they apply to all code, not just migrations:
[`docs/coding-guidelines.md`](../../../docs/coding-guidelines.md) (e.g. no `#region` blocks, no
reflection, `var` usage, nullable enabled). Don't restate or duplicate those rules here; this section
only lists conventions **specific to test migration** that supplement the common guidelines:

- 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.
- Don't use the method-delegates-to-async-helper pattern unless the helper is actually called from
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.VisualStudio.ComponentModelHost;
Expand All @@ -26,7 +21,6 @@
using NuGet.VisualStudio;
using Test.Utility;
using Xunit;
using PSCommand = System.Management.Automation.Runspaces.Command;

namespace NuGetConsole.Host.PowerShell.Test
{
Expand Down Expand Up @@ -239,8 +233,6 @@ await SimpleTestPackageUtility.CreatePackagesAsync(
package.Version.ToString().Should().Be("1.0.0-beta");
}

#region Helpers

private void SetupSourceRepositoryProvider(string localSourcePath)
{
var localSource = new PackageSource(localSourcePath);
Expand All @@ -257,78 +249,5 @@ private void SetupSourceRepositoryProvider(string localSourcePath)

return Task.FromResult<object?>(null);
}

#endregion

#region Test Infrastructure

/// <summary>
/// Encapsulates runspace and host setup for invoking the Find-Package cmdlet in tests.
/// </summary>
private sealed class CmdletRunspaceFixture : IDisposable
{
private readonly Runspace _runspace;

public CmdletRunspaceFixture(string activeSource = "https://contoso.com/v3/index.json")
{
var host = new TestPSHost(activeSource);
var initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.Commands.Add(
new SessionStateCmdletEntry("Find-Package", typeof(FindPackageCommand), null));

_runspace = RunspaceFactory.CreateRunspace(host, initialSessionState);
_runspace.Open();
}

public IList<PSObject> Invoke(string cmdletName, Dictionary<string, object> parameters)
{
using var pipeline = _runspace.CreatePipeline();
var cmd = new PSCommand(cmdletName);
foreach (var kvp in parameters)
{
cmd.Parameters.Add(kvp.Key, kvp.Value);
}
pipeline.Commands.Add(cmd);
return pipeline.Invoke().ToList();
}

public void Dispose()
{
_runspace.Close();
_runspace.Dispose();
}
}

/// <summary>
/// Minimal PSHost that provides PrivateData with properties expected by NuGet cmdlets.
/// </summary>
private sealed class TestPSHost : PSHost
{
private readonly Guid _instanceId = Guid.NewGuid();
private readonly PSObject _privateData;

public TestPSHost(string activeSource)
{
_privateData = new PSObject();
_privateData.Properties.Add(new PSNoteProperty("activePackageSource", activeSource));
_privateData.Properties.Add(new PSNoteProperty("CancellationTokenKey", CancellationToken.None));
}

public override CultureInfo CurrentCulture => CultureInfo.InvariantCulture;
public override CultureInfo CurrentUICulture => CultureInfo.InvariantCulture;
public override Guid InstanceId => _instanceId;
public override string Name => "TestNuGetHost";
public override PSObject PrivateData => _privateData;
public override PSHostUserInterface? UI => null;
public override Version Version => new Version(1, 0);

public override void EnterNestedPrompt() { }
public override void ExitNestedPrompt() { }
public override void NotifyBeginApplication() { }
public override void NotifyEndApplication() { }
public override void SetShouldExit(int exitCode) { }
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
Expand All @@ -30,7 +27,6 @@
using NuGet.VisualStudio;
using Test.Utility;
using Xunit;
using PSCommand = System.Management.Automation.Runspaces.Command;

namespace NuGetConsole.Host.PowerShell.Test
{
Expand Down Expand Up @@ -629,8 +625,6 @@ public void GetPackage_WithUnsavedSolution_Throws()
.Contain("Solution is not saved.");
}

#region Helpers

private void SetupSourceRepositoryProvider(string localSourcePath)
{
var localSource = new PackageSource(localSourcePath);
Expand Down Expand Up @@ -688,78 +682,5 @@ private void SetupProjectWithInstalledPackage(string packageId, string packageVe

return Task.FromResult<object?>(null);
}

#endregion

#region Test Infrastructure

/// <summary>
/// Encapsulates runspace and host setup for invoking NuGet PowerShell cmdlets in tests.
/// </summary>
private sealed class CmdletRunspaceFixture : IDisposable
{
private readonly Runspace _runspace;

public CmdletRunspaceFixture(string activeSource = "https://contoso.com/v3/index.json")
{
var host = new TestPSHost(activeSource);
var initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.Commands.Add(
new SessionStateCmdletEntry("Get-Package", typeof(GetPackageCommand), null));

_runspace = RunspaceFactory.CreateRunspace(host, initialSessionState);
_runspace.Open();
}

public IList<PSObject> Invoke(string cmdletName, Dictionary<string, object> parameters)
{
using var pipeline = _runspace.CreatePipeline();
var cmd = new PSCommand(cmdletName);
foreach (var kvp in parameters)
{
cmd.Parameters.Add(kvp.Key, kvp.Value);
}
pipeline.Commands.Add(cmd);
return pipeline.Invoke().ToList();
}

public void Dispose()
{
_runspace.Close();
_runspace.Dispose();
}
}

/// <summary>
/// Minimal PSHost that provides PrivateData with properties expected by NuGet cmdlets.
/// </summary>
private sealed class TestPSHost : PSHost
{
private readonly Guid _instanceId = Guid.NewGuid();
private readonly PSObject _privateData;

public TestPSHost(string activeSource)
{
_privateData = new PSObject();
_privateData.Properties.Add(new PSNoteProperty("activePackageSource", activeSource));
_privateData.Properties.Add(new PSNoteProperty("CancellationTokenKey", CancellationToken.None));
}

public override CultureInfo CurrentCulture => CultureInfo.InvariantCulture;
public override CultureInfo CurrentUICulture => CultureInfo.InvariantCulture;
public override Guid InstanceId => _instanceId;
public override string Name => "TestNuGetHost";
public override PSObject PrivateData => _privateData;
public override PSHostUserInterface? UI => null;
public override Version Version => new Version(1, 0);

public override void EnterNestedPrompt() { }
public override void ExitNestedPrompt() { }
public override void NotifyBeginApplication() { }
public override void NotifyEndApplication() { }
public override void SetShouldExit(int exitCode) { }
}

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
using System.Threading;
using NuGet.PackageManagement.PowerShellCmdlets;
using PSCommand = System.Management.Automation.Runspaces.Command;

namespace NuGetConsole.Host.PowerShell.Test
{
/// <summary>
/// Encapsulates runspace and host setup for invoking NuGet PowerShell cmdlets in tests.
/// </summary>
internal sealed class CmdletRunspaceFixture : IDisposable
{
private readonly Runspace _runspace;

public CmdletRunspaceFixture(string activeSource = "https://contoso.com/v3/index.json")
{
var host = new TestPSHost(activeSource);
var initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.Commands.Add(
new SessionStateCmdletEntry("Find-Package", typeof(FindPackageCommand), null));
initialSessionState.Commands.Add(
new SessionStateCmdletEntry("Get-Package", typeof(GetPackageCommand), null));

_runspace = RunspaceFactory.CreateRunspace(host, initialSessionState);
_runspace.Open();
}

public IList<PSObject> Invoke(string cmdletName, Dictionary<string, object> parameters)
{
using var pipeline = _runspace.CreatePipeline();
var cmd = new PSCommand(cmdletName);
foreach (var kvp in parameters)
{
cmd.Parameters.Add(kvp.Key, kvp.Value);
}
pipeline.Commands.Add(cmd);
return pipeline.Invoke().ToList();
}

public void Dispose()
{
_runspace.Close();
_runspace.Dispose();
}
}

/// <summary>
/// Minimal PSHost that provides PrivateData with properties expected by NuGet cmdlets.
/// </summary>
internal sealed class TestPSHost : PSHost
{
private readonly Guid _instanceId = Guid.NewGuid();
private readonly PSObject _privateData;

public TestPSHost(string activeSource)
{
_privateData = new PSObject();
_privateData.Properties.Add(new PSNoteProperty("activePackageSource", activeSource));
_privateData.Properties.Add(new PSNoteProperty("CancellationTokenKey", CancellationToken.None));
}

public override CultureInfo CurrentCulture => CultureInfo.InvariantCulture;
public override CultureInfo CurrentUICulture => CultureInfo.InvariantCulture;
public override Guid InstanceId => _instanceId;
public override string Name => "TestNuGetHost";
public override PSObject PrivateData => _privateData;
public override PSHostUserInterface? UI => null;
public override Version Version => new Version(1, 0);

public override void EnterNestedPrompt() { }
public override void ExitNestedPrompt() { }
public override void NotifyBeginApplication() { }
public override void NotifyEndApplication() { }
public override void SetShouldExit(int exitCode) { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public async Task GetPackage_WithUpdates_ListsMultipleUpdatesAsync()

/// <summary>
/// Verifies that Get-Package -ListAvailable returns a package whose IsUpdate property
/// is not set (falsy). The original E2E test (Test-GetPackagesWithNoUpdatesReturnPackagesWithIsUpdateNotSet)
/// is not set (false). The original E2E test (Test-GetPackagesWithNoUpdatesReturnPackagesWithIsUpdateNotSet)
/// called Assert-False on $package.IsUpdate — which succeeds because PowerShellRemotePackage
/// does not have an IsUpdate property, so PowerShell returns $null (falsy).
/// </summary>
Expand Down