From 49a88c1161a0a3fd39da5f32565fd08d4a0eab60 Mon Sep 17 00:00:00 2001 From: Philip Gai Date: Thu, 2 Jul 2026 13:17:43 -0500 Subject: [PATCH] test: address review feedback for ACTIONS_CACHE_MODE tests - Add ContainerActionHandler L0 coverage (Linux-gated) asserting ACTIONS_CACHE_MODE is exported to the container env when actions_cache_mode is set and absent otherwise, routed through the container-hooks path. - Set the cache-mode variable directly on the initialized job context instead of re-invoking InitializeJob, avoiding a redundant CancellationTokenSource. --- src/Test/L0/Worker/HandlerL0.cs | 107 +++++++++++++++++++++++++++ src/Test/L0/Worker/JobExtensionL0.cs | 3 +- 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/src/Test/L0/Worker/HandlerL0.cs b/src/Test/L0/Worker/HandlerL0.cs index f04e1b47f..c0d0a814e 100644 --- a/src/Test/L0/Worker/HandlerL0.cs +++ b/src/Test/L0/Worker/HandlerL0.cs @@ -2,14 +2,18 @@ using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using GitHub.Actions.RunService.WebApi; using GitHub.DistributedTask.Pipelines; using GitHub.DistributedTask.Pipelines.ContextData; using GitHub.DistributedTask.WebApi; +using GitHub.Runner.Common; using GitHub.Runner.Sdk; using GitHub.Runner.Worker; +using GitHub.Runner.Worker.Container; +using GitHub.Runner.Worker.Container.ContainerHooks; using GitHub.Runner.Worker.Handlers; using Moq; using Xunit; @@ -176,6 +180,109 @@ namespace GitHub.Runner.Common.Tests.Worker } } + [Theory] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + [InlineData("read")] + [InlineData("none")] + public async Task ContainerRunAsync_ExportsCacheModeEnv_WhenVariableSet(string mode) + { + // Container actions only run on Linux; RunAsync throws on other platforms. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return; + } + + using (TestHostContext hc = CreateTestContext()) + { + var container = await RunContainerActionHandlerAsync(hc, new Dictionary + { + { "actions_cache_mode", mode } + }); + + Assert.True(container.ContainerEnvironmentVariables.TryGetValue("ACTIONS_CACHE_MODE", out var value)); + Assert.Equal(mode, value); + } + } + + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public async Task ContainerRunAsync_DoesNotExportCacheModeEnv_WhenVariableAbsent() + { + // Container actions only run on Linux; RunAsync throws on other platforms. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return; + } + + using (TestHostContext hc = CreateTestContext()) + { + var container = await RunContainerActionHandlerAsync(hc, new Dictionary()); + + Assert.False(container.ContainerEnvironmentVariables.ContainsKey("ACTIONS_CACHE_MODE")); + } + } + + private async Task RunContainerActionHandlerAsync(TestHostContext hc, IDictionary variables) + { + // Route through the container-hooks path so the handler skips docker build/run. + variables[Constants.Runner.Features.AllowRunnerContainerHooks] = "true"; + Environment.SetEnvironmentVariable(Constants.Hooks.ContainerHooksPath, Path.Combine(hc.GetDirectory(WellKnownDirectory.Root), "hooks.js")); + + var tempDirectory = hc.GetDirectory(WellKnownDirectory.Temp); + Directory.CreateDirectory(Path.Combine(tempDirectory, "_runner_file_commands")); + Directory.CreateDirectory(Path.Combine(tempDirectory, "_github_workflow")); + var workspace = Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), "workspace"); + Directory.CreateDirectory(workspace); + + var serverVariables = new Variables(hc, variables); + var endpoints = new List + { + new ServiceEndpoint() + { + Name = WellKnownServiceEndpointNames.SystemVssConnection, + Url = new Uri("https://pipelines.actions.githubusercontent.com"), + Authorization = new EndpointAuthorization() + { + Scheme = "Test", + Parameters = { { "AccessToken", "token" } } + } + } + }; + + _ec.Setup(x => x.Global).Returns(new GlobalContext() + { + Variables = serverVariables, + Endpoints = endpoints, + PrependPath = new List(), + EnvironmentVariables = new Dictionary() + }); + _ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData()); + _ec.Setup(x => x.JobContext).Returns(new JobContext()); + _ec.Setup(x => x.GetGitHubContext("workspace")).Returns(workspace); + + ContainerInfo captured = null; + var hookManager = new Mock(); + hookManager.Setup(x => x.RunContainerStepAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((IExecutionContext ec, ContainerInfo container, string dockerFile) => { captured = container; }) + .Returns(Task.CompletedTask); + hc.SetSingleton(hookManager.Object); + hc.SetSingleton(new Mock().Object); + + var handler = new ContainerActionHandler(); + handler.Initialize(hc); + handler.ExecutionContext = _ec.Object; + handler.Environment = new Dictionary(); + handler.Inputs = new Dictionary(); + handler.Action = new ContainerRegistryReference() { Image = "alpine:latest" }; + handler.Data = new ContainerActionExecutionData() { Image = "docker://alpine:latest" }; + + await handler.RunAsync(ActionRunStage.Main); + + return captured; + } + private async Task> RunNodeScriptActionHandlerAsync(TestHostContext hc, IDictionary variables) { var actionDirectory = Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), Guid.NewGuid().ToString()); diff --git a/src/Test/L0/Worker/JobExtensionL0.cs b/src/Test/L0/Worker/JobExtensionL0.cs index 8dfb7627e..33954c4f7 100644 --- a/src/Test/L0/Worker/JobExtensionL0.cs +++ b/src/Test/L0/Worker/JobExtensionL0.cs @@ -249,8 +249,7 @@ namespace GitHub.Runner.Common.Tests.Worker { using (TestHostContext hc = CreateTestContext()) { - _message.Variables["actions_cache_mode"] = mode; - _jobEc.InitializeJob(_message, _tokenSource.Token); + _jobEc.Global.Variables.Set("actions_cache_mode", mode); var jobExtension = new JobExtension(); jobExtension.Initialize(hc);