mirror of
https://github.com/actions/runner.git
synced 2026-03-18 15:41:24 +08:00
Add tests for deprecation warning formatting, truncation, sorting, and sanitization
- HandlerFactoryL0: test subpath tracking and CR/LF sanitization in action names - JobExtensionL0: test warning emission, no-op when empty, truncation at 10 actions, alphabetical sorting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -317,5 +317,109 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Assert.Contains("some-org/old-action@v1", deprecatedActions);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Node20Action_WithSubpath_TrackedCorrectly()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange.
|
||||
var hf = new HandlerFactory();
|
||||
hf.Initialize(hc);
|
||||
|
||||
var variables = new Dictionary<string, VariableValue>
|
||||
{
|
||||
{ Constants.Runner.NodeMigration.WarnOnNode20Flag, new VariableValue("true") }
|
||||
};
|
||||
Variables serverVariables = new(hc, variables);
|
||||
var deprecatedActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
_ec.Setup(x => x.Global).Returns(new GlobalContext()
|
||||
{
|
||||
Variables = serverVariables,
|
||||
EnvironmentVariables = new Dictionary<string, string>(),
|
||||
DeprecatedNode20Actions = deprecatedActions
|
||||
});
|
||||
|
||||
var actionRef = new RepositoryPathReference
|
||||
{
|
||||
Name = "some-org/monorepo",
|
||||
Path = "actions/my-action",
|
||||
Ref = "v2"
|
||||
};
|
||||
|
||||
// Act.
|
||||
var data = new NodeJSActionExecutionData();
|
||||
data.NodeVersion = "node20";
|
||||
hf.Create(
|
||||
_ec.Object,
|
||||
actionRef,
|
||||
new Mock<IStepHost>().Object,
|
||||
data,
|
||||
new Dictionary<string, string>(),
|
||||
new Dictionary<string, string>(),
|
||||
new Variables(hc, new Dictionary<string, VariableValue>()),
|
||||
"",
|
||||
new List<JobExtensionRunner>()
|
||||
);
|
||||
|
||||
// Assert - subpath should be included in tracked name
|
||||
Assert.Contains("some-org/monorepo/actions/my-action@v2", deprecatedActions);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Node20Action_SanitizesNewlinesInActionName()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange.
|
||||
var hf = new HandlerFactory();
|
||||
hf.Initialize(hc);
|
||||
|
||||
var variables = new Dictionary<string, VariableValue>
|
||||
{
|
||||
{ Constants.Runner.NodeMigration.WarnOnNode20Flag, new VariableValue("true") }
|
||||
};
|
||||
Variables serverVariables = new(hc, variables);
|
||||
var deprecatedActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
_ec.Setup(x => x.Global).Returns(new GlobalContext()
|
||||
{
|
||||
Variables = serverVariables,
|
||||
EnvironmentVariables = new Dictionary<string, string>(),
|
||||
DeprecatedNode20Actions = deprecatedActions
|
||||
});
|
||||
|
||||
var actionRef = new RepositoryPathReference
|
||||
{
|
||||
Name = "evil-org/bad\r\naction",
|
||||
Ref = "v1"
|
||||
};
|
||||
|
||||
// Act.
|
||||
var data = new NodeJSActionExecutionData();
|
||||
data.NodeVersion = "node20";
|
||||
hf.Create(
|
||||
_ec.Object,
|
||||
actionRef,
|
||||
new Mock<IStepHost>().Object,
|
||||
data,
|
||||
new Dictionary<string, string>(),
|
||||
new Dictionary<string, string>(),
|
||||
new Variables(hc, new Dictionary<string, VariableValue>()),
|
||||
"",
|
||||
new List<JobExtensionRunner>()
|
||||
);
|
||||
|
||||
// Assert - CR/LF should be stripped to prevent log/annotation injection
|
||||
Assert.Contains("evil-org/badaction@v1", deprecatedActions);
|
||||
Assert.DoesNotContain("evil-org/bad\r\naction@v1", deprecatedActions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,5 +755,113 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Environment.SetEnvironmentVariable("RUNNER_ENVIRONMENT", null);
|
||||
Environment.SetEnvironmentVariable("GITHUB_ACTIONS_IMAGE_GEN_ENABLED", null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task FinalizeJob_EmitsDeprecationWarning_WhenNode20ActionsExist()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var jobExtension = new JobExtension();
|
||||
jobExtension.Initialize(hc);
|
||||
|
||||
_jobEc = new Runner.Worker.ExecutionContext { Result = TaskResult.Succeeded };
|
||||
_jobEc.Initialize(hc);
|
||||
_jobEc.InitializeJob(_message, _tokenSource.Token);
|
||||
|
||||
// Add deprecated actions
|
||||
_jobEc.Global.DeprecatedNode20Actions.Add("actions/checkout@v4");
|
||||
_jobEc.Global.DeprecatedNode20Actions.Add("actions/setup-node@v3");
|
||||
|
||||
await jobExtension.FinalizeJob(_jobEc, _message, DateTime.UtcNow);
|
||||
|
||||
// Verify warning was written to log containing the action names
|
||||
_logger.Verify(x => x.Write(It.Is<string>(s =>
|
||||
s.Contains("actions/checkout@v4") &&
|
||||
s.Contains("actions/setup-node@v3") &&
|
||||
s.Contains("Node.js 20 is approaching end-of-life"))), Times.AtLeastOnce);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task FinalizeJob_NoDeprecationWarning_WhenNoNode20Actions()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var jobExtension = new JobExtension();
|
||||
jobExtension.Initialize(hc);
|
||||
|
||||
_jobEc = new Runner.Worker.ExecutionContext { Result = TaskResult.Succeeded };
|
||||
_jobEc.Initialize(hc);
|
||||
_jobEc.InitializeJob(_message, _tokenSource.Token);
|
||||
|
||||
// DeprecatedNode20Actions is initialized but empty
|
||||
Assert.Empty(_jobEc.Global.DeprecatedNode20Actions);
|
||||
|
||||
await jobExtension.FinalizeJob(_jobEc, _message, DateTime.UtcNow);
|
||||
|
||||
// Verify no deprecation warning was emitted
|
||||
_logger.Verify(x => x.Write(It.Is<string>(s =>
|
||||
s.Contains("Node.js 20 is approaching end-of-life"))), Times.Never);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task FinalizeJob_TruncatesActionsListAtTen()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var jobExtension = new JobExtension();
|
||||
jobExtension.Initialize(hc);
|
||||
|
||||
_jobEc = new Runner.Worker.ExecutionContext { Result = TaskResult.Succeeded };
|
||||
_jobEc.Initialize(hc);
|
||||
_jobEc.InitializeJob(_message, _tokenSource.Token);
|
||||
|
||||
// Add 15 deprecated actions
|
||||
for (int i = 1; i <= 15; i++)
|
||||
{
|
||||
_jobEc.Global.DeprecatedNode20Actions.Add($"org/action-{i:D2}@v1");
|
||||
}
|
||||
|
||||
await jobExtension.FinalizeJob(_jobEc, _message, DateTime.UtcNow);
|
||||
|
||||
// Verify truncation message appears with correct count
|
||||
_logger.Verify(x => x.Write(It.Is<string>(s =>
|
||||
s.Contains("... and 5 more"))), Times.AtLeastOnce);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task FinalizeJob_SortsActionsAlphabetically()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var jobExtension = new JobExtension();
|
||||
jobExtension.Initialize(hc);
|
||||
|
||||
_jobEc = new Runner.Worker.ExecutionContext { Result = TaskResult.Succeeded };
|
||||
_jobEc.Initialize(hc);
|
||||
_jobEc.InitializeJob(_message, _tokenSource.Token);
|
||||
|
||||
// Add actions in reverse alphabetical order
|
||||
_jobEc.Global.DeprecatedNode20Actions.Add("z-org/last-action@v1");
|
||||
_jobEc.Global.DeprecatedNode20Actions.Add("a-org/first-action@v1");
|
||||
_jobEc.Global.DeprecatedNode20Actions.Add("m-org/middle-action@v1");
|
||||
|
||||
await jobExtension.FinalizeJob(_jobEc, _message, DateTime.UtcNow);
|
||||
|
||||
// Verify actions appear sorted: a-org before m-org before z-org
|
||||
_logger.Verify(x => x.Write(It.Is<string>(s =>
|
||||
s.Contains("a-org/first-action@v1, m-org/middle-action@v1, z-org/last-action@v1"))), Times.AtLeastOnce);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user