diff --git a/Directory.Build.targets b/Directory.Build.targets
index 8905fec051..10729ddb49 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -2,4 +2,8 @@
+
+ $(ArtifactsPath)/log/$(ArtifactsProjectName)/tests_$(ArtifactsPivots)/
+
+
diff --git a/eng/ci/templates/jobs/run-unit-tests.yml b/eng/ci/templates/jobs/run-unit-tests.yml
index 97ed382438..cb5743ac6c 100644
--- a/eng/ci/templates/jobs/run-unit-tests.yml
+++ b/eng/ci/templates/jobs/run-unit-tests.yml
@@ -10,7 +10,7 @@ jobs:
displayName: Publish deps.json
path: $(Build.ArtifactStagingDirectory)
artifact: WebHost_Deps
- condition: failed()
+ condition: and(failed(), eq(variables['System.JobAttempt'], 1)) # only publish on first attempt
steps:
- template: /eng/ci/templates/install-dotnet.yml@self
@@ -20,7 +20,7 @@ jobs:
inputs:
command: test
testRunTitle: Unit Tests
- arguments: -v n
+ arguments: -v n --blame
projects: |
**\ExtensionsMetadataGeneratorTests.csproj
**\WebJobs.Script.Tests.csproj
diff --git a/release_notes.md b/release_notes.md
index 30d5e2eaf6..cc087fe1cc 100644
--- a/release_notes.md
+++ b/release_notes.md
@@ -10,4 +10,5 @@
- Handles loading extensions.json with empty extensions(#11174)
- Update HttpWorkerOptions to implement IOptionsFormatter (#11175)
- Improved metadata binding validation (#11101)
+- Throw exception instead of timing out when worker channel exits before initializing gRPC (#10937)
- Skip logging errors on gRPC client disconnect (#10572)
diff --git a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs
index 7b897bfea0..e4ccecf94c 100644
--- a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs
+++ b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs
@@ -366,19 +366,36 @@ private void DispatchMessage(InboundGrpcEvent msg)
public bool IsChannelReadyForInvocations()
{
- return !_disposing && !_disposed && _state.HasFlag(RpcWorkerChannelState.InvocationBuffersInitialized | RpcWorkerChannelState.Initialized);
+ return !_disposing && !_disposed
+ && _state.HasFlag(
+ RpcWorkerChannelState.InvocationBuffersInitialized | RpcWorkerChannelState.Initialized);
}
public async Task StartWorkerProcessAsync(CancellationToken cancellationToken)
{
- RegisterCallbackForNextGrpcMessage(MsgType.StartStream, _workerConfig.CountOptions.ProcessStartupTimeout, 1, SendWorkerInitRequest, HandleWorkerStartStreamError);
- // note: it is important that the ^^^ StartStream is in place *before* we start process the loop, otherwise we get a race condition
+ RegisterCallbackForNextGrpcMessage(
+ MsgType.StartStream,
+ _workerConfig.CountOptions.ProcessStartupTimeout,
+ count: 1,
+ SendWorkerInitRequest,
+ HandleWorkerStartStreamError);
+
+ // note: it is important that the ^^^ StartStream is in place *before* we start process the loop,
+ // otherwise we get a race condition
_ = ProcessInbound();
_workerChannelLogger.LogDebug("Initiating Worker Process start up");
- await _rpcWorkerProcess.StartProcessAsync();
- _state = _state | RpcWorkerChannelState.Initializing;
- await _workerInitTask.Task;
+ await _rpcWorkerProcess.StartProcessAsync(cancellationToken);
+ _state |= RpcWorkerChannelState.Initializing;
+ Task exited = _rpcWorkerProcess.WaitForExitAsync(cancellationToken);
+ Task winner = await Task.WhenAny(_workerInitTask.Task, exited).WaitAsync(cancellationToken);
+ await winner;
+
+ if (winner == exited)
+ {
+ // process exited without throwing. We need to throw to indicate process is not running.
+ throw new WorkerProcessExitException("Worker process exited before initializing.");
+ }
}
public async Task GetWorkerStatusAsync()
diff --git a/src/WebJobs.Script/Config/ExtensionRequirementOptions.cs b/src/WebJobs.Script/Config/ExtensionRequirementOptions.cs
index ae6a996412..130e875894 100644
--- a/src/WebJobs.Script/Config/ExtensionRequirementOptions.cs
+++ b/src/WebJobs.Script/Config/ExtensionRequirementOptions.cs
@@ -1,10 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
-using System;
using System.Collections.Generic;
using Microsoft.Azure.WebJobs.Script.ExtensionRequirements;
-using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
namespace Microsoft.Azure.WebJobs.Script.Config
{
diff --git a/src/WebJobs.Script/Config/ExtensionRequirementOptionsSetup.cs b/src/WebJobs.Script/Config/ExtensionRequirementOptionsSetup.cs
index 0963e621de..b8c1454be2 100644
--- a/src/WebJobs.Script/Config/ExtensionRequirementOptionsSetup.cs
+++ b/src/WebJobs.Script/Config/ExtensionRequirementOptionsSetup.cs
@@ -1,13 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
-using System;
-using System.Collections.Generic;
-using System.Configuration;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.Azure.WebJobs.Script.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
diff --git a/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs b/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs
index 953fc56429..55b95f1323 100644
--- a/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs
+++ b/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs
@@ -36,29 +36,37 @@ public sealed class ScriptStartupTypeLocator : IWebJobsStartupTypeLocator
private readonly IExtensionBundleManager _extensionBundleManager;
private readonly IFunctionMetadataManager _functionMetadataManager;
private readonly IMetricsLogger _metricsLogger;
- private readonly Lazy> _startupTypes;
+ private readonly IEnvironment _environment;
private readonly IOptions _extensionRequirementOptions;
+ private readonly Lazy> _startupTypes;
private static string[] _builtinExtensionAssemblies = GetBuiltinExtensionAssemblies();
- public ScriptStartupTypeLocator(string rootScriptPath, ILogger logger, IExtensionBundleManager extensionBundleManager,
- IFunctionMetadataManager functionMetadataManager, IMetricsLogger metricsLogger, IOptions extensionRequirementOptions)
+ public ScriptStartupTypeLocator(
+ string rootScriptPath,
+ ILogger logger,
+ IExtensionBundleManager extensionBundleManager,
+ IFunctionMetadataManager functionMetadataManager,
+ IMetricsLogger metricsLogger,
+ IEnvironment environment,
+ IOptions extensionRequirementOptions)
{
_rootScriptPath = rootScriptPath ?? throw new ArgumentNullException(nameof(rootScriptPath));
_extensionBundleManager = extensionBundleManager ?? throw new ArgumentNullException(nameof(extensionBundleManager));
_logger = logger;
_functionMetadataManager = functionMetadataManager;
_metricsLogger = metricsLogger;
- _startupTypes = new Lazy>(() => GetExtensionsStartupTypesAsync().ConfigureAwait(false).GetAwaiter().GetResult());
+ _environment = environment;
_extensionRequirementOptions = extensionRequirementOptions;
+ _startupTypes = new Lazy>(() => GetExtensionsStartupTypesAsync().ConfigureAwait(false).GetAwaiter().GetResult());
}
private static string[] GetBuiltinExtensionAssemblies()
{
- return new[]
- {
+ return
+ [
typeof(WebJobs.Extensions.Http.HttpWebJobsStartup).Assembly.GetName().Name,
typeof(WebJobs.Extensions.ExtensionsWebJobsStartup).Assembly.GetName().Name
- };
+ ];
}
public Type[] GetStartupTypes()
@@ -102,11 +110,11 @@ public async Task> GetExtensionsStartupTypesAsync()
}
}
- bool isDotnetIsolatedApp = Utility.IsDotnetIsolatedApp(SystemEnvironment.Instance, functionMetadataCollection);
+ bool isDotnetIsolatedApp = Utility.IsDotnetIsolatedApp(_environment, functionMetadataCollection);
bool isDotnetApp = isPrecompiledFunctionApp || isDotnetIsolatedApp;
- var isLogicApp = SystemEnvironment.Instance.IsLogicApp();
+ var isLogicApp = _environment.IsLogicApp();
- if (SystemEnvironment.Instance.IsPlaceholderModeEnabled())
+ if (_environment.IsPlaceholderModeEnabled())
{
// Do not move this.
// Calling this log statement in the placeholder mode to avoid jitting during specialization
diff --git a/src/WebJobs.Script/Diagnostics/OpenTelemetry/OpenTelemetryConfigurationExtensions.cs b/src/WebJobs.Script/Diagnostics/OpenTelemetry/OpenTelemetryConfigurationExtensions.cs
index 9dd7edc4e6..e44cb89f8f 100644
--- a/src/WebJobs.Script/Diagnostics/OpenTelemetry/OpenTelemetryConfigurationExtensions.cs
+++ b/src/WebJobs.Script/Diagnostics/OpenTelemetry/OpenTelemetryConfigurationExtensions.cs
@@ -60,13 +60,13 @@ private static IOpenTelemetryBuilder ConfigureMetrics(this IOpenTelemetryBuilder
return builder.WithMetrics(builder =>
{
builder.AddAspNetCoreInstrumentation()
- .AddRuntimeInstrumentation()
- .AddProcessInstrumentation()
- .AddMeter(HostMetrics.FaasMeterName)
- .AddView(HostMetrics.FaasInvokeDuration, new ExplicitBucketHistogramConfiguration
- {
- Boundaries = new double[] { 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 }
- });
+ .AddRuntimeInstrumentation()
+ .AddProcessInstrumentation()
+ .AddMeter(HostMetrics.FaasMeterName)
+ .AddView(HostMetrics.FaasInvokeDuration, new ExplicitBucketHistogramConfiguration
+ {
+ Boundaries = new double[] { 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 }
+ });
// Avoid configuring the exporter in placeholder mode, as it will default to sending telemetry to the predefined endpoint. These transmissions will be unsuccessful and create unnecessary noise.
if (telemetryMode != TelemetryMode.Placeholder)
diff --git a/src/WebJobs.Script/Extensions/ExceptionExtensions.cs b/src/WebJobs.Script/Extensions/ExceptionExtensions.cs
index d60286aa5f..da1d36e79d 100644
--- a/src/WebJobs.Script/Extensions/ExceptionExtensions.cs
+++ b/src/WebJobs.Script/Extensions/ExceptionExtensions.cs
@@ -1,7 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
-using System.Reflection;
+using System.Collections.Generic;
+using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Azure.WebJobs.Host.Diagnostics;
@@ -32,12 +33,22 @@ or SEHException
public static string ToFormattedString(this Exception exception)
{
- if (exception == null)
+ ArgumentNullException.ThrowIfNull(exception);
+ return ExceptionFormatter.GetFormattedException(exception);
+ }
+
+ public static void ThrowIfErrorsPresent(IList exceptions, string message = null)
+ {
+ switch (exceptions)
{
- throw new ArgumentNullException(nameof(exception));
+ case null or []:
+ return;
+ case [Exception e]:
+ ExceptionDispatchInfo.Capture(e).Throw();
+ return;
+ default:
+ throw new AggregateException(message, exceptions);
}
-
- return ExceptionFormatter.GetFormattedException(exception);
}
}
}
diff --git a/src/WebJobs.Script/Host/IWorkerFunctionMetadataProvider.cs b/src/WebJobs.Script/Host/IWorkerFunctionMetadataProvider.cs
index ace3c61ed1..b19b890ec8 100644
--- a/src/WebJobs.Script/Host/IWorkerFunctionMetadataProvider.cs
+++ b/src/WebJobs.Script/Host/IWorkerFunctionMetadataProvider.cs
@@ -9,14 +9,14 @@
namespace Microsoft.Azure.WebJobs.Script
{
///
- /// Defines an interface for fetching function metadata from Out-of-Proc language workers
+ /// Defines an interface for fetching function metadata from Out-of-Proc language workers.
///
internal interface IWorkerFunctionMetadataProvider
{
ImmutableDictionary> FunctionErrors { get; }
///
- /// Attempts to get function metadata from Out-of-Proc language workers
+ /// Attempts to get function metadata from Out-of-Proc language workers.
///
/// FunctionMetadataResult that either contains the function metadata or indicates that a fall back option for fetching metadata should be used
Task GetFunctionMetadataAsync(IEnumerable workerConfigs, bool forceRefresh = false);
diff --git a/src/WebJobs.Script/Host/ScriptHostState.cs b/src/WebJobs.Script/Host/ScriptHostState.cs
index 2816f4f706..4fe31b9f95 100644
--- a/src/WebJobs.Script/Host/ScriptHostState.cs
+++ b/src/WebJobs.Script/Host/ScriptHostState.cs
@@ -6,7 +6,7 @@ namespace Microsoft.Azure.WebJobs.Script
public enum ScriptHostState
{
///
- /// The host has not yet been created
+ /// The host has not yet been created.
///
Default,
@@ -28,7 +28,7 @@ public enum ScriptHostState
Running,
///
- /// The host is in an error state
+ /// The host is in an error state.
///
Error,
@@ -43,7 +43,7 @@ public enum ScriptHostState
Stopped,
///
- /// The host is offline
+ /// The host is offline.
///
Offline
}
diff --git a/src/WebJobs.Script/Host/WorkerFunctionMetadataProvider.cs b/src/WebJobs.Script/Host/WorkerFunctionMetadataProvider.cs
index 22c2c7a0b3..d61b68db10 100644
--- a/src/WebJobs.Script/Host/WorkerFunctionMetadataProvider.cs
+++ b/src/WebJobs.Script/Host/WorkerFunctionMetadataProvider.cs
@@ -78,7 +78,7 @@ public async Task GetFunctionMetadataAsync(IEnumerable GetFunctionMetadataAsync(IEnumerable errors = null;
foreach (string workerId in channels.Keys.ToList())
{
if (channels.TryGetValue(workerId, out TaskCompletionSource languageWorkerChannelTask))
@@ -128,7 +129,7 @@ public async Task GetFunctionMetadataAsync(IEnumerable ValidateFunctionAppFormat(_scriptOptions.CurrentValue.ScriptPath, _logger, _environment));
@@ -139,9 +140,13 @@ public async Task GetFunctionMetadataAsync(IEnumerable();
- var locator = new ScriptStartupTypeLocator(applicationOptions.ScriptPath, loggerFactory.CreateLogger(), bundleManager, metadataServiceManager, metricsLogger, extensionRequirementOptions);
+ var locator = new ScriptStartupTypeLocator(
+ applicationOptions.ScriptPath,
+ loggerFactory.CreateLogger(),
+ bundleManager,
+ metadataServiceManager,
+ metricsLogger,
+ SystemEnvironment.Instance,
+ extensionRequirementOptions);
// The locator (and thus the bundle manager) need to be created now in order to configure app configuration.
// Store them so they do not need to be re-created later when configuring services.
diff --git a/src/WebJobs.Script/Workers/ProcessManagement/IWorkerProcess.cs b/src/WebJobs.Script/Workers/ProcessManagement/IWorkerProcess.cs
index dd5222e341..a39dad9cf5 100644
--- a/src/WebJobs.Script/Workers/ProcessManagement/IWorkerProcess.cs
+++ b/src/WebJobs.Script/Workers/ProcessManagement/IWorkerProcess.cs
@@ -2,8 +2,8 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Diagnostics;
+using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Azure.WebJobs.Script.Scale;
namespace Microsoft.Azure.WebJobs.Script.Workers
{
@@ -13,7 +13,9 @@ public interface IWorkerProcess
Process Process { get; }
- Task StartProcessAsync();
+ Task StartProcessAsync(CancellationToken cancellationToken = default);
+
+ Task WaitForExitAsync(CancellationToken cancellationToken = default);
void WaitForProcessExitInMilliSeconds(int waitTime);
}
diff --git a/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs b/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs
index 94a5208486..139055c916 100644
--- a/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs
+++ b/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs
@@ -7,6 +7,7 @@
using System.IO;
using System.Linq;
using System.Reactive.Linq;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Scale;
using Microsoft.Azure.WebJobs.Logging;
@@ -33,10 +34,11 @@ internal abstract class WorkerProcess : IWorkerProcess, IDisposable
private readonly IEnvironment _environment;
private readonly IOptionsMonitor _scriptApplicationHostOptions;
- private bool _useStdErrorStreamForErrorsOnly;
- private Queue _processStdErrDataQueue = new Queue(3);
+ private readonly object _syncLock = new();
+ private readonly bool _useStdErrorStreamForErrorsOnly;
+ private Queue _processStdErrDataQueue = new(3);
private IHostProcessMonitor _processMonitor;
- private object _syncLock = new object();
+ private TaskCompletionSource _processExit; // used to hold custom exceptions on non-success exit.
internal WorkerProcess(IScriptEventManager eventManager, IProcessRegistry processRegistry, ILogger workerProcessLogger, IWorkerConsoleLogSource consoleLogSource, IMetricsLogger metricsLogger,
IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IEnvironment environment, IOptionsMonitor scriptApplicationHostOptions, bool useStdErrStreamForErrorsOnly = false)
@@ -69,8 +71,9 @@ internal WorkerProcess(IScriptEventManager eventManager, IProcessRegistry proces
internal abstract Process CreateWorkerProcess();
- public Task StartProcessAsync()
+ public Task StartProcessAsync(CancellationToken cancellationToken = default)
{
+ cancellationToken.ThrowIfCancellationRequested();
using (_metricsLogger.LatencyEvent(MetricEventNames.ProcessStart))
{
Process = CreateWorkerProcess();
@@ -79,11 +82,12 @@ public Task StartProcessAsync()
AssignUserExecutePermissionsIfNotExists();
}
+ _processExit = new();
try
{
- Process.ErrorDataReceived += (sender, e) => OnErrorDataReceived(sender, e);
- Process.OutputDataReceived += (sender, e) => OnOutputDataReceived(sender, e);
- Process.Exited += (sender, e) => OnProcessExited(sender, e);
+ Process.ErrorDataReceived += OnErrorDataReceived;
+ Process.OutputDataReceived += OnOutputDataReceived;
+ Process.Exited += OnProcessExited;
Process.EnableRaisingEvents = true;
string sanitizedArguments = Sanitizer.Sanitize(Process.StartInfo.Arguments);
@@ -103,12 +107,24 @@ public Task StartProcessAsync()
}
catch (Exception ex)
{
+ _processExit.TrySetException(ex);
_workerProcessLogger.LogError(ex, $"Failed to start Worker Channel. Process fileName: {Process.StartInfo.FileName}");
return Task.FromException(ex);
}
}
}
+ public Task WaitForExitAsync(CancellationToken cancellationToken = default)
+ {
+ if (_processExit is { } tcs)
+ {
+ // We use a TaskCompletionSource (and not Process.WaitForExitAsync) so we can propagate our custom exceptions.
+ return tcs.Task.WaitAsync(cancellationToken);
+ }
+
+ throw new InvalidOperationException("Process has not been started yet.");
+ }
+
private void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
@@ -159,42 +175,58 @@ private void OnProcessExited(object sender, EventArgs e)
if (Disposing)
{
- // No action needed
return;
}
try
{
- if (Process.ExitCode == WorkerConstants.SuccessExitCode)
- {
- Process.WaitForExit();
- Process.Close();
- }
- else if (Process.ExitCode == WorkerConstants.IntentionalRestartExitCode)
+ ThrowIfExitError();
+
+ Process.WaitForExit();
+ if (Process.ExitCode == WorkerConstants.IntentionalRestartExitCode)
{
HandleWorkerProcessRestart();
}
- else
- {
- string exceptionMessage = string.Join(",", _processStdErrDataQueue.Where(s => !string.IsNullOrEmpty(s)));
- string sanitizedExceptionMessage = Sanitizer.Sanitize(exceptionMessage);
- var processExitEx = new WorkerProcessExitException($"{Process.StartInfo.FileName} exited with code {Process.ExitCode} (0x{Process.ExitCode.ToString("X")})", new Exception(sanitizedExceptionMessage));
- processExitEx.ExitCode = Process.ExitCode;
- processExitEx.Pid = Process.Id;
- HandleWorkerProcessExitError(processExitEx);
- }
+ }
+ catch (WorkerProcessExitException processExitEx)
+ {
+ _processExit.TrySetException(processExitEx);
+ HandleWorkerProcessExitError(processExitEx);
}
catch (Exception exc)
{
- _workerProcessLogger?.LogDebug(exc, "Exception on worker process exit. Process id: {processId}", Process?.Id);
// ignore process is already disposed
+ _processExit.TrySetException(exc);
+ _workerProcessLogger?.LogDebug(exc, "Exception on worker process exit. Process id: {processId}", Process?.Id);
}
finally
{
+ _processExit.TrySetResult();
UnregisterFromProcessMonitor();
+ Process.Close();
}
}
+ private void ThrowIfExitError()
+ {
+ if (Process.ExitCode is WorkerConstants.SuccessExitCode or WorkerConstants.IntentionalRestartExitCode)
+ {
+ return;
+ }
+
+ string exceptionMessage = string.Join(",", _processStdErrDataQueue.Where(s => !string.IsNullOrEmpty(s)));
+ string sanitizedExceptionMessage = Sanitizer.Sanitize(exceptionMessage);
+ WorkerProcessExitException processExitEx = new(
+ $"{Process.StartInfo.FileName} exited with code {Process.ExitCode} (0x{Process.ExitCode:X})",
+ new Exception(sanitizedExceptionMessage))
+ {
+ ExitCode = Process.ExitCode,
+ Pid = Process.Id
+ };
+
+ throw processExitEx;
+ }
+
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
diff --git a/src/WebJobs.Script/Workers/Rpc/RpcWorkerProcess.cs b/src/WebJobs.Script/Workers/Rpc/RpcWorkerProcess.cs
index 76a9766503..189cf58341 100644
--- a/src/WebJobs.Script/Workers/Rpc/RpcWorkerProcess.cs
+++ b/src/WebJobs.Script/Workers/Rpc/RpcWorkerProcess.cs
@@ -26,22 +26,23 @@ internal class RpcWorkerProcess : WorkerProcess
private readonly IOptions _hostingConfigOptions;
private readonly IEnvironment _environment;
- internal RpcWorkerProcess(string runtime,
- string workerId,
- string rootScriptPath,
- Uri serverUri,
- RpcWorkerConfig rpcWorkerConfig,
- IScriptEventManager eventManager,
- IWorkerProcessFactory processFactory,
- IProcessRegistry processRegistry,
- ILogger workerProcessLogger,
- IWorkerConsoleLogSource consoleLogSource,
- IMetricsLogger metricsLogger,
- IServiceProvider serviceProvider,
- IOptions hostingConfigOptions,
- IEnvironment environment,
- IOptionsMonitor scriptApplicationHostOptions,
- ILoggerFactory loggerFactory)
+ internal RpcWorkerProcess(
+ string runtime,
+ string workerId,
+ string rootScriptPath,
+ Uri serverUri,
+ RpcWorkerConfig rpcWorkerConfig,
+ IScriptEventManager eventManager,
+ IWorkerProcessFactory processFactory,
+ IProcessRegistry processRegistry,
+ ILogger workerProcessLogger,
+ IWorkerConsoleLogSource consoleLogSource,
+ IMetricsLogger metricsLogger,
+ IServiceProvider serviceProvider,
+ IOptions hostingConfigOptions,
+ IEnvironment environment,
+ IOptionsMonitor scriptApplicationHostOptions,
+ ILoggerFactory loggerFactory)
: base(eventManager, processRegistry, workerProcessLogger, consoleLogSource, metricsLogger, serviceProvider, loggerFactory, environment,
scriptApplicationHostOptions, rpcWorkerConfig.Description.UseStdErrorStreamForErrorsOnly)
{
@@ -74,23 +75,33 @@ internal override Process CreateWorkerProcess()
internal override void HandleWorkerProcessExitError(WorkerProcessExitException rpcWorkerProcessExitException)
{
+ ArgumentNullException.ThrowIfNull(rpcWorkerProcessExitException);
if (Disposing)
{
return;
}
- if (rpcWorkerProcessExitException == null)
- {
- throw new ArgumentNullException(nameof(rpcWorkerProcessExitException));
- }
+
// The subscriber of WorkerErrorEvent is expected to Dispose() the errored channel
- _workerProcessLogger.LogError(rpcWorkerProcessExitException, $"Language Worker Process exited. Pid={rpcWorkerProcessExitException.Pid}.", _workerProcessArguments.ExecutablePath);
- _eventManager.Publish(new WorkerErrorEvent(_runtime, _workerId, rpcWorkerProcessExitException));
+ _workerProcessLogger.LogError(rpcWorkerProcessExitException, $"Language Worker Process exited. Pid={rpcWorkerProcessExitException.Pid}.", _workerProcessArguments?.ExecutablePath);
+ PublishNoThrow(new WorkerErrorEvent(_runtime, _workerId, rpcWorkerProcessExitException));
}
internal override void HandleWorkerProcessRestart()
{
_workerProcessLogger?.LogInformation("Language Worker Process exited and needs to be restarted.");
- _eventManager.Publish(new WorkerRestartEvent(_runtime, _workerId));
+ PublishNoThrow(new WorkerRestartEvent(_runtime, _workerId));
+ }
+
+ private void PublishNoThrow(RpcChannelEvent @event)
+ {
+ try
+ {
+ _eventManager.Publish(@event);
+ }
+ catch (Exception ex)
+ {
+ _workerProcessLogger.LogWarning(ex, "Failed to publish RpcChannelEvent.");
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/test/WebJobs.Script.Tests.Integration/TestFunctionHost.cs b/test/WebJobs.Script.Tests.Integration/TestFunctionHost.cs
index 317c33d4c7..649674b965 100644
--- a/test/WebJobs.Script.Tests.Integration/TestFunctionHost.cs
+++ b/test/WebJobs.Script.Tests.Integration/TestFunctionHost.cs
@@ -104,35 +104,35 @@ public TestFunctionHost(string scriptPath, string logPath, string testDataPath =
.AddFilter("Azure.Core", LogLevel.Warning);
})
.ConfigureServices(services =>
- {
- services.Replace(new ServiceDescriptor(typeof(ISecretManagerProvider), new TestSecretManagerProvider(new TestSecretManager())));
- services.Replace(new ServiceDescriptor(typeof(IOptions), sp =>
- {
- _hostOptions.RootServiceProvider = sp;
- return new OptionsWrapper(_hostOptions);
- }, ServiceLifetime.Singleton));
- services.Replace(new ServiceDescriptor(typeof(IOptionsMonitor), sp =>
- {
- _hostOptions.RootServiceProvider = sp;
- return TestHelpers.CreateOptionsMonitor(_hostOptions);
- }, ServiceLifetime.Singleton));
- services.Replace(new ServiceDescriptor(typeof(IExtensionBundleManager), new TestExtensionBundleManager()));
- services.Replace(new ServiceDescriptor(typeof(IFunctionMetadataManager), sp =>
- {
- var montior = sp.GetService>();
- var scriptManager = sp.GetService();
- var loggerFactory = sp.GetService();
- var environment = sp.GetService();
-
- return GetMetadataManager(montior, scriptManager, loggerFactory, environment);
- }, ServiceLifetime.Singleton));
-
- services.AddSingleton();
- services.SkipDependencyValidation();
-
- // Allows us to configure services as the last step, thereby overriding anything
- services.AddSingleton(new PostConfigureServices(configureWebHostServices));
- })
+ {
+ services.Replace(new ServiceDescriptor(typeof(ISecretManagerProvider), new TestSecretManagerProvider(new TestSecretManager())));
+ services.Replace(new ServiceDescriptor(typeof(IOptions), sp =>
+ {
+ _hostOptions.RootServiceProvider = sp;
+ return new OptionsWrapper(_hostOptions);
+ }, ServiceLifetime.Singleton));
+ services.Replace(new ServiceDescriptor(typeof(IOptionsMonitor), sp =>
+ {
+ _hostOptions.RootServiceProvider = sp;
+ return TestHelpers.CreateOptionsMonitor(_hostOptions);
+ }, ServiceLifetime.Singleton));
+ services.Replace(new ServiceDescriptor(typeof(IExtensionBundleManager), new TestExtensionBundleManager()));
+ services.Replace(new ServiceDescriptor(typeof(IFunctionMetadataManager), sp =>
+ {
+ var montior = sp.GetService>();
+ var scriptManager = sp.GetService();
+ var loggerFactory = sp.GetService();
+ var environment = sp.GetService();
+
+ return GetMetadataManager(montior, scriptManager, loggerFactory, environment);
+ }, ServiceLifetime.Singleton));
+
+ services.AddSingleton();
+ services.SkipDependencyValidation();
+
+ // Allows us to configure services as the last step, thereby overriding anything
+ services.AddSingleton(new PostConfigureServices(configureWebHostServices));
+ })
.ConfigureScriptHostWebJobsBuilder(scriptHostWebJobsBuilder =>
{
/// REVIEW THIS
diff --git a/test/WebJobs.Script.Tests.Shared/TempDirectory.cs b/test/WebJobs.Script.Tests.Shared/TempDirectory.cs
index 22acb59fa5..4cb858159b 100644
--- a/test/WebJobs.Script.Tests.Shared/TempDirectory.cs
+++ b/test/WebJobs.Script.Tests.Shared/TempDirectory.cs
@@ -19,25 +19,9 @@ public TempDirectory(string path)
Directory.CreateDirectory(path);
}
- ~TempDirectory()
- {
- Dispose(false);
- }
-
public string Path { get; }
public void Dispose()
- {
- GC.SuppressFinalize(this);
- Dispose(true);
- }
-
- private void Dispose(bool disposing)
- {
- DeleteDirectory();
- }
-
- private void DeleteDirectory()
{
try
{
diff --git a/test/WebJobs.Script.Tests.Shared/TestHelpers.cs b/test/WebJobs.Script.Tests.Shared/TestHelpers.cs
index ca98a157e7..dae1a68104 100644
--- a/test/WebJobs.Script.Tests.Shared/TestHelpers.cs
+++ b/test/WebJobs.Script.Tests.Shared/TestHelpers.cs
@@ -289,23 +289,26 @@ private static async Task ReadAllLinesSafeAsync(string logFile)
public static async Task ReadStreamToEnd(Stream stream)
{
stream.Position = 0;
- using (var sr = new StreamReader(stream))
- {
- return await sr.ReadToEndAsync();
- }
+ using var sr = new StreamReader(stream);
+ return await sr.ReadToEndAsync();
}
- public static IList GetTestWorkerConfigs(bool includeDllWorker = false, int processCountValue = 1,
- TimeSpan? processStartupInterval = null, TimeSpan? processRestartInterval = null, TimeSpan? processShutdownTimeout = null, bool workerIndexing = false)
+ public static IList GetTestWorkerConfigs(
+ bool includeDllWorker = false,
+ int processCountValue = 1,
+ TimeSpan? processStartupInterval = null,
+ TimeSpan? processRestartInterval = null,
+ TimeSpan? processShutdownTimeout = null,
+ bool workerIndexing = false)
{
var defaultCountOptions = new WorkerProcessCountOptions();
TimeSpan startupInterval = processStartupInterval ?? defaultCountOptions.ProcessStartupInterval;
TimeSpan restartInterval = processRestartInterval ?? defaultCountOptions.ProcessRestartInterval;
TimeSpan shutdownTimeout = processShutdownTimeout ?? defaultCountOptions.ProcessShutdownTimeout;
- var workerConfigs = new List
- {
- new RpcWorkerConfig
+ List workerConfigs =
+ [
+ new()
{
Description = GetTestWorkerDescription("node", ".js", workerIndexing),
CountOptions = new WorkerProcessCountOptions
@@ -316,7 +319,7 @@ public static IList GetTestWorkerConfigs(bool includeDllWorker
ProcessShutdownTimeout = shutdownTimeout
}
},
- new RpcWorkerConfig
+ new()
{
Description = GetTestWorkerDescription("java", ".jar", workerIndexing),
CountOptions = new WorkerProcessCountOptions
@@ -327,7 +330,7 @@ public static IList GetTestWorkerConfigs(bool includeDllWorker
ProcessShutdownTimeout = shutdownTimeout
}
}
- };
+ ];
// Allow tests to have a worker that claims the .dll extension.
if (includeDllWorker)
diff --git a/test/WebJobs.Script.Tests/Configuration/DefaultDependencyValidatorTests.cs b/test/WebJobs.Script.Tests/Configuration/DefaultDependencyValidatorTests.cs
index 476bbc33af..3de0c8e665 100644
--- a/test/WebJobs.Script.Tests/Configuration/DefaultDependencyValidatorTests.cs
+++ b/test/WebJobs.Script.Tests/Configuration/DefaultDependencyValidatorTests.cs
@@ -28,7 +28,7 @@ public class DefaultDependencyValidatorTests
[Fact]
public async Task Validator_AllValid()
{
- LogMessage invalidServicesMessage = await RunTest();
+ LogMessage invalidServicesMessage = await RunTestAsync();
string msg = $"If you have registered new dependencies, make sure to update the DependencyValidator. Invalid Services:{Environment.NewLine}";
Assert.True(invalidServicesMessage == null, msg + invalidServicesMessage?.Exception?.ToString());
@@ -37,7 +37,7 @@ public async Task Validator_AllValid()
[Fact]
public async Task Validator_InvalidServices_ThrowsException()
{
- LogMessage invalidServicesMessage = await RunTest(configureJobHost: s =>
+ LogMessage invalidServicesMessage = await RunTestAsync(configureJobHost: s =>
{
s.AddSingleton();
s.AddSingleton();
@@ -61,7 +61,7 @@ public async Task Validator_InvalidServices_ThrowsException()
[Fact]
public async Task Validator_NoJobHost()
{
- LogMessage invalidServicesMessage = await RunTest(configureWebHost: s =>
+ LogMessage invalidServicesMessage = await RunTestAsync(configureWebHost: s =>
{
// This will force us to skip host startup (which removes the JobHost)
s.AddSingleton();
@@ -71,10 +71,10 @@ public async Task Validator_NoJobHost()
Assert.True(invalidServicesMessage == null, msg + invalidServicesMessage?.Exception?.ToString());
}
- private async Task RunTest(Action configureWebHost = null, Action configureJobHost = null, bool expectSuccess = true)
+ private async Task RunTestAsync(Action configureWebHost = null, Action configureJobHost = null, bool expectSuccess = true)
{
LogMessage invalidServicesMessage = null;
- TestLoggerProvider loggerProvider = new TestLoggerProvider();
+ TestLoggerProvider loggerProvider = new();
var builder = Program.CreateWebHostBuilder(null)
.ConfigureLogging(b =>
diff --git a/test/WebJobs.Script.Tests/Configuration/FunctionsHostingConfigOptionsSetupTest.cs b/test/WebJobs.Script.Tests/Configuration/FunctionsHostingConfigOptionsSetupTest.cs
index bac23b01c4..bf5dc64ca8 100644
--- a/test/WebJobs.Script.Tests/Configuration/FunctionsHostingConfigOptionsSetupTest.cs
+++ b/test/WebJobs.Script.Tests/Configuration/FunctionsHostingConfigOptionsSetupTest.cs
@@ -46,7 +46,7 @@ public void Configure_DoesNotSet_Options_IfFileEmpty()
[Fact]
public void Configure_DoesNotSet_Options_IfFileDoesNotExist()
{
- string fileName = Path.Combine("C://settings.txt");
+ string fileName = Path.Combine(Path.GetTempPath(), "settings.txt");
IConfiguration configuraton = GetConfiguration(fileName, string.Empty);
FunctionsHostingConfigOptionsSetup setup = new FunctionsHostingConfigOptionsSetup(configuraton);
FunctionsHostingConfigOptions options = new FunctionsHostingConfigOptions();
diff --git a/test/WebJobs.Script.Tests/Description/FunctionInvokerBaseTests.cs b/test/WebJobs.Script.Tests/Description/FunctionInvokerBaseTests.cs
index ef62d61f7e..b957a0e53c 100644
--- a/test/WebJobs.Script.Tests/Description/FunctionInvokerBaseTests.cs
+++ b/test/WebJobs.Script.Tests/Description/FunctionInvokerBaseTests.cs
@@ -9,53 +9,49 @@
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Azure.WebJobs.Script.Description;
using Microsoft.Azure.WebJobs.Script.Diagnostics;
-using Microsoft.Azure.WebJobs.Script.Eventing;
using Microsoft.Azure.WebJobs.Script.Metrics;
using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics;
-using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.WebJobs.Script.Tests;
-using Moq;
using Newtonsoft.Json.Linq;
using Xunit;
namespace Microsoft.Azure.WebJobs.Script.Tests
{
- public class FunctionInvokerBaseTests : IDisposable
+ public sealed class FunctionInvokerBaseTests : IDisposable
{
- private MockInvoker _invoker;
- private IHost _host;
- private ScriptHost _scriptHost;
- private TestMetricsLogger _metricsLogger;
- private TestLoggerProvider _testLoggerProvider;
+ private readonly TestMetricsLogger _metricsLogger = new();
+ private readonly TestLoggerProvider _testLoggerProvider = new();
+ private readonly MockInvoker _invoker;
+ private readonly IHost _host;
+ private readonly ScriptHost _scriptHost;
public FunctionInvokerBaseTests()
{
- _metricsLogger = new TestMetricsLogger();
- _testLoggerProvider = new TestLoggerProvider();
-
- ILoggerFactory loggerFactory = new LoggerFactory();
+ LoggerFactory loggerFactory = new();
loggerFactory.AddProvider(_testLoggerProvider);
- var eventManager = new ScriptEventManager();
-
- var metadata = new FunctionMetadata
+ FunctionMetadata metadata = new()
{
Name = "TestFunction",
ScriptFile = "index.js",
Language = "node"
};
+
JObject binding = JObject.FromObject(new
{
type = "manualTrigger",
name = "manual",
direction = "in"
});
+
metadata.Bindings.Add(BindingMetadata.Create(binding));
- var metadataManager = new MockMetadataManager(new[] { metadata });
+ MockMetadataManager metadataManager = new([metadata]);
+
+ // TODO: Can we instantiate a ScriptHost directly?
_host = new HostBuilder()
.ConfigureDefaultTestWebScriptHost()
.ConfigureServices(s =>
@@ -66,10 +62,7 @@ public FunctionInvokerBaseTests()
.Build();
_scriptHost = _host.GetScriptHost();
- _scriptHost.InitializeAsync().Wait();
-
var hostMetrics = _host.Services.GetService();
-
_invoker = new MockInvoker(_scriptHost, _metricsLogger, hostMetrics, metadataManager, metadata, loggerFactory);
}
@@ -182,17 +175,16 @@ await Assert.ThrowsAsync(async () =>
Assert.Equal(startLatencyEvent, completedLatencyEvent);
}
- protected virtual void Dispose(bool disposing)
+ public void Dispose()
{
- if (disposing)
+ try
{
_host?.Dispose();
}
- }
-
- public void Dispose()
- {
- Dispose(true);
+ catch (Exception)
+ {
+ // this might throw due to invalid setup.
+ }
}
private class MockInvoker : FunctionInvokerBase
diff --git a/test/WebJobs.Script.Tests/Extensions/EnvironmentExtensionsTests.cs b/test/WebJobs.Script.Tests/Extensions/EnvironmentExtensionsTests.cs
index 9ea8bcf5d5..f0743813bd 100644
--- a/test/WebJobs.Script.Tests/Extensions/EnvironmentExtensionsTests.cs
+++ b/test/WebJobs.Script.Tests/Extensions/EnvironmentExtensionsTests.cs
@@ -14,16 +14,24 @@ namespace Microsoft.Azure.WebJobs.Script.Tests.Extensions
public class EnvironmentExtensionsTests
{
[Fact]
- public void GetEffectiveCoresCount_ReturnsExpectedResult()
+ public void GetEffectiveCoresCount_NoSku_ReturnsExpectedResult()
{
- TestEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
Assert.Equal(Environment.ProcessorCount, EnvironmentExtensions.GetEffectiveCoresCount(env));
+ }
- env.Clear();
+ [Fact]
+ public void GetEffectiveCoresCount_DynamicSku_ReturnsExpectedResult()
+ {
+ TestEnvironment env = new();
env.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteSku, ScriptConstants.DynamicSku);
Assert.Equal(1, EnvironmentExtensions.GetEffectiveCoresCount(env));
+ }
- env.Clear();
+ [Fact]
+ public void GetEffectiveCoresCount_DynamicSkuWithInstanceId_ReturnsExpectedResult()
+ {
+ TestEnvironment env = new();
env.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteSku, ScriptConstants.DynamicSku);
env.SetEnvironmentVariable(EnvironmentSettingNames.RoleInstanceId, "dw0SmallDedicatedWebWorkerRole_hr0HostRole-0-VM-1");
Assert.Equal(Environment.ProcessorCount, EnvironmentExtensions.GetEffectiveCoresCount(env));
@@ -33,7 +41,7 @@ public void GetEffectiveCoresCount_ReturnsExpectedResult()
[Trait(TestTraits.Group, TestTraits.AdminIsolationTests)]
public void IsAdminIsolationEnabled_ReturnsExpectedResult()
{
- TestEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
Assert.False(EnvironmentExtensions.IsAdminIsolationEnabled(env));
env.SetEnvironmentVariable(EnvironmentSettingNames.FunctionsAdminIsolationEnabled, "0");
@@ -47,9 +55,9 @@ public void IsAdminIsolationEnabled_ReturnsExpectedResult()
[InlineData("dw0SmallDedicatedWebWorkerRole_hr0HostRole-0-VM-1", true)]
[InlineData(null, false)]
[InlineData("", false)]
- public void IsVMSS_RetrunsExpectedResult(string roleInstanceId, bool expected)
+ public void IsVMSS_ReturnsExpectedResult(string roleInstanceId, bool expected)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (roleInstanceId != null)
{
env.SetEnvironmentVariable(EnvironmentSettingNames.RoleInstanceId, roleInstanceId);
@@ -67,7 +75,7 @@ public void IsVMSS_RetrunsExpectedResult(string roleInstanceId, bool expected)
[InlineData("Foo,Bar", false)]
public void IsAzureMonitorEnabled_ReturnsExpectedResult(string value, bool expected)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (value != null)
{
env.SetEnvironmentVariable(EnvironmentSettingNames.AzureMonitorCategories, value);
@@ -82,7 +90,7 @@ public void IsAzureMonitorEnabled_ReturnsExpectedResult(string value, bool expec
[InlineData(null, "")]
public void GetAntaresComputerName_ReturnsExpectedResult(string computerName, string expectedComputerName)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (!string.IsNullOrEmpty(expectedComputerName))
{
env.SetEnvironmentVariable(EnvironmentSettingNames.AntaresComputerName, computerName);
@@ -102,7 +110,7 @@ public void GetAntaresComputerName_ReturnsExpectedResult(string computerName, st
[InlineData(false, "", null, "")]
public void GetInstanceId_ReturnsExpectedResult(bool isLinuxConsumption, string containerName, string websiteInstanceId, string expectedValue)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (isLinuxConsumption)
{
if (!string.IsNullOrEmpty(containerName))
@@ -133,7 +141,7 @@ public void GetInstanceId_ReturnsExpectedResult(bool isLinuxConsumption, string
[InlineData(false, false, "89.0.7.73", null, "")]
public void GetAntaresVersion_ReturnsExpectedResult(bool isLinuxConsumption, bool isLinuxAppService, string platformVersionLinux, string platformVersionWindows, string expectedValue)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (isLinuxConsumption)
{
env.SetEnvironmentVariable(EnvironmentSettingNames.ContainerName, "RandomContainerName");
@@ -166,7 +174,7 @@ public void GetAntaresVersion_ReturnsExpectedResult(bool isLinuxConsumption, boo
[InlineData(true, true, false, true)]
public void IsConsumptionSku_ReturnsExpectedResult(bool isLinuxConsumption, bool isWindowsConsumption, bool isFlexConsumption, bool expectedValue)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (isLinuxConsumption)
{
env.SetEnvironmentVariable(EnvironmentSettingNames.ContainerName, "RandomContainerName");
@@ -197,7 +205,7 @@ public void IsConsumptionSku_ReturnsExpectedResult(bool isLinuxConsumption, bool
[InlineData("", "", "", "", false)]
public void IsFlexConsumptionSku_ReturnsExpectedResult(string sku, string websiteInstanceId, string containerName, string legionServiceHost, bool expected)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
env.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteSku, sku);
env.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteInstanceId, websiteInstanceId);
env.SetEnvironmentVariable(EnvironmentSettingNames.ContainerName, containerName);
@@ -218,7 +226,7 @@ public void IsFlexConsumptionSku_ReturnsExpectedResult(string sku, string websit
[InlineData("RandomContainerName", null, null, true, null, false)] // Managed App Environment
public void IsAnyLinuxConsumption_ReturnsExpectedResult(string containerName, string podName, string legionServiceHostName, bool isManagedAppEnvironment, string sku, bool expectedValue)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (!string.IsNullOrEmpty(containerName))
{
env.SetEnvironmentVariable(ContainerName, containerName);
@@ -253,7 +261,7 @@ public void IsAnyLinuxConsumption_ReturnsExpectedResult(string containerName, st
[InlineData(false, false, false)]
public void IsAnyKubernetesEnvironment_ReturnsExpectedResult(bool isKubernetesManagedHosting, bool isManagedAppEnvironment, bool expectedValue)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (isKubernetesManagedHosting)
{
env.SetEnvironmentVariable(KubernetesServiceHost, "10.0.0.1");
@@ -273,7 +281,7 @@ public void IsAnyKubernetesEnvironment_ReturnsExpectedResult(bool isKubernetesMa
[InlineData(false, false)]
public void IsManagedAppEnvironment_ReturnsExpectedResult(bool isManagedAppEnvironment, bool expectedValue)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (isManagedAppEnvironment)
{
env.SetEnvironmentVariable(EnvironmentSettingNames.ManagedEnvironment, "true");
@@ -295,7 +303,7 @@ public void IsManagedAppEnvironment_ReturnsExpectedResult(bool isManagedAppEnvir
[InlineData("RandomPodName", "RandomLegionServiceHostName", ScriptConstants.DynamicSku, null, true)]
public void IsLinuxConsumptionOnLegion_ReturnsExpectedResult(string websitePodName, string legionServiceHostName, string websiteSku, string websiteSkuName, bool expectedValue)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (!string.IsNullOrEmpty(websitePodName))
{
@@ -330,7 +338,7 @@ public void IsLinuxConsumptionOnLegion_ReturnsExpectedResult(string websitePodNa
[InlineData(null, null, false)]
public void IsV2CompatMode(string extensionVersion, string compatMode, bool expected)
{
- IEnvironment env = new TestEnvironment();
+ TestEnvironment env = new();
if (extensionVersion != null)
{
@@ -353,7 +361,7 @@ public void IsV2CompatMode(string extensionVersion, string compatMode, bool expe
[InlineData("k8se-apps", "10.0.0.1", true, false)]
public void IsKubernetesManagedHosting_ReturnsExpectedResult(string podNamespace, string kubernetesServiceHost, bool isManagedAppEnvironment, bool expected)
{
- var environment = new TestEnvironment();
+ TestEnvironment environment = new();
environment.SetEnvironmentVariable(KubernetesServiceHost, kubernetesServiceHost);
environment.SetEnvironmentVariable(PodNamespace, podNamespace);
if (isManagedAppEnvironment)
@@ -374,7 +382,7 @@ public void IsKubernetesManagedHosting_ReturnsExpectedResult(string podNamespace
[InlineData("", "")]
public void Returns_WorkerRuntime(string workerRuntime, string expectedWorkerRuntime)
{
- var environment = new TestEnvironment();
+ TestEnvironment environment = new();
environment.SetEnvironmentVariable(FunctionWorkerRuntime, workerRuntime);
Assert.Equal(expectedWorkerRuntime, environment.GetFunctionsWorkerRuntime());
}
@@ -388,9 +396,9 @@ public void Returns_WorkerRuntime(string workerRuntime, string expectedWorkerRun
[InlineData("", "")]
public void Returns_FunctionsExtensionVersion(string functionsExtensionVersion, string functionsExtensionVersionExpected)
{
- var enviroment = new TestEnvironment();
- enviroment.SetEnvironmentVariable(FunctionsExtensionVersion, functionsExtensionVersion);
- Assert.Equal(functionsExtensionVersionExpected, enviroment.GetFunctionsExtensionVersion());
+ TestEnvironment environment = new();
+ environment.SetEnvironmentVariable(FunctionsExtensionVersion, functionsExtensionVersion);
+ Assert.Equal(functionsExtensionVersionExpected, environment.GetFunctionsExtensionVersion());
}
[Theory]
@@ -407,7 +415,7 @@ public void Returns_FunctionsExtensionVersion(string functionsExtensionVersion,
[InlineData("", false, false, false)]
public void Returns_SupportsAzureFileShareMount(string workerRuntime, bool useLowerCase, bool useUpperCase, bool supportsAzureFileShareMount)
{
- var environment = new TestEnvironment();
+ TestEnvironment environment = new();
if (useLowerCase && !useUpperCase)
{
workerRuntime = workerRuntime.ToLowerInvariant();
@@ -427,7 +435,7 @@ public void Returns_SupportsAzureFileShareMount(string workerRuntime, bool useLo
[InlineData("", "")]
public void Returns_GetHttpLeaderEndpoint(string httpLeaderEndpoint, string expected)
{
- var environment = new TestEnvironment();
+ TestEnvironment environment = new();
if (!string.IsNullOrEmpty(httpLeaderEndpoint))
{
@@ -448,7 +456,7 @@ public void Returns_GetHttpLeaderEndpoint(string httpLeaderEndpoint, string expe
[InlineData("10.0.0.1", null, true)]
public void IsDrainOnApplicationStopping_ReturnsExpectedResult(string serviceHostValue, string drainOnStoppingValue, bool expected)
{
- var environment = new TestEnvironment();
+ TestEnvironment environment = new();
environment.SetEnvironmentVariable(KubernetesServiceHost, serviceHostValue);
environment.SetEnvironmentVariable(DrainOnApplicationStopping, drainOnStoppingValue);
Assert.Equal(expected, environment.DrainOnApplicationStoppingEnabled());
@@ -461,7 +469,7 @@ public void IsDrainOnApplicationStopping_ReturnsExpectedResult(string serviceHos
[InlineData("true", null, true)]
public void IsWorkerDynamicConcurrencyEnabled_ReturnsExpectedResult(string concurrencyEnabledValue, string processCountValue, bool expected)
{
- var environment = new TestEnvironment();
+ TestEnvironment environment = new();
environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionsWorkerDynamicConcurrencyEnabled, concurrencyEnabledValue);
environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionsWorkerProcessCountSettingName, processCountValue);
Assert.Equal(expected, environment.IsWorkerDynamicConcurrencyEnabled());
@@ -476,7 +484,7 @@ public void IsWorkerDynamicConcurrencyEnabled_ReturnsExpectedResult(string concu
[InlineData("node", "", "node")]
public void GetLanguageWorkerListToStartInPlaceholder_ReturnsExpectedResult(string workerRuntime, string workerRuntimeList, string expected)
{
- var environment = new TestEnvironment();
+ TestEnvironment environment = new();
environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, workerRuntime);
environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerPlaceholderModeListSettingName, workerRuntimeList);
var resultSet = environment.GetLanguageWorkerListToStartInPlaceholder();
@@ -496,7 +504,7 @@ public void GetLanguageWorkerListToStartInPlaceholder_ReturnsExpectedResult(stri
[InlineData("test", "test", true)]
public void AzureFilesAppSettingsExist_ReturnsExpectedResult(string connectionString, string contentShare, bool expected)
{
- var environment = new TestEnvironment();
+ TestEnvironment environment = new();
environment.SetEnvironmentVariable(AzureFilesConnectionString, connectionString);
environment.SetEnvironmentVariable(AzureFilesContentShare, contentShare);
Assert.Equal(expected, environment.AzureFilesAppSettingsExist());
@@ -512,7 +520,7 @@ public void AzureFilesAppSettingsExist_ReturnsExpectedResult(string connectionSt
[InlineData("node", false)]
public void IsInProc_ReturnsExpectedResult(string value, bool expected)
{
- var environment = new TestEnvironment();
+ TestEnvironment environment = new();
if (value != null)
{
environment.SetEnvironmentVariable(FunctionWorkerRuntime, value);
@@ -530,7 +538,7 @@ public void IsInProc_ReturnsExpectedResult(string value, bool expected)
[InlineData("node", false)]
public void IsInProc_WithRuntimeParameter_ReturnsExpectedResult(string value, bool expected)
{
- var environment = new TestEnvironment();
+ TestEnvironment environment = new();
Assert.Equal(expected, environment.IsInProc(value));
}
diff --git a/test/WebJobs.Script.Tests/FunctionMetadataProviderTests.cs b/test/WebJobs.Script.Tests/FunctionMetadataProviderTests.cs
index 277de03c16..eca068cd5f 100644
--- a/test/WebJobs.Script.Tests/FunctionMetadataProviderTests.cs
+++ b/test/WebJobs.Script.Tests/FunctionMetadataProviderTests.cs
@@ -1,11 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
-using System;
using System.Collections.Generic;
using System.Collections.Immutable;
-using System.IO;
-using System.Linq;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Script.Config;
using Microsoft.Azure.WebJobs.Script.Description;
@@ -31,95 +28,97 @@ public FunctionMetadataProviderTests()
}
[Fact]
- public void GetFunctionMetadataAsync_WorkerIndexing_HostFallback()
+ public async Task GetFunctionMetadataAsync_WorkerIndexing_HostFallback()
{
// Arrange
_logger.ClearLogMessages();
+ ImmutableArray functionMetadataCollection = GetTestFunctionMetadata();
+ IList workerConfigs = TestHelpers.GetTestWorkerConfigs();
+ foreach (RpcWorkerConfig config in workerConfigs)
+ {
+ config.Description.WorkerIndexing = "true";
+ }
- var function = GetTestRawFunctionMetadata(useDefaultMetadataIndexing: true);
- IEnumerable rawFunctionMetadataCollection = new List() { function };
- var functionMetadataCollection = new List();
- functionMetadataCollection.Add(GetTestFunctionMetadata());
-
- var workerConfigs = TestHelpers.GetTestWorkerConfigs().ToImmutableArray();
- workerConfigs.ToList().ForEach(config => config.Description.WorkerIndexing = "true");
- var scriptjobhostoptions = new ScriptJobHostOptions();
- scriptjobhostoptions.RootScriptPath = Path.Combine(Environment.CurrentDirectory, @"..", "..", "..", "..", "sample", "node");
-
- var environment = SystemEnvironment.Instance;
+ TestEnvironment environment = new();
environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime, "node");
environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, "EnableWorkerIndexing");
- var defaultProvider = new FunctionMetadataProvider(_logger, _workerFunctionMetadataProvider.Object, _hostFunctionMetadataProvider.Object, new OptionsWrapper(new FunctionsHostingConfigOptions()), SystemEnvironment.Instance);
+ FunctionMetadataProvider defaultProvider = new(
+ _logger,
+ _workerFunctionMetadataProvider.Object,
+ _hostFunctionMetadataProvider.Object,
+ new OptionsWrapper(new FunctionsHostingConfigOptions()),
+ environment);
- FunctionMetadataResult result = new FunctionMetadataResult(true, functionMetadataCollection.ToImmutableArray());
- _workerFunctionMetadataProvider.Setup(m => m.GetFunctionMetadataAsync(workerConfigs, false)).Returns(Task.FromResult(result));
- _hostFunctionMetadataProvider.Setup(m => m.GetFunctionMetadataAsync(workerConfigs, false)).Returns(Task.FromResult(functionMetadataCollection.ToImmutableArray()));
+ FunctionMetadataResult result = new(true, functionMetadataCollection);
+ _workerFunctionMetadataProvider.Setup(m => m.GetFunctionMetadataAsync(workerConfigs, false))
+ .ReturnsAsync(result);
+ _hostFunctionMetadataProvider.Setup(m => m.GetFunctionMetadataAsync(workerConfigs, false))
+ .ReturnsAsync(functionMetadataCollection);
// Act
- var functions = defaultProvider.GetFunctionMetadataAsync(workerConfigs, false).GetAwaiter().GetResult();
+ ImmutableArray functions = await defaultProvider
+ .GetFunctionMetadataAsync(workerConfigs, false);
// Assert
Assert.Equal(1, functions.Length);
- var traces = _logger.GetLogMessages();
- var functionLoadLogs = traces.Where(m => string.Equals(m.FormattedMessage, "Fallback to host indexing as worker denied indexing"));
- Assert.True(functionLoadLogs.Any());
+ Assert.Contains(
+ _logger.GetLogMessages(),
+ m => string.Equals(m.FormattedMessage, "Fallback to host indexing as worker denied indexing"));
}
[Fact]
- public void GetFunctionMetadataAsync_HostIndexing()
+ public async Task GetFunctionMetadataAsync_HostIndexing()
{
// Arrange
_logger.ClearLogMessages();
+ ImmutableArray functionMetadataCollection = GetTestFunctionMetadata();
+ IList workerConfigs = TestHelpers.GetTestWorkerConfigs();
+ foreach (RpcWorkerConfig config in workerConfigs)
+ {
+ config.Description.WorkerIndexing = "true";
+ }
- var function = GetTestRawFunctionMetadata(useDefaultMetadataIndexing: true);
- IEnumerable rawFunctionMetadataCollection = new List() { function };
- var functionMetadataCollection = new List();
- functionMetadataCollection.Add(GetTestFunctionMetadata());
-
- var workerConfigs = TestHelpers.GetTestWorkerConfigs().ToImmutableArray();
- workerConfigs.ToList().ForEach(config => config.Description.WorkerIndexing = "true");
- var scriptjobhostoptions = new ScriptJobHostOptions();
- scriptjobhostoptions.RootScriptPath = Path.Combine(Environment.CurrentDirectory, @"..", "..", "..", "..", "sample", "node");
-
- var environment = SystemEnvironment.Instance;
+ TestEnvironment environment = new();
environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime, "node");
environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, string.Empty);
- var optionsMonitor = TestHelpers.CreateOptionsMonitor(new FunctionsHostingConfigOptions());
- var workerMetadataProvider = new Mock();
- workerMetadataProvider.Setup(m => m.GetFunctionMetadataAsync(It.IsAny>(), false)).Returns(Task.FromResult(new FunctionMetadataResult(true, ImmutableArray.Empty)));
+ Mock workerMetadataProvider = new();
+ workerMetadataProvider.Setup(m => m.GetFunctionMetadataAsync(It.IsAny>(), false))
+ .ReturnsAsync(new FunctionMetadataResult(true, []));
- var defaultProvider = new FunctionMetadataProvider(_logger, workerMetadataProvider.Object, _hostFunctionMetadataProvider.Object, new OptionsWrapper(new FunctionsHostingConfigOptions()), SystemEnvironment.Instance);
+ FunctionMetadataProvider defaultProvider = new(
+ _logger,
+ workerMetadataProvider.Object,
+ _hostFunctionMetadataProvider.Object,
+ new OptionsWrapper(new FunctionsHostingConfigOptions()),
+ environment);
- FunctionMetadataResult result = new FunctionMetadataResult(true, functionMetadataCollection.ToImmutableArray());
- _hostFunctionMetadataProvider.Setup(m => m.GetFunctionMetadataAsync(workerConfigs, false)).Returns(Task.FromResult(functionMetadataCollection.ToImmutableArray()));
+ FunctionMetadataResult result = new(true, functionMetadataCollection);
+ _hostFunctionMetadataProvider.Setup(m => m.GetFunctionMetadataAsync(workerConfigs, false))
+ .ReturnsAsync(functionMetadataCollection);
// Act
- var functions = defaultProvider.GetFunctionMetadataAsync(workerConfigs, false).GetAwaiter().GetResult();
+ ImmutableArray functions = await defaultProvider
+ .GetFunctionMetadataAsync(workerConfigs, false);
// Assert
Assert.Equal(1, functions.Length);
- var traces = _logger.GetLogMessages();
- var functionLoadLogs = traces.Where(m => string.Equals(m.FormattedMessage, "Fallback to host indexing as worker denied indexing"));
- Assert.True(functionLoadLogs.Any());
+ Assert.Contains(
+ _logger.GetLogMessages(),
+ m => string.Equals(m.FormattedMessage, "Fallback to host indexing as worker denied indexing"));
}
- private static RawFunctionMetadata GetTestRawFunctionMetadata(bool useDefaultMetadataIndexing)
+ private static ImmutableArray GetTestFunctionMetadata(string name = "testFunction")
{
- return new RawFunctionMetadata()
- {
- UseDefaultMetadataIndexing = useDefaultMetadataIndexing
- };
- }
-
- private static FunctionMetadata GetTestFunctionMetadata(string name = "testFunction")
- {
- return new FunctionMetadata()
- {
- Name = name,
- Language = "node"
- };
+ return
+ [
+ new FunctionMetadata()
+ {
+ Name = name,
+ Language = "node"
+ }
+ ];
}
}
}
\ No newline at end of file
diff --git a/test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs b/test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs
index f43eef9209..4bf3e53d2a 100644
--- a/test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs
+++ b/test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs
@@ -28,39 +28,43 @@
namespace Microsoft.Azure.WebJobs.Script.Tests
{
- public class ScriptStartupTypeDiscovererTests
+ public sealed class ScriptStartupTypeDiscovererTests : IDisposable
{
+ private readonly TempDirectory _directory = new();
+ private readonly TestMetricsLogger _metricsLogger = new();
+ private readonly TestLoggerProvider _loggerProvider = new();
+ private readonly TestEnvironment _environment = new();
+ private readonly Mock _bundleManager = new();
+ private readonly Mock _metadataManager = new();
+
+ public ScriptStartupTypeDiscovererTests()
+ {
+ SetupMetadataManager(null);
+ }
+
+ public void Dispose()
+ {
+ _directory.Dispose();
+ }
+
[Fact]
public async Task GetExtensionsStartupTypes_UsesDefaultMinVersion()
{
- using (var directory = GetTempDirectory())
- {
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var binPath = Path.Combine(directory.Path, "bin");
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails("2.1.0")));
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var exception = await Assert.ThrowsAsync(async () => await discoverer.GetExtensionsStartupTypesAsync());
- var traces = testLoggerProvider.GetAllLogMessages();
-
- // Assert
- Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Referenced bundle Microsoft.Azure.Functions.ExtensionBundle of version 2.1.0 does not meet the required minimum version of 2.6.1. Update your extension bundle reference in host.json to reference 2.6.1 or later.")));
- }
+ InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
+ var binPath = Path.Combine(_directory.Path, "bin");
+
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ _bundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails("2.1.0"));
+
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var exception = await Assert.ThrowsAsync(discoverer.GetExtensionsStartupTypesAsync);
+ var traces = _loggerProvider.GetAllLogMessages();
+
+ // Assert
+ Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Referenced bundle Microsoft.Azure.Functions.ExtensionBundle of version 2.1.0 does not meet the required minimum version of 2.6.1. Update your extension bundle reference in host.json to reference 2.6.1 or later.")));
}
[Theory]
@@ -68,46 +72,32 @@ public async Task GetExtensionsStartupTypes_UsesDefaultMinVersion()
[InlineData("2.6.1", "2.1.0")]
public async Task GetExtensionsStartupTypes_RejectsBundleConfiguredviaHostingEnvConfig(string expectedBundleVersion, string actualBundleVersion)
{
- using (var directory = GetTempDirectory())
- {
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var binPath = Path.Combine(directory.Path, "bin");
+ var binPath = Path.Combine(_directory.Path, "bin");
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails(actualBundleVersion)));
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ _bundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails(actualBundleVersion));
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
-
- var extensionRequirementOptions = new Config.ExtensionRequirementOptions();
- extensionRequirementOptions.Bundles = new List()
- {
- new BundleRequirement()
+ ExtensionRequirementOptions extensionRequirementOptions = new()
+ {
+ Bundles =
+ [
+ new()
{
Id = "Microsoft.Azure.Functions.ExtensionBundle",
MinimumVersion = expectedBundleVersion
}
- };
-
- OptionsWrapper optionsWrapper = new(extensionRequirementOptions);
-
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
+ ]
+ };
- // Act
- var exception = await Assert.ThrowsAsync(async () => await discoverer.GetExtensionsStartupTypesAsync());
- var traces = testLoggerProvider.GetAllLogMessages();
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest(extensionRequirements: extensionRequirementOptions);
+ var exception = await Assert.ThrowsAsync(async () => await discoverer.GetExtensionsStartupTypesAsync());
+ var traces = _loggerProvider.GetAllLogMessages();
- // Assert
- Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Referenced bundle Microsoft.Azure.Functions.ExtensionBundle of version {actualBundleVersion} does not meet the required minimum version of {expectedBundleVersion}. Update your extension bundle reference in host.json to reference {expectedBundleVersion} or later.")));
- }
+ // Assert
+ Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Referenced bundle Microsoft.Azure.Functions.ExtensionBundle of version {actualBundleVersion} does not meet the required minimum version of {expectedBundleVersion}. Update your extension bundle reference in host.json to reference {expectedBundleVersion} or later.")));
}
[Theory]
@@ -118,72 +108,38 @@ public async Task GetExtensionsStartupTypes_RejectsBundleConfiguredviaHostingEnv
public async Task GetExtensionsStartupTypes_AcceptsRequiredBundleVersions(string minBundleVersion, string actualBundleVersion, string minExtensionVersion)
{
// "TypeName": , Microsoft.Azure.WebJobs.Extensions.Storage, Version=4.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
- using (var directory = GetTempDirectory())
- {
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var binPath = Path.Combine(directory.Path, "bin");
-
- var mockExtensionBundleManager = new Mock();
-
- if (string.IsNullOrEmpty(actualBundleVersion))
- {
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
- }
- else
- {
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- }
-
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails(actualBundleVersion)));
+ string binPath = InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
+ if (string.IsNullOrEmpty(actualBundleVersion))
+ {
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
+ AssertNoErrors(_loggerProvider.GetAllLogMessages());
+ }
+ else
+ {
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ }
- var extensionRequirementOptions = new Config.ExtensionRequirementOptions();
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ _bundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails(actualBundleVersion));
- IEnumerable bundleRequirment = string.IsNullOrEmpty(minBundleVersion)
- ? null
- : new List() { new BundleRequirement() { Id = "Microsoft.Azure.Functions.ExtensionBundle", MinimumVersion = minBundleVersion } };
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest(extensionRequirements: GetExtensionRequirementOptions(minBundleVersion, minExtensionVersion));
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
+ var traces = _loggerProvider.GetAllLogMessages();
- IEnumerable extensionRequirements = string.IsNullOrEmpty(minExtensionVersion)
- ? null
- : new List()
- {
- new ExtensionStartupTypeRequirement()
- {
- Name = "AzureStorageWebJobsStartup",
- AssemblyName = "Microsoft.Azure.WebJobs.Extensions.Storage",
- MinimumAssemblyVersion = minExtensionVersion
- }
- };
-
- extensionRequirementOptions.Bundles = bundleRequirment;
- extensionRequirementOptions.Extensions = extensionRequirements;
-
- OptionsWrapper optionsWrapper = new(extensionRequirementOptions);
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
- var types = await discoverer.GetExtensionsStartupTypesAsync();
- // Act
- var traces = testLoggerProvider.GetAllLogMessages();
-
- if (string.IsNullOrEmpty(actualBundleVersion))
- {
- Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Extension Bundle not loaded")));
- }
- else
- {
- Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading extension bundle")));
- }
- Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading startup extension 'Storage")));
- AssertNoErrors(traces);
+ // Assert
+ if (string.IsNullOrEmpty(actualBundleVersion))
+ {
+ Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Extension Bundle not loaded")));
+ }
+ else
+ {
+ Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading extension bundle")));
}
+
+ Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading startup extension 'Storage")));
}
[Theory]
@@ -193,65 +149,29 @@ public async Task GetExtensionsStartupTypes_AcceptsRequiredBundleVersions(string
public async Task GetExtensionsStartupTypes_RejectsRequiredBundleVersions(string minBundleVersion, string actualBundleVersion, string minExtensionVersion)
{
// "TypeName": , Microsoft.Azure.WebJobs.Extensions.Storage, Version=4.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
- using (var directory = GetTempDirectory())
+ string binPath = InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
+ if (string.IsNullOrEmpty(actualBundleVersion))
{
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var binPath = Path.Combine(directory.Path, "bin");
-
- var mockExtensionBundleManager = new Mock();
-
- if (string.IsNullOrEmpty(actualBundleVersion))
- {
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
- }
- else
- {
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- }
-
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails(actualBundleVersion)));
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
+ }
+ else
+ {
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ }
- var extensionRequirementOptions = new Config.ExtensionRequirementOptions();
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ _bundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails(actualBundleVersion));
- IEnumerable bundleRequirment = string.IsNullOrEmpty(minBundleVersion)
- ? null
- : new List() { new BundleRequirement() { Id = "Microsoft.Azure.Functions.ExtensionBundle", MinimumVersion = minBundleVersion } };
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest(extensionRequirements: GetExtensionRequirementOptions(minBundleVersion, minExtensionVersion));
+ var exception = await Assert.ThrowsAsync(discoverer.GetExtensionsStartupTypesAsync);
+ var traces = _loggerProvider.GetAllLogMessages();
- IEnumerable extensionRequirements = string.IsNullOrEmpty(minExtensionVersion)
- ? null
- : new List()
- {
- new ExtensionStartupTypeRequirement()
- {
- Name = "AzureStorageWebJobsStartup",
- AssemblyName = "Microsoft.Azure.WebJobs.Extensions.Storage",
- MinimumAssemblyVersion = minExtensionVersion
- }
- };
-
- extensionRequirementOptions.Bundles = bundleRequirment;
- extensionRequirementOptions.Extensions = extensionRequirements;
-
- OptionsWrapper optionsWrapper = new(extensionRequirementOptions);
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
- var exception = await Assert.ThrowsAsync(async () => await discoverer.GetExtensionsStartupTypesAsync());
- var traces = testLoggerProvider.GetAllLogMessages();
-
- if (!string.IsNullOrEmpty(minBundleVersion))
- {
- // Assert
- Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Referenced bundle Microsoft.Azure.Functions.ExtensionBundle of version 4.9.0 does not meet the required minimum version of 4.12.0. Update your extension bundle reference in host.json to reference 4.12.0 or later.")));
- }
+ // Assert
+ if (!string.IsNullOrEmpty(minBundleVersion))
+ {
+ Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Referenced bundle Microsoft.Azure.Functions.ExtensionBundle of version 4.9.0 does not meet the required minimum version of 4.12.0. Update your extension bundle reference in host.json to reference 4.12.0 or later.")));
}
}
@@ -263,54 +183,23 @@ public async Task GetExtensionsStartupTypes_RejectsRequiredBundleVersions(string
public async Task GetExtensionsStartupTypes_AcceptsRequiredExtensionVersions(string minBundleVersion, bool extensionConfigured, string minExtensionVersion)
{
// "TypeName": , Microsoft.Azure.WebJobs.Extensions.Storage, Version=4.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
- using (var directory = GetTempDirectory(extensionConfigured))
+ if (extensionConfigured)
{
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var binPath = Path.Combine(directory.Path, "bin");
-
- var mockExtensionBundleManager = new Mock();
-
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
+ InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
+ }
- var extensionRequirementOptions = new Config.ExtensionRequirementOptions();
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
- IEnumerable bundleRequirment = string.IsNullOrEmpty(minBundleVersion)
- ? null
- : new List() { new BundleRequirement() { Id = "Microsoft.Azure.Functions.ExtensionBundle", MinimumVersion = minBundleVersion } };
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest(extensionRequirements: GetExtensionRequirementOptions(minBundleVersion, minExtensionVersion));
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
+ var traces = _loggerProvider.GetAllLogMessages();
- IEnumerable extensionRequirements = string.IsNullOrEmpty(minExtensionVersion)
- ? null
- : new List()
- {
- new ExtensionStartupTypeRequirement()
- {
- Name = "AzureStorageWebJobsStartup",
- AssemblyName = "Microsoft.Azure.WebJobs.Extensions.Storage",
- MinimumAssemblyVersion = minExtensionVersion
- }
- };
-
- extensionRequirementOptions.Bundles = bundleRequirment;
- extensionRequirementOptions.Extensions = extensionRequirements;
-
- OptionsWrapper optionsWrapper = new(extensionRequirementOptions);
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
- var types = await discoverer.GetExtensionsStartupTypesAsync();
- // Act
- var traces = testLoggerProvider.GetAllLogMessages();
- Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Extension Bundle not loaded")));
- if (extensionConfigured)
- {
- Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading startup extension 'Storage")));
- }
+ // Assert
+ Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Extension Bundle not loaded")));
+ if (extensionConfigured)
+ {
+ Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading startup extension 'Storage")));
AssertNoErrors(traces);
}
}
@@ -320,235 +209,95 @@ public async Task GetExtensionsStartupTypes_AcceptsRequiredExtensionVersions(str
[InlineData("4.12.0", "4.0.6")]
public async Task GetExtensionsStartupTypes_RejectsRequiredExtensionVersions(string minBundleVersion, string minExtensionVersion)
{
- using (var directory = GetTempDirectory())
- {
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var binPath = Path.Combine(directory.Path, "bin");
+ string binPath = InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
- var mockExtensionBundleManager = new Mock();
-
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
-
- var extensionRequirementOptions = new Config.ExtensionRequirementOptions();
-
- IEnumerable bundleRequirment = string.IsNullOrEmpty(minBundleVersion)
- ? null
- : new List() { new BundleRequirement() { Id = "Microsoft.Azure.Functions.ExtensionBundle", MinimumVersion = minBundleVersion } };
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest(extensionRequirements: GetExtensionRequirementOptions(minBundleVersion, minExtensionVersion));
+ var exception = await Assert.ThrowsAsync(discoverer.GetExtensionsStartupTypesAsync);
- IEnumerable extensionRequirements = string.IsNullOrEmpty(minExtensionVersion)
- ? null
- : new List()
- {
- new ExtensionStartupTypeRequirement()
- {
- Name = "AzureStorageWebJobsStartup",
- AssemblyName = "Microsoft.Azure.WebJobs.Extensions.Storage",
- MinimumAssemblyVersion = minExtensionVersion
- }
- };
-
- extensionRequirementOptions.Bundles = bundleRequirment;
- extensionRequirementOptions.Extensions = extensionRequirements;
-
- OptionsWrapper optionsWrapper = new(extensionRequirementOptions);
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
- var exception = await Assert.ThrowsAsync(async () => await discoverer.GetExtensionsStartupTypesAsync());
-
- // Act
- var traces = testLoggerProvider.GetAllLogMessages();
- Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Extension Bundle not loaded")));
- Assert.True(traces.Any(m => m.FormattedMessage.Contains($"ExtensionStartupType AzureStorageWebJobsStartup from assembly 'Microsoft.Azure.WebJobs.Extensions.Storage, Version=4.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' does not meet the required minimum version of 4.0.6")));
- }
+ // Assert
+ var traces = _loggerProvider.GetAllLogMessages();
+ Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Extension Bundle not loaded")));
+ Assert.True(traces.Any(m => m.FormattedMessage.Contains($"ExtensionStartupType AzureStorageWebJobsStartup from assembly 'Microsoft.Azure.WebJobs.Extensions.Storage, Version=4.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' does not meet the required minimum version of 4.0.6")));
}
[Fact]
public async Task GetExtensionsStartupTypes_FiltersBuiltinExtensionsAsync()
{
- var references = new[]
- {
- new ExtensionReference { Name = "Http", TypeName = typeof(HttpWebJobsStartup).AssemblyQualifiedName },
- new ExtensionReference { Name = "Timers", TypeName = typeof(ExtensionsWebJobsStartup).AssemblyQualifiedName },
- new ExtensionReference { Name = "Storage", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName },
- };
-
- var extensions = new JObject
- {
- { "extensions", JArray.FromObject(references) }
- };
-
- var mockExtensionBundleManager = new Mock();
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
-
- using (var directory = new TempDirectory())
- {
- var binPath = Path.Combine(directory.Path, "bin");
- Directory.CreateDirectory(binPath);
+ InstallExtensions(ExtensionInstall.Http(), ExtensionInstall.Timers(), ExtensionInstall.Storage());
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
- void CopyToBin(string path)
- {
- File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
- }
-
- CopyToBin(typeof(HttpWebJobsStartup).Assembly.Location);
- CopyToBin(typeof(ExtensionsWebJobsStartup).Assembly.Location);
- CopyToBin(typeof(AzureStorageWebJobsStartup).Assembly.Location);
-
- File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensions.ToString());
-
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
-
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions, ImmutableArray.Empty);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
- var traces = testLoggerProvider.GetAllLogMessages();
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
+ var traces = _loggerProvider.GetAllLogMessages();
- // Assert
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Single(types);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
- Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[0].TypeName}' belongs to a builtin extension")));
- Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[1].TypeName}' belongs to a builtin extension")));
- AssertNoErrors(traces);
- }
+ // Assert
+ AreExpectedMetricsGenerated();
+ Assert.Single(types);
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
+ Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{typeof(HttpWebJobsStartup).AssemblyQualifiedName}' belongs to a builtin extension")));
+ Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{typeof(ExtensionsWebJobsStartup).AssemblyQualifiedName}' belongs to a builtin extension")));
+ AssertNoErrors(traces);
}
[Fact]
public async Task GetExtensionsStartupTypes_ExtensionBundleReturnsNullPath_ReturnsNull()
{
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundlePath()).ReturnsAsync(string.Empty);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails()));
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundlePath()).ReturnsAsync(string.Empty);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails());
- using (var directory = new TempDirectory())
- {
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions, ImmutableArray.Empty);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
- var traces = testLoggerProvider.GetAllLogMessages();
-
- // Assert
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.NotNull(types);
- Assert.Equal(types.Count(), 0);
- Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Unable to find or download extension bundle")));
- }
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
+ var traces = _loggerProvider.GetAllLogMessages();
+
+ // Assert
+ AreExpectedMetricsGenerated();
+ Assert.NotNull(types);
+ Assert.Equal(types.Count(), 0);
+ Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Unable to find or download extension bundle")));
}
[Fact]
public async Task GetExtensionsStartupTypes_ValidExtensionBundle_FiltersBuiltinExtensionsAsync()
{
- var references = new[]
- {
- new ExtensionReference { Name = "Http", TypeName = typeof(HttpWebJobsStartup).AssemblyQualifiedName },
- new ExtensionReference { Name = "Timers", TypeName = typeof(ExtensionsWebJobsStartup).AssemblyQualifiedName },
- new ExtensionReference { Name = "Storage", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName },
- };
-
- var extensions = new JObject
- {
- { "extensions", JArray.FromObject(references) }
- };
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails()));
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
-
- using (var directory = new TempDirectory())
- {
- var binPath = Path.Combine(directory.Path, "bin");
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- Directory.CreateDirectory(binPath);
-
- void CopyToBin(string path)
- {
- File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
- }
+ string binPath = InstallExtensions(ExtensionInstall.Http(), ExtensionInstall.Timers(), ExtensionInstall.Storage());
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails());
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
- CopyToBin(typeof(HttpWebJobsStartup).Assembly.Location);
- CopyToBin(typeof(ExtensionsWebJobsStartup).Assembly.Location);
- CopyToBin(typeof(AzureStorageWebJobsStartup).Assembly.Location);
-
- File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensions.ToString());
-
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions, ImmutableArray.Empty);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
- var traces = testLoggerProvider.GetAllLogMessages();
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
+ var traces = _loggerProvider.GetAllLogMessages();
- // Assert
- Assert.Single(types);
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
- Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[0].TypeName}' belongs to a builtin extension")));
- Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[1].TypeName}' belongs to a builtin extension")));
- AssertNoErrors(traces);
- }
+ // Assert
+ Assert.Single(types);
+ AreExpectedMetricsGenerated();
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
+ Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{typeof(HttpWebJobsStartup).AssemblyQualifiedName}' belongs to a builtin extension")));
+ Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{typeof(ExtensionsWebJobsStartup).AssemblyQualifiedName}' belongs to a builtin extension")));
+ AssertNoErrors(traces);
}
[Fact]
public async Task GetExtensionsStartupTypes_UnableToDownloadExtensionBundle_ReturnsNull()
{
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails()));
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundlePath()).ReturnsAsync(string.Empty);
-
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions, ImmutableArray.Empty);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(string.Empty, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails());
+ _bundleManager.Setup(e => e.GetExtensionBundlePath()).ReturnsAsync(string.Empty);
// Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest(string.Empty);
var types = await discoverer.GetExtensionsStartupTypesAsync();
- var traces = testLoggerProvider.GetAllLogMessages();
+ var traces = _loggerProvider.GetAllLogMessages();
// Assert
Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Unable to find or download extension bundle")));
- AreExpectedMetricsGenerated(testMetricsLogger);
+ AreExpectedMetricsGenerated();
Assert.NotNull(types);
Assert.Equal(types.Count(), 0);
}
@@ -556,164 +305,56 @@ public async Task GetExtensionsStartupTypes_UnableToDownloadExtensionBundle_Retu
[Fact]
public async Task GetExtensionsStartupTypes_BundlesConfiguredBindingsNotConfigured_LoadsAllExtensions()
{
- var storageExtensionReference = new ExtensionReference { Name = "Storage", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName };
- var sendGridExtensionReference = new ExtensionReference { Name = "SendGrid", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName };
- var references = new[] { storageExtensionReference, sendGridExtensionReference };
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
+ string binPath = InstallExtensions(ExtensionInstall.Storage(), ExtensionInstall.SendGrid());
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails());
- var extensions = new JObject
- {
- { "extensions", JArray.FromObject(references) }
- };
-
- using (var directory = new TempDirectory())
- {
- var binPath = Path.Combine(directory.Path, "bin");
- Directory.CreateDirectory(binPath);
-
- void CopyToBin(string path)
- {
- File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
- }
-
- CopyToBin(typeof(AzureStorageWebJobsStartup).Assembly.Location);
-
- File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensions.ToString());
-
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails()));
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
- var traces = testLoggerProvider.GetAllLogMessages();
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
- // Assert
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Equal(types.Count(), 2);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.FirstOrDefault().FullName);
- AssertNoErrors(traces);
- }
+ // Assert
+ AreExpectedMetricsGenerated();
+ Assert.Equal(types.Count(), 2);
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.FirstOrDefault().FullName);
+ AssertNoErrors(_loggerProvider.GetAllLogMessages());
}
[Fact]
public async Task GetExtensionsStartupTypes_BundlesNotConfiguredBindingsNotConfigured_LoadsAllExtensions()
{
- var references = new[]
- {
- new ExtensionReference { Name = "Storage", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName }
- };
-
- var extensions = new JObject
- {
- { "extensions", JArray.FromObject(references) }
- };
-
- var mockExtensionBundleManager = new Mock();
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
+ InstallExtensions(ExtensionInstall.Storage());
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
- using (var directory = new TempDirectory())
- {
- var binPath = Path.Combine(directory.Path, "bin");
- Directory.CreateDirectory(binPath);
-
- void CopyToBin(string path)
- {
- File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
- }
-
- CopyToBin(typeof(AzureStorageWebJobsStartup).Assembly.Location);
-
- File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensions.ToString());
-
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- // mock Function metadata
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions, ImmutableArray.Empty);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
- var traces = testLoggerProvider.GetAllLogMessages();
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
- // Assert
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Single(types);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
- AssertNoErrors(traces);
- }
+ // Assert
+ AreExpectedMetricsGenerated();
+ Assert.Single(types);
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
+ AssertNoErrors(_loggerProvider.GetAllLogMessages());
}
[Fact]
public async Task GetExtensionsStartupTypes_BundlesConfiguredBindingsConfigured_PerformsSelectiveLoading()
{
- var storageExtensionReference = new ExtensionReference { Name = "Storage", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName };
- storageExtensionReference.Bindings.Add("blob");
- var sendGridExtensionReference = new ExtensionReference { Name = "SendGrid", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName };
- sendGridExtensionReference.Bindings.Add("sendGrid");
- var references = new[] { storageExtensionReference, sendGridExtensionReference };
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
-
- var extensions = new JObject
- {
- { "extensions", JArray.FromObject(references) }
- };
-
- using (var directory = new TempDirectory())
- {
- var binPath = Path.Combine(directory.Path, "bin");
- Directory.CreateDirectory(binPath);
-
- void CopyToBin(string path)
- {
- File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
- }
-
- CopyToBin(typeof(AzureStorageWebJobsStartup).Assembly.Location);
-
- File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensions.ToString());
+ InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(Path.Combine(_directory.Path, "bin"));
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails());
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails()));
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
- var traces = testLoggerProvider.GetAllLogMessages();
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
- //Assert
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Single(types);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
- AssertNoErrors(traces);
- }
+ //Assert
+ AreExpectedMetricsGenerated();
+ Assert.Single(types);
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
+ AssertNoErrors(_loggerProvider.GetAllLogMessages());
}
[Theory]
@@ -721,83 +362,51 @@ void CopyToBin(string path)
[InlineData(true)]
public async Task GetExtensionsStartupTypes_LegacyBundles_UsesExtensionBundleBinaries(bool hasPrecompiledFunctions)
{
- using (var directory = GetTempDirectory())
+ string binPath = InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ _bundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails());
+
+ if (hasPrecompiledFunctions)
{
- var binPath = Path.Combine(directory.Path, "bin");
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- var testLogger = GetTestLogger();
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails()));
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions, hasPrecompiledFunction: hasPrecompiledFunctions);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
-
- //Assert
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Single(types);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
+ SetupMetadataManager(DotNetScriptTypes.DotNetAssembly);
}
+
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
+
+ //Assert
+ AreExpectedMetricsGenerated();
+ Assert.Single(types);
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
}
[Fact]
public async Task GetExtensionsStartupTypes_WorkerRuntimeNotSetForNodeApp_LoadsExtensionBundle()
{
- var vars = new Dictionary();
-
- using (var directory = GetTempDirectory())
- using (var env = new TestScopedEnvironmentVariable(vars))
- {
- var binPath = Path.Combine(directory.Path, "bin");
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails()));
-
- RpcWorkerConfig nodeWorkerConfig = new RpcWorkerConfig() { Description = TestHelpers.GetTestWorkerDescription("node", "none", false) };
- RpcWorkerConfig dotnetIsolatedWorkerConfig = new RpcWorkerConfig() { Description = TestHelpers.GetTestWorkerDescription("dotnet-isolated", "none", false) };
-
- var tempOptions = new LanguageWorkerOptions();
- tempOptions.WorkerConfigs = new List();
- tempOptions.WorkerConfigs.Add(nodeWorkerConfig);
- tempOptions.WorkerConfigs.Add(dotnetIsolatedWorkerConfig);
+ string binPath = InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ _bundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails());
+ SetupMetadataManager(RpcWorkerConstants.NodeLanguageWorkerName);
- var optionsMonitor = new TestOptionsMonitor(tempOptions);
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(optionsMonitor, hasNodeFunctions: true);
-
- var languageWorkerOptions = new TestOptionsMonitor(tempOptions);
-
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
- //Assert
- var traces = testLoggerProvider.GetAllLogMessages();
- var traceMessage = traces.FirstOrDefault(val => string.Equals(val.EventId.Name, "ScriptStartNotLoadingExtensionBundle"));
- bool loadingExtensionBundle = traceMessage == null;
+ //Assert
+ var traces = _loggerProvider.GetAllLogMessages();
+ var traceMessage = traces.FirstOrDefault(val => val.EventId.Name.Equals("ScriptStartNotLoadingExtensionBundle"));
+ bool loadingExtensionBundle = traceMessage == null;
- Assert.True(loadingExtensionBundle);
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Single(types);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
- }
+ Assert.True(loadingExtensionBundle);
+ AreExpectedMetricsGenerated();
+ Assert.Single(types);
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
+ AssertNoErrors(_loggerProvider.GetAllLogMessages());
}
[Theory(Skip = "This test is failing on CI and needs to be fixed.")]
@@ -807,73 +416,44 @@ public async Task GetExtensionsStartupTypes_WorkerRuntimeNotSetForNodeApp_LoadsE
[InlineData(false, false)]
public async Task GetExtensionsStartupTypes_DotnetIsolated_ExtensionBundleConfigured(bool isLogicApp, bool workerRuntimeSet)
{
- var vars = new Dictionary();
-
if (isLogicApp)
{
- vars.Add(EnvironmentSettingNames.AppKind, ScriptConstants.WorkFlowAppKind);
+ _environment.SetEnvironmentVariable(EnvironmentSettingNames.AppKind, ScriptConstants.WorkFlowAppKind);
}
if (workerRuntimeSet)
{
- vars.Add(EnvironmentSettingNames.FunctionWorkerRuntime, "dotnet-isolated");
+ _environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime, "dotnet-isolated");
}
- using (var directory = GetTempDirectory())
- using (var env = new TestScopedEnvironmentVariable(vars))
- {
- var binPath = Path.Combine(directory.Path, "bin");
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails()));
-
- RpcWorkerConfig workerConfig = new RpcWorkerConfig() { Description = TestHelpers.GetTestWorkerDescription("dotnet-isolated", "none", true) };
- var tempOptions = new LanguageWorkerOptions();
- tempOptions.WorkerConfigs = new List();
- tempOptions.WorkerConfigs.Add(workerConfig);
-
- RpcWorkerConfig nodeWorkerConfig = new RpcWorkerConfig() { Description = TestHelpers.GetTestWorkerDescription("node", "none", false) };
- tempOptions.WorkerConfigs.Add(nodeWorkerConfig);
-
- var optionsMonitor = new TestOptionsMonitor(tempOptions);
-
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(optionsMonitor, hasDotnetIsolatedFunctions: true);
-
- var languageWorkerOptions = new TestOptionsMonitor(tempOptions);
-
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
+ var binPath = Path.Combine(_directory.Path, "bin");
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ _bundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails());
+ SetupMetadataManager(RpcWorkerConstants.DotNetIsolatedLanguageWorkerName);
- //Assert
- var traces = testLoggerProvider.GetAllLogMessages();
- var traceMessage = traces.FirstOrDefault(val => val.EventId.Name.Equals("ScriptStartNotLoadingExtensionBundle"));
-
- bool loadingExtensionBundle = traceMessage == null;
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
- if (isLogicApp)
- {
- Assert.True(loadingExtensionBundle);
- }
- else
- {
- Assert.False(loadingExtensionBundle);
- }
+ //Assert
+ var traces = _loggerProvider.GetAllLogMessages();
+ var traceMessage = traces.FirstOrDefault(val => val.EventId.Name.Equals("ScriptStartNotLoadingExtensionBundle"));
+ bool loadingExtensionBundle = traceMessage == null;
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Single(types);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
+ if (isLogicApp)
+ {
+ Assert.True(loadingExtensionBundle);
+ }
+ else
+ {
+ Assert.False(loadingExtensionBundle);
}
+
+ AreExpectedMetricsGenerated();
+ Assert.Single(types);
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
}
[Theory]
@@ -881,141 +461,62 @@ public async Task GetExtensionsStartupTypes_DotnetIsolated_ExtensionBundleConfig
[InlineData(true)]
public async Task GetExtensionsStartupTypes_NonLegacyBundles_UsesBundlesForNonPrecompiledFunctions(bool hasPrecompiledFunctions)
{
- using (var directory = GetTempDirectory())
- {
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- var testLogger = GetTestLogger();
-
- string bundlePath = hasPrecompiledFunctions ? "FakePath" : directory.Path;
- var binPath = Path.Combine(directory.Path, "bin");
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails()));
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions, hasPrecompiledFunction: hasPrecompiledFunctions);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
-
- //Assert
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Single(types);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
- }
+ InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
+ string bundlePath = hasPrecompiledFunctions ? "FakePath" : _directory.Path;
+ var binPath = Path.Combine(_directory.Path, "bin");
+
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ _bundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails());
+ SetupMetadataManager(DotNetScriptTypes.DotNetAssembly);
+
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
+
+ //Assert
+ AreExpectedMetricsGenerated();
+ Assert.Single(types);
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
}
[Fact]
public async Task GetExtensionsStartupTypes_BundlesNotConfiguredBindingsConfigured_LoadsAllExtensions()
{
- var storageExtensionReference = new ExtensionReference { Name = "Storage", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName };
- storageExtensionReference.Bindings.Add("blob");
- var sendGridExtensionReference = new ExtensionReference { Name = "SendGrid", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName };
- sendGridExtensionReference.Bindings.Add("sendGrid");
- var references = new[] { storageExtensionReference, sendGridExtensionReference };
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
-
- var extensions = new JObject
- {
- { "extensions", JArray.FromObject(references) }
- };
-
- using (var directory = new TempDirectory())
- {
- var binPath = Path.Combine(directory.Path, "bin");
- Directory.CreateDirectory(binPath);
-
- void CopyToBin(string path)
- {
- File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
- }
-
- CopyToBin(typeof(AzureStorageWebJobsStartup).Assembly.Location);
-
- File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensions.ToString());
+ InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
- // Assert
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Equal(types.Count(), 2);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.FirstOrDefault().FullName);
- }
+ // Assert
+ AreExpectedMetricsGenerated();
+ Assert.Equal(types.Count(), 2);
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.FirstOrDefault().FullName);
}
[Fact]
public async Task GetExtensionsStartupTypes_NoBindings_In_ExtensionJson()
{
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
-
- using var directory = new TempDirectory();
- var binPath = Path.Combine(directory.Path, "bin");
- Directory.CreateDirectory(binPath);
-
- void CopyToBin(string path)
+ ExtensionInstall storage1 = new("AzureStorageBlobs", typeof(AzureStorageWebJobsStartup));
+ ExtensionInstall storage2 = new("Storage", typeof(AzureStorageWebJobsStartup))
{
- File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
- }
+ HintPath = "Microsoft.Azure.WebJobs.Extensions.Storage.dll"
+ };
- CopyToBin(typeof(AzureStorageWebJobsStartup).Assembly.Location);
-
- string extensionJson = $$"""
- {
- "extensions": [
- {
- "name": "Storage",
- "typeName": "{{typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName}}",
- "hintPath": "Microsoft.Azure.WebJobs.Extensions.Storage.dll"
- },
- {
- "Name": "AzureStorageBlobs",
- "TypeName": "{{typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName}}"
- }
- ]
- }
- """;
-
- File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensionJson);
-
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(new ExtensionBundleDetails() { Id = "bundleID", Version = "1.0.0" }));
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
+ string binPath = InstallExtensions(storage1, storage2);
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(new ExtensionBundleDetails() { Id = "bundleID", Version = "1.0.0" });
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
// Act
var types = await discoverer.GetExtensionsStartupTypesAsync();
// Assert
- AreExpectedMetricsGenerated(testMetricsLogger);
+ AreExpectedMetricsGenerated();
Assert.Equal(types.Count(), 2);
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.FirstOrDefault().FullName);
}
@@ -1023,273 +524,214 @@ void CopyToBin(string path)
[Fact]
public async Task GetExtensionsStartupTypes_RejectsBundleBelowMinimumVersion()
{
- using (var directory = GetTempDirectory())
- {
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var binPath = Path.Combine(directory.Path, "bin");
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
- mockExtensionBundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetBundleDetails("2.1.0")));
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var exception = await Assert.ThrowsAsync(async () => await discoverer.GetExtensionsStartupTypesAsync());
- var traces = testLoggerProvider.GetAllLogMessages();
-
- // Assert
- Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Referenced bundle Microsoft.Azure.Functions.ExtensionBundle of version 2.1.0 does not meet the required minimum version of 2.6.1. Update your extension bundle reference in host.json to reference 2.6.1 or later.")));
- }
+ var binPath = Path.Combine(_directory.Path, "bin");
+
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+ _bundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false);
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(GetBundleDetails("2.1.0"));
+
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var exception = await Assert.ThrowsAsync(discoverer.GetExtensionsStartupTypesAsync);
+ var traces = _loggerProvider.GetAllLogMessages();
+
+ // Assert
+ Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Referenced bundle Microsoft.Azure.Functions.ExtensionBundle of version 2.1.0 does not meet the required minimum version of 2.6.1. Update your extension bundle reference in host.json to reference 2.6.1 or later.")));
}
[Fact]
public async Task GetExtensionsStartupTypes_RejectsExtensionsBelowMinimumVersion()
{
- var mockExtensionBundleManager = new Mock();
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(false);
- using (var directory = new TempDirectory())
- {
- var binPath = Path.Combine(directory.Path, "bin");
- Directory.CreateDirectory(binPath);
+ var binPath = Path.Combine(_directory.Path, "bin");
+ Directory.CreateDirectory(binPath);
- void CopyToBin(string path)
- {
- File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
- }
+ void CopyToBin(string path)
+ {
+ File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
+ }
- // create a bin folder that has out of date extensions
- var extensionBinPath = Path.Combine(Environment.CurrentDirectory, @"TestScripts\OutOfDateExtension\bin");
- foreach (var f in Directory.GetFiles(extensionBinPath))
- {
- CopyToBin(f);
- }
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions, ImmutableArray.Empty);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var exception = await Assert.ThrowsAsync(async () => await discoverer.GetExtensionsStartupTypesAsync());
- var traces = testLoggerProvider.GetAllLogMessages();
-
- // Assert
-
- var storageTrace = traces.FirstOrDefault(m => m.FormattedMessage.StartsWith("ExtensionStartupType AzureStorageWebJobsStartup"));
- Assert.NotNull(storageTrace);
- Assert.Equal("ExtensionStartupType AzureStorageWebJobsStartup from assembly 'Microsoft.Azure.WebJobs.Extensions.Storage, Version=3.0.10.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' does not meet the required minimum version of 4.0.4.0. Update your NuGet package reference for Microsoft.Azure.WebJobs.Extensions.Storage to 4.0.4 or later.",
- storageTrace.FormattedMessage);
+ // create a bin folder that has out of date extensions
+ var extensionBinPath = Path.Combine(Environment.CurrentDirectory, @"TestScripts\OutOfDateExtension\bin");
+ foreach (var f in Directory.GetFiles(extensionBinPath))
+ {
+ CopyToBin(f);
}
+
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var exception = await Assert.ThrowsAsync(discoverer.GetExtensionsStartupTypesAsync);
+ var traces = _loggerProvider.GetAllLogMessages();
+
+ // Assert
+ var storageTrace = traces.FirstOrDefault(m => m.FormattedMessage.StartsWith("ExtensionStartupType AzureStorageWebJobsStartup"));
+ Assert.NotNull(storageTrace);
+ Assert.Equal("ExtensionStartupType AzureStorageWebJobsStartup from assembly 'Microsoft.Azure.WebJobs.Extensions.Storage, Version=3.0.10.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' does not meet the required minimum version of 4.0.4.0. Update your NuGet package reference for Microsoft.Azure.WebJobs.Extensions.Storage to 4.0.4 or later.",
+ storageTrace.FormattedMessage);
}
[Fact]
public async Task GetExtensionsStartupTypes_WorkerIndexing_PerformsSelectiveLoading()
{
- var storageExtensionReference = new ExtensionReference { Name = "Storage", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName };
- storageExtensionReference.Bindings.Add("blob");
- var sendGridExtensionReference = new ExtensionReference { Name = "SendGrid", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName };
- sendGridExtensionReference.Bindings.Add("sendGrid");
- var references = new[] { storageExtensionReference, sendGridExtensionReference };
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
-
- var extensions = new JObject
+ string binPath = InstallExtensions(ExtensionInstall.Storage(true), ExtensionInstall.SendGrid(true));
+ _bundleManager.Setup(e => e.GetExtensionBundleDetails()).ReturnsAsync(new ExtensionBundleDetails() { Id = "bundleID", Version = "1.0.0" });
+ _bundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
+ _bundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).ReturnsAsync(binPath);
+
+ _environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, ScriptConstants.FeatureFlagEnableWorkerIndexing);
+ _environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime, "python");
+
+ // Act
+ ScriptStartupTypeLocator discoverer = CreateSystemUnderTest();
+ var types = await discoverer.GetExtensionsStartupTypesAsync();
+
+ //Assert that filtering did not take place because of worker indexing
+ Assert.True(types.Count() == 1);
+ Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.ElementAt(0).FullName);
+ AssertNoErrors(_loggerProvider.GetAllLogMessages());
+ }
+
+ private static ExtensionBundleDetails GetBundleDetails(string version = "2.7.0")
+ {
+ return new ExtensionBundleDetails
{
- { "extensions", JArray.FromObject(references) }
+ Id = "Microsoft.Azure.Functions.ExtensionBundle",
+ Version = version
};
+ }
- using (var directory = new TempDirectory())
- {
- var binPath = Path.Combine(directory.Path, "bin");
- Directory.CreateDirectory(binPath);
+ private static void AssertNoErrors(IList traces)
+ {
+ Assert.False(traces.Any(m => m.Level == LogLevel.Error || m.Level == LogLevel.Critical));
+ }
- void CopyToBin(string path)
- {
- File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
- }
+ private static ExtensionRequirementOptions GetExtensionRequirementOptions(string minBundleVersion, string minExtensionVersion)
+ {
+ ExtensionRequirementOptions extensionRequirementOptions = new();
+ IEnumerable bundleRequirment = string.IsNullOrEmpty(minBundleVersion)
+ ? null
+ : [new() { Id = "Microsoft.Azure.Functions.ExtensionBundle", MinimumVersion = minBundleVersion }];
+
+ IEnumerable extensionRequirements = string.IsNullOrEmpty(minExtensionVersion)
+ ? null :
+ [
+ new()
+ {
+ Name = "AzureStorageWebJobsStartup",
+ AssemblyName = "Microsoft.Azure.WebJobs.Extensions.Storage",
+ MinimumAssemblyVersion = minExtensionVersion
+ }
+ ];
- CopyToBin(typeof(AzureStorageWebJobsStartup).Assembly.Location);
-
- File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensions.ToString());
-
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
-
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(new ExtensionBundleDetails() { Id = "bundleID", Version = "1.0.0" }));
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
-
- // mock worker config and environment variables to make host choose worker indexing
- RpcWorkerConfig workerConfig = new RpcWorkerConfig() { Description = TestHelpers.GetTestWorkerDescription("python", "none", true) };
- var tempOptions = new LanguageWorkerOptions();
- tempOptions.WorkerConfigs = new List();
- tempOptions.WorkerConfigs.Add(workerConfig);
- var optionsMonitor = new TestOptionsMonitor(tempOptions);
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(optionsMonitor);
-
- var languageWorkerOptions = new TestOptionsMonitor(tempOptions);
- Environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, ScriptConstants.FeatureFlagEnableWorkerIndexing);
- Environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime, "python");
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
-
- // Act
- var types = await discoverer.GetExtensionsStartupTypesAsync();
- var traces = testLoggerProvider.GetAllLogMessages();
- Environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, null);
- Environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime, null);
-
- //Assert that filtering did not take place because of worker indexing
- Assert.True(types.Count() == 1);
- Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.ElementAt(0).FullName);
- AssertNoErrors(traces);
- }
+ extensionRequirementOptions.Bundles = bundleRequirment;
+ extensionRequirementOptions.Extensions = extensionRequirements;
+ return extensionRequirementOptions;
}
- [Fact]
- public async Task GetExtensionsStartupTypes_EmptyExtensionsArray()
+ private string InstallExtensions(params ExtensionInstall[] extensions)
{
- TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
-
- using var directory = new TempDirectory();
- var binPath = Path.Combine(directory.Path, "bin");
+ string binPath = Path.Combine(_directory.Path, "bin");
Directory.CreateDirectory(binPath);
- // extensions.json file with an empty extensions array (simulating extensions.json produced by in-proc app)
- string extensionJson = """
+ JArray jArray = [];
+ foreach (ExtensionInstall e in extensions)
{
- "extensions": []
+ ExtensionReference reference = e.GetReference();
+ jArray.Add(JObject.FromObject(reference));
+ e.CopyTo(binPath);
}
- """;
- File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensionJson);
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
+ JObject jObject = new()
+ {
+ { "extensions", jArray },
+ };
- var mockExtensionBundleManager = new Mock();
- mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(new ExtensionBundleDetails() { Id = "bundleID", Version = "1.0.0" }));
- mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
+ File.WriteAllText(Path.Combine(binPath, "extensions.json"), jObject.ToString());
+ return binPath;
+ }
- var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions());
- var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
- OptionsWrapper optionsWrapper = new(new ExtensionRequirementOptions());
- var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
+ private void SetupMetadataManager(string language)
+ {
+ FunctionMetadata functionMetadata = new();
+ functionMetadata.Bindings.Add(new BindingMetadata() { Type = "blob" });
+ functionMetadata.Language = language;
+ ImmutableArray result = [functionMetadata];
+ _metadataManager.Setup(m => m.GetFunctionMetadata(true, true, false)).Returns(result);
+ }
- var types = await discoverer.GetExtensionsStartupTypesAsync();
- var traces = testLoggerProvider.GetAllLogMessages();
+ private ScriptStartupTypeLocator CreateSystemUnderTest(string rootPath = null, ExtensionRequirementOptions extensionRequirements = null)
+ {
+ LoggerFactory factory = new();
+ factory.AddProvider(_loggerProvider);
+ OptionsWrapper optionsWrapper = new(extensionRequirements ?? new());
+ return new(
+ rootPath ?? _directory.Path,
+ factory.CreateLogger(),
+ _bundleManager.Object,
+ _metadataManager.Object,
+ _metricsLogger,
+ _environment,
+ optionsWrapper);
+ }
- AreExpectedMetricsGenerated(testMetricsLogger);
- Assert.Empty(types); // Ensure no types are loaded because the extensions array is empty
- AssertNoErrors(traces);
+ private bool AreExpectedMetricsGenerated()
+ {
+ return _metricsLogger.EventsBegan.Contains(MetricEventNames.ParseExtensions) && _metricsLogger.EventsEnded.Contains(MetricEventNames.ParseExtensions);
}
- private IFunctionMetadataManager GetTestFunctionMetadataManager(IOptionsMonitor options, ICollection metadataCollection = null, bool hasPrecompiledFunction = false, bool hasNodeFunctions = false, bool hasDotnetIsolatedFunctions = false)
+ private class ExtensionInstall(string name, Type startupType, params string[] bindings)
{
- var functionMetadata = new FunctionMetadata();
- functionMetadata.Bindings.Add(new BindingMetadata() { Type = "blob" });
+ public string HintPath { get; init; }
- if (hasPrecompiledFunction)
+ public static ExtensionInstall Storage(bool includeBinding = false)
{
- functionMetadata.Language = DotNetScriptTypes.DotNetAssembly;
+ string[] bindings = includeBinding ? ["blob"] : [];
+ return new("Storage", typeof(AzureStorageWebJobsStartup), bindings);
}
- if (hasNodeFunctions)
+
+ public static ExtensionInstall SendGrid(bool includeBinding = false)
{
- functionMetadata.Language = RpcWorkerConstants.NodeLanguageWorkerName;
+ string[] bindings = includeBinding ? ["sendGrid"] : [];
+ return new("SendGrid", typeof(AzureStorageWebJobsStartup), bindings);
}
- if (hasDotnetIsolatedFunctions)
+ public static ExtensionInstall Timers()
{
- functionMetadata.Language = RpcWorkerConstants.DotNetIsolatedLanguageWorkerName;
+ return new("Timers", typeof(ExtensionsWebJobsStartup));
}
- var functionMetadataCollection = metadataCollection ?? new List() { functionMetadata };
-
- var functionMetadataManager = new Mock();
- functionMetadataManager.Setup(e => e.GetFunctionMetadata(true, true, false)).Returns(functionMetadataCollection.ToImmutableArray());
- return functionMetadataManager.Object;
- }
-
- private bool AreExpectedMetricsGenerated(TestMetricsLogger metricsLogger)
- {
- return metricsLogger.EventsBegan.Contains(MetricEventNames.ParseExtensions) && metricsLogger.EventsEnded.Contains(MetricEventNames.ParseExtensions);
- }
-
- private TempDirectory GetTempDirectory(bool copyExtensionsToBin = true)
- {
- var directory = new TempDirectory();
-
- if (copyExtensionsToBin)
+ public static ExtensionInstall Http()
{
- var storageExtensionReference = new ExtensionReference { Name = "Storage", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName };
- storageExtensionReference.Bindings.Add("blob");
- var sendGridExtensionReference = new ExtensionReference { Name = "SendGrid", TypeName = typeof(AzureStorageWebJobsStartup).AssemblyQualifiedName };
- sendGridExtensionReference.Bindings.Add("sendGrid");
- var references = new[] { storageExtensionReference, sendGridExtensionReference };
+ return new("Http", typeof(HttpWebJobsStartup));
+ }
- var extensions = new JObject
+ public ExtensionReference GetReference()
+ {
+ ExtensionReference reference = new()
{
- { "extensions", JArray.FromObject(references) }
+ Name = name,
+ TypeName = startupType.AssemblyQualifiedName,
+ HintPath = HintPath,
};
-
- var binPath = Path.Combine(directory.Path, "bin");
- Directory.CreateDirectory(binPath);
-
- void CopyToBin(string path)
+ foreach (string binding in bindings ?? Enumerable.Empty())
{
- File.Copy(path, Path.Combine(binPath, Path.GetFileName(path)));
+ reference.Bindings.Add(binding);
}
- CopyToBin(typeof(AzureStorageWebJobsStartup).Assembly.Location);
-
- File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensions.ToString());
+ return reference;
}
- return directory;
- }
-
- private ExtensionBundleDetails GetBundleDetails(string version = "2.7.0")
- {
- return new ExtensionBundleDetails
+ public void CopyTo(string path)
{
- Id = "Microsoft.Azure.Functions.ExtensionBundle",
- Version = version
- };
- }
-
- private ILogger GetTestLogger()
- {
- TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
- LoggerFactory factory = new LoggerFactory();
- factory.AddProvider(testLoggerProvider);
- var testLogger = factory.CreateLogger();
- return testLogger;
- }
-
- private static void AssertNoErrors(IList traces)
- {
- Assert.False(traces.Any(m => m.Level == LogLevel.Error || m.Level == LogLevel.Critical));
+ string file = startupType.Assembly.Location;
+ string destination = Path.Combine(path, Path.GetFileName(file));
+ if (!File.Exists(destination))
+ {
+ File.Copy(file, destination);
+ }
+ }
}
}
}
diff --git a/test/WebJobs.Script.Tests/WorkerFunctionMetadataProviderTests.cs b/test/WebJobs.Script.Tests/WorkerFunctionMetadataProviderTests.cs
index 7fc0737a8e..86fa090147 100644
--- a/test/WebJobs.Script.Tests/WorkerFunctionMetadataProviderTests.cs
+++ b/test/WebJobs.Script.Tests/WorkerFunctionMetadataProviderTests.cs
@@ -193,11 +193,11 @@ public async void ValidateFunctionMetadata_Logging()
{
});
- var environment = SystemEnvironment.Instance;
+ TestEnvironment environment = new();
environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime, "node");
- var workerFunctionMetadataProvider = new WorkerFunctionMetadataProvider(optionsMonitor, logger, SystemEnvironment.Instance,
- mockWebHostRpcWorkerChannelManager.Object, mockScriptHostManager.Object);
+ var workerFunctionMetadataProvider = new WorkerFunctionMetadataProvider(
+ optionsMonitor, logger, environment, mockWebHostRpcWorkerChannelManager.Object, mockScriptHostManager.Object);
await workerFunctionMetadataProvider.GetFunctionMetadataAsync(workerConfigs, false);
var traces = logger.GetLogMessages();
diff --git a/test/WebJobs.Script.Tests/Workers/Http/HttpWorkerChannelTests.cs b/test/WebJobs.Script.Tests/Workers/Http/HttpWorkerChannelTests.cs
index 4429e589fc..d102ba42b2 100644
--- a/test/WebJobs.Script.Tests/Workers/Http/HttpWorkerChannelTests.cs
+++ b/test/WebJobs.Script.Tests/Workers/Http/HttpWorkerChannelTests.cs
@@ -35,7 +35,7 @@ public async Task TestStartWorkerProcess(bool isWorkerReady)
Mock httpWorkerService = new Mock();
httpWorkerService.Setup(a => a.IsWorkerReady(It.IsAny())).Returns(Task.FromResult(isWorkerReady));
- workerProcess.Setup(a => a.StartProcessAsync()).Returns(Task.CompletedTask);
+ workerProcess.Setup(a => a.StartProcessAsync(default)).Returns(Task.CompletedTask);
TestLogger logger = new TestLogger("HttpWorkerChannel");
IHttpWorkerChannel testWorkerChannel = new HttpWorkerChannel("RandomWorkerId", _eventManager, workerProcess.Object, httpWorkerService.Object, logger, _metricsLogger, 3);
Task resultTask = null;
@@ -61,7 +61,7 @@ public async Task TestStartWorkerProcess_WorkerServiceThrowsException()
IMetricsLogger metricsLogger = new TestMetricsLogger();
httpWorkerService.Setup(a => a.IsWorkerReady(It.IsAny())).Throws(new Exception("RandomException"));
- workerProcess.Setup(a => a.StartProcessAsync()).Returns(Task.CompletedTask);
+ workerProcess.Setup(a => a.StartProcessAsync(default)).Returns(Task.CompletedTask);
TestLogger logger = new TestLogger("HttpWorkerChannel");
IHttpWorkerChannel testWorkerChannel = new HttpWorkerChannel("RandomWorkerId", _eventManager, workerProcess.Object, httpWorkerService.Object, logger, _metricsLogger, 3);
Task resultTask = null;
@@ -85,7 +85,7 @@ public async Task TestStartWorkerProcess_WorkerProcessThrowsException()
IMetricsLogger metricsLogger = new TestMetricsLogger();
httpWorkerService.Setup(a => a.IsWorkerReady(It.IsAny())).Throws(new Exception("RandomException"));
- workerProcess.Setup(a => a.StartProcessAsync()).Throws(new Exception("RandomException"));
+ workerProcess.Setup(a => a.StartProcessAsync(default)).Throws(new Exception("RandomException"));
TestLogger logger = new TestLogger("HttpWorkerChannel");
IHttpWorkerChannel testWorkerChannel = new HttpWorkerChannel("RandomWorkerId", _eventManager, workerProcess.Object, httpWorkerService.Object, logger, _metricsLogger, 3);
Task resultTask = null;
diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs
index 767b9e232d..8a9a7422fe 100644
--- a/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs
+++ b/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs
@@ -37,9 +37,9 @@ public class GrpcWorkerChannelTests : IDisposable
private static string _expectedSystemLogMessage = "Random system log message";
private static string _expectedLoadMsgPartial = "Sending FunctionLoadRequest for ";
- private readonly Mock _mockrpcWorkerProcess = new Mock();
+ private readonly Mock _mockRpcWorkerProcess = new Mock();
private readonly string _workerId = "testWorkerId";
- private readonly string _scriptRootPath = "c:\testdir";
+ private readonly string _scriptRootPath = "c:\\testdir";
private readonly IScriptEventManager _eventManager = new ScriptEventManager();
private readonly Mock _mockScriptHostManager = new Mock(MockBehavior.Strict);
private readonly TestMetricsLogger _metricsLogger = new TestMetricsLogger();
@@ -55,7 +55,6 @@ public class GrpcWorkerChannelTests : IDisposable
private readonly IOptionsMonitor _hostOptionsMonitor;
private readonly IMemoryMappedFileAccessor _mapAccessor;
private readonly ISharedMemoryManager _sharedMemoryManager;
- private readonly IFunctionDataCache _functionDataCache;
private readonly IOptions