public async Task Compilation_WithMissingBindingArguments_LogsAF004Warning(IDictionary <string, string> environment) { using (var testEnvironment = new TestScopedEnvironmentVariable(environment)) { // Create the compilation exception we expect to throw during the reload string rootFunctionsFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(rootFunctionsFolder); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(); // Create a dummy file to represent our function string filePath = Path.Combine(rootFunctionsFolder, Guid.NewGuid().ToString() + ".csx"); File.WriteAllText(filePath, Resources.TestFunctionWithMissingBindingArgumentsCode); var metadata = new FunctionMetadata { ScriptFile = filePath, FunctionDirectory = Path.GetDirectoryName(filePath), Name = Guid.NewGuid().ToString(), ScriptType = ScriptType.CSharp }; metadata.Bindings.Add(new BindingMetadata() { Name = "myQueueItem", Type = "ManualTrigger" }); var testBinding = new Mock <FunctionBinding>(null, new BindingMetadata() { Name = "TestBinding", Type = "blob" }, FileAccess.Write); var invoker = new DotNetFunctionInvoker(dependencies.Host.Object, metadata, new Collection <FunctionBinding>(), new Collection <FunctionBinding> { testBinding.Object }, new FunctionEntryPointResolver(), new DotNetCompilationServiceFactory(null)); try { await invoker.GetFunctionTargetAsync(); } catch (CompilationErrorException exc) { var builder = new StringBuilder(); builder.AppendLine(Resources.TestFunctionWithMissingBindingArgumentsCode); builder.AppendLine(); string compilationDetails = exc.Diagnostics.Aggregate( builder, (a, d) => a.AppendLine(d.ToString()), a => a.ToString()); throw new Exception(compilationDetails, exc); } Assert.Contains(dependencies.LoggerProvider.GetAllLogMessages(), t => t.FormattedMessage.Contains($"warning {DotNetConstants.MissingBindingArgumentCompilationCode}") && t.FormattedMessage.Contains("'TestBinding'")); } }
public void ValidateFunctionBindingArguments_ReturnBinding_Succeeds() { Collection <FunctionParameter> parameters = new Collection <FunctionParameter>() { new FunctionParameter("input", "String", false, RefKind.None) }; FunctionSignature signature = new FunctionSignature("Test", "Test", ImmutableArray.CreateRange <FunctionParameter>(parameters), "Test", false); Collection <FunctionBinding> inputBindings = new Collection <FunctionBinding>() { TestHelpers.CreateTestBinding(new JObject { { "type", "blobTrigger" }, { "name", "input" }, { "direction", "in" }, { "path", "test" } }) }; Collection <FunctionBinding> outputBindings = new Collection <FunctionBinding>() { TestHelpers.CreateTestBinding(new JObject { { "type", "blob" }, { "name", ScriptConstants.SystemReturnParameterBindingName }, { "direction", "out" }, { "path", "test/test" } }) }; var diagnostics = DotNetFunctionInvoker.ValidateFunctionBindingArguments(signature, "input", inputBindings, outputBindings); Assert.Equal(0, diagnostics.Count()); }
public async Task ReloadScript_WithInvalidCompilationAndMissingMethod_ReportsResults() { // Create the compilation exception we expect to throw during the reload var descriptor = new DiagnosticDescriptor(DotNetConstants.MissingFunctionEntryPointCompilationCode, "Test compilation exception", "Test compilation error", "AzureFunctions", DiagnosticSeverity.Error, true); var exception = new CompilationErrorException("Test compilation exception", ImmutableArray.Create(Diagnostic.Create(descriptor, Location.None))); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(); dependencies.Compilation.Setup(c => c.GetEntryPointSignature(It.IsAny <IFunctionEntryPointResolver>())) .Throws(exception); dependencies.Compilation.Setup(c => c.Emit(It.IsAny <CancellationToken>())) .Returns(typeof(object).Assembly); string rootFunctionsFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(rootFunctionsFolder); // Create a dummy file to represent our function string filePath = Path.Combine(rootFunctionsFolder, Guid.NewGuid().ToString() + ".csx"); File.WriteAllText(filePath, string.Empty); var metadata = new FunctionMetadata { ScriptFile = filePath, Name = Guid.NewGuid().ToString(), ScriptType = ScriptType.CSharp }; metadata.Bindings.Add(new BindingMetadata() { Name = "Test", Type = "ManualTrigger" }); var invoker = new DotNetFunctionInvoker(dependencies.Host.Object, metadata, new Collection <Script.Binding.FunctionBinding>(), new Collection <FunctionBinding>(), dependencies.EntrypointResolver.Object, new FunctionAssemblyLoader(string.Empty), dependencies.CompilationServiceFactory.Object, dependencies.TraceWriterFactory.Object); // Update the file to trigger a reload File.WriteAllText(filePath, string.Empty); await TestHelpers.Await(() => { return(dependencies.TraceWriter.Traces.Any(t => t.Message.Contains("Compilation failed.")) && dependencies.TraceWriter.Traces.Any(t => t.Message.Contains(DotNetConstants.MissingFunctionEntryPointCompilationCode))); }); dependencies.TraceWriter.Traces.Clear(); CompilationErrorException resultException = await Assert.ThrowsAsync <CompilationErrorException>(() => invoker.GetFunctionTargetAsync()); await TestHelpers.Await(() => { return(dependencies.TraceWriter.Traces.Any(t => t.Message.Contains("Function compilation error")) && dependencies.TraceWriter.Traces.Any(t => t.Message.Contains(DotNetConstants.MissingFunctionEntryPointCompilationCode))); }); }
internal static async Task <object> Unwrap(object result) { // unwrap the task if (result is Task) { result = await((Task)result).ContinueWith(t => DotNetFunctionInvoker.GetTaskResult(t), TaskContinuationOptions.ExecuteSynchronously); } return(result); }
public async Task RestorePackagesAsync_WithUpdatedReferences_TriggersShutdown(bool initialInstall, bool referencesChanged, bool shutdownExpected) { using (var tempDirectory = new TempDirectory()) { var environmentMock = new Mock <IScriptJobHostEnvironment>(); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(s => { s.AddSingleton <IScriptJobHostEnvironment>(environmentMock.Object); }); // Create a dummy file to represent our function string filePath = Path.Combine(tempDirectory.Path, Guid.NewGuid().ToString() + ".csx"); File.WriteAllText(filePath, Resources.TestFunctionWithMissingBindingArgumentsCode); var metadata = new FunctionMetadata { ScriptFile = filePath, Name = Guid.NewGuid().ToString(), Language = DotNetScriptTypes.CSharp, }; metadata.Bindings.Add(new BindingMetadata { Type = "TestTrigger", Direction = BindingDirection.In }); var metadataResolver = new Mock <IFunctionMetadataResolver>(); metadataResolver.Setup(r => r.RestorePackagesAsync()) .ReturnsAsync(new PackageRestoreResult { IsInitialInstall = initialInstall, ReferencesChanged = referencesChanged }); var testBinding = new Mock <FunctionBinding>(null, new BindingMetadata() { Name = "TestBinding", Type = "blob" }, FileAccess.Write); var invoker = new DotNetFunctionInvoker(dependencies.Host, metadata, new Collection <FunctionBinding>(), new Collection <FunctionBinding> { testBinding.Object }, new FunctionEntryPointResolver(), new DotNetCompilationServiceFactory(null), dependencies.LoggerFactory, dependencies.MetricsLogger, new Collection <IScriptBindingProvider>(), metadataResolver.Object); await invoker.RestorePackagesAsync(true); // Delay the check as the shutdown call is debounced // and won't be made immediately await Task.Delay(1000); environmentMock.Verify(e => e.Shutdown(), Times.Exactly(shutdownExpected ? 1 : 0)); } }
public async Task Compilation_WithMissingBindingArguments_LogsAF004Warning() { // Create the compilation exception we expect to throw during the reload string rootFunctionsFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(rootFunctionsFolder); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(); // Create a dummy file to represent our function string filePath = Path.Combine(rootFunctionsFolder, Guid.NewGuid().ToString() + ".csx"); File.WriteAllText(filePath, Resources.TestFunctionWithMissingBindingArgumentsCode); var metadata = new FunctionMetadata { ScriptFile = filePath, Name = Guid.NewGuid().ToString(), ScriptType = ScriptType.CSharp }; metadata.Bindings.Add(new BindingMetadata() { Name = "myQueueItem", Type = "ManualTrigger" }); var testBinding = new Mock <FunctionBinding>(null, new BindingMetadata() { Name = "TestBinding", Type = "blob" }, FileAccess.Write); var invoker = new DotNetFunctionInvoker(dependencies.Host.Object, metadata, new Collection <FunctionBinding>(), new Collection <FunctionBinding> { testBinding.Object }, new FunctionEntryPointResolver(), new FunctionAssemblyLoader(string.Empty), new DotNetCompilationServiceFactory()); await invoker.GetFunctionTargetAsync(); // Verify that our expected messages were logged, including the compilation result await TestHelpers.Await(() => { Collection <string> logs = TestHelpers.GetFunctionLogsAsync(metadata.Name, false).Result; if (logs != null) { //Check that our warning diagnostic was logged (e.g. "warning AF004: Missing binding argument named 'TestBinding'."); return(logs.Any(s => s.Contains($"warning {DotNetConstants.MissingBindingArgumentCompilationCode}") && s.Contains("'TestBinding'"))); } return(false); }, 10 * 1000); }
public async Task Compilation_OnSecondaryHost_SuppressesLogs() { // Create the compilation exception we expect to throw during the reload string rootFunctionsFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(rootFunctionsFolder); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(); // Set the host to secondary dependencies.Host.SetupGet(h => h.IsPrimary).Returns(false); // Create a dummy file to represent our function string filePath = Path.Combine(rootFunctionsFolder, Guid.NewGuid().ToString() + ".csx"); File.WriteAllText(filePath, Resources.TestFunctionWithMissingBindingArgumentsCode); var metadata = new FunctionMetadata { ScriptFile = filePath, FunctionDirectory = Path.GetDirectoryName(filePath), Name = Guid.NewGuid().ToString(), ScriptType = ScriptType.CSharp }; metadata.Bindings.Add(new BindingMetadata() { Name = "myQueueItem", Type = "ManualTrigger" }); var testBinding = new Mock <FunctionBinding>(null, new BindingMetadata() { Name = "TestBinding", Type = "blob" }, FileAccess.Write); var invoker = new DotNetFunctionInvoker(dependencies.Host.Object, metadata, new Collection <FunctionBinding>(), new Collection <FunctionBinding> { testBinding.Object }, new FunctionEntryPointResolver(), new FunctionAssemblyLoader(string.Empty), new DotNetCompilationServiceFactory(NullTraceWriter.Instance, null)); await invoker.GetFunctionTargetAsync(); // Verify that logs on the second instance were suppressed int count = dependencies.TraceWriter.Traces.Count(); Assert.Equal(0, count); }
public async Task Compilation_WithMissingBindingArguments_LogsAF004Warning() { // Create the compilation exception we expect to throw during the reload string rootFunctionsFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(rootFunctionsFolder); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(); // Create a dummy file to represent our function string filePath = Path.Combine(rootFunctionsFolder, Guid.NewGuid().ToString() + ".csx"); File.WriteAllText(filePath, Resources.TestFunctionWithMissingBindingArgumentsCode); var metadata = new FunctionMetadata { ScriptFile = filePath, FunctionDirectory = Path.GetDirectoryName(filePath), Name = Guid.NewGuid().ToString(), ScriptType = ScriptType.CSharp }; metadata.Bindings.Add(new BindingMetadata() { Name = "myQueueItem", Type = "ManualTrigger" }); var testBinding = new Mock <FunctionBinding>(null, new BindingMetadata() { Name = "TestBinding", Type = "blob" }, FileAccess.Write); var invoker = new DotNetFunctionInvoker(dependencies.Host.Object, metadata, new Collection <FunctionBinding>(), new Collection <FunctionBinding> { testBinding.Object }, new FunctionEntryPointResolver(), new FunctionAssemblyLoader(string.Empty), new DotNetCompilationServiceFactory(NullTraceWriter.Instance, null)); await invoker.GetFunctionTargetAsync(); Assert.Contains(dependencies.TraceWriter.Traces, t => t.Message.Contains($"warning {DotNetConstants.MissingBindingArgumentCompilationCode}") && t.Message.Contains("'TestBinding'")); }
public async Task GetFunctionTargetAsync_CompilationError_ReportsResults() { // Create the compilation exception we expect to throw var descriptor = new DiagnosticDescriptor(DotNetConstants.MissingFunctionEntryPointCompilationCode, "Test compilation exception", "Test compilation error", "AzureFunctions", DiagnosticSeverity.Error, true); var exception = new CompilationErrorException("Test compilation exception", ImmutableArray.Create(Diagnostic.Create(descriptor, Location.None))); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(); dependencies.Compilation.Setup(c => c.GetEntryPointSignature(It.IsAny <IFunctionEntryPointResolver>(), It.IsAny <Assembly>())) .Throws(exception); dependencies.Compilation.Setup(c => c.EmitAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(DotNetCompilationResult.FromPath(typeof(DotNetFunctionInvokerTests).Assembly.Location)); string functionName = Guid.NewGuid().ToString(); string rootFunctionsFolder = Path.Combine(Path.GetTempPath(), functionName); Directory.CreateDirectory(rootFunctionsFolder); // Create a dummy file to represent our function string filePath = Path.Combine(rootFunctionsFolder, Guid.NewGuid().ToString() + ".csx"); File.WriteAllText(filePath, string.Empty); var metadata = new FunctionMetadata { ScriptFile = filePath, FunctionDirectory = Path.GetDirectoryName(filePath), Name = functionName, Language = DotNetScriptTypes.CSharp }; metadata.Bindings.Add(new BindingMetadata() { Name = "Test", Type = "ManualTrigger" }); var invoker = new DotNetFunctionInvoker(dependencies.Host, metadata, new Collection <FunctionBinding>(), new Collection <FunctionBinding>(), dependencies.EntrypointResolver.Object, dependencies.CompilationServiceFactory.Object, dependencies.LoggerFactory, dependencies.MetricsLogger, new Collection <IScriptBindingProvider>()); // Send file change notification to trigger a reload var fileEventArgs = new FileSystemEventArgs(WatcherChangeTypes.Changed, Path.GetTempPath(), Path.Combine(Path.GetFileName(rootFunctionsFolder), Path.GetFileName(filePath))); dependencies.Host.EventManager.Publish(new FileEvent(EventSources.ScriptFiles, fileEventArgs)); LogMessage[] logMessages = null; var loggerProvider = dependencies.LoggerProvider; await TestHelpers.Await(() => { logMessages = loggerProvider.GetAllLogMessages().ToArray(); return(logMessages.Any(t => t.FormattedMessage.Contains("Compilation failed.")) && logMessages.Any(t => t.FormattedMessage.Contains(DotNetConstants.MissingFunctionEntryPointCompilationCode))); }); // verify expected logs when the function target is retrieved // NOT on the invocation path dependencies.LoggerProvider.ClearAllLogMessages(); await Assert.ThrowsAsync <CompilationErrorException>(async() => { await invoker.GetFunctionTargetAsync(); }); Assert.Equal(3, logMessages.Length); Assert.Equal($"Script for function '{functionName}' changed. Reloading.", logMessages[0].FormattedMessage); Assert.Equal(LogLevel.Information, logMessages[0].Level); Assert.Equal("error AF001: Test compilation error", logMessages[1].FormattedMessage); Assert.Equal(LogLevel.Error, logMessages[1].Level); Assert.True(logMessages[1].State.Any(q => q.Key == ScriptConstants.LogPropertyIsUserLogKey)); Assert.Equal("Compilation failed.", logMessages[2].FormattedMessage); Assert.Equal(LogLevel.Information, logMessages[2].Level); Assert.True(logMessages.All(p => p.State.Any(q => q.Key == ScriptConstants.LogPropertyPrimaryHostKey))); await TestHelpers.Await(() => { logMessages = loggerProvider.GetAllLogMessages().ToArray(); return(logMessages.Any(t => t.FormattedMessage.Contains("Function compilation error")) && logMessages.Any(t => t.FormattedMessage.Contains(DotNetConstants.MissingFunctionEntryPointCompilationCode))); }); // now test the invoke path and verify that the compilation error // details are written loggerProvider.ClearAllLogMessages(); await Assert.ThrowsAsync <CompilationErrorException>(async() => { await invoker.GetFunctionTargetAsync(isInvocation: true); }); logMessages = loggerProvider.GetAllLogMessages().ToArray(); Assert.Equal(2, logMessages.Length); Assert.Equal("Function compilation error", logMessages[0].FormattedMessage); Assert.Equal(LogLevel.Error, logMessages[0].Level); Assert.Same("Test compilation exception", logMessages[0].Exception.Message); Assert.Equal("error AF001: Test compilation error", logMessages[1].FormattedMessage); Assert.Equal(LogLevel.Error, logMessages[1].Level); Assert.True(logMessages[1].State.Any(q => q.Key == ScriptConstants.LogPropertyIsUserLogKey)); Assert.True(logMessages.All(p => !(p.State != null && p.State.Any(q => q.Key == ScriptConstants.LogPropertyPrimaryHostKey)))); }
public async Task CompilerError_IsRetried_UpToLimit() { // Set the host to primary var stateProviderMock = new Mock <IPrimaryHostStateProvider>(); stateProviderMock.Setup(m => m.IsPrimary).Returns(false); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(configureServices: s => { s.AddSingleton(stateProviderMock.Object); }); var metadata = new FunctionMetadata { ScriptFile = "run.csx", FunctionDirectory = "c:\\somedir", Name = Guid.NewGuid().ToString(), Language = DotNetScriptTypes.CSharp }; metadata.Bindings.Add(new BindingMetadata() { Name = "myQueueItem", Type = "ManualTrigger" }); var testBinding = new Mock <FunctionBinding>(null, new BindingMetadata() { Name = "TestBinding", Type = "blob" }, FileAccess.Write); var dotNetCompilation = new Mock <IDotNetCompilation>(); var dotnetCompilationService = new Mock <ICompilationService <IDotNetCompilation> >(); dotnetCompilationService .SetupSequence(s => s.GetFunctionCompilationAsync(It.IsAny <FunctionMetadata>())) .ThrowsAsync(new CompilationServiceException("1")) .ThrowsAsync(new CompilationServiceException("2")) .ThrowsAsync(new CompilationServiceException("3")) .ThrowsAsync(new CompilationServiceException("4")); // This should not be reached var compilationFactory = new Mock <ICompilationServiceFactory <ICompilationService <IDotNetCompilation>, IFunctionMetadataResolver> >(); compilationFactory .Setup(f => f.CreateService(DotNetScriptTypes.CSharp, It.IsAny <IFunctionMetadataResolver>())) .Returns(dotnetCompilationService.Object); var invoker = new DotNetFunctionInvoker(dependencies.Host, metadata, new Collection <FunctionBinding>(), new Collection <FunctionBinding> { testBinding.Object }, new FunctionEntryPointResolver(), compilationFactory.Object, dependencies.LoggerFactory, dependencies.MetricsLogger, new Collection <IScriptBindingProvider>(), new Mock <IFunctionMetadataResolver>().Object); var arguments = new object[] { new ExecutionContext() { FunctionDirectory = "c:\\test", FunctionName = "test", InvocationId = Guid.NewGuid() } }; for (int i = 1; i <= 10; i++) { var expectedAttempt = Math.Min(i, 3); CompilationServiceException exception = await Assert.ThrowsAsync <CompilationServiceException>(() => invoker.Invoke(arguments)); Assert.Equal(expectedAttempt.ToString(), exception.Message); } var compilerErrorTraces = dependencies.LoggerProvider.GetAllLogMessages() .Where(t => string.Equals(t.FormattedMessage, "Function loader reset. Failed compilation result will not be cached.")); // 3 attempts total, make sure we've logged the 2 retries. Assert.Equal(2, compilerErrorTraces.Count()); }
public async Task Compilation_OnSecondaryHost_SuppressesLogs(IDictionary <string, string> environment) { using (new TestScopedEnvironmentVariable(environment)) { // Create the compilation exception we expect to throw during the reload string rootFunctionsFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(rootFunctionsFolder); // Set the host to secondary var stateProviderMock = new Mock <IPrimaryHostStateProvider>(); stateProviderMock.Setup(m => m.IsPrimary).Returns(false); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(configureServices: s => { s.AddSingleton(stateProviderMock.Object); }); // Create a dummy file to represent our function string filePath = Path.Combine(rootFunctionsFolder, Guid.NewGuid().ToString() + ".csx"); File.WriteAllText(filePath, Resources.TestFunctionWithMissingBindingArgumentsCode); var metadata = new FunctionMetadata { ScriptFile = filePath, FunctionDirectory = Path.GetDirectoryName(filePath), Name = Guid.NewGuid().ToString(), Language = DotNetScriptTypes.CSharp }; metadata.Bindings.Add(new BindingMetadata() { Name = "myQueueItem", Type = "ManualTrigger" }); var testBinding = new Mock <FunctionBinding>(null, new BindingMetadata() { Name = "TestBinding", Type = "blob" }, FileAccess.Write); var invoker = new DotNetFunctionInvoker(dependencies.Host, metadata, new Collection <FunctionBinding>(), new Collection <FunctionBinding> { testBinding.Object }, new FunctionEntryPointResolver(), new DotNetCompilationServiceFactory(null), dependencies.LoggerFactory, dependencies.MetricsLogger, new Collection <IScriptBindingProvider>()); try { await invoker.GetFunctionTargetAsync(); } catch (CompilationErrorException exc) { var builder = new StringBuilder(); builder.AppendLine(Resources.TestFunctionWithMissingBindingArgumentsCode); builder.AppendLine(); string compilationDetails = exc.Diagnostics.Aggregate( builder, (a, d) => a.AppendLine(d.ToString()), a => a.ToString()); throw new Exception(compilationDetails, exc); } // Verify that we send the log, but that it has MS_PrimaryHost set so the logger can filter appropriately. var logMessage = dependencies.LoggerProvider.GetAllLogMessages().Single(); Assert.True((bool)logMessage.State.Single(k => k.Key == ScriptConstants.LogPropertyPrimaryHostKey).Value); } }
public async Task ReloadScript_WithInvalidCompilationAndMissingMethod_ReportsResults() { // Create the compilation exception we expect to throw during the reload var descriptor = new DiagnosticDescriptor(DotNetConstants.MissingFunctionEntryPointCompilationCode, "Test compilation exception", "Test compilation error", "AzureFunctions", DiagnosticSeverity.Error, true); var exception = new CompilationErrorException("Test compilation exception", ImmutableArray.Create(Diagnostic.Create(descriptor, Location.None))); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(); dependencies.Compilation.Setup(c => c.GetEntryPointSignature(It.IsAny <IFunctionEntryPointResolver>(), It.IsAny <Assembly>())) .Throws(exception); dependencies.Compilation.Setup(c => c.EmitAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(DotNetCompilationResult.FromPath(typeof(DotNetFunctionInvokerTests).Assembly.Location)); string functionName = Guid.NewGuid().ToString(); string rootFunctionsFolder = Path.Combine(Path.GetTempPath(), functionName); Directory.CreateDirectory(rootFunctionsFolder); // Create a dummy file to represent our function string filePath = Path.Combine(rootFunctionsFolder, Guid.NewGuid().ToString() + ".csx"); File.WriteAllText(filePath, string.Empty); var metadata = new FunctionMetadata { ScriptFile = filePath, FunctionDirectory = Path.GetDirectoryName(filePath), Name = functionName, ScriptType = ScriptType.CSharp }; metadata.Bindings.Add(new BindingMetadata() { Name = "Test", Type = "ManualTrigger" }); var invoker = new DotNetFunctionInvoker(dependencies.Host.Object, metadata, new Collection <Script.Binding.FunctionBinding>(), new Collection <FunctionBinding>(), dependencies.EntrypointResolver.Object, dependencies.CompilationServiceFactory.Object); // Send file change notification to trigger a reload var fileEventArgs = new FileSystemEventArgs(WatcherChangeTypes.Changed, Path.GetTempPath(), Path.Combine(Path.GetFileName(rootFunctionsFolder), Path.GetFileName(filePath))); dependencies.Host.Object.EventManager.Publish(new FileEvent(EventSources.ScriptFiles, fileEventArgs)); await TestHelpers.Await(() => { IEnumerable <LogMessage> logMessages = dependencies.LoggerProvider.GetAllLogMessages(); return(logMessages.Any(t => t.FormattedMessage.Contains("Compilation failed.")) && logMessages.Any(t => t.FormattedMessage.Contains(DotNetConstants.MissingFunctionEntryPointCompilationCode))); }); dependencies.LoggerProvider.ClearAllLogMessages(); CompilationErrorException resultException = await Assert.ThrowsAsync <CompilationErrorException>(() => invoker.GetFunctionTargetAsync()); await TestHelpers.Await(() => { IEnumerable <LogMessage> logMessages = dependencies.LoggerProvider.GetAllLogMessages(); return(logMessages.Any(t => t.FormattedMessage.Contains("Function compilation error")) && logMessages.Any(t => t.FormattedMessage.Contains(DotNetConstants.MissingFunctionEntryPointCompilationCode))); }); }